一:事务四大特性
- 原子性(Atomicity);事务必须是一个不可分割的整体,就像我们在化学课里学到的原子,原子是构成物质的最小单位。于是,人们就归纳出事务的第一个特性,
- 一致性(Consistency):也就是说,执行完数据库操作后,数据不会被破坏。打个比方,如果从 A 账户转账到 B 账户,不可能因为 A
账户扣了钱,而 B 账户没有加钱吧。 - 隔离性:当我们编写了一条 update 语句,提交到数据库的一刹那间,有可能别人也提交了一条 delete
语句到数据库中。也许我们都是对同一条记录进行操作,可以想象,如果不稍加控制,就会出大麻烦来。我们必须保证数据库操作之间是“隔离”的 - 持久性(Durability):数据永久地存放在磁盘中,这个也算事务的一条特性
注:这 4 条特性,是事务管理的基石,一定要透彻理解。此外还要明确,这四个家伙当中,谁才是老大?
其实想想也就清楚了:原子性是基础,隔离性是手段,持久性是目的,真正的老大就是一致性。数据不一致了,就相当于“江湖乱套了”。所以说,这三个小弟都是跟着“一致性”这个老大混,为他全心全意服务。
这四个家伙当中,其实最难理解的反倒不是一致性,而是隔离性。因为它是保证一致性的重要手段,是工具,使用它不能有半点差池,否则后果自负!
二:常规数据问题
三类数据读问题
1.Dirty Read(脏读)
2.Unrepeatable Read(不可重复读)
3.Phantom Read(幻读)
两类数据更新问题
1.第一类丢失更新
2.第二类丢失更新
5种问题可以自行百度;
简单点就是:
1.脏读:事务 A 读取了事务 B 未提交的数据,并在这个基础上又做了其他操作。
两个事务都做了修改操作,做完后都做了一次查询,事务没有隔离开,被互相影响到了;
2.不可重复读:事务 A 读取了事务 B 已提交的更改数据。
3.幻读:事务 A 读取了事务 B 已提交的新增数据。
第一条是坚决抵制的,后两条在大多数情况下可不作考虑。
第一类更新可以理解为:
事务A,事务b都做了写操作,A因为回滚覆盖了B
第二类更新可以理解为:
事务B后提交的,吧事务A覆盖了;
理论上,第一种更新问题不允许出现,,第二类,一般数据库靠锁来等待,或者你的sql是个覆盖的sql
三:数据库事务和锁
数据库四个隔离级别:
事务隔离级别(Transaction Isolation Level)
1.READ_UNCOMMITTED
2.READ_COMMITTED
3.REPEATABLE_READ
4.SERIALIZABLE
从上往下,级别越来越高,并发性越来越差,安全性越来越高,反之则反。
数据库锁也是保证数据库数据不发生紊乱的法则之一:
例如,orcal:
读操作不影响:一般查询共享锁;加入for update 会锁响应数据,不希望别人来更新;
修改某条数据,一般会加行锁,看是否用到索引,行锁与表锁的切换;
新增某条记录,一般加表锁
可参考:orcal数据库的锁
四:java程序中的事务、以及锁
第三步保证的数据库中事务的处理,以及不影响数据,
但是程序中,如何在数据库正常的情况下,自己程序导致的数据紊乱呢;
Spring 一下子就提供了 7 种事务传播行为,
1.PROPAGATION_REQUIRED
2.RROPAGATION_REQUIRES_NEW
3.PROPAGATION_NESTED
4.PROPAGATION_SUPPORTS
5.PROPAGATION_NOT_SUPPORTED
6.PROPAGATION_NEVER
7.PROPAGATION_MANDATORY
4.1事务的传播行为:
也可以理解为,一次请求,会访问多个方法,多个方法直接的事务是怎么影响的;
例如:
从方法 A访问方法 B。Spring 解决的只是方法之间的事务传播:
假如方法B出现了问题,如果方法a有写操作,会有什么影响呢?
报错还是一起回滚呢?一起回滚的花,主事务是A还是B呢?
假设事务从方法 A 传播到方法 B,您需要面对方法 B,问自己一个问题:
方法 A 有事务吗?
如果没有,就开启一个事务;如果有,就加入当前事务(方法A加入到方法B)。这就是 PROPAGATION_REQUIRED,它也是 Spring 提供的默认事务传播行为,适合绝大多数情况。
如果没有,就开启一个事务;如果有,就将当前事务挂起。这就是 RROPAGATION_REQUIRES_NEW,意思就是创建了一个新事务,它和原来的事务没有任何关系了。
如果没有,就开启一个事务;如果有,就在当前事务中嵌套其他事务。这就是 PROPAGATION_NESTED,也就是传说中的“嵌套事务”了,所嵌套的子事务与主事务之间是有关联的(当主事务提交或回滚,子事务也会提交或回滚)。
如果没有,就以非事务方式执行;如果有,就使用当前事务。这就是 PROPAGATION_SUPPORTS,这种方式非常随意,没有就没有,有就有,有点无所谓的态度,反正我是支持你的。
如果没有,就以非事务方式执行;如果有,就将当前事务挂起。这就是 PROPAGATION_NOT_SUPPORTED,这种方式非常强硬,没有就没有,有我也不支持你,把你挂起来,不鸟你。
如果没有,就以非事务方式执行;如果有,就抛出异常。这就是 PROPAGATION_NEVER,这种方式更猛,没有就没有,有了反而报错,确实够牛的,它说:我从不支持事务!
如果没有,就抛出异常;如果有,就使用当前事务。这就是 PROPAGATION_MANDATORY,这种方式可以说是牛逼中的牛逼了,没有事务直接就报错,确实够狠的,它说:我必须要有事务!
默认事务传播行为: PROPAGATION_REQUIRED ,有就加入;
Spring 给我们带来了事务传播行为,这确实是一个非常强大而又实用的功能。除此以外,也提供了一些小的附加功能,比如:
1.事务超时(Transaction Timeout):为了解决事务时间太长,消耗太多的资源,所以故意给事务设置一个最大时常,如果超过了,就回滚事务。
2.只事务(Readonly Transaction):为了忽略那些不需要事务的方法,比如读取数据,这样可以有效地提高一些性能。
注解事务比较方便:只需:
@Transactional
public void xxx() {
...
}
4.2 多线程情况下的数据问题
4.1可以理解为单线程,或者多线程情况下,一次请求,各种操作事务如何嵌套的,如果出现问题,事务回滚
多线程情况下,一般都是私有变量,或者常量,或者每次查询操作,最后一步统一实现写入数据库操作,事务最后一次commit提交;
还有一种情况就是,多个线程共用一个公共数据了,并且需要修改,,这这时候,就需要用到程序上的,同步、锁、等api操作了,保证数据不能出现问题
:额外在补充4.2锁
参考文献:https://blog.csdn.net/u014079773/article/details/52808193?spm=1001.2014.3001.5506