合理地对mysql数据表加上索引可以大大加快数据的访问效率,但也并不是在给表加上索引后就可以在sql中随心所欲地对加上索引的字段进行各种查询;如果sql的结构不当,会导致原本设置的索引失效。所以为避免采坑,写下此文,以做记录。以下用到的例子假设有这样一张表:
CREATE TABLE `user_info` (
`id` bigint(32) NOT NULL,
`name` varchar(32) DEFAULT NULL,
`age` int(3) DEFAULT NULL,
`address` varchar(32) DEFAULT NULL,
`nick_name` varchar(32) DEFAULT NULL,
`role_id` bigint(32) DEFAULT NULL,
`create_time` date DEFAULT NULL,
`update_time` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
一、尽量选择复合索引
在对表创建索引时,如果可以使用复合索引,尽量使用复合索引而不是单列索引。理由如下,现创建的一个复合索引:
CREATE INDEX idx_name_age_status ON user(name, age, status);
这里创建了一个名为idx_name_age_status的复合索引,实际上是创建了三个索引,分别是name, name + age, name + age + stauts,那么在使用name或name + age或name + age + stauts进行查询时,都可以使用到索引。而如果分别给三个字段创建索引,查询这张表时条件有这三个字段,数据库会选择一个最优的索引,而不会使用全部三个索引。
二、最左前缀法
关系到复合索引时,注意最左前缀法,也就是查询的条件从索引的最左列开始,并且不跳过中间列,比如以下索引:
CREATE INDEX idx_name_age_status ON user(name, age, status);
使用name或name + age或name + age + status进行查询能使索引生效,而使用单个查询条件age或status或者age + status 进行查询不会使索引生效,而使用name + stauts进行查询只会有name字段的索引生效。
三、复合索引中使用到范围查询右边的列索引不会生效
在用到复合索引时,复合索引中使用到范围查询右边的列索引不会生效。比如创建如下索引:
CREATE INDEX idx_name_age_status ON user(name, age, status);
使用这三个字段作为查询条件,处于中间的age如果用到了范围查询,则status上的索引不会生效。
-- name + age + nick_name索引生效
SELECT * FROM user_info WHERE name = '张三' AND age = 22 AND nick_name = '三儿';
-- age用到了范围查询,仅name + age索引生效
SELECT * FROM user_info WHERE name = '张三' AND age > 22 AND nick_name = '三儿';
四、在索引列上进行运算操作后,索引将失效
在索引列上进行运算操作后,索引将失效。比如有这样一个索引:
CREATE INDEX idx_age ON user_info(age);
如果是where age = 22,这个索引会生效,但如果是age > 22,这个索引不会生效。
五、模糊匹配不生效的情况
查询条件中的模糊条件,如果是以%开头,会不走索引,比如SELECT * FROM user_info WHERE name like '%三儿%'或SELECT * FROM user_info WHERE name like '%三儿',这样不会走索引;而SELECT * FROM user_info WHERE name like '三儿%',索引是有效的。
六、查询时字符串如果没加引号,索引失效
查询时字符串如果没加引号,索引失效。在mysql中,假设有一个字段名称是name,类型为varchar,使用where name = 张三这种写法是错误语法,如果使用SELECT * FROM user_info WHERE name = 12虽可以进行查询的,但这样索引是不会生效的。
七、以or分隔的查询条件中,如果存在没有索引的列,分隔所关联的索引不会生效
以or分隔的查询条件中,如果存在没有索引的列,分隔所关联的索引不会生效,比如给name列加上索引,以SELECT * FROM user_info WHERE name = '张三',这样写索引自然会生效,而如果有一列,比如nick_name上没有索引那么使用SELECT * FROM user_info WHERE name = '张三' OR nick_name = '三儿',name列上的索引也会失效。
八、尽量不使用SELECT *,最好只查询索引列
尽量使用覆盖索引,也就是尽量只查询索引列,比如复合索引如下:
CREATE INDEX idx_name_age_status ON user(name, age, status);
使用SELECT * FROM user_info WHERE name = '张三' AND age = 12 AND status = 1进行查询,索引自然会生效,但是效率是肯定不如SELECT name, age, status FROM user_info WHERE name = '张三' AND age = 12 AND status = 1。另外,在sql中尽量不要使用SELECT * FROM user_info这样的语法,而是尽量只查询需要的列,因为这样会影响查询的效率。