【发布时间】:2019-07-26 16:25:03
【问题描述】:
我正在尝试在 mysql 中建立一个索引来支持键集分页查询。我的查询如下所示:
SELECT * FROM invoice
WHERE company_id = 'someguid'
AND id > 'lastguidfromlastpage'
ORDER BY id
LIMIT 10
这方面的常识表明,company_id 上的索引将包含表的主键 (id)。因此,我希望能够直接使用索引中的行,而无需查询首先对结果进行排序,但是我的解释计划显示了文件排序和索引合并:
mysql> explain SELECT *
-> FROM invoice
-> WHERE company_id = '37687714-2e9d-4daa-aee6-f7d56962f903'
-> AND id > '525ae038-0cc3-4f9a-85e6-6f36d43fae40'
-> ORDER BY id
-> LIMIT 10;
+----+-------------+---------+------------+-------------+-----------------------------+-----------------------------+---------+------+------+----------+---------------------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------------+-----------------------------+-----------------------------+---------+------+------+----------+---------------------------------------------------------------------------+
| 1 | SIMPLE | invoice | NULL | index_merge | PRIMARY,invoice__company_id | invoice__company_id,PRIMARY | 76,38 | NULL | 48 | 100.00 | Using intersect(invoice__company_id,PRIMARY); Using where; Using filesort |
+----+-------------+---------+------------+-------------+-----------------------------+-----------------------------+---------+------+------+----------+---------------------------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)
如果我明确地将 id 添加到索引中,那么我会得到我期望的解释计划:
mysql> explain SELECT *
-> FROM invoice
-> WHERE company_id = '37687714-2e9d-4daa-aee6-f7d56962f903'
-> AND id > '525ae038-0cc3-4f9a-85e6-6f36d43fae40'
-> ORDER BY id
-> LIMIT 10;
+----+-------------+---------+------------+-------+--------------------------------+--------------------------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+--------------------------------+--------------------------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | invoice | NULL | range | PRIMARY,invoice__company_id_id | invoice__company_id_id,PRIMARY | 76 | NULL | 98 | 100.00 | Using index condition |
+----+-------------+---------+------------+-------+--------------------------------+--------------------------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
显示创建表:
CREATE TABLE `invoice` (
`id` varchar(36) NOT NULL,
`company_id` varchar(36) NOT NULL DEFAULT '0',
`invoice_number` varchar(36) NOT NULL DEFAULT '0',
`identifier` varchar(255) NOT NULL,
`created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_by` varchar(36) DEFAULT NULL,
`data_source` varchar(36) NOT NULL,
`type` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `invoice__company_id_id` (`company_id`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
选择@@optimizer_switch;
use_index_extensions=on
MySQL 版本:
- 版本:5.7.26-29-57-log
- innodb_version: 5.7.26-29
- version_comment:Percona XtraDB Cluster (GPL),Release rel29,Revision 03540a3,WSREP 版本 31.37,wsrep_31.37
显示 'char%' 等变量;
character_set_client utf8
character_set_connection utf8
character_set_database latin1
character_set_filesystem binary
character_set_results utf8
character_set_server latin1
character_set_system utf8
character_sets_dir /usr/share/mysql/charsets/
有一些消息来源解释说,company_id 索引本身就足够了:
我一直无法找到有关预期结果的官方文档。这与 id 的数据类型有关吗? mysql+innodb行为的常识有误吗?
【问题讨论】:
-
此手册页可能是您正在寻找的内容:dev.mysql.com/doc/refman/5.6/en/index-extensions.html 注意这仅适用于 MySQL 5.6 及更高版本。如果您使用的是旧版本的 MySQL,则未实现索引扩展优化。
-
能否添加“SHOW CREATE TABLE invoice”的输出?
-
@jkavalik 是的,完成了。抱歉,我是想发布那个...
-
mysql版本是多少?
-
@Michael 据我所知,它应该可以工作,但我已经报告了一个错误(尽管是 MariaDB),优化器选择了无意义的 index_merge (就像你的,合并已经存在的列)所以我认为它是相似的optimizer hickup - 您可以尝试禁用 index_merge 或使用
ignore index(PRIMARY)来查看原始索引是否能够像手动扩展索引一样工作。很难说是什么触发了它,可能是那些 UUID 的统计信息,可能是 varchar 本身(有什么理由不使用固定大小的字符?)
标签: mysql indexing query-optimization innodb