数据库事务概念
数据库事务说的是在一次逻辑中对数据库执行的一系列操作。这一系列的操作要成为事务就需要满足四个条件,分别是原子性、一致性、隔离性和持久性。也就是经常听到的事务的ACID。
原子性
我对原子性的理解是事务中定义的一系列操作,要么全部执行,要么就都不执行。原子性体现在代码上就是rollback,如果在执行一系列操作中出了异常,那么为了体现原子性,我们就需要rollback之前的操作。
例子:银行转账操作,A账户转到B账户100块,我们需要从A账户扣掉100,同时在B账户上加100,如果在A账户减掉100后,准备在B账户加100的过程中抛出了异常,那么这个时候就要rollback。体现的就是原子性。
一致性
一致性的概念百度百科说的事务完成时,必须使所有的数据都保持一致状态。维基百科说的是事务应确保数据库的状态从一个一致状态转变为另一个一致状态。
说个简单例子,就拿上面转账来说,假设A账户和B账户各有1000元,不管A账户和B账户怎么转账,都应该保持两个账户加起来是2000元。一致性会涉及到脏读的问题,后文将详解。
隔离性
隔离性说的是如果有多个事务操作同一条数据,应该保证事务之间不应该相互影响。这个属性会涉及到数据的锁机制和我们写代码时候经常遇到的悲观锁和乐观锁的问题。
持久性
持久性说的是事务成功提交的数据应该永远保存在数据库,不能说数据库宕了,重启之后就没了。
数据库事务出现的问题
事务出问题的原因就是多线程操作,即多个线程操作同一条数据。这些问题已经由大牛们整理出来了,这些问题如下:
1、第一类更新问题
2、脏读
3、幻读
4、不可重复读
5、第二类更新问题
第一类更新问题
先说第一类更新问题,这个问题总结起来说是,事物之间会覆盖掉其他已提交事务。
例子如下,表格形式
时间 | 事务A | 事务B |
T1 | 开始事务 | 开始事务 |
T2 | 查询余额1000 | 查询余额1000 |
T3 |
| 执行存款1000+100,余额改为1100 |
T4 |
| 提交事务 |
T5 | 执行转账1000-100 余额改为900 |
|
T6 | 撤销事务 |
|
最后账户的余额是1000,中间出现的问题很明显,事务A覆盖掉了事务B的操作,最后银行客户就损失了100块。
脏读
这个问题简单,就是读了不该读(未提交)的数据。还是表格
时间 | 事务A | 事务B |
T1 | 开始事务 | 开始事务 |
T2 | 查询余额1000 | 查询余额1000 |
T3 |
| 执行存款1000+100,余额改为1100 |
T4 | 查询余额1100 |
|
T5 |
| 因为各种原因,事务回滚,撤销之前+100操作 |
T6 | 执行转账1100-100余额改为1000 |
|
T7 | 提交事务 |
|
事务过程如上,问题很明显读了不该读的数据,最后银行赔了100块。
幻读
幻读和下面提到的不可重复读很像。幻读说的前后读取结果的记录数不一样。
举一个例子:
老板对财务说对员工A的工资做调整,除了工龄工资,其他所有的费用加100块钱,
时间 | 事务A | 事务B |
T1 | 开始事务 | 开始事务 |
T2 | 查询员工A除了工龄工资外 ,有基本工资、绩效工资两类 |
|
T3 |
| 老板给员工A本月的工资添加一类新的工资,项目奖金 |
T4 |
| 提交事务 |
T5 | 再次查询员工A的工资,发现多了一类 |
|
T6 | 前后两次查询不一致 |
|
这个时候出现的问题就叫幻读。
不可重复读
不可重复度说的是前后读取的结果的值不一样。
时间 | 事务A | 事务B |
T1 | 开始事务 | 开始事务 |
T2 | 查询员工A除了工龄工资外 ,有基本工资、绩效工资两类 |
|
T3 |
| 老板自己给员工A本月的绩效工资加了100 |
T4 |
| 提交事务 |
T5 | 再次查询员工A的工资,绩效工资已经加了100 |
|
T6 | 前后两次查询不一致 |
|
第二类更新问题
第二类问题和第一类问题也很像不同的是,第二类更新问题在事务A最后的操作是提交事务,结果是一样的都是覆盖了事务B。
对这个问题,我理解的很浅,还欢迎大家指教。
我会其他文章讨论Hibernate的悲观锁和乐观锁