02. 事务与锁
MySQL 中的事务管理
InnoDB 中使用索引作为检索条件修改数据时采用行锁,否则使用表锁InnoDB 自动给修改操作加锁,给查询操作不自动加锁- 在
REPEATABLE READ 级别下,如果要完全杜绝幻读,需要手动给关键查询语句加锁;LBCC 解决的是当前读情况下的幻读,MVCC 解决的是普通读(快照读)的幻读 - 表的大部分数据需要修改时,行锁反而不如表锁更有效率
LBCC
在
在实际使用下,加锁往往意味着高昂的开销,
- 表锁:表锁会锁定整张表,如果当前有用户正在执行写操作并且获取了写锁,这可能导致整张表被锁定,阻塞其他用户的读写操作。如果用户执行的是读操作,则会获取读锁,此时其他用户的并发读操作将被接受,写操作会被阻塞。以
UPDATE table SET a = 1 where b = 2;
为例,如果b 字段不存在索引,那么会锁住所有的记录,即锁上了表锁。 - 行锁:行锁的粒度是在每一条行数据,这意味行锁可以尽可能的支持并发处理,相应的行锁开销也会比较大。并且,在
InnoDB 中的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则行锁将会自动升级为表锁。
相比较而言,表锁的优势在于开销小,加锁快,无死锁,劣势是锁的粒度大,发生锁冲突的概率较高,并发能力较弱。而行锁则相反。实际使用中,两者都会由
MVCC
MVCC(multiple-version-concurrency-control)是个行级锁的变种,它在普通读情况下避免了加锁操作,因此开销更低。其原理具体为,在
相应的,其
SELECT 语句先查找DATA_TRX_ID 早于当前事务ID 的数据行。这样就保证了读取的数据要么是在这个事务开始之前就已经commit 了的(早于当前事务ID ) ,要么是在这个事务中自身创建的数据(等于当前事务ID ) 。查找行的DELETE_BIT 为1 时,查找删除事务ID 对应的事务,确定此条记录在当前事务开始之前,行没有被删除。INSERT 语句会在新插入行数据之后,保存当前事务ID 作为行的DATA_TRX_ID 。DELETE 语句为每一条删除的记录保存当前的事务ID 作为行的删除标记。UPDATE 语句将复制变更的记录,并把新记录的DATA_TRX_ID 置为当前事务ID ,同时更新老记录中的DB_ROLL_PT 指向了上一个版本。
所以在并发读的时候,不需要等到访问行上的锁释放,只需要读取一个行的快照即可。既然是多版本的读取,就肯定读取不到其他事务中的新插入的数据了,也就避免了上述场景中提到的幻读。避免了部分幻读现象,但是实际使用中,还是会有幻读产生,先看场景:
-- 会话 1
START TRANSACTION;
SELECT * FROM xx;
-- 此时查询表为空,且事务未提交
-- 会话 2
START TRANSACTION;
SELECT * FROM xx;
-- 此时查询表为空,且事务未提交
INSERT INTO t_bitfly VALUES (1, 'test');
-- 插入一条主键为 1 的记录
commit;
-- 提交会话 2 中的事务
-- 会话 1
INSERT INTO t_bitfy VALUES(1, 'test');
-- 尝试插入主键为 1 的一条记录,此时会受到主键重复的报错,但是再查询语句中明明没有这条记录,幻读出现
通过
- Record Lock:在单行记录上的锁
- Gap Lock:间隙锁,锁定一个范围,但不包括记录本身,。
GAP 锁的目的,是为了防止同一事务的两次读出现幻读的情况 Next-Key Lock: 前两个锁的共同使用,即锁定了记录本身,也锁定了一定的范围。
通常情况下,
Links
- https://parg.co/LeO
Mysql 事务- 你想知道的都在这 - https://sctrack.sendcloud.net/track/click/eyJuZXRlYXNlIjogImZhbHNlIiwgIm1haWxsaXN0X2lkIjogMCwgInRhc2tfaWQiOiAiIiwgImVtYWlsX2lkIjogIjE2MTg1NzIxNDc3NjdfMTg3XzU3MzA0XzQ1MTYuc2MtMTBfOV8xM18yMTMtaW5ib3VuZDAkMzg0OTI0NTUyQHFxLmNvbSIsICJzaWduIjogImUxM2UxYmM4ODBiYWM0NWQyNDk3OTNkMTdkMWQzMDFlIiwgInVzZXJfaGVhZGVycyI6IHt9LCAibGFiZWwiOiAiNjE5MjAzMCIsICJ0cmFja19kb21haW4iOiAic2N0cmFjay5zZW5kY2xvdWQubmV0IiwgInJlYWxfdHlwZSI6ICIiLCAibGluayI6ICJodHRwcyUzQS8vdmlwLm1hbm9uZy5pby9ib3VuY2UlM0ZuaWQlM0Q0OSUyNmFpZCUzRDIwNTIlMjZ1cmwlM0RodHRwcyUyNTNBJTI1MkYlMjUyRnRvdXRpYW8uaW8lMjUyRmslMjUyRm9ta2I5Z3klMjZuJTNETVRNeS43MGt5ckJjZ0l1OHNJZHlnWDE4RVhRZFJFYUkiLCAib3V0X2lwIjogIjEyMC4xMzIuNTQuMTk0IiwgImNvbnRlbnRfdHlwZSI6ICIwIiwgInVzZXJfaWQiOiAxODcsICJvdmVyc2VhcyI6ICJmYWxzZSIsICJjYXRlZ29yeV9pZCI6IDYwMzQ5fQ==.html
InnoDB 解决幻读的方案–LBCC&MVCC