MySql 锁和MVCC

MySql 锁和MVCC

数据库有四种隔离级别,级别越高,造成的事务并发执行的问题越少。

同时这里依次有事务并发执行的问题

他们之间的关系

隔离级别 脏读可能性 不可重复读可能性 幻读可能性
未提交读 Yes Yes Yes
提交读 No Yes Yes
可重复读 No No Yes
可串行化 No No No

锁

Mysql 中分为 表锁 和 行锁 ,行锁并发性能高。其中 MyISAM 支持表锁, InnoDB 支持表锁和行锁。

表锁更适用于以查询为主,只有少量按索引条件更新数据的应用;行锁更适用于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用。

读写锁

Mysql 表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock),也就是读写锁。

简单来说,除了读读,其他情况如果有一个持有写锁的事务,都会串行执行。

并发插入(Concurrent Inserts)

MyISAM存储引擎可以设置 concurrent_insert ,来控制其并发插入的模式

锁调度

MyISAM存储引擎在同时有请求时,会有一个请求队列,写锁的优先级大于读锁的优先级,因为写请求的操作要避免阻塞。

InnoDB 行锁

InnoDB存储引擎的行锁除了使用共享锁和排它锁之外,还使用了 意向共享锁 和 意向排它锁 。InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。

除了意向锁内部相互兼容,对于共享锁和排它锁都是读写锁兼容模式

请求锁模式是否兼容当前锁模式 X S IX IS
X 冲突 冲突 冲突 冲突
S 冲突 兼容 冲突 兼容
IX 冲突 冲突 兼容 兼容
IS 冲突 兼容 兼容 兼容

有了意向锁,防止表锁级别上的请求冲突,就不需要全表扫描来寻找行锁了。

行锁会根据索引来锁定范围。对于键值在条件范围内但并不存在的记录,也会加锁,这种锁叫间隙锁。

MVCC

MVCC解决了幻读的问题,在查询的时候,看到的是一个 快照 版本。读写并存的时候,写操作会根据目前数据库的状态,创建一个新版本,并发的读则依旧访问旧版本的数据。这样就可以实现无锁。

在mysql中,有两个关于MVCC 的隐藏列,分别是

插入

InnoDB为新插入的每一行保存当前系统版本在 DB_TRX_ID 上。

删除

执行删除语句之后数据并没有被真正删除,而是对 DB_ROLL_PT 做改变

更新

先执行MVCC逻辑里面的删除流程,也就是改变原本数据的 DB_ROLL_PT

然后将新的数据插入进来,使用的是同一个 id ,但是 新数据的 DB_ROLL_PT 为 null

查询

首先查找数据行版本号( DB_ROLL_PT )早于当前事务版本号的数据行记录,这样保证的查询的结果是查询之前的。

接下来查找删除版本号( DB_ROLL_PT )要么为 null ,要么大于当前事务版本号的记录。这样保证的查询的结果,在事务开始之前未被删除。