为什么MySQL会发出一个文件出口?

时间:2017-09-09 01:51:53

标签: mysql sql indexing

这是我的表定义:

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执行文件排序而不是使用复合索引?

3 个答案:

答案 0 :(得分:2)

在对优化程序对它们的处理做出结论之前,您应该用更多的测试数据填充这些表。在每个表中只有一行或两行可能会触发非典型的优化器计划。

我尝试用几十行填充表格,并得到了这个EXPLAIN:

%matplotlib tk

仍然是一个文件分区,但至少它以正确的顺序访问了表:*************************** 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类型的访问权限。

使用索引提示表扫描成本太高而无法考虑,它使用modifiedTimestamp上的索引:

eq_ref

不再是任何文件存储,但我们看到向后索引扫描这是MySQL 8.0中的新功能(我使用预览版本进行测试)。

介绍此功能的博客http://mysqlserverteam.com/mysql-8-0-labs-descending-indexes-in-mysql/提到MySQL 5.7可以向后扫描索引,但它比进行正向索引扫描的成本高出约15%。 Jeremey Cole已经在InnoDB index internals上做了博客和演示,我记得他确切地说明了为什么降序索引扫描成本更高,但我不能清楚地回想起它来解释这里。

答案 1 :(得分: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 

顺便说一下,当你拥有庞大的桌子时,UUID很糟糕。除了一些“分布式”架构情况外,并不是真正需要的。请考虑AUTO_INCREMENT的用户。

答案 2 :(得分:0)

我们通过不加入难度来解决问题,而是使用" WHERE IN"来做一个查询。之后加载所有这些。

@Bill Karwin建议的修复程序不起作用,因为我们没有使用MySQL 8.0(尤其不是预览版)。在MariaDB 10.2.8下它没有用。

里克詹姆斯'建议也不起作用,因为我们从difficulty表中加载了多个列。