วิธีเพิ่มความเร็วคิวรีในตาราง 220 ล้านแถวขนาดใหญ่ (ข้อมูล 9 กิกะไบต์)


31

ปัญหา:

เรามีเว็บไซต์โซเชียลที่สมาชิกสามารถให้คะแนนซึ่งกันและกันเพื่อความเข้ากันได้หรือการจับคู่ นี้user_match_ratingsตารางที่มีมากกว่า 220 ล้านแถว (9 ข้อมูลกิ๊กหรือเกือบ 20 กิ๊กในดัชนี) ข้อความค้นหาที่อยู่ในตารางนี้แสดงเป็นประจำใน slow.log (threshold> 2 วินาที) และเป็นข้อความค้นหาช้าที่บันทึกบ่อยที่สุดในระบบ

Query_time: 3  Lock_time: 0  Rows_sent: 3  Rows_examined: 1051
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 395357 group by rating;"

Query_time: 4  Lock_time: 0  Rows_sent: 3  Rows_examined: 1294
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 4182969 group by rating;"

Query_time: 3  Lock_time: 0  Rows_sent: 3  Rows_examined: 446
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 630148 group by rating;"

Query_time: 5  Lock_time: 0  Rows_sent: 3  Rows_examined: 3788
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1835698 group by rating;"

Query_time: 17  Lock_time: 0  Rows_sent: 3  Rows_examined: 4311
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1269322 group by rating;"

รุ่น MySQL:

  • รุ่นโปรโตคอล: 10
  • รุ่น: 5.0.77-log
  • version bdb: ซอฟต์แวร์ Sleepycat: Berkeley DB 4.1.24: (29 มกราคม 2009)
  • เครื่องคอมไพล์เวอร์ชั่น: x86_64 version_compile_os: redhat-linux-gnu

ข้อมูลตาราง:

SHOW COLUMNS FROM user_match_ratings;

ให้:

╔═══════════════╦════════════╦════╦═════╦════════╦════════════════╗
 id             int(11)     NO  PRI  NULL    auto_increment 
 rater_user_id  int(11)     NO  MUL  NULL                   
 rated_user_id  int(11)     NO  MUL  NULL                   
 rating         varchar(1)  NO       NULL                   
 created_at     datetime    NO       NULL                   
╚═══════════════╩════════════╩════╩═════╩════════╩════════════════╝

แบบสอบถามตัวอย่าง:

select * from mutual_match_ratings where id=221673540;

ให้:

╔═══════════╦═══════════════╦═══════════════╦════════╦══════════════════════╗
 id         rater_user_id  rated_user_id  rating  created_at           
╠═══════════╬═══════════════╬═══════════════╬════════╬══════════════════════╣
 221673540  5699713        3890950        N       2013-04-09 13:00:38  
╚═══════════╩═══════════════╩═══════════════╩════════╩══════════════════════╝

ดัชนี

ตารางมีการตั้งค่า 3 ดัชนี:

  1. ดัชนีเดี่ยวบน rated_user_id
  2. ดัชนีคอมโพสิตเปิดrater_user_idและcreated_at
  3. ดัชนีคอมโพสิตเปิดrated_user_idและrater_user_id
แสดงดัชนีจาก user_match_ratings;

ให้:

╔════════════════════╦════════════╦═══════════════════════════╦══════════════╦═══════════════╦═══════════╦═════════════╦══════════╦════════╦═════════════════════════╦════════════╦══════════════════╗
 Table               Non_unique  Key_name                   Seq_in_index  Column_name    Collation  Cardinality  Sub_part  Packed  Null                     Index_type  Comment          
╠════════════════════╬════════════╬═══════════════════════════╬══════════════╬═══════════════╬═══════════╬═════════════╬══════════╬════════╬═════════════════════════╬════════════╬══════════════════╣
 user_match_ratings  0           PRIMARY                    1             id             A          220781193    NULL      NULL    BTREE                                                 
 user_match_ratings  1           user_match_ratings_index1  1             rater_user_id  A          11039059     NULL      NULL    BTREE                                                 
 user_match_ratings  1           user_match_ratings_index1  2             created_at     A          220781193    NULL      NULL    BTREE                                                 
 user_match_ratings  1           user_match_ratings_index2  1             rated_user_id  A          4014203      NULL      NULL    BTREE                                                 
 user_match_ratings  1           user_match_ratings_index2  2             rater_user_id  A          220781193    NULL      NULL    BTREE                                                 
 user_match_ratings  1           user_match_ratings_index3  1             rated_user_id  A          2480687      NULL      NULL    BTREE                                                 
