索引下推:
ICP过滤的条件可以不限于用于索引查找(index lookup)的字段。只要存储引擎在扫描当前索引时能够访问到该字段的值,就可以用它来过滤。
索引可以分为聚簇索引和非聚簇索引
没有索引下推:
当使用聚簇索引的时候,b+树的叶子结点就是数据项,但是如果没有索引下推的话,即使数据中已经可以拿到其余的数据项,但是仍然不会进行过滤
例如where name = 张% and age < 30,只在name上有聚簇索引
即使在存储引擎根据name查询得到的数据是完整的数据,可以拿到age字段的值,但是仍然不会对age条件进行过滤
使用非聚簇索引的时候,b+树的叶子结点是主键索引的值,例如一个联合索引(a,b,c),如果只匹配到a,那么即使where条件中有c的条件,也不会在这个时候进行过滤,只会根据a的索引得到所有主键id,然后返回给服务器,服务器再根据主键id去查询所有数据,返回到服务层再进行过滤,这个过程就是回表
有索引下推:
会将where的条件下推到存储引擎层,存储引擎层就会利用自己所能获得的数据更多的进行过滤
同样上面两个例子:
如果是聚簇索引的话,存储引擎可以得到完整的数据,就可以在这个时候根据age字段进行过滤,将过滤之后的数据返回给服务器层
如果是非聚簇索引的话,在联合索引(a,b,c)中虽然只匹配到a的索引,但是可以获得c字段的值,也可以通过where中c的值进行过滤,返回更少的主键id给服务器层去进行回表
索引失效:
就是明明可以使用索引来提升效率但是没用上索引
1.索引类型不匹配
例如索引age的数据类型是整数类型,但是查询的时候使用的是age = '20'
这样就会导致隐式类型转换,导致无法使用age上的索引
2.使用函数表达式
age上有索引,但是使用的是where f(age) = 30,索引无法生效
3.进行运算
age有索引,但是where age + 20 < 30,这样的运算实际也会导致索引失效
虽然后面mysql优化可以给带运算的字段加索引,但是不是一定生效的
4.like查询且以通配符(如 %
或 _
)开头时
name上有索引,但是like '%张'会导致name上的索引失效,但是like '张%'可以用上索引
5.or条件
or两侧是同一个索引:索引生效
or两侧是多个索引:优化器通常会评估索引之间的效率,选择其中一个索引使用或者全表搜索,如果有索引合并的话,会分别将索引查找的集合进行union合并
6.not in和not like
NOT IN
的否定性质使其难以利用索引的有序性进行快速定位(B+树的优势在于快速找到“等于”或“在某个范围内”的值),但它仍然可以利用索引进行扫描,但是不是利用索引查询
not in的时候如果范围内有null的话,会导致查不到数据
7.在联合索引中,如果前导索引是范围查询,后续索引不会生效
比如联合索引(a,b),如果是where a>10 and b = 20;只会匹配到索引a,索引b不会生效
对not in的改进:
例如:
- 表
users
:有 10000 个用户,id
是主键(有索引)。 - 表
banned_users
:有 10 个被封禁的用户,user_id
列有索引
1.使用not in
SELECT * FROM users
WHERE id NOT IN (SELECT user_id FROM banned_users);
如果banned_users中存在一条user_id为null的记录的话,那这条查询就会出问题,查不到任何数据
2.使用not exists
SELECT * FROM users u
WHERE NOT EXISTS (SELECT 1 FROM banned_users b WHERE b.user_id = u.id
);
实际上对user表也是全表检索的,但是如果banned_users的user_id有索引的话可以用上这个的索引
3.使用left join
SELECT u.*
FROM users u
LEFT JOIN banned_users b ON u.id = b.user_id
WHERE b.user_id IS NULL;
左连接以左边的表为主,把右边的表按照u.id=b.user_id拼过来,如果不存在b.user_id的话,就是null