【数据库】MySQL事务详解

事务的隔离级别

  • 读未提交(read-uncommitted):最低级的隔离级别,允许其他事务读到未提交的值;
  • 读已提交(read-committed):事务只能读取到其他事务提交的数据;
  • 可重复读(repeatable-read):对同一条数据多次读取结果都是一样(mysql默认隔离级别);
  • 串行化(serializable):最高的隔离级别,所有事务穿行执行,事务间不会产生干扰
隔离级别 存在的问题
读未提交 脏读、幻读、不可重复读
读已提交 幻读、不可重复读
可重复读 幻读
串行化

MySQL的Innodb 存储引擎默认隔离级别是 可重复读,并且不存在幻读的情况

不可重复读和幻读的区别

  • 不可重复读:重点在于updatedelete
  • 幻读:重点在于insert

读已提交隔离级别

  • 该隔离级别下,读数据,不进行加锁,增删改的时候是添加行锁的
    以下table表为例 (id是主键,再无其他索引)

    id name age
    1 A 12
    2 B 15
    3 C 16

MySQL默认隔离级别可重复读,修改隔离级别

SET session transaction isolation level read committed;

更新数据

事务A 事务B
begin; begin;
update table set name='A1' where id=1; update table set name='A2' where id=1;
commit;

为了防止并发过程冲修改冲突,mysql在事务A中给 id=1的数据加了行锁,并且一直不commit,事务B因为执行更新语句,拿不到锁就会一直阻塞,直到超时。

因为id是有索引,所以在执行事务时mysql会给相应的数据行加行锁,如果调用update table set age=10 where name='A';的语句,会怎么样呢?

MySQL会给表中所有的数据行添加行锁,再执行完过滤条件后将不满足条件的行锁释放;这样做的原因是name字段并没有索引,MySQL并不知道哪些数据行是name='A'的。

读取数据

事务A 事务B
begin; begin;
1.select * from table where id=1; 2. update table set name='A2' where id=1;
commit
3. select * from table where id=1;
commit;
  • 事务A 第一次读取到数据1,A,12
  • 事务B 将id=1的数据进行修改
  • 事务A 第二次读取到数据 1,A1,12

读已提交隔离级别下,事务A读取到了事务B提交的数据,导致了不可重复读。

MySQL的读已提交可重复读都是基于MVCC(多版本并发控制)来搞的,为什么可重复读在查询事务没有结束前不会读到别人提交的数据,读已提交会呢?

  • 可重复读:实现原理是在执行第一条SELECT语句时,创建一个快照(read view),事务未提交前都是依据者个快照来进行的
  • 读已提交:事务开始后,每执行一次SELECT都会创建一个新的快照。

可重复读

事务A 事务B 事务C
begin; begin; begin;
1 select * from table where id in (1,3)
2 update table set name=’A2’ where id=1
commit;
insert into table values(4,’D’,20);
commit;
3 select * from table where id in (1,3)
commit;
  • 事务A 第一次读取到数据1,A,12, 3,C,16
  • 事务B 将id=1的数据进行修改
  • 事务A 第二次读取到数据 1,A,12,3,C,16 和第一次读取结果是一样的,证明是可重复读的,没有读到事务B的更新和事务C的新增

可重复读隔离级别下,事务A第二次读取与第一次读取完全相同,所以是可重复读的。

可重复读和读已提交实现原理

这两个隔离级别的都是依赖MVCC来实现的,各位有兴趣的话可以看下这篇

posted @ 2025-03-11 14:14  此木|西贝  阅读(83)  评论(0)    收藏  举报