╚════════════════════╩════════════╩═══════════════════════════╩══════════════╩═══════════════╩═══════════╩═════════════╩══════════╩════════╩═════════════════════════╩════════════╩══════════════════╝

แม้จะมีดัชนีแล้วแบบสอบถามเหล่านี้ก็ยังช้า

คำถามของฉัน:

จะแยกตาราง / ข้อมูลนี้ไปยังฐานข้อมูลอื่นบนเซิร์ฟเวอร์ที่มีหน่วยความจำเพียงพอที่จะเก็บข้อมูลนี้ไว้ในหน่วยความจำซึ่งจะทำให้การสืบค้นเร็วขึ้นหรือไม่ มีอะไรอีกไหมในการตั้งค่าตาราง / ดัชนีที่เราสามารถปรับปรุงเพื่อให้การสืบค้นเหล่านี้เร็วขึ้น?

ขณะนี้เรามีหน่วยความจำ 16GB; อย่างไรก็ตามเรากำลังพิจารณาที่จะอัพเกรดเครื่องที่มีอยู่เป็น 32GB หรือเพิ่มเครื่องใหม่ด้วยอย่างน้อยก็อาจจะเป็นไดรฟ์ของรัฐที่มั่นคงเช่นกัน


1
คำถามของคุณเหลือเชื่อ ฉันสนใจวิธีแก้ปัญหาปัจจุบันของคุณเป็นอย่างมากว่าวิธีที่คุณจัดการเพื่อให้ได้ผลลัพธ์ใน <= 2 วินาทีหรือไม่ เพราะผมมีตารางหนึ่งซึ่งมีเพียง 20 ล้านระเบียนและยังคงใช้เวลา 30 SELECT QUERYวินาที คุณช่วยแนะนำไหม ป.ล. คำถามของคุณบังคับให้ฉันเข้าร่วมชุมชนนี้ (y);)
NullPointer

2
ดูดัชนีในตารางที่คุณกำลังสอบถาม .. บ่อยครั้งที่การปรับปรุงแบบสอบถามสามารถทำได้โดยการสร้างดัชนีที่เหมาะสม ไม่เสมอไป แต่จะเห็นอินสแตนซ์จำนวนมากที่มีการสืบค้นอย่างรวดเร็วโดยจัดทำดัชนีเทียบกับคอลัมน์ในส่วนคำสั่งในแบบสอบถาม โดยเฉพาะถ้าโต๊ะโตขึ้นเรื่อย ๆ
อันดับที่

Sure @Ranknoodle ขอขอบคุณ. ฉันจะตรวจสอบตามลำดับ
NullPointer

คำตอบ:


28

ความคิดเกี่ยวกับปัญหาโยนในลำดับสุ่ม:

  • (rated_user_id, rating)ดัชนีที่ชัดเจนสำหรับแบบสอบถามนี้: แบบสอบถามที่ได้รับข้อมูลสำหรับผู้ใช้เพียงหนึ่งในล้านคนและความต้องการ 17 วินาทีกำลังทำสิ่งผิดปกติ: การอ่านจาก(rated_user_id, rater_user_id)ดัชนีแล้วอ่านจากตารางค่า (หลายร้อยถึงพัน) สำหรับratingคอลัมน์ตามที่ratingไม่อยู่ในดัชนี ดังนั้นเคียวรีจึงต้องอ่านหลายแถวของตารางซึ่งอยู่ในตำแหน่งดิสก์ที่แตกต่างกัน

  • ก่อนที่จะเริ่มเพิ่มดัชนีจำนวนมากในตารางพยายามวิเคราะห์ประสิทธิภาพของฐานข้อมูลทั้งหมดทั้งชุดของการสืบค้นช้าตรวจสอบตัวเลือกของประเภทข้อมูลเครื่องยนต์ที่คุณใช้และการตั้งค่าการกำหนดค่าอีกครั้ง

  • ลองพิจารณาการย้ายไปใช้ MySQL เวอร์ชันใหม่กว่า 5.1, 5.5 หรือแม้กระทั่ง 5.6 (เช่น: รุ่น Percona และ MariaDB) ข้อดีหลายประการเนื่องจากข้อผิดพลาดได้รับการแก้ไขตัวเพิ่มประสิทธิภาพได้รับการปรับปรุง (เช่น 10 มิลลิวินาที) สิ่งนี้จะให้ข้อมูลที่ดีกว่าเกี่ยวกับการสืบค้นที่ช้า

  • ตัวเลือกสำหรับประเภทข้อมูลของratingนั้นแปลก VARCHAR(1)? ทำไมไม่CHAR(1)? ทำไมไม่TINYINT? สิ่งนี้จะช่วยคุณประหยัดพื้นที่บางส่วนทั้งดีบุกและตารางในดัชนีที่ (จะ) รวมคอลัมน์นั้น คอลัมน์ varchar (1) ต้องการหนึ่งไบต์มากกว่าถ่าน (1) และถ้าเป็น utf8 คอลัมน์ถ่าน (var) จะต้องมี 3 (หรือ 4) ไบต์แทนที่จะเป็น 1 (จิ๋ว)


