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 ,要么大于当前事务版本号的记录。这样保证的查询的结果,在事务开始之前未被删除。