MySQL技术内幕InnoDB存储引擎(六)——锁

什么是数据库的锁?
锁是数据库系统区别于文件系统的一个关键特性。锁机制用于管理对共享资源的并发访问。让数据库事务满足隔离性的要求。

InnoDB 中锁的作用
不仅用于对数据进行并发访问,还还包括了缓冲池中给LRU列表的操作。从而提供了数据的完整一致性。

latch与lock的区别?

  • latch是轻量级的锁,也成为闩锁。
  • latch要求锁定的持续时间短,否则性能会很差。
  • latch用来保证并发时临界资源的正确性,而lock是整个事务过程。
  • latch没有死锁检测机制,lock有。
  • latch应用对象时线程,而lock是事务。
  • latch保护内存数据结构,而lock保护数据库内存。

InnoDB的锁

分类

  • 共享锁:允许事务读一行数据。
  • 排他锁:允许事务删除或者更新一行数据。
  • 意向共享锁:事务想要获得一张表中某几行的共享锁。
  • 意向派排他锁:事务想要获得一张表中某几行的排他锁。

所谓意向锁,就是更细粒度的锁。

一致性非锁定读

原理:
如果在读取数据的时候,该数据正在执行删除或者更新操作,那么这个读取操作就不会等待之前的操作结束,而是直接读取之前版本中备份的信息,也就是undo日志上的数据。

特点:

  • 极大程度地提高了并发性能。
  • 这是数据库默认的读取方式。
  • 无法保证数据逻辑的一致性,因为读到的可能是之前的数据。

对应事务隔离级别:

  • 读已提交
  • 可重复读

多版本并发控制

什么是多版本并发控制?
对于保留数据库快照数据的之前的各个历史版本,由此带来的并发控制,就是多版本并发控制。所谓快照数据,就是保留某一时刻的数据。

不同事务级别下读取版本:

  • 读已提交:读取最新一份的快照数据。
  • 可重复读:读取事务开始时的行数据版本。

一致性锁定读

也就是读的时候,去获取数据的锁,防止有其他会话在更改数据。保证督导的数据是最新的,也不会有人在修改。

对应事务隔离级别:

  • 串行化。

自增长锁

对于表中的自增长数据,插入的时候就会对一个计数器加一。但是大量的数据插入会使其他事务阻塞。
InnoDB中提供了一种轻量级互斥量的自增长实现机制,这种机制大大提高了自增长插入的性能。

外键和锁

对于外键值的插入和更新,会先去给其父表加一个共享锁,防止父表在期间被修改。

所谓父表,就是外键引用的源表。

锁的算法

InnoDB上有三种行锁的算法,分别是:

  • 记录锁:单个行记录上的锁。
  • 间隙锁:锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。
  • Next-Key Lock:是上两种锁的结合,锁定一个范围,并锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。InnoDB对于行的查询都是采用了Next-Key Lock的算法

当查询的索引含有唯一属性的时候,Next-Key Lock 会进行优化,将其降级为Record Lock,即仅锁住索引本身,不是范围。

并发的问题

1.脏读

脏读就是在不同事务下,两次读的数据不同,当前事务读取了别的事务还没有提交的数据。

现在基本不会发生,因为现在的事务隔离界别都是读已提交。

2.不可重复读

不可重复读就是一个事务内多次读取同一个数据,两次读到数据不同,当前事务读取了别的事务已经提交的数据。

这个问题在很多情况下是容忍的,因为别人提交的数据也是正确的数据。因为如果没有提交,是可能被回滚的

将事务隔离级别设置为可重复读就能解决。

3.幻读

指同一事务下,连续执行两次同样的SQL产生不同的结果,第二次的SQL返回之前不存在的行。也就是幻读。

采用InnoDB默认的事务隔离级别RR,RR下读行默认采用Next-Key Locking算法避免幻读。其原理就是对一个范围的行进行加锁,反正其他会话插入数据产生幻读。

4.丢失更新

就是一个事务的更新操作被另一个事务的更新操作覆盖了,从而导致数据不一致。

这不是数据库层面的问题,而是应用层面的问题,很难避免,唯一的途径就是串行化。比如用户1才操作的时候,不能让其他用户进行操作,因为同时两个用户操作,就有可能丢失一个用户的更新操作。

脏读、不可重复读和幻读的区别

  • 这三者的表现都是同一事务多次读取的结果不一样。
  • 脏读侧重于读取了别人未提交的同一条数据。
  • 不可重复读侧重于读了别人已经提交的数据。
  • 幻读侧重于读取了数据条数发生了改变,而上面两条是数据本身发生了改变。

死锁

死锁就是两个或者以上的事务相互等待对方的数据,导致谁都不释放数据,谁都不能获取数据的造成一直等待的现象。

解决死锁的方法

  • 超时回滚
    如果一个事务等待的时间超过了某一阈值,那么它就要回滚数据,释放资源,另个事务就能拿到资源继续。
    问题就是如果这个需要超时回滚的事务是一个大事务(undol量很大),回滚的时间就会很长,不划算。
  • 等待图
    构建锁的信息链表和事务的等待链表,如果图中存在环,那就说明存在死锁。如果引擎检测到死锁,就会释放环中undo量最小的事务。
    这是InnoDB采用的方法。主动检测死锁。

锁升级

锁升级就是将当前锁的粒度降低,锁住的东西变多了。

InnoDB不存在锁升级的问题,因为它是根据事务访问的每个页对锁进行管理的。锁的开销很平均。

原文地址:https://www.cnblogs.com/lippon/p/14117616.html