(1)、编程式事务和声明式事务
编程式事务是直接通过JDBC,JTA编写代码的方式设置事务的边界和隔离级别。
声明式事务依赖应用服务器提供的服务或者框架(Spring Framework) 在配置文件中设置事务边界和隔离级别
(2)、并发访问数据可能引发的问题
a、丢失更新
事务A和事务B更新一个数据,A事务执行更新结束提交事务,B执行更新后回滚,造成A事务数据更新丢失。
时刻 | A 事务 | B 事务 |
t1 | 启动事务 | |
t2 | 读取账号,为1000元 | |
t3 | 启动事务 | |
t4 | 读取帐号为1000元 | |
t5 | 更新,增加1000元,帐号余额为2000元 | |
t6 | 提交事务 | |
t7 | 更新,增加2000元,帐号余额为3000元 | |
t8 | 事务回滚,帐号最终为1000元 |
b、脏读
A 和 B两个事务,A事务读取了B事务尚未提交到数据库中的数据后,如果B事务回滚,A事务读取的数据是脏数据
时刻 | A 事务 | B 事务 |
t1 | 启动事务 | |
t2 | ||
t3 | 启动事务 | |
t4 | 读取账号,为1000元 | |
t5 | 更新,增加2000元,帐号余额为3000元 | |
t6 | 读取账号,数据为3000元 | |
t7 | 事务回滚 | |
t8 | 更新,增加1000元,帐号余额为4000元 | |
t9 | 提交事务,账号最终为4000元 |
c、不可重复读
A 和 B 两个事务,A事务读取了两遍数据库中的同一行数据,但是第二次读取的数据要么和第一次不同,要么发现该
数据被删除。
时刻 | A 事务 | B 事务 |
t1 | 启动事务 | |
t2 | 读取账号 1000 | |
t3 | 启动事务 | |
t4 | 读取账号1000 | |
t5 | 更新帐号 增加2000 ,账号变为3000 | |
t6 | 提交事务 | |
t7 | 再次读取账号为3000 | |
t8 | 提交事务 |
d、幻读
A 和 B 两个事务执行两次相同SQL语句查询,获取的数据结果集不同。
时刻 | A 事务 | B 事务 |
t1 | 启动事务 | |
t2 | 查询account表数据,返回5000条记录 | |
t3 | 启动事务 | |
t4 | 在accounts表中增加了一条数据 | |
t5 | 提交事务 | |
t6 | 再次查询accounts 表数据,返回5001条记录 | |
t7 | commit |
(3)、事务隔离级别
隔离性是事务的特性之一,它通过数据库中的表或者字段加锁实现,锁定后在同一个时间内允许一个事务执行更新或者
读取。所以隔离性越强,数据库并发处理数据能力越弱。
事务的隔离级别分为如下四种(严格性分别由低到高):
a、Read Uncommited
一个事务可以读取另外一个事务已经更新但未提交的数据。但在另外一个事务提交前不允许其他事务写入。
b、Read commited
某个事务仅仅可以读取(不可以修改或者删除)另外一个事务提交的更新数据。
c、Repeatable Read
一个事务已读取的数据不允许其他事务写入。
d、Serializable
最严格的事务隔离级别,要求所有的事务序列化执行,不能并发执行。
隔离级别 | 丢失更新 | 脏读 | 不可重复读 | 幻读 |
Read Uncommitted | 不可能 | 可能 | 可能 | 可能 |
Read Committed | 不可能 | 不可能 | 可能 | 可能 |
Repeatable Read | 不可能 | 不可能 | 不可能 | 可能 |
Serializable | 不可能 | 不可能 | 不可能 | 不可能 |
不同隔离级别可能发生的并发问题