数据库事务四大特性(ACID),四种隔离级别

事务四大特性和隔离级别有太多东西要学了......分开记录比较好......

一.数据库事务的四大特性

1.原子性(Atomicity)(数据库的操作):原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。

这个刚好最近有类似的业务用到了这个性质。(这个性质其实是很常见的)

业务:一个用户可以拥有一个自定义的属性,可以自定义多条,但是这个自定义数据的量不是特别大,用户可以自行增加或者删除自定义属性的条数,也可以修改里面的内容。

如果是想在不删除用户原有数据下,判断增加删除修改操作还是比较麻烦的,所以一开始我就选择了先删除原来用户自定义的数据,然后再往里面添加数据。但是这样会存在一个问题,如果删除的事务成功了,但是保存失败了呢?这样就会导致用户的数据丢失。后来我又想那就先保存,再删除,这样数据就不会丢失了,这么做确实可以保证数据不会丢失,但是万一删除失败了呢,用户的数据就会重复了。所以这个时候事务的特性就体现的尤为重要。

解决方案:Service层同时进行数据库的添加和删除操作,并且为方法加上注解@Transactional

@Transactional(rollbackFor = Exception.class)
public void updateSave(List<Userinfo> saveList, String userId) {
        aDao.deleteAllByUserId(userId);
        super.save(saveList);
}

此时的效果就是绑定了两个操作,使他们成为一个原子操作,要么一起提交成功,要么一起回滚。

补充:数据库中是如何实现原子性的?undo log

undo log称之为回滚日志,每条数据的变化(insert/update/delete)都会产生一条记录,并且日志持久化到磁盘,undo log用来记录数据修改前的信息,比如说要插入一条记录,那么undo log就会记录一条删除该信息的语句,这样需要回滚的时候undo log就会执行删除之前插入的那条记录,达到没有修改前的状态,更新一个记录也会生成一条sql记录更新前的字段状态,从而实现了原子性。

2.一致性(Consistency)(数据库中数据的状态一致):一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

 

拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

在原子性的示例中,也是体现了一致性原则的,删除就是减一操作,保存就是加一操作。目的就是让数据库中用户的数据只保留一份。实际上是通过了原子性的操作保证了数据的一致性。

3.隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。关于事务的隔离性数据库提供了多种隔离级别。

关于隔离性,还分为四种隔离级别(这里内容有点多,详细记录在第二节....)

01:Read uncommitted(读未提交):最低级别,任何情况都会发生。表示可以读取其他事务没有提交的数据。
02:Read Committed(读已提交):可避免脏读的发生。表示只能读取别的事务提交的数据。
03:Repeatable read(可重复读):可避免脏读、不可重复读的发生。指在数据库访问中,一个事务范围内两个相同的查询需要返回相同的数据,也就是一个事务开启查询后,不允许别的事务对查询的数据进行更改。 不可重复读和脏读的区别,脏读是某一个事务读取另一个事务未提交的脏数据;不可重复读则是读取原始数据和前一事务已提交的数据。在Repeatable read的事务隔离级别下:对银行进行余额查询的事务未结束前,别的事务不可对账号下的余额进行修改,要保持此次查询与下次一查询的金额相同。

一开始被可重复度、不可重复度绕晕了,其实蛮好记的。可重复读就是不管怎么读都是一样的数据(这是需要的效果),不可重复读就是无法再读取和上一次重复的数据(这在事务开启之后是要避免的),另外,可重复度隔离级别下也可能会引起幻读。
04:Serializable(串行化):避免脏读、不可重复读,幻读的发生。最高的事务隔离级别,代价花费最高,性能很低,很少使用,在此级别下,事务顺序执行。

4.持久性(Durability):持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

mysql为了提升性能不会把每次的修改都实时同步到磁盘中,而是先存到缓存中,然后再使用线程去做缓冲池和磁盘中的同步。这样必然会存在问题,假如电脑突然停电,那么没有持久化到磁盘的信息必然会丢失。所以需要有一个机制(redo log)能够让数据库具有数据持久性,确保数据不管在何种情况都能够将数据存到磁盘。

补充:数据库中是如何实现原子性的?redo log

redo log叫重做日志,当事务提交的时候会把所有的修改信息存放在redo log中,并且会把redo log持久化到磁盘,当电脑出现故障,重启后就会重新执行redo log的修改信息来恢复数据,这样就能够达到持久化的目的。

二.事务隔离级别以及各级别下的并发访问问题

1.更新丢失

mysql所有事务隔离级别几乎都避免了更新丢失。

开始存款事务:查询账户余额为100元,存入100元,余额变为200元,提交事务。更新丢失。

开始取款事务:查询账户余额为100元,取出10元,将余额修改为90元,回滚事务,余额回复为100元。

2.read uncommitted引起的脏读(最低事务隔离级别)

将事务隔离级别设置成未提交读(可以读其它session未提交的数据)set session transaction isolation level  read uncommitted;

session1:

start transaction;//开启事务

update account_innodb set balance = 100-10 where id =1;  //取钱操作

selete balance from account_innodb where id=1;//结果balance=90

此时事务未提交,开启另外一个session

session2:

start transaction;//开启事务

selete balance from account_innodb where id=1;//结构balance=90   按照正常的业务逻辑,在别的事务未提交之前,结果应该要是100才是对的,因为session1还不确定是否要回滚!这就是脏读。(读取别的事务未提交的数据)

此时session1进行回滚

rollback; //回滚操作

selete balance from account_innodb where id=1;//结果balance=100

但是此时session1还在进行操作,并不知道session2已经将数据回滚,所以还会继续使用未提交的数据

update account_innodb set balance=balance+20;//存20块钱

commit; //提交事务

selete balance from account_innodb where id=1;//结果balance=110!

白白丢了10块钱啊......

3.如何避免第二点出现的脏读?

使用read committed(只允许读其他事务已提交的数据)(Oracle默认事务隔离级别)

将事务隔离级别提升一个档次 set session transaction isolation level  read committed

区别:

在第二点的session2操作:selete balance from account_innodb where id=1;结果为100!

此时session1回滚之后,session2读取的是原始未提交的数据,再进行存钱操作,结果是符合实际业务的。

今天发了工资2000块,真开心

4.可重复读Repeatable read

引出问题:

假设事务A要进行存钱操作,在read committed的隔离级别下,A事务读取到的数据是100

此时事务B开启事务之后进行取钱操作:update account_innodb set balance = 100-10 where id =1; 

此时事务A再次进行读取余额,数据居然900???也就是说第一次和第二次读取的数据不一样了???这就是不可重复读引起的幻读。

场景模拟:(在read committed隔离级别下)

今天发工资了,不知道发了多少,去查一下余额,发现这个月发了2000块,余额显示2000整,我想着我也用不了那么多,给我妈转1000块吧,可是在我点击转账的时候居然提示我余额不足??再次查询居然发现余额为0??究竟是什么原因啊,明明余额显示是2000的,问了下老婆才知道,刚才这笔钱已经转给学校老师当孩子的学费了。

Repeatable read可以避免这种情况的发生,即在他老婆转账之后,他查询的余额还是2000。

关于此问题详细文章:MySQL-InnoDB-REPEATABLE READ(可重复读)

5.Serializable(串行化)

最高的事务隔离级别,代价花费最高,性能很低,很少使用,在此级别下,事务顺序执行,避免上述产生的情况。


 

 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值