隔离级别:
读未提交:
一个事务中可以读到别的事务任何数据,包括已提交 和 未提交的脏数据
读已提交:
一个事务中只能读到其他事物已经提交的数据
可重复读:
一个事务在本事务提交之前读到的数据不会受其它事务crud的影响,所以前后读的数据是一致的,可以重复读
注意:实际上这个可重复读只是针对读(select)所看到的的数据是可以重复读的,仅此而已;
为什么这么说呢?
1 当你事务还没提交,这时外界插入了一条id=10的数据,你select查看,发现没有这条数据,你也想插入id=10的数据,是会报错的,因为有冲突,这就是出现了幻读,数据库感知的是实际的环境,而你事务还没提交之前,你select看到的数据是虚幻的,这就是这个隔离级别的一大特点。
2 当你update 表 set money = money+100,进行这种操作时,这个money是会取到真实的money的。举例:你先select 发现money是100,然后另外一个人开启事务并提交把100更新成200,这时候你再select发现还是100,然后你update 表 set money = money+100,这时候实际上会被修改为300而不是200,但是如果你是update 表 set money = 200,就真的变成200,这还是会发生第二类更新丢失。
所以说为什么说!!可重复读可以解决第二类更新丢失啊??????
明明就不可以!!!!
而且,可重复读到底有什么好处啊,没觉得它比读已提交好在哪啊!!!!
串行化:
某一个事务select的时候会对对应行加上共享锁,这时其他事务直接无法更新改数据
这时最高的隔离级别,不会出现任何问题。
通过对select操作加上共享锁解决第二类更新问题和幻读问题。
问题:
脏读:
某一个事务读到别的事务还没提交的数据,换种说法:即将读到被回滚的虚假数据
不可重复读:
一个事务没提交之前,读写某一行的数据前后不一致
(这个不一致就不一致咯有什么问题吗)
幻读:
近似脏读,就是读取了到了错误的数据,举例:别的事务新增了一条数据,但是我这边没看到,当我我也想新增一条同样的数据时系统报错说已经存在这条数据了。
第一类更新:
事务A | 事务B |
---|---|
开启事务 | 开启事务 |
查询到余额为1000 | 查询到余额为1000 |
- | 添加100,修改为1100 |
- | 提交事务 |
添加100,修改为1100 | |
回滚事务 | |
余额变成1000 事务B更新丢失 |
现在所有的隔离级别都不会出现这种错误,可以忽略。
第二类更新:
事务A | 事务B |
---|---|
开启事务 | 开启事务 |
查询到余额为1000 | 查询到余额为1000 |
- | 添加100,修改为1100 |
- | 提交事务 |
添加100,修改为1100 | |
提交事务 | |
余额变成1100 事务B更新丢失 |
这也是多线程下经典的并发问题。
解决方案:
通常可通过乐观锁或悲观锁解决。
悲观锁:
前置知识:
数据库悲观锁有共享锁和排他锁两种。
对于任何隔离级别,update,insert,delete会自动添加排它锁。
非串行化隔离级别下,select是不会添加任何锁的,串行化下,select自动添加共享锁。
一行数据被加了共享锁,其它事务也可以一起加共享锁来访问这个数据。但是不能加排它锁来修改它。
一行数据被加了排它锁,其它事务都不能对他加任何锁。
在事务中,对于每个select操作都主动显式地加上共享锁或者排他锁(最好是共享锁),update,delete,insert已经自动加上排他锁了
共享锁获取方式:select * from account where name=‘jack’ lock in share mode;
排它锁获取方式:select * from account where name=‘jack’ for update;
(注意!!单纯的select是不会添加任何锁的,也就是说select在非串行化的隔离级别之下是不受任何控制的)
或者直接把事务隔离设为串行化set tx_isolation=‘SERIALIZABLE’; 这个隔离级别其实恰好就是上诉的加共享锁的方法(已经过测试)
所以说在串行化的隔离级别下是不会出现第二类更新丢失问题的
乐观锁(推荐):
版本号机制,增加一个版本或者时间戳的列,对于进行修改行时先获取版本号,修改的时候比较一下版本号是否一致,一致再进行修改并将版本号加一更新。
附:
(上图可重复读解决第二类更新那里值得商榷)