2012-09-26 37 views
12

Nhóm của chúng tôi vừa mới gỡ lỗi tuần trước và cố gắng tìm nguồn gốc của nhiều thời gian chờ khóa mysql và nhiều truy vấn chạy rất dài. Cuối cùng, có vẻ như truy vấn này là thủ phạm.Tại sao truy vấn này gây ra thời gian chờ chờ khóa?

mysql> explain 

SELECT categories.name AS cat_name, 
COUNT(distinct items.id) AS category_count 
FROM `items` 
INNER JOIN `categories` ON `categories`.`id` = `items`.`category_id` 
WHERE `items`.`state` IN ('listed', 'reserved') 
    AND (items.category_id IS NOT NULL) 
GROUP BY categories.name 
ORDER BY category_count DESC 
LIMIT 10\G 

*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: items 
     type: range 
possible_keys: index_items_on_category_id,index_items_on_state 
      key: index_items_on_category_id 
     key_len: 5 
      ref: NULL 
     rows: 119371 
     Extra: Using where; Using temporary; Using filesort 
*************************** 2. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: categories 
     type: eq_ref 
possible_keys: PRIMARY 
      key: PRIMARY 
     key_len: 4 
      ref: production_db.items.category_id 
     rows: 1 
     Extra: 
2 rows in set (0.00 sec) 

Tôi có thể thấy rằng nó đang thực hiện quét bảng khó chịu và tạo bảng tạm thời để chạy.

Tại sao truy vấn này làm cho thời gian phản hồi của cơ sở dữ liệu tăng lên 10 lần và một số truy vấn thường mất 40-50ms (cập nhật trên bảng mục), phát nổ đến 50.000 mili giây và cao hơn?

+1

Bạn đã thử định dạng * mà không có * 'khác biệt'? Phải mất khá nhiều công việc để làm như vậy và bạn có khá nhiều hàng để lọc quá :) – PhD

+0

Rất đẹp. Nope không làm thế. Nó chắc chắn giúp tối ưu hóa nó. Vẫn chưa rõ lý do tại sao một truy vấn chậm như thế này có thể gây ra nhiều vấn đề cho chúng tôi. – chrishomer

+0

Chỉ cần tự hỏi tại sao bạn cần điều này 'AND (items.category_id IS NOT NULL)' - vì nó là 'INNER JOIN' - là category.id được cho phép là' NULL' –

Trả lời

5

là khó có thể nói mà không cần biết thêm thông tin như

  1. Có phải đó là chạy bên trong một giao dịch?
  2. Nếu vậy, mức cô lập là gì?
  3. Có bao nhiêu danh mục?
  4. Có bao nhiêu mục?

Tôi đoán sẽ là truy vấn là quá chậm và chạy nó bên trong một giao dịch (mà nó có lẽ là kể từ khi bạn có vấn đề này) và là lẽ phát hành loạt Khóa trên bảng mục mà có thể không cho phép ghi để tiếp tục do đó làm chậm các bản cập nhật cho đến khi chúng có thể lấy khóa trên bàn.

Và tôi có một vài ý kiến ​​dựa trên những gì tôi có thể nhìn thấy từ truy vấn và thực hiện kế hoạch của bạn:

1) items.state bạn sẽ lẽ sẽ tốt hơn là một danh mục, thay vì có các chuỗi trên mỗi hàng trong các mục, điều này là cho hiệu quả không gian và so sánh ID là cách nhanh hơn so sánh chuỗi (bất kể bất kỳ tối ưu hóa động cơ có thể làm).

2) Tôi đoán items.state là một cột có số lượng thẻ thấp (vài giá trị duy nhất), do đó một chỉ mục trong cột đó có thể làm bạn tổn thương hơn là giúp bạn. Mỗi chỉ mục thêm vào đầu khi chèn/xóa/cập nhật các hàng vì các chỉ mục phải được duy trì, chỉ mục cụ thể này có thể không được sử dụng nhiều đến mức đáng giá. Tất nhiên, tôi chỉ đoán, nó phụ thuộc vào phần còn lại của các truy vấn.

SELECT 
    ; Grouping by name, means comparing strings. 
    categories.name AS cat_name, 
    ; No need for distinct, the same item.id cannot belong to different categories 
    COUNT(distinct items.id) AS category_count 
FROM `items` 
INNER JOIN `categories` ON `categories`.`id` = `items`.`category_id` 
WHERE `items`.`state` IN ('listed', 'reserved') 
    ; Not needed, the inner join gets rid of items with no category_id 
    AND (items.category_id IS NOT NULL) 
GROUP BY categories.name 
ORDER BY category_count DESC 
LIMIT 10\G 

Cách truy vấn này được cấu trúc cơ bản là phải quét toàn bộ bảng mục từ sử dụng của nó chỉ số category_id, sau đó lọc theo mệnh đề where, sau đó, tham gia với bảng mục, có nghĩa là một chỉ mục tìm kiếm trên chỉ mục khóa chính (categories.id) trên mỗi hàng của mục trong tập kết quả mục. Sau đó nhóm theo tên (sử dụng so sánh chuỗi) để đếm, sau đó loại bỏ mọi thứ trừ 10 kết quả.

Tôi sẽ viết các truy vấn như:

SELECT categories.name, counts.n 
FROM (SELECT category_id, COUNT(id) n 
     FROM items 
     WHERE state IN ('listed', 'reserved') AND category_id is not null 
     GROUP BY category_id ORDER BY COUNT(id) DESC LIMIT 10) counts 
JOIN categories on counts.category_id = categories.id 
ORDER BY counts.n desc   

(Tôi xin lỗi nếu cú ​​pháp không hoàn hảo Tôi không chạy MySQL)

Với truy vấn này những gì mà cơ thể bạn sẽ làm là:

Sử dụng các mục.chỉ số trạng thái để có được các mục 'được liệt kê', 'đã đặt trước' và nhóm theo category_id so sánh các số, chứ không phải chuỗi chỉ nhận được 10 số trên cùng, sau đó tham gia với các danh mục để lấy tên (nhưng chỉ sử dụng 10 chỉ mục tìm kiếm).

Các vấn đề liên quan