事务(Transaction)
- 是由一系列对系统中数据进行访问与更新的操作所组成的一个程序执行逻辑单元。
1.事物的ACID特性
原子性
- 事物的原子性是指事物必须是一个原子的操作序列单元。事物中包含的各项操作在一次执行过程中,只允许出现两种状态之一。
- 全部执行成功
- 全部执行失败
- 任何一个异常的操作,都会使目前正在进行的事物中所有的操作都失败,并回滚到事物开始之初。只有最终事物执行完毕,提交事物之后,所有的操作才都会真正执行。
- 比如:A和B在银行系统中,进行一次转账的操作,系统先扣除A的钱,然后再增加B的钱。但是在做完第一步操作之后,突然断电或者异常。如果不是事物操作,像上面这种情况,在生活中就会引起纠纷。所以一般这种操作都是放在一个事物中进行,这样就算发生异常,A的钱也没有扣除,就跟什么都没有发生一样。
一致性
- 一致性官方的解释是:数据库一致性(Database Consistency)是指事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。保证数据库一致性是指当事务完成时,必须使所有数据都具有一致的状态。在关系型数据库中,所有的规则必须应用到事务的修改上,以便维护所有数据的完整性。
- 比如:还是转钱的操作,本来两个人的总钱数应该是一样的,但是当第一个人扣钱之后,发生异常了并且没有回滚的话,两个人的总钱数就变了,这就是数据的不一致。
- 事务的其他三个特性都是为了保证事务一致性的手段。而一致性是状态。
- 如果没有原子性,A转账给B的过程中失败了,那么A的钱减少了,但是B的钱没有增加,两人的钱总数将会和事务开始之前不一样,这就导致了数据不一致。
- 如果没有隔离性,A转账给B两次,每次转50,但是A是从两个事务中同时转账的。也就是自身减少50,然后取出B的值,B+50,然后再写入给B。如果在取出B的值的时候,是两个事务同时取出的,那么A减少了两次50,B只会增加一次50.也会导致数据的不一致。
- 如果没有持久性,数据在写入硬盘的过程中宕机了,再次开机,数据就会不一致。所以要用到数据库中的日志信息,保证持久性。
隔离性
- 事务的隔离性是指在并发环境中,并发的事务是互相隔离的,一个事务的执行不能被其它事务干扰。也就是说,不同的事务并发操作相同的数据时,每个事务都有各自完整的数据空间。
- 一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务是不能互相干扰的。
- 隔离性分4个级别
1. 读未提及(READ_UNCOMMITTED)
- 读未提及,该隔离级别允许脏读取,其隔离级别是最低的。换句话说,如果一个事务正在处理某一数据,并对其进行了更新,但同时尚未完成事务,因此还没有提交事务;而以此同时,允许另一个事务也能够访问该数据。
- 脏读示例:
在事物A和事物B同时执行时可能会出现如下场景:
时间 | 事务A | 事务B |
---|---|---|
T1 | 开始事务 | 开始事务 |
T2 | —— | 查询余额(1000元) |
T3 | —— | 取出1000元(余额0元) |
T4 | 查询余额(0元) | —— |
T5 | —— | 撤销事务(余额恢复1000元) |
在T4时刻事物A读到了0元,但是事物B最后又回滚了事物,所以这时候事物A读到的就是“脏数据”,很明显是事务没有进行隔离造成的。
2. 读已提交(READ_COMMITTED)
- 读已提交是不同的时候执行的时候只能获取到已经提交的数据。
这样就不会出现上面的脏读的情况了。 - 可是解决了脏读问题,但是还是解决不了不可重复读问题。
时间 | 事务A | 事务B |
---|---|---|
T1 | 开始事务 | 开始事务 |
T2 | —— | 查询余额(1000元) |
T3 | 查询余额(1000元) | —— |
T4 | —— | 取出1000元(余额0元) |
T5 | —— | 提交事物 |
T6 | 查询余额(0元) | —— |
事务A其实除了查询两次以外,其它什么事情都没做,结果钱就从1000编程0了,这就是不可重复读的问题。
3. 可重复读(REPEATABLE_READ)
- 可重复读就是保证在事务处理过程中,多次读取同一个数据时,该数据的值和事务开始时刻是一致的。因此该事务级别进制了不可重复读取和脏读,但是有可能出现幻读的数据。
- 幻读指的是,虽然给查询的行加锁了,但是可以添加新的行,比如:事物A对表中所有行进行了修改,这时候A还没有提交,虽然对这些行被加锁了,但是事物B这时候新插入了一行,那么A再次查询一下刚才修改的数据,发现表中多出来一行,就好像发生了幻觉一样。
- 在MySQL的InnDB数据库中,使用MVCC(多版本控制)。在RR级别下也解决了上述问题,即没有使用行级锁的概念。 使用MVCC,在事务第一次使用select语句的时候,会生成一张查询表的快照,无论外部如何改变,当前事务总是查询的这张快照表。
- 所以InnDB中幻读发生可以是这样的:事务A查询快照有两条数据, 事务B插入一条数据并提交。 事务A再次查询还是两条数据, 当事务A尝试插入第三条数据的时候, 发现主键冲突。
- 可重复读的好处: 假如有个业务是在月末的时候, 需要给表中A组的用户发送信息1 , 给B组的用户发送信息2 , 要进行两次查询, 如果是不可重复读的话 , 在两次查询的间隔, 有其他事务将A组中的一些用户添加到了B组, 那么A组用户将收到两次信息。 但是我只想考虑 , 上个月的A组和B组,并不需要考虑下个月的变化。 所以就需要在一个事务中 保证数据的可重复读。
4. 顺序读(SERIALIZABLE)
顺序读是最严格的事务隔离级别。它要求所有的事务排队顺序执行,即事务只能一个接一个地处理,不能并发。
事物隔离级别对比
事物隔离级别 | 脏 读 | 不可重复读 | 幻 读 |
---|---|---|---|
读未提及(READ_UNCOMMITTED) | 允许 | 允许 | 允许 |
读已提交(READ_COMMITTED) | 不发生 | 允许 | 允许 |
可重复读(REPEATABLE_READ) | 不发生 | 不发生 | 允许 |
顺序读(SERIALIZABLE) | 不发生 | 不发生 | 不发生 |
4种事务隔离级别从上往下,级别越高,并发性越差,安全性就越来越高。
一般数据默认级别是读以提交或可重复读。
持久性
- 这是最好理解的一个特性:持久性,意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。(完成的事务是系统永久的部分,对系统的影响是永久性的,该修改即使出现致命的系统故障也将一直保持)