高性能的索引策略
1 独立的列
“独立的列”:索引列不能是表达式的一部分,也不能是参数的参数,必须将索引列单独放在比较符号的一侧
mysql> select actor_id from sakila.actor where actore_id + 1 = 5;【X】
mysql> select ... where to_days(current_date) - to_days(date_col) <= 10;【X】
2 前缀索引和索引选择性
有时候需要索引很长的字符串列,但这样会让索引变得很大且慢。这时候需要索引字符串列的一部分字符串,这样可以大大节约索引空间,从而提高索引效率,但是这样有可能降低索引的选择性。
【索引选择性:不重复的索引值(基数)和数据表的记录总数(#T)的比值,范围从1/#T 到 1 之间,索引选择性越高,查询效率越高,因为选择性高的索引可以让mysql过滤掉更多的行】
我们需要通过特定的方法确认,当前缀索引长度为N时,这N个字符的选择性已经接近整个字段的选择性,如下面例子所示,当N为7时,再增加前缀长度,选择性的增加已经很小。
select count(distinct left(city,3))/count(*) as sel3, count(distinct left(city,4))/count(*) as sel4, count(distinct left(city,5))/count(*) as sel5, count(distinct left(city,6))/count(*) as sel6, count(distinct left(city,7))/count(*) as sel7 from sakila.city;<style></style>
+--------+--------+--------+--------+--------+
| sel3 | sel4 | sel5 | sel6 | sel7 |
+--------+--------+--------+--------+--------+
| 0.7633 | 0.9383 | 0.9750 | 0.9900 | 0.9933 |
+--------+--------+--------+--------+--------+
增加前缀索引:mysql> alter table sakila.city add key(city(7));
-- 原始ddl,city字段上没有索引 CREATE TABLE `city` ( `city_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, `city` varchar(50) NOT NULL, `country_id` smallint(5) unsigned NOT NULL, `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`city_id`), KEY `idx_fk_country_id` (`country_id`), KEY `city` (`city`(7)), CONSTRAINT `fk_city_country` FOREIGN KEY (`country_id`) REFERENCES `country` (`country_id`) ON UPDATE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=601 DEFAULT CHARSET=utf8mb4 -- 查询的执行计划 mysql> explain select * from city where city = 'Ahmadnagar'; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | city | NULL | ALL | NULL | NULL | NULL | NULL | 600 | 10.00 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) -- 增加city字段索引后的ddl CREATE TABLE `city` ( `city_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, `city` varchar(50) NOT NULL, `country_id` smallint(5) unsigned NOT NULL, `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`city_id`), KEY `idx_fk_country_id` (`country_id`), KEY `city` (`city`(7)), CONSTRAINT `fk_city_country` FOREIGN KEY (`country_id`) REFERENCES `country` (`country_id`) ON UPDATE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=601 DEFAULT CHARSET=utf8mb4 -- 查询的执行计划,查询已经可以走上索引了。 mysql> explain select * from city where city = 'Ahmadnagar'; +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+ | 1 | SIMPLE | city | NULL | ref | city | city | 30 | const | 1 | 100.00 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
前缀索引是一种能让索引更小、更快的方法,但是也有缺点:
1 无法使用前缀索引做order by 和 group by
2 无法使用前缀索引做覆盖扫描