MySQL索引与锁

MySQL存储引擎的基础知识

  • MySQL的基本存储结构是页,所有记录都存储在页里面。

  • 每个页之间组成的是一个双向链表。
  • 每个数据页里面的记录可以组成一个单向链表。
    • 每个数据页都会为存储在本页里面的数据生成一个页目录,在通过主键查找某条记录时可以使用二分法快速定位
    • 而根据其它列进行检索时,就只能通过遍历的手段

在没有任何索引的的表中,select语句的执行会进行如下两次遍历

  • 遍历双向链表,找到所在页
  • 遍历页内的单链表,找到所在的记录

索引提高检索速度

索引的主要作用就是将无序变成有序。

record_type=1 代表存放的是普通目录项的记录
record_type=0 代表存放的是普通用户的记录

底层结构一般都是B+树,B+树是平衡树的一种,它是一个空树或者是左右子树的高度差的绝对值不会超过1,左右子树都是一颗平衡二叉树。深度为lgn

索引在提高检索速度的同时,同时会降低增删改的速度,因为要对B+树做增删改的话,会破坏它原来的结构,而且要维护平衡树,就必要做额外的工作。

聚集索引和非聚集索引

概括:

  • 聚集索引是以主键创建的索引。
  • 非聚集索引是以非主键创建的索引。
    InnoDB要求表必须有主键(MyISAM可以没有),Innodb会按照如下规则进行处理:
    • 如果一个主键被定义了,那么这个主键就是作为聚集索引
    • 如果没有主键被定义,那么该表的第一个唯一非空索引被作为聚集索引
    • 如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键是一个6个字节的列,改列的值会随着数据的插入自增。

区别:

  • 聚集索引在叶子节点存储的是表中的数据。
  • 非聚集索引在叶子节点存储的是主键和索引列。
  • 使用非聚集索引查询出数据时,拿到叶子上的主键再去查想要查找的数据。(拿到主键再去查找的这个过程叫做回表
  • 聚集索引是物理上的连续,而非聚集索引是逻辑上的连续,物理存储并不连续。
  • 非聚集索引和聚集索引的区别在于:通过聚集索引可以查到需要查找的数据, 而通过非聚集索引可以查到记录对应的主键值 , 再使用主键的值通过聚集索引查找到需要的数据

覆盖索引:

  • 如果不是聚集索引,叶子节点存储的是主键+索引列,如果需要查询的列,叶子节点都存在,那么就不用回表,提高效率。

索引最左匹配原则

最左匹配原则:

  • 索引可以简单如一个列(a),也可以复杂如多个列(a, b, c, d),即联合索引。
  • 如果是联合索引,那么key也由多个列组成,同时,索引只能用于查找key是否存在(相等),遇到范围查询(>、<、between、like左匹配)等就不能进一步匹配了,后续退化为线性查找。
  • 因此,列的排列顺序决定了可命中索引的列数。

例子:

  • 如有索引(a, b, c, d),查询条件a = 1 and b = 2 and c > 3 and d = 4,则会在每个节点依次命中a、b、c,无法命中d。(很简单:索引命中只能是相等的情况,不能是范围匹配)

索引总结

  • 最左匹配原则
  • 尽量选择区分度高的列作为索引
  • 索引列不能参与计算,尽量保持列干净
  • 尽可能扩展索引,不要新建立索引

  • 对于UPDATE、DELETE、INSERT语句,InnoDB会自动给涉及数据集加排他锁

表锁(InnoDB行锁表锁都支持,MyISAM只支持表锁)

  • 表锁:开销小,加锁快,不会出现死锁,锁的粒度大,并发度低。
  • 行锁:开销大,加锁慢,会出现死锁,锁的粒度小,并发度大

行锁

  • InnoDB支持行锁
  • InnoDB支持表锁

行锁类型:

  • 共享锁(S锁):允许多个获得共享锁的事物同时读取同一个资源,但不允许其他客户端修改
  • 排他锁(X锁):允许获得排他锁的事物更新数据,阻止其他事物修改或读取同一数据集。

乐观锁与悲观锁

  • 乐观锁(认为一个用户读数据时,别人不会去写自己所读的数据):在表中添加一个版本字段,第一次读的时候,获取到这个字段,处理完业务逻辑准备更新的时候,需要再次查看这个字段是否和第一次获取到的字段是否一样,若一样则更新,否则则拒绝。
  • 悲观锁(在读取数据是,不允许别人去修改):直接在把数据库层面上加锁。

事物的隔离级别

什么是事务

事物是逻辑上的一组操作,要么全部执行,要么全部不执行。

事务的特性(ACID)

  • 原子性:事务是最小的执行单位,要么全部执行,要么全部不执行
  • 一致性:在事务执行的前后,所有事务对同一数据源的读取结果的一致的
  • 隔离性:并发访问数据库时,各个事务互不干扰
  • 持久性:在事务提交之后,它对数据库的改变是持久的,即使数据库发生故障,也不会有任何影响。

并发事务带来的问题

  • 脏读:当一个事务对数据进行了修改之后就马上释放了排它锁,导致其它事务对未修改的数据进行了“脏数据”。
  • 丢失修改:指两个事务同时对同一个数据进行了修改操作,导致一个事务的修改丢失
  • 不可重复读:指在同一个事务中,对同一个数据进行多次读取,但是读出来的结果不一致,这里侧重于数据修改
  • 幻读:在同一个事务中,同一次查询会多出或者少了一些数据,这是因为另一个并发的任务作出了增删操作,这里侧重与数据的增删

SQL标准定义的四个隔离级别(InnoDB 默认支持可重复读)

  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

参考资料

本文标题:MySQL索引与锁

文章作者:Enda Lin

发布时间:2019年06月03日 - 10:58

最后更新:2019年07月08日 - 09:15

原始链接:https://wt-git-repository.github.io/2019/06/03/MySQL索引与锁/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。