MySQL索引应用
MySQL有哪些索引(按字段特性分类)
MySQL有主键索引、唯一索引、普通索引、前缀索引、联合索引(tip:看建立在什么字段上面就是什么索引)
- 主键索引:就是建立在主键上的索引,通常与表一起创建,一张表最多只有一个主键索引,索引列的值不能为NULL
- 唯一索引:建立在UNIQUE字段上的索引,一张表可以有多个唯一索引,索引列的值必须唯一,但是允许有空值
- 普通索引:建立在普通字段上的索引,既不要求字段为主键,也不要求字段为UNIQUE
- 前缀索引:对字符类型字段的前几个字符建立的索引,而不是整个字段,目的是减少索引占用的空间,提升查询效率
- 联合索引:将多个字段组合成一个索引
MySQL主键是聚簇索引吗
是的。聚簇索引就是在定义索引时,将数据和索引放在同一课B+树上
- 每张表只有一个聚簇索引,会根据不同的情况选择不同的列作为索引
- 如果定义了主键,默认使用主键作为聚簇索引
- 如果没有主键,会选择第一个不包含NULL的唯一列作为聚簇索引的索引键
- 如果上述都没有,InnoDB将自动生成一个隐式自增id(row_id)作为聚簇索引的索引键
主键索引为什么不推荐有业务含义
- 首先业务具有变动的可能性,可能导致主键字段发生了变动,然而维护主键是成本很高的一件事
- 其次业务含义的主键可能不是顺序递增的,有可能发生页分裂的问题,从而影响性能
主键用自增还是UUID
- 用自增ID比较好,因为UUID是随机值,在数据插入的过程中,会导致页分裂的问题,性能下降
- 其次自增ID在分库分表的环境下不适用,因为没法保证全局唯一,需要使用雪花算法等来作为主键
普通索引和唯一索引有什么区别,哪个更好
普通索引的值可以是任意的,唯一索引的值必须是唯一的。
- 我认为普通索引性能更好,因为普通索引在更新的时候,如果更新的数据页不在内存的话,可以直接把更新操作缓存在change buffer中,更新操作就结束了
- 然而唯一索引因为需要有唯一性约束,如果更新的页不在内存中的话,需要从磁盘读取对应的数据页到内存中,判断是否发生唯一性冲突,会涉及到磁盘IO的访问,性能不如普通索引
介绍一下什么是外键约束
外键就是从表中用来引用主表中数据的那个字段,外键约束确保了数据的引用完整性,也就是从表中的外键必须存在于主表中的主键。如果发现要删除的主键记录,正在被从表的外键引用,就会发生外键约束错误,从而保证了两个表的数据一致性
外键有什么优劣势
优点:
- 一致性:如果一个表的键被另外一个表引用,外键可以保证这个字段一定存在于那个表中,从而保证了数据的一致性
- 完整性:外键可以防止在引用表中删除正在被其他表引用的记录,从而保证了数据的完整性
劣势:
- 性能问题:数据库的每次操作都要检查外键约束,硬性保证数据的一致性,会造成性能下降
- 锁竞争问题:在使用外键的情况下,每次修改数据库都要检查外键约束,这需要额外获取读锁,在高并发情况下很容易造成死锁
- 无法使用分库分表场景:因为外键难以跨越不同的库来建立关系,然而现在大部分项目为了应付高并发都会采用分库分表,因此外键也不适用了
为什么要建立索引
- 加快查询速度,在不加索引的情况下查询一条记录是O(n),有索引的情况下查询复杂度是O(logn)
- 避免外部排序和使用临时表等问题
- 在表数据量大的时候,为索引分配就不是按页,而是按区。每个区的大小是1MB,可以放下64个页,这样使得链表中相邻的页物理位置也相邻。可以将随机IO变成顺序IO
一般选择什么样的字段来建立索引
使用于索引的场景:
- 字段具有唯一性的建立唯一性索引
- 经常用于WHERE查询条件的字段,为了提高查询速度,可以建立索引
- 经常用于GROUP BY 和 ORDER BY的字段建立索引,这样查询的时候就不用再做一次排序了,因为索引在B+树中就是排序好的
不适合索引的场景:
- WHERE、GROUP BY、ORDER BY里用不到的字段,不需要建立索引
- 字段中存在大量重复数据的不需要建立索引,比如性别字段。就算建立了索引每次也可能搜索到一半的数据,在这种情况下,MySQL的优化器发现某个值在表中出现比例较高的时候,会忽略索引,进行全表扫描,这时候建立索引就没起到作用,反而还占用空间
- 经常更新的字段不需要建立索引,经常维护索引会影响数据库性能
索引越多越好吗
不是
- 因为索引需要占用空间,当索引过多的时候,占用空间很大
- 其次索引过多数据库维护成本过高,每次对表增删查改时,都需要维护B+树的各个索引
什么时候不用索引最好
- 建立了索引,空间上索引需要占用磁盘空间。其次时间上每次对数据库表增删查改时,需要维护B+树的各个索引,需要性能开销
- 首先如果一张表写多读少的情况下,不建议建立索引,因为维护索引的开销可能超过索引性能的提升
- 其次当一张表中某个列的值高度重复,那么建立了索引也没用,优化器会忽略索引,进行全表扫描,这样不仅占用了存储空间,还影响了增删查改的效率
字段为什么要定义成NOT NULL
- 如果查询的列包含NULL,对MySQL优化器来说,可能成本过高而使用全盘扫描
- 如果某列存在NULL的情况,可能导致count()函数不准确,因为count()函数不会统计值为NULL的列
- NULL虽然是没有意义的值,但会占用物理空间,行格式中会至少用1字节来存储NULL值列表
索引怎么优化
- 对于只需要查询几个字段的SQL来说,对这些字段建立联合索引,这样查询方式就变成了覆盖索引,避免了回表,减少了大量的I/O操作
- 主键索引最好是递增的值,因为随机值,在数据插入的情况下可能会造成页分裂,而页分裂会产生内存碎片,导致索引结构不紧凑,影响查询效率
- 避免写出发生索引失效的SQL,比如不要对索引进行计算、函数、类型转换操作,联合索引要遵循最左匹配原则
- 对于一些大字符串的索引,我们可以用前缀索引只对索引列的前缀部分建立索引,节省索引的空间,提高查询效率
建立了索引,查询的时候一定用得到索引吗
- 索引失效:对索引字段进行左模糊匹配,对索引进行计算、函数、类型转换操作,联合索引不遵循最左匹配,就会造成索引失效,查询不会走索引
- 优化器成本:在使用二级索引的时候,优化器会计算回表的成本和全盘扫描的成本,如果回表的代价太大,优化器会选择走全盘扫描
一个VARCHAR类型的字段time,WHERE time = 20230922 会命中索引吗
- 不会,因为MySQL在遇到字符串类型和数字类型比较的时候,会自动把字符串转换成数字,这个过程会执行CAST函数
- 于是在执行过程中会索引字段使用了函数,会导致索引失效
- 但如果反过来,where id = ‘1’, 如果id是整数不会导致索引失效,因为字符串对象是‘1’,函数发生在它的身上,而不是索引身上
MySQL最新版本解决了索引失效的问题了吗
MySQL8.0新特性:函数索引和索引跳跃扫描机制
- 函数索引:可以针对函数计算后的值建立索引,可以解决对索引使用函数导致索引失效的问题
- 索引跳跃扫描:在使用联合索引扫描的场景下,即使不满足最左匹配原则,在部分场景下依然会使用联合索引
索引跳跃扫描的条件:
- 查询只能涉及一张表,多表关联不能使用该特性
- 查询SQL不能涉及GROUY BY 和 DISTINCT子句
- 查询字段必须是索引中的字段(不能发生回表)
- 满足一定的组合索引形式,比如对于联合索引(a,b,c)来说,a,c可以为空,但是b不能为空
什么是最左匹配原则
假设有一个联合索引(a,b,c),它的存储顺序是先按a排序,a相同的情况下再按b排序,b相同的情况下再按c排序。最左匹配原则:
- 从联合索引最左边的索引列开始匹配查询条件,然后依次从左到右的顺序匹配,如果查询条件右边没有用到某个列,那么该列右边的所有列都无法走索引
- 当查询条件中使用了某个列,但是该列的值包含范围查询,范围查询的字段可以走联合索引,但是该字段后面的所有字段不能走索引
建立联合索引需要注意什么
- 把区分度大的字段放在联合索引最左侧,有助于提高索引的过滤效果,比如UUID这类字段放最左边
- 区分度小的放在联合索引最左侧,优化器可能根据成本考虑会选择全盘扫描,不走索引
索引下推是什么
索引下推能够减少二级索引在查询时的回表操作,将Server层负责的事情交给存储引擎层去处理
- 比如联合索引(a,b,c),查询条件为a = ?和 c = ? 时,由于最左匹配原则,c无法走索引。在没有索引下推之前,查询语句走二级索引的时候,会回表读取c的值,然后在server层进行过滤。如果有索引下推,由于二级索引中存在c,那么将server层的过滤交给存储引擎层,在二级索引中将c的值过滤,减少了回表的次数
- 查询条件 where a > 100 and b = 100 and c = 100 order by d 怎么建立索引
- 建立(b,c,d,a)的联合索引,这样b,c字段都会走索引,然后d能利用索引的有序性,避免了filesort。order by d 相当于范围查询,最后的字段a无法用到索引,但是可以通过索引下推,避免了回表
select id, name from XX where age > 10 and name like ‘xx%’, 有联合索引(name,age),说一下查询过程
tip:
先说哪些能走索引,哪些不能走索引 再说哪个字段可以索引下推 最后说查询需不需要回表(发不发生索引覆盖)
- 联合索引先走name,再age。结构上是name先排序,然后age排序。查询的过程先匹配name,由于name是右模糊匹配,并不会发生索引失效,但是在右模糊匹配后(相当于范围查询),age并不是有序,因此age无法走索引
- 但是age在二级索引中,因此age可以索引下推
- 最后,id由于是主键,因此id和name都可以在二级索引中找到,会发生索引覆盖,不需要回表
where id not in (X, X, X)会走索引吗
要看查询成本,如果走某个索引花费的随机IO从聚簇索引查(顺序IO)的成本还要高,就不会走索引
比如,num字段(非唯一二级索引)只包含3个值,1,2,3。 然而1,2各有100W行,3只有几行,那么not in (1, 2)会走索引,not in (3)不会走索引
如果查询条件包括索引列和非索引列,查询过程是怎么样的
查询过程先根据二级索引拿到主键id,再回表过滤非索引列,查询过程会查两个B+树,涉及回表
比如select a from XX where a = ? and d = ? ,a是索引,d不是索引,会先按索引查询,再回表过滤d
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 cloud_fly blog!