- vừa được xem lúc

MySQL Performance Tuning With Index

0 0 73

Người đăng: Quiet

Theo Viblo Asia

Lâu rồi không viết bài trên viblo nên để cái tiêu đề hơi công nghiệp =)) Xin chào các bạn, đợt rồi mình mới được làm vài task cải thiện thiện tốc độ truy vấn mysql nên hôm nay xin phép chia sẻ lại chút kinh nghiệm cải thiện. Bài viết hơi nhiều chữ nhưng chứa khá khá hàm lượng kiến thức nên khuyến cáo các bạn đọc lúc không buồn ngủ nhé ^^

Nói qua một chút thì thì project sử dụng Laravelmysql, hệ thống trả về cái response tương ứng với những đầu API.

Mình nhảy vào support vài màn hình mà tester và phía khách hàng feedback là chậm. Quy trình của mình để nhanh chóng tìm ra vấn đề như sau.

  • Đồng bộ lại cơ sở dữ liệu ở môi trường staging, production về local bằng các sử dụng chức năng import, export trên các hệ quản trị cơ sở dữ liệu. Mục đích của việc này để đảm bảo lượng dữ liệu trong quá trình làm việc trên local đủ lớn. Thường thì hệ thống sẽ chỉ chậm như rùa khi có dữ liệu lớn.

  • Sử dụng một số package như debugbar, telescope phục vụ quá trình điều tra request, response, dump mysql để nhanh chóng xác định những câu truy vấn chậm.

Vì hệ thống chỉ viết API nên mình sử dụng telescope trong trường hợp này. Cách cài đặt package vào dự án vui lòng xem tại những tài liệu chính thống.

1. Explain SQL

Sau khi điều tra với telescope mình xác định được một vài câu truy vấn có thời gian thực thi khá lâu (khoảng 2s, 3s)

mysql> explain select SUM(duration) as duration, MINUTE(start_time) div 15 as quarterly, `window_title_hash`, `application_name` from `logs` -> where `user_id` = 53 and `start_time` LIKE '2021-04-06 13%' -> group by `quarterly`, `window_title_hash`, `application_name`\G;
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: logs partitions: NULL type: ALL
possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 584684 filtered: 1.11 Extra: Using where; Using temporary; Using filesort

Trên đây là câu truy vấn gây chậm hệ thống sau khi đã được explain. Một tips khi làm việc với explain của mình là sẽ quan tâm đến yếu tố đầu tiên là rows. Ở đây bạn có thể nhìn thấy con số khá lớn 584684

Rows cho ta biết số lượng bản ghi đã được duyệt để trả về kết quả, nói cách khác khi duyệt qua càng ít rows thì câu truy vấn của bạn càng tối ưu.

Type cũng là một thuộc tính mình hay quan tâm khi explain. Trong thực tế với một truy vấn có khá nhiều type nhưng mình hay để ý nhất khi type là ALL. Điều này đồng nghĩa với việc câu truy vấn của bạn đang thao tác với bảng chưa được đánh index.

Trên viblo có những bài viết rất hay về IndexingExplain, các bạn có thể vui lòng đọc lại. Trong khuôn khổ bài viết này mình sẽ không giới thiệu về chúng cũng như cách hoạt động nữa. Mình sẽ chỉ ra với trường hợp thực tế việc đánh index có hiệu quả như thế nào.

Okay, vấn đề tiếp theo là giải quyết bài toán làm sao để truy vấn càng scan qua ít row càng tốt, ở trường hợp này mình lựa chọn phương án đánh index cho start_time, user_id.

2. Where like with index

Đáng ra bài viết sẽ chẳng có gì khi đánh index là xong, nhưng mà trong quá trình đánh index gặp vài vấn đề nên mình sẽ trao đổi tiếp.

Theo bản năng mách của thanh niên chưa trải sự đời mình lao vào đánh index cho trường start_time, user_id

Sau khi đánh index xong mình explain lại thì kết quả không được như mong muốn.

mysql> explain select SUM(duration) as duration, MINUTE(start_time) div 15 as quarterly, `window_title_hash`, `application_name` from `logs` -> where `user_id` = 53 and `start_time` LIKE '2021-04-06 13%' -> group by `quarterly`, `window_title_hash`, `application_name`\G;
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: logs partitions: NULL type: ref
possible_keys: start_time_idx,user_id_idx key: user_id_idx key_len: 4 ref: const rows: 292342 filtered: 11.11 Extra: Using index condition; Using where; Using temporary; Using filesort 

