锁是InnoDB保障事务隔离性的重要手段,各个存储引擎以及DB的锁的实现是不同的。
- MySql中,MyISAM采用表锁,InnoDB基于行锁。
- Sql Server 2005支持乐观锁和悲观锁并发;
- InnoDB提供一致性的非锁定读和行级锁,类似Oracle;
Lock和Latch
latch为轻量级锁,称为闩锁,latch类似Java的synchronize,但是Lock更宏观,latch可以认为是程序性控制,控制程序逻辑,但是lock是对数据的保护。
Lock | Latch |
---|---|
对象 | 事务 |
保护 | 数据库内容 |
持续时间 | 整个事务过程 |
模式 | 行锁、表锁、意向锁 |
死锁处理 | 通过wait-for graph、time out等进制进行处理 |
存在位置 | Lock Manager的哈希表中 |
InnoDB的锁
InnoDB实现了两个标准锁,这两个锁是基于行设计的。
- 共享锁:S Lock,允许对row数据共享的read;
- 排他锁:X Lock,允许对row数据update、delete,但是是独占的;
意向锁:InnoDB实现的一种特殊的表锁,揭示接下来将要获取的锁的类型,意向锁分为以下两类:
- IX:意向排他锁;
- IS:意向共享锁;
- 意向锁不会阻塞除全表扫描以外的任何请求;
锁相关Sql
#查看引擎状态查看锁
show engine innodb status;
#查看处理过程
show full processlist;
# 查询schema数据库
select * from information_schema.INNODB_TRX \G
一致性非锁定读
InnoDB的读策略是:在读取数据的时候不加锁,也不需要等待X锁的释放即可读取数据,这时读取的是数据的快照版本。每个数据,由于并发的事务不同,可能存在多个快照数据,这就是多版本快照。
InnoDB使用多版本快照并发控制技术 - MVVC技术控制读取数据的快照版本。
MVVC技术也是InnoDB实现非锁定读的关键技术。
事务不同隔离级别的快照版本确定方式不一样。InnoDB尽在下面两个级别下使用非锁定读技术,其数据快照版本确定方式如下:
- read commited:读已提交级别,读取最新的一个快照版本;
- repeatable read:可重复读级别,读取事务开始时的快照版本;
MVVC技术中确定数据快照版本的时候通过事务号,所以事务号是以关键!
显示加锁Sql
InnoDB支持显示加锁语句,通过在Select的时候加入关键语句可以加X和S锁。这些语句是InnoDB特有的,不是Sql标准。
#Select语句的支持:
# 加X锁;
select ... for update;
# 加S锁;
select ... lock in share mode;
自增长字段和锁
InnoDB实现自增长算法有两种模式,一种轻量级互斥量,一种是表锁,MyISAM则采用全表锁。
InnoDB中自增长的列必须是索引的第一列,也必须是索引。
外键和锁
InnoDB自动为外键添加索引,这可以避免表锁。如果有外键,InnoDB在Insert的时候会去check父表数据,这是会有一个父表的隐式的select操作,这种select不采用一致性非锁定读,而且采用Select id from parent_table in lock share model,即主动加S锁。所以外键固然可以保证数据完整性,但是同样有性能隐患。
锁算法
InnoDB有3种行锁算法:
- Record Lock:单个行上锁;
- Grap Lock:间隙锁,锁定一个范围,但是不锁定本身,锁定范围为开区间;
- Next-Key Lock:锁定范围包括本身,锁定范围为闭区间;
这三种锁在不同的事务隔离级别下别使用。如InnoDB通过Nex-Key Lock算法锁定一个范围数据,这样可以避免幻读。
如果字段是唯一索引,则Next-Key Lock有可能降级为Record Lock;
锁带来的问题
锁可以很好的支持数据库事务,保证事务的隔离性,但是锁的不正确使用也会带来一些问题。在事务的隔离级别中,各个隔离级别都会有一定的问题,这就是锁带来的。
- 在read-ncommitted级别下,因为没有使用锁,所以会发生脏读问题;
- 在read committed级别下,因为使用Record Lock策略,但是没有对范围加锁,所以会有幻读和不可重复读问题;
- 在repeatable read级别下,因为使用的是Next-Key Lock策略,所以可以消除脏读和幻读,但是是否有效率问题呢?
Next-Key Lock :InnoDB解决幻读策略
幻读简单描述是一个事务两次读取时读取到了不同的行数,幻读和不可重复读的区别在于,不可重复读是两次读取同一个数据行,数据行内容发生了update,但是幻读多发生在insert和delete中。
InnoDB使用Next-Key Lock算法作为Repeatable Read的加锁策略,这种方式锁定一个范围,保证事务隔离性。Read Commited 使用Record Lock作为加锁策略,无法保证事务隔离性。