2017-09-09 30 views
0

這是我的表定義:爲什麼MySQL發佈文件?

CREATE TABLE difficulty (
    uuid binary(16) NOT NULL, 
    createdTimestamp datetime(6) DEFAULT NULL, 
    modifiedTimestamp datetime(6) DEFAULT NULL, 
    name varchar(255) DEFAULT NULL, 
    PRIMARY KEY (uuid) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

CREATE TABLE exercise_session (
    uuid binary(16) NOT NULL, 
    createdTimestamp datetime(6) DEFAULT NULL, 
    modifiedTimestamp datetime(6) DEFAULT NULL, 
    type varchar(16) DEFAULT NULL, 
    status int(11) DEFAULT NULL, 
    difficulty_uuid binary(16) DEFAULT NULL, 

    PRIMARY KEY (uuid), 
    KEY (difficulty_uuid), 
    KEY (difficulty_uuid,modifiedTimestamp), 
    KEY (modifiedTimestamp), 
    KEY (status), 
    FOREIGN KEY (difficulty_uuid) REFERENCES difficulty (uuid) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

這是我的查詢:

SELECT s.difficulty_uuid, s.modifiedTimestamp, d.name 
FROM exercise_session s 
INNER JOIN difficulty d ON s.difficulty_uuid=d.uuid 
ORDER BY s.modifiedTimestamp DESC 
LIMIT 20 

一些數據:

INSERT INTO difficulty (uuid, createdTimestamp, modifiedTimestamp, name) VALUES 
    (0x00000000000000000000000000000000, NULL, NULL, 'difficulty'); 

INSERT INTO exercise_session (uuid, createdTimestamp, modifiedTimestamp, type, status, difficulty_uuid) VALUES 
    (0x00000000000000000000000000000000, NULL, '2017-09-09 03:47:42.000000', '0', 0, 0x00000000000000000000000000000000), 
    (0x00000000000000000000000000000001, NULL, '2017-09-09 03:47:42.000000', '0', 0, 0x00000000000000000000000000000000); 

當我運行我的查詢前綴EXPLAIN是這樣的輸出:

+------+-------------+-------+------+-----------------+-----------------+---------+--------------+------+---------------------------------+ 
| id | select_type | table | type | possible_keys | key    | key_len | ref   | rows | Extra       | 
+------+-------------+-------+------+-----------------+-----------------+---------+--------------+------+---------------------------------+ 
| 1 | SIMPLE  | d  | ALL | PRIMARY   | NULL   | NULL | NULL   | 1 | Using temporary; Using filesort | 
| 1 | SIMPLE  | s  | ref | difficulty_uuid | difficulty_uuid | 17  | dbname.d.uuid | 1 |         | 
+------+-------------+-------+------+-----------------+-----------------+---------+--------------+------+---------------------------------+ 

爲什麼MySQL/MariaDB做一個文件而不是使用複合索引?

+1

我可能猜測它與降序排序有關。 –

回答

2

在對優化器的處理作出結論之前,您應該用更多的測試數據填充這些表。每個表中只有一行或兩行可能會觸發非典型的優化程序計劃。

我試着用幾十行的填充表格,並得到這個解釋道:

*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: s 
    partitions: NULL 
     type: index 
possible_keys: difficulty_uuid,difficulty_uuid_2 
      key: difficulty_uuid_2 
     key_len: 26 
      ref: NULL 
     rows: 11 
    filtered: 100.00 
     Extra: Using where; Using index; Using filesort 
*************************** 2. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: d 
    partitions: NULL 
     type: eq_ref 
possible_keys: PRIMARY 
      key: PRIMARY 
     key_len: 16 
      ref: test.s.difficulty_uuid 
     rows: 1 
    filtered: 100.00 
     Extra: NULL 

仍然是一個文件排序,但至少它訪問正確的順序表:s,再加入到d的主鍵,導致eq_ref類型的訪問。

使用索引提示一個表掃描的成本太高考慮,它採用上modifiedTimestamp指數:

EXPLAIN SELECT s.difficulty_uuid, s.modifiedTimestamp, d.name 
FROM exercise_session s FORCE INDEX(modifiedTimestamp) 
JOIN difficulty d ON s.difficulty_uuid=d.uuid 
ORDER BY s.modifiedTimestamp DESC LIMIT 20 

*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: s 
    partitions: NULL 
     type: index 
possible_keys: NULL 
      key: modifiedTimestamp 
     key_len: 9 
      ref: NULL 
     rows: 11 
    filtered: 100.00 
     Extra: Using where; Backward index scan 
*************************** 2. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: d 
    partitions: NULL 
     type: eq_ref 
possible_keys: PRIMARY 
      key: PRIMARY 
     key_len: 16 
      ref: test.s.difficulty_uuid 
     rows: 1 
    filtered: 100.00 
     Extra: NULL 

不再有任何文件排序,但我們看到反向索引掃描這是新的MySQL 8.0(我正在測試預覽版本)。

介紹此功能的博客http://mysqlserverteam.com/mysql-8-0-labs-descending-indexes-in-mysql/提到MySQL 5.7可以向後掃描索引,但比進行正向索引掃描要貴15%左右。 Jeremey Cole在InnoDB index internals上完成了博客和演示文稿,我記得他詳細說明了爲什麼降序索引掃描的成本更高,但我無法清楚地記得這裏解釋清楚。

+0

8.0之前無法進行向後索引掃描(但沒有拼寫出來)? –

+0

@RickJames,見上面最後一段的我的編輯。 –

+0

是的,這是關於前進的東西,然後備份。與無法向後掃描相比,15%的開銷是微不足道的,所以我忽略了它。 –

1

見什麼EXPLAIN這個現像,尤其是多行:

SELECT s.difficulty_uuid, s.modifiedTimestamp, 
     (
      SELECT name 
       FROM difficulty 
       WHERE uuid = s.difficulty_uuid 
     ) AS name 
    FROM exercise_session s 
    ORDER BY s.modifiedTimestamp DESC 
    LIMIT 20 

BTW,是的UUID作爲可怕的按鍵,當你有巨大的表。除了少數「分佈式」架構情況外,並不需要這麼做。改爲考慮使用AUTO_INCREMENT

+0

+1我同意避免UUID的建議。使用UUID的原因只有*,您試圖在分片或分佈式體系結構中保持唯一性。 –

0

我們通過不加入難題來解決問題,而是使用「WHERE IN」執行一個查詢來加載所有這些問題。

由@Bill Karwin建議的修正建議不起作用,因爲我們沒有使用MySQL 8.0(特別是不是預覽版)。在MariaDB 10.2.8下,它不起作用。

Rick James的建議也不起作用,因爲我們從difficulty表中加載了多個列。

相關問題