Mình nhận ra có 2 vấn đề sau khi đã đánh index với câu query này

  • Trường start_time mặc dù đã được đánh index nhưng không sử dụng được trong trường hơp này. Sau khi tìm hiểu thì do mình sử dụng WHERE LIKE. Còn tại sao dùng WHERE LIKE không hoạt động với index thì lại phải tìm hiểu về cách hoạt động của index, B-Tree nên mình sẽ nợ các bạn trong bài viết này.

  • Số lượng bản ghi đang scan quá vẫn quá lớn, lí do là trong bảng logs của mình đang có quá nhiều userid = 53. Vì vậy mặc dù đã được đánh index nhưng vẫn không làm giảm được nhiều số lượng bản ghi được scan.

3. Index Hints and something new

Đầu tiên ý tưởng của mình thử tìm các để start_time hoạt động được với index. Nhìn lại câu truy vấn một chút chỗ WHERE LIKE

WHERE `start_time` LIKE '2021-04-06 13%'
// Refactor thành
WHERE `start_time` >= '2021-04-06 13:00:00' and `start_time` < '2021-04-06 14:00:00'

Check lại với explain thì Amazing good job

mysql> explain select SUM(duration) as duration, MINUTE(start_time) div 15 as quarterly, `window_title_hash`, `application_name` from `logs` where `user_id` = 53 and `start_time` >= '2021-04-06 13:00:00' and `start_time` <= '2021-04-06 14:00:00' group by `quarterly`, `window_title_hash`, `application_name`\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: logs partitions: NULL type: range
possible_keys: user_id_idx,start_time_idx key: start_time_idx key_len: 5 ref: NULL rows: 2598 filtered: 50.00 Extra: Using index condition; Using where; Using temporary; Using filesort
1 row in set, 1 warning (0.00 sec) 

Chúng ta có thấy ở cột rows thì số lượng bản ghi phải scan qua đã giảm đi rất nhiều từ khoảng 292342 xuống 2598.

Đừng vội vui mừng trước thành quả mà hãy nhìn lại một chút tại sao mysql lại hoạt động như vậy, ý mình là việc index đang hoạt động.

Trong trường hợp này mình đang đánh index cho cả 2 trường user_id, start_time. Chúng ta có thể thấy nó được thể hiện ở thuộc tính possible_keys. Nhưng tại sao chỉ có start_time được chọn làm index thay vì user_id. Mặc dù mình đã cố gắng thay đổi vị trí where của user_id lên đầu nhưng giá trị key được chọn vẫn là start_time ???

Index Hints là keyword cho bạn tìm hiểu !!!

Index Hints cung cấp cho query optimizer về cách chọn index trong quá trình xử lí truy vấn. Tức là mysql luôn cố gắng tìm index tối ưu nhất cho truy vấn. Trong ví dụ trên chúng ta có thể thấy

  • Nếu sử dụng user_id làm index thì truy vấn phải scan qua 292342 bản ghi

  • Nếu dùng start_time làm index thì truy vấn chỉ scan qua 2598 bản thi thôi.

Ngoài ra, trong nhiều trường hợp, các bạn vẫn muốn sử dụng user_id làm index thì có thể xem xét đến các phương án như USE INDEX, IGNORE INDEX hay FORCE INDEX trong các câu truy vấn của mình.

Quay lại câu truy vấn ban đầu, nếu như trong một số trường hợp chỉ lọc theo user_id và k có start_time thì sao.

mysql> explain select SUM(duration) as duration, MINUTE(start_time) div 15 as quarterly, `window_title_hash`, `application_name` from `logs` where `user_id` = 53 group by `quarterly`, `window_title_hash`, `application_name`\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: logs partitions: NULL type: ref
possible_keys: user_id_idx key: user_id_idx key_len: 4 ref: const rows: 292342 filtered: 100.00 Extra: Using index condition; Using temporary; Using filesort
1 row in set, 1 warning (0.00 sec)

