Mysql
mariadb 什麼時候創建 tmp 表?
我發現,這
copying to tmp table
是我想要優化的慢查詢的大部分時間。我在終端中觀看了“分析”的輸出。這發生在一個有 6000 行的圖片表和一個有 30 行的使用者表上。... | Copying to tmp table | 0.000263 | | Copying to tmp table | 0.052237 | | Sorting result | 0.000035 | ...
為什麼(或何時)mariadb 使用 tmp 表?我可以更改查詢以避免使用 tmp 表嗎?
SELECT `pics`.*, `users`.`username`, `users`.`displayname` FROM `pics` LEFT JOIN `users` ON `users`.`id` = `pics`.`user_id` ORDER BY RAND() asc LIMIT 4;
我有這些索引:
圖片:
PRIMARY id UNIQUE created_at INDEX user_id INDEX location
使用者:
PRIMARY id UNIQUE username
編輯:
我還有一個,也很慢,也是因為
copying to tmp table
:SELECT t.tag, count(pt.tag_id) as anzahl FROM tags t JOIN pic_tag pt ON t.id = pt.tag_id GROUP BY t.tag COLLATE utf8_unicode_ci ORDER BY RAND() LIMIT 12;
顯示查詢 1 的配置文件;
+----------------------+----------+ | Status | Duration | +----------------------+----------+ | starting | 0.000149 | | checking permissions | 0.000009 | | checking permissions | 0.000008 | | Opening tables | 0.000031 | | After opening tables | 0.000010 | | System lock | 0.000010 | | Table lock | 0.000008 | | After table lock | 0.000012 | | init | 0.000058 | | optimizing | 0.000028 | | statistics | 0.000061 | | preparing | 0.000045 | | executing | 0.000007 | | Copying to tmp table | 0.000101 | | Copying to tmp table | 0.034019 | | Sorting result | 0.000426 | | Sending data | 0.000023 | | end | 0.000006 | | removing tmp table | 0.000129 | | end | 0.000004 | | query end | 0.000003 | | closing tables | 0.000011 | | freeing items | 0.000006 | | updating status | 0.000012 | | cleaning up | 0.000003 | +----------------------+----------+
SHOW STATUS like ‘Hand%’;
+----------------------------+-------+ | Handler_commit | 0 | | Handler_delete | 0 | | Handler_discover | 0 | | Handler_external_lock | 0 | | Handler_icp_attempts | 0 | | Handler_icp_match | 0 | | Handler_mrr_init | 0 | | Handler_mrr_key_refills | 0 | | Handler_mrr_rowid_refills | 0 | | Handler_prepare | 0 | | Handler_read_first | 0 | | Handler_read_key | 13759 | | Handler_read_last | 0 | | Handler_read_next | 11501 | | Handler_read_prev | 0 | | Handler_read_rnd | 12 | | Handler_read_rnd_deleted | 0 | | Handler_read_rnd_next | 4292 | | Handler_rollback | 0 | | Handler_savepoint | 0 | | Handler_savepoint_rollback | 0 | | Handler_tmp_update | 9495 | | Handler_tmp_write | 2031 | | Handler_update | 0 | | Handler_write | 0 | +----------------------------+-------+
解釋
+------+-------------+-------+------+---------------+--------+---------+--------------+------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+-------+------+---------------+--------+---------+--------------+------+---------------------------------+ | 1 | SIMPLE | t | ALL | PRIMARY | NULL | NULL | NULL | 2258 | Using temporary; Using filesort | | 1 | SIMPLE | pt | ref | tag_id | tag_id | 4 | website.t.id | 5 | Using index | +------+-------------+-------+------+---------------+--------+---------+--------------+------+---------------------------------+
大約需要 23 毫秒。tags 表有大約 2000 行,而 pic_tag 是一個 pivot(?) 表,其中保存了圖片和標籤的關係。(pic_id - tag_id) 它有大約 11000 行。
我認為 UNION 幾乎是太多的程式碼,對於 12 或 15 或我想顯示的標籤。有沒有辦法以某種方式處理 tmp 表?還是 tmp 表本身不是問題?
根據著名的文章“修復
ORDER BY rand()
”,特別是通過 union 進行多選,我們可以這樣寫:( SELECT `p1`.*, `users`.`username`, `users`.`displayname` FROM pics p1 JOIN (SELECT ceil(rand() * (SELECT max(id) FROM pics)) AS id ) AS p2 JOIN users ON users.id = p1.user_id WHERE p1.id >= p2.id ORDER BY p1.id ASC limit 1 ) UNION ALL ( SELECT `p1`.*, `users`.`username`, `users`.`displayname` FROM pics p1 JOIN (SELECT ceil(rand() * (SELECT max(id) FROM pics)) AS id ) AS p2 JOIN users ON users.id = p1.user_id WHERE p1.id >= p2.id ORDER BY p1.id ASC limit 1 ) UNION ALL ( SELECT `p1`.*, `users`.`username`, `users`.`displayname` FROM pics p1 JOIN (SELECT ceil(rand() * (SELECT max(id) FROM pics)) AS id ) AS p2 JOIN users ON users.id = p1.user_id WHERE p1.id >= p2.id ORDER BY p1.id ASC limit 1 ) UNION ALL ( SELECT `p1`.*, `users`.`username`, `users`.`displayname` FROM pics p1 JOIN (SELECT ceil(rand() * (SELECT max(id) FROM pics)) AS id ) AS p2 JOIN users ON users.id = p1.user_id WHERE p1.id >= p2.id ORDER BY p1.id ASC limit 1 )
此方法不會創建完美的隨機數,因為它假設 id 的分佈是有規律的。此外,聯合都可以選擇一行兩次。這也不是完全相同的查詢,我使用 an
INNER JOIN
是為了不選擇沒有圖片的使用者,但這很容易修復。您在 5.6 中的方法 (
ORDER BY rand()
)(可能已經針對 5.5 進行了優化):mysql> SHOW STATUS like 'Hand%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Handler_commit | 1 | | Handler_delete | 0 | | Handler_discover | 0 | | Handler_external_lock | 4 | | Handler_mrr_init | 0 | | Handler_prepare | 0 | | Handler_read_first | 1 | | Handler_read_key | 6041 | | Handler_read_last | 0 | | Handler_read_next | 0 | | Handler_read_prev | 0 | | Handler_read_rnd | 4 | | Handler_read_rnd_next | 12122 | | Handler_rollback | 0 | | Handler_savepoint | 0 | | Handler_savepoint_rollback | 0 | | Handler_update | 0 | | Handler_write | 6060 | +----------------------------+-------+ 18 rows in set (0.00 sec)
我的方法(文章一):
mysql> SHOW STATUS like 'Hand%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Handler_commit | 1 | | Handler_delete | 0 | | Handler_discover | 0 | | Handler_external_lock | 24 | | Handler_mrr_init | 0 | | Handler_prepare | 0 | | Handler_read_first | 4 | | Handler_read_key | 12 | | Handler_read_last | 4 | | Handler_read_next | 0 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 9 | | Handler_rollback | 0 | | Handler_savepoint | 0 | | Handler_savepoint_rollback | 0 | | Handler_update | 0 | | Handler_write | 8 | +----------------------------+-------+ 18 rows in set (0.06 sec)
如您所見,讀取和寫入已從數千次更改為 12 次讀取和 8 次寫入。
試一試 !!!