事务并发引起的问题
事务并发所引起的跟读取数据有关的问题,各用一句话来描述一下:
1.脏读:事务 A 读取了事务 B 未提交的数据,并在这个基础上又做了其他操作。(读取未提交)
2.不可重复读:事务 A 读取了事务 B 已提交的更改数据。(读取新提交update)
3.幻读:事务 A 受到事务 B 已提交的新增数据影响。(看不到已提交更新,但是执行写操作时受影响)
事务隔离性
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable ,这四个级别可以逐个解决脏读 、不可重复读 、幻读 这几类问题。
脏读 | 不可重复读 | 幻读 | |
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable read | × | × | √ |
Serializable | × | × | × |
隔离性测试:https://blog.csdn.net/johnstrive/article/details/46724315
https://www.cnblogs.com/huanongying/p/7021555.html
设置隔离级别
设置生效范围是当前事务
mysql 5
select @@tx_isolation;
set tx_isolation='read-uncommitted';
mysql 8
select @@transaction_isolation;
set transaction_isolation='read-uncommitted';
Repeatable read(默认)
事务第一次执行select之后会记录快照,之后再执行select,只会读取快照内容。其他事务commit任何改变信息(insert,delete,update),对本事务不可见。会出现幻读现象,但是不影响当前事务执行Update,Insert的正确性。
一定要注意update、insert、delete 条件能够命中索引。否则将直接上表锁。
A | B | 说明 |
---|---|---|
select * fromn tb_pet; | select * fromn tb_pet; | A,B获取到的数据记录一致, 有数据(id,name,shl,ml) (3,“old”,1,500); |
start transaction | - | 事务A开始 |
- | start transaction | 事务开始 |
- | insert into tb_pet(id,name,shl,ml) values(6,“bull”,1,1000); | B插入数据,上锁(事务B未提交,其他事物需要等待事务B提交才能修改)
|
update tb_pet set name="new",shl=2 where id=3 | B修改数据,上锁 | |
select * from tb_pet; | select * from tb_pet; | A没有获取到B插入的数据,不产生脏读,A产生tb_pet快照 |
- | commit; | 事务B提交 |
select * from tb_pet; 结果:(3,“old”,1,500) | - | A读取不到B新增和更新的数据,可重复读(此时事务A并未提交) |
update tb_pet set name="first" where id=3 and name="old" ; | - | 0行受影响。A看不到id=3的name已经被修改,产生幻读。但是系统中已经没有符合条件的数据。 |
update tb_pet set shl=shl+1 where id=3 ; | - | 此时A以为结果数量会是2 |
select * from tb_pet where id=3; 结果:(3,“new”,3,500) | - | 产生幻读 |
insert into tb_pet(id,name,shl,ml) values(6,“bull”,1,1000); | - | A插入失败,报Duplicate key错误(i是主键),产生幻读,A查询不到id=6的记录,理论上可以插入该记录,但事实上B已经插入了这条记录,对于A来说幻读了id=6的记录 |
read-committed
两个mysql 客户端A,B, 都设置为 "read-committed"隔离级别,A设置事务不自动提交,B设置为事务不自动提交。
A | B | 说明 |
---|---|---|
select * fromn tb_pet; | select * fromn tb_pet; | A,B获取到的数据记录一致 |
start transaction | - | 事务A开始 (事务A未提交) |
- | start transaction | 事务开始(事务B未提交) |
- | insert into tb_pet values(5,“mouse”,1,500); | B插入数据(事务B未提交) |
select * from tb_pet; | select * from tb_pet; | A没有获取到B插入的数据,不产生脏读 |
- | commit; | 事务B提交 |
select * from tb_pet; | - | A读取到B新增的数据了,产生不可重复读(此时事务A并未提交) |
补充:
1、事务隔离级别为读已提交时,写数据只会锁住相应的行
2、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
3、事务隔离级别为串行化时,读写数据都会锁住整张表