Các bạn có thể thấy trong trường hợp này, user_id vẫn mặc dù được đánh index nhưng vẫn phải scan qua nhiều bản ghi. Lúc này tùy thuộc vào bài toán mà chúng ta có thể tiến hành tách ra làm bảng riêng cho dễ truy vấn. Ví dụ trong trường hợp này bạn hoàn toàn có thể tạo thêm bảng log_duration

id total_duration user_id
1 100 53

Lúc này truy vấn của chúng ta có thể vẫn đạt được kết quả như mong muốn mà không phải sử dụng truy vấn tốn nhiều thời gian.

4. Tổng kết

Trên đây là một số ghi chép của mình trong quá trình làm task lại mà chúng ta có thể tổng kết lại như sau

  1. Để nhanh chóng xác định những câu truy vấn chậm trong các màn hình phức tạp, chưa rõ spec hãy sử dụng những package dump các truy vấn raw sql khi mà thời người người nhà nhà sử dụng ORM
  2. Sử dụng explain để điều tra truy vấn, theo dõi trường type xem đã sử dụng index trong các truy vấn WHERE hay chưa ? theo dõi trường rows xem đang scan qua bao nhiêu bản ghi, có thể đánh index nhưng vẫn chưa giải quyết được vẫn đề
  3. Theo dõi trường possible_keys để check đang có bao nhiêu trường được đánh index, theo dõi trường key để chắc chắn index nào đang được sử dụng.
  4. Trong một số trường hợp, hãy nghĩ đến việc tạo thêm bảng mới lưu trữ data thay vì cố gắng lưu vào một bảng

Cảm ơn các bạn theo dõi bài viết, nếu thấy bài viết bổ ích vui lòng upvotetheo dõi mình để có động lực ra nhiều bài viết hơn nhé ^^

Bình luận

Bài viết tương tự

- vừa được xem lúc

Index trong Mysql và cách sử dụng

Một số database là một cấu trúc dữ liệu để cải thiện tốc độ của các hoạt động trong một bảng. Trong khi tạo index, nó cần được xem xét rằng các cột đó sẽ được sử dụng để thực hiện các truy vấn SQL và tạo ra một hoặc nhiều chỉ số trên các cột đó là gì.

0 0 32

- vừa được xem lúc

Tạo ER Diagram của một Database bằng MySQL Workbench

Trong số chúng ta ai cũng đều đã từng trải qua một thời sinh viên tràn ngập đồ án này, đồ án kia đúng không? Mình cũng đã từng có một thời như thế Mà chuyên ngành chúng ta là công nghệ thông tin thì làm việc với Database trong mỗi đồ án là điều không thể thiếu rồi. Chuyện sẽ chẳng có gì to tát cho đ

0 0 49

- vừa được xem lúc

Window Functions trong MySQL, Nâng cao và cực kì hữu dụng (Phần II).

Chào mọi người, lại là mình đây, ở phần trước mình đã giới thiệu với mọi người về Window Functions Phần I. Nếu chưa rõ nó là gì thì mọi người nên đọc lại trước nha, để nắm được định nghĩa và các key words, tránh mắt chữ O mồm chứ A vì phần này mình chủ yếu sẽ thực hành với các Window Functions.

0 0 90

- vừa được xem lúc

Window Functions trong MySQL, Nâng cao và cực kì hữu dụng (Phần I).

Chào mọi người, mình mới tìm hiểu đc topic Window Functions cá nhân mình cảm thấy khá là hay và mình đánh giá nó là phần nâng cao. Vì ít người biết nên Window Functions thấy rất ít khi sử dụng, thay vì đó là những câu subquery dài dằng dặc như tin nhắn nhắn cho crush, và người khác đọc hiểu được câu

0 0 897

- vừa được xem lúc

Mysql index strategy

Trong Mysql, index hỗ trợ việc tìm kiếm các rows theo từng giá trị của các columns trong bảng trở nên nhanh chóng. Việc tìm kiếm sẽ phải scan toàn bộ table nếu các column trong câu query không được đánh index một cách thích hợp. . .

0 0 50

- vừa được xem lúc

CRUD Nodejs với mysql

Mở Đầu. Xin chào các bạn tiếp tục với series Nodejs cơ bản, bài hôm nay mình sẽ tiếp tục làm thêm các chức năng xem chi tiết và sửa và xóa sản phẩm.

0 0 64