Làm cách nào để tăng tốc truy vấn trên một bảng lớn 220 triệu hàng (dữ liệu 9 gig)?


31

Vấn đề:

Chúng tôi có một trang xã hội nơi các thành viên có thể đánh giá lẫn nhau về tính tương thích hoặc kết hợp. Đây user_match_ratingsbảng chứa hơn 220 triệu hàng (9 dữ liệu gig hoặc gần 20 buổi biểu diễn trong các chỉ số). Các truy vấn đối với bảng này thường xuyên hiển thị trong Slow.log (ngưỡng> 2 giây) và là truy vấn chậm được ghi lại thường xuyên nhất trong hệ thống:

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;"

Phiên bản MySQL:

  • phiên bản giao thức: 10
  • phiên bản: 5.0.77-log
  • phiên bản bdb: Phần mềm Sleepycat: Berkeley DB 4.1.24: (ngày 29 tháng 1 năm 2009)
  • phiên bản máy biên dịch: x86_64 phiên bản_compile_os: redhat-linux-gnu

Bảng thông tin:

SHOW COLUMNS FROM user_match_ratings;

Cung cấp:

╔═══════════════╦════════════╦════╦═════╦════════╦════════════════╗
 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                   
╚═══════════════╩════════════╩════╩═════╩════════╩════════════════╝

Truy vấn mẫu:

select * from mutual_match_ratings where id=221673540;

cho:

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

Chỉ mục

Bảng có 3 chỉ mục được thiết lập:

  1. chỉ số duy nhất trên rated_user_id
  2. chỉ số tổng hợp trên rater_user_idcreated_at
  3. chỉ số tổng hợp trên rated_user_idrater_user_id
hiển thị chỉ mục từ user_match_ratings;

cho:

╔════════════════════╦════════════╦═══════════════════════════╦══════════════╦═══════════════╦═══════════╦═════════════╦══════════╦════════╦═════════════════════════╦════════════╦══════════════════╗
 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                                                 
╚════════════════════╩════════════╩═══════════════════════════╩══════════════╩═══════════════╩═══════════╩═════════════╩══════════╩════════╩═════════════════════════╩════════════╩══════════════════╝

Ngay cả với các chỉ mục, các truy vấn này là chậm.

Câu hỏi của tôi:

Việc tách bảng / dữ liệu này cho đến một cơ sở dữ liệu khác trên một máy chủ có đủ ram để lưu trữ dữ liệu này trong bộ nhớ liệu điều này có làm tăng tốc các truy vấn này không? Có bất cứ điều gì trong dù sao các bảng / chỉ mục được thiết lập mà chúng ta có thể cải thiện để làm cho các truy vấn này nhanh hơn không?

Hiện tại chúng tôi có 16GB bộ nhớ; tuy nhiên, chúng tôi đang xem xét nâng cấp máy hiện có lên 32GB hoặc thêm một máy mới với ít nhất là nhiều ổ đĩa trạng thái rắn.


1
Câu hỏi của bạn thật đáng kinh ngạc. Tôi rất quan tâm đến giải pháp hiện tại của bạn rằng làm thế nào bạn quản lý để có kết quả trong <= 2 giây? Bởi vì tôi có một bảng chỉ có 20 triệu bản ghi và vẫn mất 30 giây SELECT QUERY. Bạn có thể đề nghị? PS Câu hỏi của bạn đã buộc tôi tham gia cộng đồng này (y);)
NullPulum

2
Nhìn vào các chỉ mục trên bảng bạn đang truy vấn .. thường có rất nhiều cải tiến cho các truy vấn có thể được thực hiện bằng cách tạo chỉ mục thích hợp. Không phải lúc nào cũng thấy rất nhiều trường hợp trong đó các truy vấn được thực hiện nhanh bằng cách cung cấp một chỉ mục dựa trên các cột trên mệnh đề where trên một truy vấn. Đặc biệt là nếu một bảng phát triển ngày càng lớn hơn.
Ranknoodle

Chắc chắn @Ranknoodle. Cảm ơn bạn. Tôi sẽ kiểm tra tương ứng.
NullPulum

Câu trả lời:


28

