实习日志4.mysql事务

参考:官方文档

X S锁 IX IS锁

X排他锁,S共享锁,只有S锁和S锁的获取之间是不互斥的。

意向锁

其规则:
事务获取X锁前,必须获取IX,获取S前,必须获取IS

IX,IS又被称为意向锁,该锁被设计的目的是:
考虑以下场景:
在一个事务中执行了select * … for update(添加IX锁,for share添加IS锁),并为其中某几条记录添加了行锁,
另外一个事务 执行了 lock table xx lock mode write(添加IX锁,);会检查IX锁,因为表级的X锁和IX锁是互斥的,所以无法获取

X,S分别有表级别和行级别之分,而IX,IS只有表级,
X IX S IS
X Conflict Conflict Conflict Conflict
IX Conflict Compatible Conflict Compatible
S Conflict Conflict Compatible Compatible
IS Conflict Compatible Compatible Compatible

行锁

行锁会在索引上加索,
以SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;为例,会在c1上加锁

间隙锁

锁住一段范围,防止对其中的insert操作(因为 insert也要获取gap锁,可以防止幻读)
且间隙锁不分X,S锁,他们都是不互斥的 ,事务 A获取了间隙锁,事务B也可以获取

举个 例子,切记id不能是主键,文档说明,当where条件加在唯一索引上(包括主键)是不会加gap锁 的

create table test(
  id int,
  t varchar(5),
  key(id)
);

insert into test(id, t) values(10, "a"),(20, "b"),(30, 'c');

  begin;
  update test set t = '' where id = 20;


  begin;
  insert into test(id, t) values(11, "a"); -- 会直到抛出超时异常


  begin;
  insert into test(id, t) values(31, "a"); -- 正常

  -- 说明了在10~30上都被加了锁

  如果换成t呢?
  begin;
  select * from test where t = '' for update;


  begin;
  insert into test(id, t) values(50, 'ccc'); -- 仍然出现了问题,加上了table级的锁 

同时文档中补充了,read commited只会不会使用gap锁,read uncommited更不会了,所以read commited是无法防止幻读的 ,至于 串行化隔离级别,无论你select是否 for update都会假设gap锁。

next-key

== gap lock + record lock (顺序不能乱)
还是举个例子
来自 博客

MVCC与一致读

MVCC用于查询一些正在被其他更新的数据,用于 提高并发(原理:逻辑上删除事务id和创建事务id) – 即 创建版本和删除版本
select:1创建版本小于当前 事务(保证了事务之前行存在)2.删除版本undefined or 删除版本大于当前版本 事务开始的时候还没删除
insert:创建版本号 = 当前版本号
update:先加入,创建时间为当前版本,并把修改的数据删除版本设为当前版本
delete:删除版本为当前版本
Read-REPEATED 默认select都是一致读,避免不可重复读,也避免select加锁,增加并发性能

Read-COMMITED则不同,它的mvcc管理select会不会保证读当前事务进行前的 事务,而是最新 的事务,因此可能出现 不可重复读。
同时要注意read-commited如果 想要达到read-repeated的效果(避免不可重复读)可以使用 select * for share,在事务结束前都会尝试加锁。

总结事务隔离级别

主要还是
READ-REPEATED: 普通读:快照读,一致性读,加锁:加行锁还是next-key还是表锁取决于索引

READ-COMMITED: 普通读:快照读,但是读的是新版本 加锁:不会有gap锁, 对于更新语句,
还提供了半一致读:简单来说,semi-consistent read是read committed与consistent read两者的结合。一个update语句,如果读到一行已经加锁的记录,此时InnoDB返回记录最近提交的版本,由MySQL上层判断此版本是否满足update的where条件。若满足(需要更新),则MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)。
read-uncommited和read-commit类似,唯一不同是它的读可以读到一个 更早的版本,因此被称为脏读
serialzable:序列化,更像read-repeated,但是每次查询的都不是快照读 ,而是for share加锁读 ,如果设置了自动提交,每次select都将是一个事务。
因此我认为,在设置了自动提交,默认的隔离 级别 ,无需手动调用commit

猜你喜欢

转载自blog.csdn.net/jsh_941112341/article/details/84865703