MYSQL技术内幕 InnoDB ch6锁 ch7事务


2013.6 MYSQL 5.6 INNODB 1.2.x

ch6 锁

不同数据库对锁的实现完全不同
  区分lock和latch
  latch是轻量级锁,要求锁定时间非常短,分为mutex/relock,用来保证并发线程操作临界资源的正确性,通常没死锁检测
  lock的对象是事务,用来锁定db中的对象,如表/页/行。对象仅在事务commit/rollback后释放,有死锁机制
  在这里插入图片描述
   SHOW ENGINE INNODB MUTEX可以查看latch
   SHOW ENGINE INNODB STATUS/information_schema下的表INNODB_TRX/INNODB_LOCKS看lock

INNODB实现了两种标准行级锁:

  • 共享锁,允许事务读一行
  • 排它锁,允许删除或更新一行
    在这里插入图片描述
      X锁和任何锁都不兼容,S仅和S兼容。都是行锁,兼容是指对同一记录的兼容
      支持多粒度锁定,允许事务行级锁表级锁同时存在。为了支持不同粒度加锁,INNODB支持一种额外锁方式,称为意向锁。意向锁是将锁定的对象分为多个层次,意味着事务希望在更细粒度加锁

在这里插入图片描述
  若将上锁对象堪称一颗树,对最下层对象上锁,就是对最细粒度对象上锁,那么需要先对粗粒度对象上锁。如需对页上记录r上X锁,需要分别对数据库A/表/页上意向锁IX,最后对记录r上X锁。其中任何一部分等待,该操作需要等待粗粒度锁完成。距离,对记录r上X锁前,已经有事务对表1上S表锁,那么表1已存在S锁,之后事务需对记录r在表1上加IX,不兼容,该事务等待表锁操作完成
  INNODB支持意向锁比较简单,即为表级别的锁。目的是为了在一个事务中揭示下一行将被请求的锁类型,支持两种意向锁:

  • 意向共享锁IS,事务想获得一张表中某几行S锁
  • 意向排他锁IX

由于INNODB支持行级别锁,所以意向锁不会堵塞除全表扫以外的请求
在这里插入图片描述
  可以通过三张表监控当前事务并分析可能存在的锁问题。INFORMATION_SCHEMA INNODB_TRX/INNODB_LOCKS/INNODB_LOCK_WAITS

一致性非锁定读

一致性的非锁定读 consistent nonlocking read是指INNODB通过多版本控制multi versioning方式读取当前执行时间DB中行数据。如果读取的行正在执行DELETE/UPDATE,读取不会等待行上锁释放。会去读取行的一个快照数据
  
在这里插入图片描述
  称为非锁定读,因为不需要等待访问的行上X锁释放。快照是之前版本的数据,实现通过undo段完成,undo用来在事务中回滚数据,因此快照没额外开销。此外读快照不用上锁,因为没事务对历史数据修改
  非锁定读极大提高了并发性。默认设置下默认读取方式,读取不会占用和等待表上的锁。但在不同事务隔离级别中,读取的方式不同,不是每个事务隔离级别下都采用非锁定读。即便都使用,对快照数据的定义也不同
  快照是当前行数据历史版本。一个行记录不止一个快照,称技术为行多版本技术。带来的并发控制,称为多版本并发控制MVCC
  READ COMMITTED / REPEATABLE READ(默认)时,INNODB使用非锁定一致性读。对快照数据定义不同。READ COMMITYED下,对快照,总是读取最新一份快照。而在REPEATABLE READ ,总是读取事务开始时的行数据版本

一致性锁定读

默认配置下隔离级别为REPEATABLE READ,SELECT 使用一致性非锁定读。但某些情况,需要显式对读取加锁保证逻辑一致性。这要求数据库支持加锁,即便对只读操作。对SELECT 支持两种锁定读操作

  • SELECT …… FOR UPDATE
  • SELECT ……LOCK IN SHARE MODE

FOR UPDATE加X锁,SHARE MODE加S锁
  对于一致性非锁定读,即使读取行已被执行FOR UPDATE,依然可读

6.4 锁的算法

INNODB 3种行锁算法

  • Record Lock:单个行记录上的锁
  • Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
  • Next-Key Lock:GAP+RECORD,锁定一个范围并锁定记录本身

Record总会锁住索引记录,如果没设置任何索引,会使用隐式主键锁定
  Next-Key,对行查询都采用这种算法。比如一个索引有10/11/13/20 四个值,该索引被Next-Key locking的区间为:
  
在这里插入图片描述
  next-key设计目的是为了解决Phantom Problem。锁定的是一个范围
  previous-key
  在这里插入图片描述
  当查询索引含有唯一属性时,INNODB会对next-key进行优化,降级为Record lock,仅锁住索引本身
  由于有2个索引,其需要分别进行锁定,对聚集索引,仅对a=5索引加上record,而对辅助索引,加next-key,范围是(1,3),还会对辅助索引下一个键值加上gap,即(3,6)的锁
  显式关闭gap lock

  • READ COMMITED
  • innodb_locks_unsafe_for_binlog = 1

