事务具有以下4个特性
ACID
A:原子性,事务中的一组操作,一起成功或一起失败
C:一致性,事务中的多个操作的数据,数据状态一致
I:隔离性,一个事务不会影响另一个事务中的数据
D:持久性,事务执行完后,落表
事务问题
脏写:后面的事务值覆盖了前一个事务内的值
脏读:事务B读到了事务A未提交的数据,事务A的数据可能回滚。
不可重复读:事务B内相同的查询语句在不同时刻查到的结果不同
幻读:事务读取到了其他事务新增的数据,主要说的是新增数据。
表锁/行锁
表锁 开销小,加锁快 ; lock table t_user read(write)应用:数据迁移的时候可使用。
行锁 开销大,加锁慢,发生锁冲突的概率低,并发度高
隔离级别
- 读未提交 :事务A读取到了事务B内还未提交的数据,如果事务B之后发生了异常回滚,就会造成事务A脏读、脏写。
- 读已提交:事务A只能读取事务B已经提交的数据;解决了“读未提交”的脏读脏写问题;但是引入了新问题:事务A相同的查询语句,两次查询,由于事务B提交了数据,可能查到的结果不同,也就是不可重复读问题。
- 可重复读:事务A在本事务内执行相同的查询,得到的结果相同。该隔离级别是Mysql数据库默认的隔离级别,底层使用了MVCC机制解决了“读已提交”的不可重复读问题,MVCC可参看我上编文章https://blog.csdn.net/weixin_37545216/article/details/122052249?spm=1001.2014.3001.5501https://blog.csdn.net/weixin_37545216/article/details/122052249?spm=1001.2014.3001.5501如果事务A内执行了次查询,比如 select * from t_user where id <10 ,得到了2条数据,id=1 和id=5;之后事务B提交了id=3的数据,由于MVCC机制,事务A还是只能查到id=1 和id=5的数据,符合重复读的表现;但是如果事务A还执行了 update t_user set salary=salary+10 where id < 10,则事务A再执行刚才相同的查询语句,得到了id是【1,3,5】的数据,此时事务A懵逼了,以为自己产生了幻觉。所以“可重复读”隔离级别无法避免“幻读”问题。
- 串行化:对表所有的操作,包括查询都加锁,可避免幻读问题。但是性能很差,几乎没有使用。
解决幻读问题,还可使用“间隙锁”和“临近锁”;
在可重复读隔离级别下,假如有 id=1,id=2, id=3,id=7,id=8,id=11 这6条数据,如果有条查询语句:select * from t_user where id >5 and id < 10,那么where后面的范围与数据库中记录间隙([3,7],[8,11])有交集的部分都会加上锁,也就是 (3,7) 和 (8,11)区间的数据都会加上锁,其他事务不能操作这些数据,这就是间隙锁。
同时 id >= 11的部分也会加锁,即临近区间的部分也会加上,这就是临近锁。
事务注解
@Transaction 注解事务适用于流程很短,执行很快的方法上。否则会导致数据库吞吐量降低
而如果方法很长,且方法里还调用了其他很多无需事务的方法时候,最好不用注解事务@Transaction,而用编程事务(下图)