事务并发问题和隔离级别
事务基本性质
事务有四个属性,ACID。
- 原子性Atomicity,事务里的命令全部执行,或全部不执行,是不可分割的整体。
- 一致性Consistency,保证操作前后数据和数据结构的一致性。
- 隔离性Isolation,不同事物之间互补干扰。同一时间只允许一个事务请求一个数据。
- 持久性Durability,数据更新持久化,更改时永久的。
事务的并发问题
事务并发会造成三个问题:脏读,不可重复读,幻读。
- 脏读:事务A读了事务B更新的数据,然后事务B回滚撤销了,事务A读取的数据就是脏读。
- 不可重复读:事务A读取数据num后,事务B对其修改,事务A再次读取数据num,前后不一致,这情况为不可重复读。
- 幻读:事务A读取数据(一张表),事务B对此表新增或删除记录,这种情况为幻读。
不可重复读和幻读的区别:一个针对一条记录的更新,一个针对表的更新。因此要解决两者,分别要用到行锁和表锁。这也就造成解决两者的隔离级别不同。
事务隔离级别
隔离级别有四个:
- read-uncommitted:不能解决脏读、不可重复读、幻读
- read-committed:解决脏读,不能解决不可重复读、幻读
- repeatable-read:解决脏读、不可重复读、幻读
- serializable:解决脏读、不可重复读、幻读
其中mysql的事务隔离级别为reapeatable-read
事务隔离级别四个例子
打开两个mysql进程,创建一个test表,插入简单的数据,可以在两个mysql进程都看到表内容。
read-uncommitted
两个进程mysql都要将隔离级别设置为read-uncommitted。
步骤:
- 进程A开启事务读数据num为50
- 进程B开启事务读数据num为50,修改数据为100
- 进程A再次读数据为100
- 进程B回滚,则进程A读取到的数据100为脏数据
进程B的情况
read-committed
步骤:
- 进程A开启事务读数据num为50
- 进程B开启事务读数据num为50,修改数据为100
- 进程A读数据num仍然为50,并没有受到进程B事务的影响
- 进程B提交事务,读取数据为100
- 进程A读取数据num为100,则出现数据不可重复读现象
repeatable-read
步骤:
- 进程A开启事务读取数据num为50
- 进程B开启事务读取数据num为50,修改数据为100
- 进程B提交事务,读取数据num为100
- 进程A读取数据num仍然为50,即使进程B已经提交事务,进程A并没有受到进程B的影响
- 进程B插入一条数据
- 进程A没有查出新增数据,没有数据幻读
- 进程A提交事务后,查询数据num为100,并查询到新增数据
serializable
步骤:
- 进程A开启事务读取数据为100
- 进程B开启事务读取数据为100,修改数据为50,进程B卡住
个人认为,是因为表被锁了,在等待进程A释放锁,然后进程B可以对表进行修改
小结
- 事务隔离级别为read-committed时,写数据会锁住相应行,进程A再读时,就会读取上个版本的数据,但是会增加或删除别的记录,就会出现幻读现象。
- 事务隔离为repeatable-read时,更新数据会锁住整张表,从而防止幻读现象。
- 事务隔离级别为串行化时,读写数据都会锁住整张表。
在进行相关操作后,对事务的理解更加深刻。关于读取上版本,MVCC内容需要抽空去学习一下。