Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Truy vấn tổng hợp AVG () rất đơn giản trên máy chủ MySQL mất nhiều thời gian

Để đếm số hàng với một ngày cụ thể, MySQL phải xác định giá trị đó trong chỉ mục (khá nhanh, sau tất cả đó là chỉ mục được tạo cho) và sau đó đọc các mục tiếp theo của chỉ mục cho đến khi nó tìm thấy ngày tiếp theo. Tùy thuộc vào loại dữ liệu của esi , điều này sẽ tổng hợp để đọc một số MB dữ liệu để đếm 700 nghìn hàng của bạn. Việc đọc một số MB không mất nhiều thời gian (và dữ liệu đó thậm chí có thể đã được lưu vào bộ đệm trong vùng đệm, tùy thuộc vào tần suất bạn sử dụng chỉ mục).

Để tính giá trị trung bình cho một cột không có trong chỉ mục, MySQL sẽ sử dụng chỉ mục để tìm tất cả các hàng cho ngày đó (giống như trước đó). Nhưng ngoài ra, đối với mỗi hàng nó tìm thấy, nó phải đọc dữ liệu bảng thực tế cho hàng đó, có nghĩa là sử dụng khóa chính để định vị hàng, đọc một số byte và lặp lại điều này 700 nghìn lần. Đây " truy cập ngẫu nhiên " rất nhiều chậm hơn so với đọc tuần tự trong trường hợp đầu tiên. (Điều này trở nên tồi tệ hơn do vấn đề "một số byte" là innodb_page_size (16KB theo mặc định), vì vậy bạn có thể phải đọc tới 700k * 16KB =11GB, so với "một số MB" cho count(*); và tùy thuộc vào cấu hình bộ nhớ của bạn, một số dữ liệu này có thể không được lưu vào bộ nhớ đệm và phải được đọc từ đĩa.)

Giải pháp cho điều này là đưa tất cả các cột đã sử dụng vào chỉ mục ("chỉ mục bao gồm"), ví dụ:tạo chỉ mục vào ngày date, 01 . Sau đó, MySQL không cần phải truy cập chính bảng và có thể tiếp tục, tương tự như phương pháp đầu tiên, chỉ bằng cách đọc chỉ mục. Kích thước của chỉ mục sẽ tăng lên một chút, vì vậy MySQL sẽ cần đọc "thêm một số MB" (và thực hiện avg -khai thác), nhưng nó vẫn sẽ là vấn đề trong vài giây.

Trong phần nhận xét, bạn đã đề cập rằng bạn cần tính giá trị trung bình trên 24 cột. Nếu bạn muốn tính toán avg cho một số cột cùng một lúc, bạn sẽ cần một chỉ mục bao trùm trên tất cả chúng, ví dụ:date, 01, 02, ..., 24 để ngăn truy cập bảng. Lưu ý rằng một chỉ mục chứa tất cả các cột yêu cầu nhiều không gian lưu trữ như chính bảng đó (và sẽ mất nhiều thời gian để tạo một chỉ mục như vậy), vì vậy nó có thể phụ thuộc vào mức độ quan trọng của truy vấn này nếu nó có giá trị các tài nguyên đó hay không.

Để tránh MySQL-giới hạn 16 cột cho mỗi chỉ mục , bạn có thể chia nó thành hai chỉ mục (và hai truy vấn). Tạo v.d. các chỉ mục date, 01, .., 12date, 13, .., 24 , sau đó sử dụng

select * from (select `date`, avg(`01`), ..., avg(`12`) 
               from mytable where `date` = ...) as part1
cross join    (select avg(`13`), ..., avg(`24`) 
               from mytable where `date` = ...) as part2;

Đảm bảo ghi chép đầy đủ vấn đề này, vì không có lý do rõ ràng để viết truy vấn theo cách này, nhưng nó có thể đáng giá.

Nếu bạn chỉ tính trung bình trên một cột duy nhất, bạn có thể thêm 24 chỉ mục riêng biệt (vào ngày date, 01 , date, 02 , ...), mặc dù về tổng thể, chúng sẽ yêu cầu nhiều không gian hơn, nhưng có thể nhanh hơn một chút (vì chúng nhỏ hơn riêng lẻ). Nhưng vùng đệm có thể vẫn ưu tiên chỉ mục đầy đủ, tùy thuộc vào các yếu tố như kiểu sử dụng và cấu hình bộ nhớ, vì vậy bạn có thể phải kiểm tra nó.

Kể từ date là một phần của khóa chính của bạn, bạn cũng có thể xem xét việc thay đổi khóa chính thành date, esi . Nếu bạn tìm thấy ngày bằng khóa chính, bạn sẽ không cần thêm một bước để truy cập dữ liệu bảng (vì bạn đã truy cập bảng), vì vậy hành vi sẽ tương tự như chỉ mục bao trùm. Nhưng đây là một thay đổi đáng kể đối với bảng của bạn và có thể ảnh hưởng đến tất cả các truy vấn khác (ví dụ:sử dụng esi để xác định vị trí các hàng), vì vậy nó phải được xem xét cẩn thận.

Như bạn đã đề cập, một tùy chọn khác sẽ là tạo bảng tóm tắt nơi bạn lưu trữ các giá trị được tính toán trước, đặc biệt nếu bạn không thêm hoặc sửa đổi các hàng cho những ngày trước (hoặc có thể cập nhật chúng bằng trình kích hoạt).



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Các ràng buộc C ++ cho MySQL

  2. Mệnh đề IN không sử dụng chỉ mục

  3. Nhóm ngày giờ của MySQL thành các khoảng thời gian bất kể múi giờ

  4. Laravel thuộc vềToMany nơi không có một trong những

  5. SQL Thay thế nhiều biến từ một bảng khác trong kết quả truy vấn