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

Làm cách nào để tôi có thể tối ưu hóa hơn nữa truy vấn bảng dẫn xuất hoạt động tốt hơn truy vấn tương đương với JOINed?

Vâng, tôi đã tìm thấy một giải pháp. Nó đã mất rất nhiều thử nghiệm và tôi nghĩ rằng một chút may mắn mù quáng, nhưng đây là:

CREATE TABLE magic ENGINE=MEMORY
SELECT
  s.shop_id AS shop_id,
  s.id AS shift_id,
  st.dow AS dow,
  st.start AS start,
  st.end AS end,
  su.user_id AS manager_id
FROM shifts s
JOIN shift_times st ON s.id = st.shift_id
JOIN shifts_users su ON s.id = su.shift_id
JOIN shift_positions sp ON su.shift_position_id = sp.id AND sp.level = 1

ALTER TABLE magic ADD INDEX (shop_id, dow);

CREATE TABLE tickets_extra ENGINE=MyISAM
SELECT 
  t.id AS ticket_id,
  (
    SELECT m.manager_id
    FROM magic m
    WHERE DAYOFWEEK(t.created) = m.dow
    AND TIME(t.created) BETWEEN m.start AND m.end
    AND m.shop_id = t.shop_id
  ) AS manager_created,
  (
    SELECT m.manager_id
    FROM magic m
    WHERE DAYOFWEEK(t.resolved) = m.dow
    AND TIME(t.resolved) BETWEEN m.start AND m.end
    AND m.shop_id = t.shop_id
  ) AS manager_resolved
FROM tickets t;
DROP TABLE magic;

Giải thích dài dòng

Bây giờ, tôi sẽ giải thích lý do tại sao điều này hoạt động và quy trình và các bước tương đối của tôi để đến được đây.

Đầu tiên, tôi biết truy vấn mà tôi đang thử đang gặp phải vì bảng dẫn xuất khổng lồ và các THAM GIA tiếp theo vào điều này. Tôi đang sử dụng bảng vé đã được lập chỉ mục tốt của mình và kết hợp tất cả dữ liệu shift_times vào đó, sau đó để MySQL nhai nó trong khi nó cố gắng tham gia vào bảng ca và shift_positions. Khối tài sản kếch xù này có thể lên tới 2 triệu hàng không được lập chỉ mục.

Bây giờ, tôi biết điều này đang xảy ra. Mặc dù vậy, lý do tôi đi theo con đường này là vì cách "thích hợp" để làm điều này, sử dụng JOIN nghiêm ngặt đã mất nhiều thời gian hơn. Điều này là do một chút hỗn loạn khó chịu cần thiết để xác định ai là người quản lý của một ca làm việc nhất định. Tôi phải tham gia vào shift_times để tìm ra độ thay đổi chính xác là bao nhiêu, đồng thời tham gia xuống shift_positions để tìm ra cấp độ của người dùng. Tôi không nghĩ rằng trình tối ưu hóa MySQL xử lý điều này rất tốt và kết thúc việc tạo ra một bảng tạm thời của các phép nối, sau đó lọc ra những gì không phù hợp.

Vì vậy, vì bảng dẫn xuất có vẻ là "con đường đi", tôi đã kiên trì kiên trì điều này trong một thời gian. Tôi đã thử rút gọn nó thành một mệnh đề JOIN, không có cải tiến nào. Tôi đã thử tạo một bảng tạm thời với bảng dẫn xuất trong đó, nhưng một lần nữa nó quá chậm vì bảng tạm thời không được lập chỉ mục.

Tôi nhận ra rằng tôi phải xử lý việc tính toán ca, thời gian, vị trí này một cách thông minh. Tôi nghĩ, có lẽ XEM sẽ là cách để đi. Điều gì sẽ xảy ra nếu tôi tạo CHẾ ĐỘ XEM có chứa thông tin này:(shop_id, shift_id, dow, start, end, manager_id). Sau đó, tôi chỉ cần tham gia vào bảng vé theo shop_id và tính toàn bộ DAYOFWEEK / TIME, và tôi sẽ kinh doanh. Tất nhiên, tôi không nhớ rằng MySQL xử lý VIEWs khá dễ dàng. Nó hoàn toàn không hiện thực hóa chúng, nó chỉ đơn giản là chạy truy vấn mà bạn đã sử dụng để có được chế độ xem cho bạn. Vì vậy, bằng cách kết hợp các vé vào điều này, về cơ bản tôi đang chạy truy vấn ban đầu của mình - không cải thiện.