Suy nghĩ về vấn đề này, được ném theo thứ tự ngẫu nhiên:

  • Chỉ số rõ ràng cho truy vấn này là : (rated_user_id, rating). Một truy vấn chỉ nhận được dữ liệu cho một trong số một triệu người dùng và cần 17 giây đang làm điều gì đó sai: đọc từ (rated_user_id, rater_user_id)chỉ mục và sau đó đọc từ bảng các giá trị (hàng trăm đến hàng nghìn) cho ratingcột, như ratingkhông có trong bất kỳ chỉ mục nào. Vì vậy, truy vấn phải đọc nhiều hàng của bảng được đặt ở nhiều vị trí đĩa khác nhau.

  • Trước khi bắt đầu thêm nhiều chỉ mục vào các bảng, hãy thử phân tích hiệu suất của toàn bộ cơ sở dữ liệu, toàn bộ các truy vấn chậm, kiểm tra lại các lựa chọn của kiểu dữ liệu, công cụ bạn sử dụng và cài đặt cấu hình.

  • Cân nhắc chuyển sang phiên bản mới hơn của MySQL, 5.1, 5.5 hoặc thậm chí 5.6 (cũng: phiên bản Percona và MariaDB.) Một số lợi ích khi lỗi đã được sửa, trình tối ưu hóa được cải thiện và bạn có thể đặt ngưỡng thấp cho các truy vấn chậm dưới 1 giây (như 10 mili giây). Điều này sẽ cung cấp cho bạn thông tin tốt hơn nhiều về các truy vấn chậm.

  • Sự lựa chọn cho kiểu dữ liệu ratinglà kỳ lạ. VARCHAR(1)? Tại sao không CHAR(1)? Tại sao không TINYINT? Điều này sẽ giúp bạn tiết kiệm một số không gian, cả bảng tin và trong các chỉ mục (sẽ) bao gồm cột đó. Một cột varchar (1) cần thêm một byte so với char (1) và nếu chúng là utf8, các cột char (var) sẽ cần 3 (hoặc 4) byte, thay vì 1 (tinyint).


2
Bao nhiêu tác động hiệu suất hoặc lãng phí lưu trữ theo% nếu bạn sử dụng kiểu dữ liệu sai?
FlyingAtom

1
@FellingAtom Tùy thuộc vào trường hợp, nhưng đối với một số cột được lập chỉ mục vẫn cần quét (ví dụ: khi bạn không có mệnh đề where nhưng bạn chỉ truy xuất cột đó), công cụ có thể quyết định quét chỉ mục thay vì bảng và nếu bạn tối ưu hóa loại dữ liệu của mình thành một nửa kích thước thì quá trình quét sẽ nhanh gấp đôi và phản hồi sẽ bằng một nửa kích thước. Nếu bạn vẫn đang quét bảng thay vì chỉ mục (ví dụ: khi bạn truy xuất nhiều cột hơn không chỉ các cột trong chỉ mục), thì lợi ích sẽ ít đáng kể hơn.
Sebastián Grignoli

-1

Tôi đã xử lý các bảng cho Chính phủ Đức với đôi khi 60 triệu hồ sơ.

Chúng tôi đã có rất nhiều bảng này.

Và chúng ta cần biết nhiều lần tổng số hàng từ một bảng.

Sau khi nói chuyện với các lập trình viên của Oracle và Microsoft, chúng tôi không vui lắm ...

Vì vậy, chúng tôi, nhóm lập trình viên cơ sở dữ liệu, đã quyết định rằng trong mỗi bảng là bản ghi luôn luôn là bản ghi trong đó tổng số bản ghi được lưu trữ. Chúng tôi đã cập nhật số này, tùy thuộc vào các hàng CHERTN hoặc XÓA.

Chúng tôi đã thử tất cả các cách khác. Đây là cách nhanh nhất.

Chúng tôi sử dụng cách này kể từ năm 1998 và chưa bao giờ có số lượng hàng sai, trong tất cả các bảng kỷ lục nhiều triệu của chúng tôi.


7
Tôi đề nghị xem xét một số tính năng được giới thiệu trong 18 năm qua. Trong số những người khác, count(*)có một số cải tiến.
dezso

Làm thế nào để bạn biết rằng bạn chưa bao giờ có bất kỳ số sai nếu bạn không thể đếm chúng? uhmmmm ...
Tonca

-3

Tôi sẽ cố gắng phân vùng trên các loại xếp hạng, như:

lẫn nhau_match_ratings_N, lẫn_match_ratings_S, v.v.

Bạn nên thực hiện một truy vấn cho từng loại, nhưng có lẽ nhanh hơn cách khác. Hãy thử một lần.

Điều này giả định rằng bạn có một số loại xếp hạng cố định và rằng bạn không cần bảng này cho các truy vấn khác sẽ tệ nhất với cấu trúc mới này.

Nếu đó là trường hợp, bạn nên tìm cách tiếp cận khác hoặc duy trì hai bản sao của bảng (bảng ban đầu và bảng được phân vùng) nếu giá cả phải chăng về không gian và khả năng bảo trì (hoặc logic ứng dụng).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.