2
ผลกระทบต่อประสิทธิภาพการทำงานหรือการสิ้นเปลืองพื้นที่เก็บข้อมูลในแง่ของ% หากคุณใช้ประเภทข้อมูลที่ไม่ถูกต้อง
FlyingAtom

1
@FlyingAtom ขึ้นอยู่กับกรณี แต่สำหรับคอลัมน์ที่จัดทำดัชนีบางส่วนที่ยังคงต้องสแกน (เช่นเมื่อคุณไม่มีส่วนคำสั่งที่ไหน แต่คุณเรียกเฉพาะคอลัมน์นั้น) เอ็นจิ้นอาจตัดสินใจสแกนดัชนีแทน ตารางและหากคุณเพิ่มประสิทธิภาพประเภทข้อมูลของคุณให้มีขนาดครึ่งเดียวการสแกนจะเร็วเป็นสองเท่าและการตอบสนองจะมีขนาดครึ่งหนึ่ง หากคุณยังคงสแกนตารางแทนดัชนี (ตัวอย่างเช่นเมื่อคุณดึงคอลัมน์เพิ่มเติมไม่เพียง แต่คอลัมน์ในดัชนี) ผลประโยชน์จะมีความสำคัญน้อยกว่า
Sebastián Grignoli

-1

ฉันจัดการตารางสำหรับรัฐบาลเยอรมันด้วยบางครั้งมีการบันทึกถึง 60 ล้านครั้ง

เรามีโต๊ะนี้เยอะมาก

และเราจำเป็นต้องรู้หลายครั้งรวมแถวจากตาราง

หลังจากพูดคุยกับโปรแกรมเมอร์ของ Oracle และ Microsoft เราไม่มีความสุข ...

ดังนั้นเรากลุ่มโปรแกรมเมอร์ฐานข้อมูลจึงตัดสินใจว่าในทุกตารางจะมีการบันทึกหนึ่งระเบียนเสมอซึ่งมีการจัดเก็บหมายเลขระเบียนทั้งหมด เราอัปเดตหมายเลขนี้ขึ้นอยู่กับแถว INSERT หรือ DELETE

เราลองวิธีอื่นทั้งหมด นี่คือวิธีที่เร็วที่สุด

เราใช้วิธีนี้ตั้งแต่ปี 1998 และไม่เคยมีจำนวนแถวที่ไม่ถูกต้องในตารางบันทึกหลายล้านของเราทั้งหมด


7
ฉันขอแนะนำให้มองหาคุณสมบัติบางอย่างที่เปิดตัวในช่วง 18 ปีที่ผ่านมา กลุ่มอื่น ๆcount(*)มีการปรับปรุงบางอย่าง
dezso

คุณจะรู้ได้อย่างไรว่าคุณไม่เคยมีหมายเลขผิดถ้าคุณไม่สามารถนับได้ uhmmmm ...
Tonca

-3

ฉันจะพยายามแบ่งพาร์ติชันตามประเภทการให้คะแนนเช่น:

mutual_match_ratings_N, mutual_match_ratings_S ฯลฯ

คุณควรทำการสืบค้นสำหรับแต่ละประเภท แต่อาจเร็วกว่าวิธีอื่น ให้มันลอง.

สิ่งนี้ถือว่าคุณมีประเภทคะแนนที่แน่นอนและคุณไม่จำเป็นต้องใช้ตารางนี้สำหรับข้อความค้นหาอื่น ๆ ที่จะแย่ที่สุดในโครงสร้างใหม่นี้

หากเป็นกรณีนี้คุณควรมองหาวิธีอื่นหรือรักษาสำเนาสองชุดของตาราง (ตารางเริ่มต้นของคุณและสำเนาที่แบ่งพาร์ติชัน) หากราคาไม่แพงในแง่ของพื้นที่และการบำรุงรักษา (หรือตรรกะของแอปพลิเคชัน)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.