数据库事务管理

/ Spring / 没有评论 / 403浏览

在日常的软件开发中除了需要考虑软件性能指标外,还需要特别考虑的地方就是软件的安全性了,提到安全性,那我们就不得不考虑事务管理。也就是在同一个事务下,对数据库的操作要么全成功提交,只要有一个失败,那么已经更新的数据也必须回滚。在spring中使用事务比较简单,因为spring中不但提供了和底层事务无关的事务对象,还提供了声明性事务的功能,目的是让程序从事务代码中解耦,方便我们随时随地的添加事务

在介绍spring事务之前,我们先简单了解一下数据库事务的知识,说的简单点就是多条SQL语句在执行时,要么全部执行成功,要么全部执行失败。数据库事务包括四个特性:

上面的4种特性目的都是一个就是保证数据库的事物安全,那么在数据库的底层是怎么实现上述特性的呢?

实现的方式和Java有些类似,我们知道在Java中是通过对象锁的方式来保证线程安全的,那么同样数据库也是采用锁的方式来实现的。只是这种锁叫做数据库锁。当多个事务试图对相同的数据进行操作时,只有获得到数据库锁的事务才能操作数据,只有当这个事务提交完成后,后面的事务才可以对这个数据进行操作。

在数据库事物中,如果有多个客户端同时访问数据库,也就是并发时数据库中的数据可能会被多个事务同时访问,这时如果没有采取数据库隔离措施,就会导致出现各种并发问题,从而破坏了数据的完整性。我们下面看一下,在这种情况下都会出同哪些问题:

时间转账事物A取款事务B
T1
开始事务
T2
开始事务
T3

查询账户余额为1000元
T4

取出500元把余额修改为500元
T5
查询账户余额为500元(脏读)
T6

撤销事务余额恢复为1000元
T7
存入100元把余额修改为600元
T8
提交事务

在上述的场景中,B希望取款500元而后又撤销了此操作,而A在相同的账户中存入了100元,但因为A事务读取了B事务未提交的事务数据,因此导致该账户白白丢失了500元。

时间取款事物A转账事务B
T1
开始事务
T2
开始事务
T3

查询账户余额为1000元
T4
查询账户余额为1000元

T5

取出100元把余额修改为900元
T6

提交事物
T7
查询账户余额为900元(和T4获取的数据不一致)

上述情景出现的问题就是在同一个事务中T4时间点和T7时间点读取的账户余额不一样。

时间统计事物A转账事务B
T1
开始事务
T2
开始事务
T3
统计总金额为10000元
T4

新存入100元
T5

提交事物
T6
统计总金额为10100元(幻象读)

幻象读和不可重复读是两个不同的概念,前者是指已经读到了其它已经提交的事务的新增数据,而后者是指读到了已经提交事务的更改数据或删除数据。两种不同场景,采取的策略也是不同的,防止读取到更改的数据,只需要对操作的数据添加行级锁即可,这样就可阻止操作中的数据发生变化,而防止读取到新增数据,则往往要添加表级锁,也就是将整个表锁定,以防止新增数据。

时间取款事物A转账事务B
T1开始事务

T2

开始事务
T3
查询账户余额为1000元
T4

查询账户余额为1000元
T5

存入100元把余额修改为1100元
T6

提交事务
T7
取出100元把余额修改为900元
T8
撤销事务
T9
余额恢复为1000元(丢失更新)

时间转账事物A取款事务B
T1
开始事务
T2
开始事务

T3

查询账户余额为1000元
T4
查询账户余额为1000元

T5

取出100元把余额修改为900元
T6

提交事务
T7
存入100元
T8
提交事务
T9
把余额修改为1100元(丢失更新)