文章目录
- 引言
- 正文
- B站网课
- 索引基础
- 创建索引
- 如何在一个表中查看索引
- 为字符串建立索引
- 全文索引
- 复合索引
- 复合索引中的排序问题
- 索引失效的情况
- 使用索引进行排序
- 覆盖索引
- 维护索引
- 数据库基础——文档资料学习整理
- 创建索引
- 删除索引
- 创建唯一索引
- 索引提示
- 复合索引
- 聚集索引
- 索引基数
- 字符串前缀索引
- 索引顺序
- 对索引的隐式类型转换
- 索引下推
- 结合面试题回顾
- 如何避免索引失效
- 如何优化索引
- 总结
引言
- 今天怎么都得看完,不然项目都没时间整了,简历这周怎么都得搞完,不然肯定得挂!不行!
- 这里重在于讲述索引的东西,先回顾一下索引基础知识、然后就是索引的原理以及如何设计索引才能更加高效。
- 数据来源主要有两部分,分别是
- B站的SQL进阶教程
- 数据库教程网站
正文
B站网课
索引基础
-
索引是能够提高数据库从表中检索数据行的速度的一种数据结构,但是需要额外的写入和存储来维护
- 查询优化器,可以使用索引来快速定位数据,不需要全表逐行扫描
-
索引很小,大部分都是保存在内存中的,所以,从内存中读取数据总是比磁盘中读取数据来得快
索引的代价 -
增加数据库空间消耗,作为数据库的一部分,会一直和表格一块保存
-
每次增删改都需要的维护更新索引
综上
- 为性能关键的查询保留索引,需要基于查询创建索引,主要是为了加快部分查询十分缓慢的查找。
创建索引
- 通过Explain关键字查看是否使用了索引,主要是通过Type关键字和row关键字查看
- type是使用的索引类型
- row是查询的行数
创建索引,加速查询的过程
create index idx_state on table_name(col_name);
- possible_keys
- 在查询过程中,可能用到的索引,这里是使用了idx_state,如果是联合查询,会是多个索引值
- key
- 在实际查询中,所使用索引或者键,上面那个是可能使用的,下面那个是实际使用的
- 在实际查询中,所使用索引或者键,上面那个是可能使用的,下面那个是实际使用的
select customer_id from customers where points > 100; // 选择所有分数大于100的顾客的id
create index idx_points on customers(pointers); // 对customer表格的pointer列创建索引
如何在一个表中查看索引
show indexes in customers;
- 聚簇索引
- 二级索引
- 每一次创建一个二级索引,都会自动将主键索引加入到对应二级索引中
- 二级索引:二级索引字段 + 主键索引
为字符串建立索引
- 主要是为
- char、varchar、text和blob创建索引
- 这类索引会占据大量空间,无法达到很好的性能。所以,这里要尽量包含字符串的前几个字母,也就是前缀。
创建一个长度为20的前缀索引
- 创建针对customers表格的last_name列的前二十个字符构成的字符串
create index idx_lastname on customers (last_name(20));
- 这个长度选择,要能偶尽可能在短的情况下,遍历到所有的数据
全文索引
- 查询文章或者题目中和“react redux”相关的所有的posts,下述方法存在一些问题
- 随着数据库越来越大,搜索的范围越大,时间越慢
- 全表扫描,没有索引
创建全文索引==》实现模糊查询,像搜索引擎一样
# 创建全文索引
create fulltext index on table_name(col_A,col_B);
# 使用全文索引
select *
from table_name
where match(col_A,col_B) against ('这里输入相关的关键字');
- 相关性得分
- MySQL会基于若干因素,为包含了搜索短语的每一行计算相关性得分,是0到1之间的浮点数。
全文索引的两种方式
- 自然语言模式,默认模式,就是上文使用模式
- 布尔模式,包括或者是排除某些单词
- 下述使用的布尔模式,查询包含了react和form,但是不包含redux 关键字的。
- 下述使用的布尔模式,查询包含了react和form,但是不包含redux 关键字的。
复合索引
- 找到位于加州并且收入大于8000的,这里加州和收入是两张表里面的数据
# 这里是做了一个连表查询
select customers_id from customers where state ='CA' and points > 1000;
- 具体执行过程
- 因为只用到一个索引,所以就是先找到所有州为CA的用户,然后在查询这些用的points
- 因为只用到了一个索引,如果能够CA里面是有points >1000的索引,就快很多了。这就是联合索引的作用
- 对于州和point两个列建立联合索引,然后能够通过州和point快速访问到目标
create index idx_state_points on customers(state,points);
补充
- 复合索引如果匹配到的范围查找,就不走索引了,后续会走索引下推
- 复合索引的最左匹配原则,不是说顺序,是说具体的值,where a and b and c 对于索引(a,b,c)是满足最左匹配原则的,但是如果是where c and b就不满足了,因为少了一个。
复合索引中的排序问题
遵循以下两个原则
- 使用最频繁的列放在前面
- 将使用最频繁的放在前面,能够有效缩减搜索范围
- 将基数最高的放在前面
- 可以将总得样本,划分成数量跟少的样本,前面的搜索的范围会更小
- 关注查询本身,根据查询本身进行优化,尽量缩减问题搜索的空间
在州和用户姓氏两个关键字上创建一个联合索引
create index idx_lastname_satte on customer(last_name,state);
- 下述是last_name在前,state在后,扫描的列是40
- 下述是使用州在前,然后姓氏在后,仅仅查询了两行,效率更好
索引失效的情况
-
下述是使用or进行索引联合,通过explain可以看到,是查询了1010个数据,相当于全表扫描
-
下述是使用union将两个子索引查询进行拼接,总共扫描了660个数据,远远小于第一个索引拼接方式
调用列进行了相关的运算
- 下述进行了数字迁移,然后扫描量变成了3,因为虽然使用了比较函数,但是并没有调用对应对的列进行运算
使用索引进行排序
- 添加索引的时候,MySQL会获取该列中的所有的值,并对结果进行排序,并将他们存储在索引中
使用没有对应索引的列进行排序==》产生外部排序,外部排序十分耗时,通过下图可以看到外部排序的时间耗费是第一个时间复杂度的10倍。using filesort关键字进行排序
- 下述做的排序是一个全标扫描,进行的排序
- 正常情况下,如果你要对数据进行排序,而且使用的是联合索引的中的两个列,那么必须要按照的相同顺序或者相同的升降顺序进行查询和排序的,否则会增加消耗时间。
特殊情况:一定要按照的联合索引的列进行排序查询,否则就会出现对应的全表扫描 - 因为建立联合索引的时候,实现按照第一个列state进行分类的,然后在同一个state中,是按照points进行排序的。现在要直接points进行全部排序,就用不到索引了。
覆盖索引
- 下述是覆盖索引,需要查询的数据在索引中就存在,不需要在会表进行查询即可获得,效率很高。这种现象就是索引覆盖!
- select子句中查看的所有的数据列,都在索引中,就不需要在通过回表进行查询,这就是索引覆盖
维护索引
- 重复索引:相同列的不同顺序(A,B,C)和(B,A,C)
- 多余索引:索引重复的情况,A和(A,B)
数据库基础——文档资料学习整理
创建索引
索引的定义
- 索引是一种能够加快数据检索的数据结构,但是需要额外的写入和存储来维护
- 查询优化器能够通过索引,快速定位数据,不必扫描表中的每一行
- 索引本身和数据一起存储在同一表中。
聚簇索引和非聚簇索引
- 聚簇索引:
- 使用主键或者唯一键创建表的时候,会自动创建一个名为primary的索引
- 非聚簇索引
- 二级索引或者非聚簇索引是除了聚簇索引以外的索引。
创建索引的语法
CREATE [UNIQUE] INDEX index_name
[USING {BTREE | HASH}]
ON table_name (column_list)
[algorithm_option | lock_option];
- 使用explain来查看对应的SQL执行情况
- 查看当前已经创建的索引
删除索引
- 索引需要有空间代价和时间代价,所以需要删除
drop index index_name on table_name;
创建唯一索引
- 虽然已经有了主键索引唯一索引,但是有的时候,还是需要创建自增的数字列,比如说订单表中的订单编号,用户表中的电子邮件等
create unique index index_name on table_name (col_name);
索引提示
- MySQL的查询优化器为SQL语句制定最佳执行计划,根据索引基数进行决策,有的时候,你创建了索引但是没有使用也是因为索引基数不对。
- 使用use index强制sql语句建议查询优化器使用指定的索引。
SELECT column_list
FROM table_name
USE INDEX (index_list)
WHERE condition;
复合索引
定义
- 复合索引又称为组合索引或者是多列索引,最多能够创建16个列
创建语法
CREATE INDEX index_name
ON table_name(column_1, column_2, column_3);
复合索引规则
- 将where子句中常用的列放在索引列列表的开头
- 将不常用的列放在索引列列表的后面
MySQL总是按照最左匹配的原则展开对应的查询语句
CREATE INDEX index_name
ON table_name(a, b, c);
- 在上述索引的情况下,如果查询使用的是下述顺序都会使用对应的索引,a先满足了,然后在去看b
WHERE a = v1 and b = v2 and c = v3;
WHERE a = v1 and b = v2;
WHERE a = v1;
- 如果是下述情况,就不会使用对应的索引
WHERE b = v2 and c = v3;
WHERE c = v3
聚集索引
定义
- 聚集索引是一种特殊的索引,该索引中的键值顺序决定了表中相应行的物理顺序。
- 表格中的数据,只能按照一种顺序进行存储,所以表中只能有一个聚集索引。
InnoDB中的MySQL聚集索引
- 如果指定了主键,主键就是聚集索引
- 如果没有逐渐,第一个非空的列,并且是Unique的列,就是聚集索引
- 如果没有合适的,MySQL会在内部生成一个隐藏的聚集索引。
- InnoDB中二级索引中的每条记录都包含该行的主键列和非聚集索引指定的列
索引基数
定义
- 一个索引的基数,就是这个索引列中唯一值的数量
- 是根据统计信息生成的估计值,并不准确
- 是查询优化器决定是否索引的依据,基数越高,索引越有效!
- 基数越低,索引越无效,还不如全表扫描!
查看索引基数
show indexes from table_name;
字符串前缀索引
定义
- 为字符串列创建前缀索引,
- 相比于对整个字符串创建索引,前缀索引能够减少磁盘的使用量,提高索引的写入速度。
具体语法
create index idx_name on table_name (col(prefix_length));
- 如果你使用对应的where的部分匹配,如果这对对应的字段创建了前缀索引就会使用对应索引加快速度,而不是全局扫描
select * from table_name where first_name like 'ge%';
索引顺序
定义
- 在创建索引的时候,指定索引的顺序,默认情况下,是按照升序存储的。
CREATE [UNIQUE] INDEX index_name
[USING {BTREE | HASH}]
ON table_name (column_name [ASC | DESC], ...)
[algorithm_option | lock_option];
对索引的隐式类型转换
- 如果索引字段是字符串类型,但是在条件查询中,输入的参数是整型的话,会走全表扫描。
- 如果索引字段是 整型类型,查询条件中输入的参数是字符串,不会导致索引失效的
MySQL在遇到字符串和数字比较的时候,会自动把字符串转为数字,然后在进行比较
- 自动类型转换是函数操作,CAST操作
# 下述两个SQL语句是等价的
select * from t_user where phone = 1300000001;
select * from t_user where CAST(phone AS signed int) = 1300000001; # 这里是对phone进行了函数操作
# 下述的SQL语句会使用索引进行扫描,因为是将输入的参数的进行类型转换,而不是将索引进行类型转换
select * from t_user where id = "1";
select * from t_user where id = CAST("1" AS 1);
索引下推
这部分的所有资料都是来自这个链接——五分钟搞懂MySQL索引下推
定义
- 能够减少回表查询的次数,提高查询的效率
- 将部分上层也就是服务层负责的事情,交给下层引擎层去处理
- 通过Extra中using index condition来进行判定
原理
-
没有索引下推的情况
- 存储引擎读取索引记录
- 根据索引中的主键值,定位并读取完整的行记录。
- 存储引擎吧记录交给Server层,检测记录是否满足Where子句条件
-
有索引下推的情况
- 存储引擎读取索引记录
- 新增操作:判断where条件部分能够用索引中的列来检查,条件不满足,就处理下一行索引记录
- 条件满足,使用索引中的之间,去定位并读取完整的行记录,也就是回表
- 存储引擎吧记录交给Server层,检测记录是否满足Where子句条件
具体执行实例
- 这里已经建立了联合索引,会按照最左匹配的原则,先查的name然后就是age
select * from tuser where name like '张%' and age = 10;
-
没有ICP的时候
- 数据引擎仅仅会使用第一个索引,返回所有姓张的列,然后由server层进行过滤,每一个姓张的样本都会进行回表查询,效率低
-
有ICP
- 数据引擎会在底层同时使用两个索引,查找到姓张的行后,会对年龄进行判定, 减少了回表的次数,效率比较高!
这里还是看一下原来的链接更容易理解
索引下推使用的条件
- 只能用于range、ref、eq_ref、ref_of_null访问方法
- 对于InnoDB来说,只能用于二级索引
- 使用子查询的条件不能下推
- 引用了存储函数条件的不能下推
具体应用场景
- 联合索引在遇到范围查询时,会停止匹配,后续的字段就不会在使用
select * from t_user where age >20 and reward = 10;
- 有了索引下推之后,即使reward无法走索引,但是在二级索引里面,会在存储引擎中进行过滤,减少回表次数
结合面试题回顾
如何避免索引失效
个人学完之后的回答
- 不要使用or语句,如果是or的话,建议拆成多个不同的语句进行拼接
- 不要对创建索引的列加上对应的运算,比如说加减乘除等,col + 2 > edge,不如改成 col > edge -2;
- 如果是联合索引,在where子句中,一定要先先使用满足最左列,然后依次往后
- 尽量使用基数比较大的索引。
参考回答
- 使用左或者左右模糊匹配的时候,会造成索引失效,比如说like %xxx 或者 like %xxx%
- 在查询条件中对索引列做了计算、函数、类型转换的操作,会造成索引失效的。
- 联合索引要遵循最左匹配原则,按照最左有限的方式进行索引匹配,否则会导致索引失效。
- 使用where子句时,or的前列是索引列,后列不是索引列,索引会失效
如何优化索引
- 在创建联合索引的时候,观察select对应列,尽量创建覆盖索引,避免回表,减少大量的IO操作性能。
- 防止索引失效,尽量不要写会让索引失效的SQL语句
- 主键索引最好是单调递增的值
- 主键是随机的值,插入会引起页分裂现象,导致大量的内存碎片
- 对于大的字符串索引,考虑使用前缀索引只对前缀部分简历索引,节省索引的存储空间。
总结
- 如果我在面试拼多多之前,就把这个东西整理了,或者说看了,也就不会那么尴尬,现在已经去实习了,很难顶!
- 不过等到秋招的正式批,这个问题应该是难不倒我了,看一下,回顾一下就行了!
- 加油吧!整理这个的时候,满心都是后悔,下次不能让这种事情发生!