除了外键和唯一性检查会需要gap,其余情况仅用record。上述设置破坏了事务隔离性,对于replication,会导致主从不一致。从性能来看,READ COMMITTED不优于默认READ REPEATABLE

默认隔离级别REPEATABLE READ下,INNODB采用next-key解决phantom幻像问题
  phantom指同一事务下,连续执行两次SQL结果不同

解决思索方法之一是超时,事务超时回滚
  当前db普遍采用wait-for graph等待图进行死锁检测。一种主动的死锁检测方法。INNODB使用这种方法。等待图要求db保存两个信息:

  • 锁的信息链表
  • 事务等待链表
      通过上述链表可以构造出一张图,若在图中存在回路,代表存在死锁
      在这里插入图片描述
    在这里插入图片描述
      每个事务请求锁并发生等待时都会判断是否存在回路,若存在则有死锁,通常选择回滚undo量最小的事务
      死锁检测通常采用深度优先实现,1.2开始优化,将递归改用非递归实现

锁升级是指将锁粒度降低。可以把表的1000个行锁升级为一个页锁,页锁升级为表锁

ch7事务

事务的ACID

  • A - 原子操作
  • C - 从一种状态转变为下一种一致的状态,事务开始和结束,db的完整性约束没有被破坏。比如,表中某字段为姓名,有唯一约束,如果事务修改后,但在事务提交或回滚后,姓名变唯一了,就破坏了一致性要求。事务是一致性单位,如果事务某动作失败了,系统可以自动撤销事务
  • I - 并发控制,事务提交前对其他事务不可见,通常锁实现。当前rdbms提供粒度锁策略,允许事务仅锁住实体对象的子集以提高事务间并发度
  • D - 事务提交后,所有变化都是永久的,保证事务系统的高可靠性

InnoDB支持扁平事务/带保存点的事务/链事务/分布式事务,不支持嵌套事务

事务的实现

事务的隔离性由锁来实现,持久性通过redo log(重做日志)实现,原子性/一致性通过undo log实现。

redo和undo的作用都是恢复,redo恢复提交事务修改的页操作,undo回滚行记录到特定版本。因此两者记录内容不同,redo是物理日志,记录的是页的物理操作,undo是逻辑日志,根据每行记录进行记录

redo

重做日志实现持久性,包括两部分,内存中redo log buffer,易失的;redo log file,持久的。
InnoDB通过force log at commit机制实现事务持久性,即事务commit时,必须先将事务日志写到重做日志文件进行持久化,待事务的commit操作完成才算完成。这里的日志由redo log和undo log组成。redo log保证事务的持久性,undo log帮助事务回滚以及MVCC功能。redo log是顺序写的,不需要对redo log文件进行读取操作,undo log需要随机读写。
binlog用来进行point-in-time的恢复以及主从复制环境的建立。两者并不相同

  • 重做日志是在InnoDB引擎层产生的,binlog是mysql db上层产生的,binlog不仅仅针对innoDb,任何存储引擎更改db都会产生binlog
  • 日志记录内容形式不同,binlog是逻辑日志,对应sql语句,innodb层的重做日志是物理格式,是对于每个页的修改
  • 写入磁盘时间点不同,binlog在事务提交完成后一次写入,innodb重做日志在事务进行中不断写入

在这里插入图片描述
binlog仅在事务提交时记录,对每个事务,仅包含一个日志。而重做日志,每个事务对应多个日志条目,而且事务的重做日志写入是并发的,所以记录仪顺序并非事务开始的顺序
innoDB中重做日志以512b存储,重做日志缓存/文件都以块的方式保存。日志块由三部分组成,header/body/tailer
重做日志是物理日志,恢复速度比binlog(逻辑日志)恢复快很多

undo

重做日志记录了事务的行为,可以对页进行“重做”操作。但事务有时需要回滚,需要undo
redo存放在重做日志文件中,undo不同,存放在db内部一个特殊的段中,这个段成为undo segment。undo segment在共享表空间内
undo是逻辑日志,所有修改都被逻辑取消,但数据结构和页本身在回滚后可能不相同,这是因为在并发系统中,可能会有数十数百个并发事务,db主要任务就是协调对数据记录的并发访问,不能将一个页回滚到事务开始的样子,会影响到其他事务正进行的工作
比如,用户插入10W条记录,这个事务会导致分配一个新段,表空间增大,执行rollback时,会将插入事务回滚,但表空间不会收缩。innodb回滚时,实际做的是与之前相反的工作。对insert完成delete;对delete执行insert;对update执行相反update
除了回滚,undo另一个作用是MVCC,若读记录时该记录已被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取
undo log也会产生redo log,undo log也需要持久性保护

purge

真正的删除操作被延时,最终在purge操作中完成
  purge用于最终完成delete和update操作。因为支持MVCC,所以记录不能在事务提交时立即处理。是否可删除通过purge判断。若该行记录已不被其他事务引用,就可以进行真正的delete

INNODB在REPEATABLE READ,使用next-key lock算法避免幻读。已经能完全保证事务隔离性要求,即达到SERIALIZABLE

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值