Vì vậy, thay vì CHẾ ĐỘ XEM, tôi quyết định sử dụng BẢNG TẠM THỜI. Điều này hoạt động tốt nếu tôi chỉ tìm nạp một trong các trình quản lý (được tạo hoặc giải quyết) tại một thời điểm, nhưng nó vẫn khá chậm. Ngoài ra, tôi phát hiện ra rằng với MySQL, bạn không thể tham chiếu đến cùng một bảng hai lần trong cùng một truy vấn (tôi sẽ phải nối bảng tạm thời của mình hai lần để có thể phân biệt giữa manager_create và manager_resolved). Đây là một WTF lớn, vì tôi có thể làm điều đó, miễn là tôi không chỉ định "TEMPORARY" - đây là nơi mà KỸ THUẬT CREATE TABLE magic ENGINE =MEMORY phát huy tác dụng.

Với bảng tạm thời giả này trong tay, tôi đã thử lại JOIN cho chỉ manager_create. Nó hoạt động tốt, nhưng vẫn còn khá chậm. Tuy nhiên, khi tôi THAM GIA một lần nữa để nhận manager_resolved trong cùng một truy vấn, thời gian truy vấn được đánh dấu trở lại tầng bình lưu. Nhìn vào GIẢI THÍCH cho thấy bảng quét toàn bộ các vé (hàng ~ 2mln), như mong đợi và các THAM GIA trên bảng ma thuật ở mức ~ 2.087 mỗi vé. Một lần nữa, tôi dường như sắp gặp thất bại.

Bây giờ tôi bắt đầu suy nghĩ về cách tránh hoàn toàn các THAM GIA và đó là khi tôi tìm thấy một số bài đăng trên bảng tin cổ xưa khó hiểu nơi ai đó đề xuất sử dụng các danh mục con (không thể tìm thấy liên kết trong lịch sử của tôi). Đây là những gì đã dẫn đến truy vấn SELECT thứ hai được hiển thị ở trên (một tạo ticket_extra). Trong trường hợp chỉ chọn một trường người quản lý duy nhất, nó hoạt động tốt, nhưng với cả hai trường đó thì thật là tào lao. Tôi đã xem GIẢI THÍCH và thấy điều này:

*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: t
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 173825
        Extra: 
*************************** 2. row ***************************
           id: 3
  select_type: DEPENDENT SUBQUERY
        table: m
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 2037
        Extra: Using where
*************************** 3. row ***************************
           id: 2
  select_type: DEPENDENT SUBQUERY
        table: m
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 2037
        Extra: Using where
3 rows in set (0.00 sec)

Ack, ĐỒ PHỤ THUỘC đáng sợ. Bạn thường nên tránh những điều này, vì MySQL thường sẽ thực thi chúng theo kiểu bên ngoài, thực thi truy vấn bên trong cho mọi hàng của bên ngoài. Tôi bỏ qua điều này, và tự hỏi:"Chà ... điều gì sẽ xảy ra nếu tôi chỉ lập chỉ mục cho cái bảng ma thuật ngu ngốc này?". Do đó, chỉ mục ADD (shop_id, dow) đã được sinh ra.

Kiểm tra cái này:

mysql> CREATE TABLE magic ENGINE=MEMORY
<snip>
Query OK, 3220 rows affected (0.40 sec)

mysql> ALTER TABLE magic ADD INDEX (shop_id, dow);
Query OK, 3220 rows affected (0.02 sec)

mysql> CREATE TABLE tickets_extra ENGINE=MyISAM
<snip>
Query OK, 1933769 rows affected (24.18 sec)

mysql> drop table magic;
Query OK, 0 rows affected (0.00 sec)

Bây giờ là ĐÓ những gì tôi đang nói về!

Kết luận

Đây chắc chắn là lần đầu tiên tôi tạo một bảng không TẠM THỜI ngay lập tức và INDEXed nó một cách nhanh chóng, chỉ đơn giản là để thực hiện một truy vấn duy nhất một cách hiệu quả. Tôi đoán rằng tôi luôn cho rằng việc thêm một chỉ mục khi đang di chuyển là một hoạt động cực kỳ tốn kém. (Việc thêm một chỉ mục trên bảng vé gồm 2 triệu hàng của tôi có thể mất hơn một giờ). Tuy nhiên, chỉ với 3.000 hàng, đây là một con đường nhỏ.

Đừng sợ CÁC NỘI DUNG PHỤ THUỘC, tạo các bảng TẠM THỜI thực sự không có, lập chỉ mục một cách nhanh chóng hoặc người ngoài hành tinh. Tất cả chúng đều có thể là những điều tốt đẹp trong hoàn cảnh phù hợp.

Cảm ơn tất cả sự giúp đỡ của StackOverflow. :-D



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. tại sao chúng ta cần kết hợp trái và phải

  2. Có các ký hiệu như Â và như vậy trong cơ sở dữ liệu, phải làm gì?

  3. Có thể lập chỉ mục giữa các bảng không?

  4. MySQL LOAD DATA LOCAL INFILE không được phép qua ODBC

  5. Chọn hàng MYSQL nhưng hàng thành cột và cột thành hàng