Mat*_*ick 5 mysql performance index-tuning query-performance
在这个查询中:
select count(*) from largetable;
Run Code Online (Sandbox Code Playgroud)
选择二级索引:
mysql> explain select count(*) from largetable;
+----+-------------+------------+-------+---------------+------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+-------+---------------+------+---------+------+----------+-------------+
| 1 | SIMPLE | largetable | index | NULL | iif | 5 | NULL | 50000169 | Using index |
+----+-------------+------------+-------+---------------+------+---------+------+----------+-------------+
1 row in set (0.00 sec)
mysql> select count(*) from largetable;
+----------+
| count(*) |
+----------+
| 50000000 |
+----------+
1 row in set (5 min 52.02 sec)
Run Code Online (Sandbox Code Playgroud)
而强制使用聚集索引:
select count(*) from largetable force index (primary);
Run Code Online (Sandbox Code Playgroud)
提供更好的性能:
mysql> explain select count(*) from largetable force index (primary);
+----+-------------+------------+-------+---------------+---------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+-------+---------------+---------+---------+------+----------+-------------+
| 1 | SIMPLE | largetable | index | NULL | PRIMARY | 4 | NULL | 50000169 | Using index |
+----+-------------+------------+-------+---------------+---------+---------+------+----------+-------------+
1 row in set (0.00 sec)
mysql> select count(*) from largetable force index (primary);
+----------+
| count(*) |
+----------+
| 50000000 |
+----------+
1 row in set (2 min 23.07 sec)
Run Code Online (Sandbox Code Playgroud)
所以这是 5 分 52 秒对 2 分 23 秒。
我想了解为什么 MySQL 的查询优化器选择二级索引。
表中有 5000 万行,ID 为 1 到 5000 万(无间隙),按顺序插入。
这是在 MySQL 5.5.11 上。
这是桌子的设计:
create table largetable (
id int primary key auto_increment,
field1 int,
index iif (field1),
... some more columns, some with indexes ... each row is about 115 bytes ...
);
Run Code Online (Sandbox Code Playgroud)
该问题可能源于 MySQL 查询优化器做出选择的方式以及索引在 InnoDB 内部表示的方式。
首先看一下索引的基数。主键的基数必须始终是 InnoDB 表的实际行数。现在,看看字段1 的基数。如果索引iif小于主键的iif,MySQL查询优化器将选择辅助索引。要验证 field1 的基数是否较低,请运行以下查询:
SELECT COUNT(DISTINCT field1) FROM largetable;
SELECT field1,COUNT(1) fieldcount FROM largetable
GROUP BY field1 WITH ROLLUP;
Run Code Online (Sandbox Code Playgroud)
现在,看看索引的内部表示。二级索引将包含两项:1) 被索引的列值,2) 来自聚集索引的 rowid(也称为 gen_clust_index )。每次在二级索引中引用一列时,也会查找实际行。想象一下:两个键查找 InnoDB 中的每一行。
将这两个问题放在一起,您会发现基数低于主键的二级索引仍将使用主键查找实际行。这解释了为什么选择二级索引而不是主键,并且查询时间需要两倍甚至更长的时间。
如果 Percona 的任何人看到这个问题和我的答案,并发现我的推理有任何缺陷,请纠正我,以便所有人都能学习。
InnoDB 存储引擎深入研究 BTREE 索引,对基数进行有根据的猜测。尝试将innodb_stats_on_metadata设置为关闭
[mysqld]
innodb_stats_on_metadata = 0
Run Code Online (Sandbox Code Playgroud)
根据文档,禁用后,InnoDB 在这些操作期间不会更新统计信息。禁用此变量可以提高具有大量表或索引的模式的访问速度。它还可以提高涉及 InnoDB 表的查询的执行计划的稳定性。