这里主要讲的是Mysql InnoDB引擎相关事务和锁。Mysql事务主要和上诉数据库理论中相似,有所不一样的是在事务隔离级别的Repeatable Read(可重复读)、和锁有着不一样的实现。html
Mysql一样有4种事务隔离级别,其中Repeatable Read(可重复读)是Mysql默认隔离级别,其经过MVCC机制实现了不会出现幻读现象。mysql
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read Uncommitted | 可能 | 可能 | 可能 |
Read Committed | 不能 | 可能 | 可能 |
Repeatable Read | 不能 | 不能 | 不能 |
Serializable | 不能 | 不能 | 不能 |
Mysql能够经过下诉API操做事务隔离级别:sql
查看系统隔离级别: select @@global.tx_isolation; 查看当前会话隔离级别 select @@tx_isolation; 设置当前会话隔离级别 SET session TRANSACTION ISOLATION LEVEL serializable; 设置全局系统隔离级别 SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Mysql InnoDB引擎有悲观锁、乐观锁(MVCC),其共同组成Mysql相关锁机制,而且Mysql经过其使用Repeatable Read可以防止幻读的出现。数据库
Mysql主要有下面4种悲观锁:网络
IS、S、IX、X锁的兼容性为:session
Mysql InnoDB引擎默认支持行锁,来尽可能缩小锁定元组的粒度,行锁分为三级,粒度从小到大依次是:mvc
前面所说,乐观锁机制经过在表中添加version
字段进行实现。在Mysql InnoDB引擎中,其会在每行数据中额外添加两个隐藏值来实现MVCC,这两个值一个记录这行数据什么时候被建立,另一个记录这行数据什么时候更新(或者被删除)。在实际操做中,每开启一个新事务,事务的版本号就会递增。.net
任何操做都不加锁code
操做 | 锁对应级别 |
---|---|
select * | 无锁 |
select * for update | 排它锁 |
select * lock in share mode | 共享锁 |
insert | 排它锁,行锁中的记录锁 |
delete | 排它锁,行锁中的记录锁 |
update | 排它锁,行锁中的记录锁 |
操做 | 锁对应级别 |
---|---|
select * | 乐观锁(MVCC机制),也叫快照读(snapshot read) |
select * for update | 排它锁 |
select * lock in share mode | 共享锁 |
insert | 排它锁,行锁中的记录锁 |
delete | 排它锁,行锁中的防插入锁 |
update | 排它锁,行锁中的防插入锁 |
该隔离级别下,读(select)下会加共享锁,至关于select lock in share mode,写会加排他锁,读写互斥,至关于所有使用悲观锁实现事务隔离级别。htm
Mysql在Repeatable Read事务隔离级别下,经过MVCC机制、update和delete操做的防插入锁解决可重复读和幻读问题。
这里主要由Mysql的MVCC机制所解决的。mysql会在每行数据中额外添加什么时候被建立
、什么时候被更新
两个版本号字段,在RP级别下,每一个操做对应得MVCC版本号字段的操做:
因此咱们由上可知在事务A在select操做时,事务B的update和delete操做并不会影响事务A的select操做。
幻读问题咱们主要是因为insert操做致使,因此这里面主要涉及到select时有insert操做和update(delete)时有insert操做,下面主要讲这两种Mysql InnoDB引擎是如何解决的。
由上所诉解决可重复读问题,该操做已由Mysql的MVCC机制所解决
该操做主要由Mysql InnoDB引擎的防插入锁(Next-Key Lock)解决的。防插入锁的区间是根据索引来肯定的。对于没有索引的列会直接锁表。下面是一个例子:
-------------------------------------------------------------------------------------------------- 事务A | 事务B -------------------------------------------------------------------------------------------------- begin | begin -------------------------------------------------------------------------------------------------- update isolation set name = '-2' where name = '2'; | -------------------------------------------------------------------------------------------------- | insert into isolation(name) values('2'); | // 这里会阻塞等待 -------------------------------------------------------------------------------------------------------- commit | --------------------------------------------------------------------------------------------------------
注意:上面事务A的update操做会形成name='2'的行锁和name=(-∞, 2]和[2, +∞)的间隔锁,其统称为防插入锁。
https://tech.meituan.com/innodb-lock.html http://chenzhou123520.iteye.com/blog/1860954 https://blog.csdn.net/sadfishsc/article/details/51027734