事务(Transaction)是由一系列对系统中数据进行访问与更新的操作所组成的一个程序执行逻辑单元。 或许这么说你可能难以理解,那我们举个经典例子来说明事务的用途吧:
用户 A 向用户 B 转账,我们需要进行以下操作:从 A 账号中把余额读出来。对 A 账号做减法操作。把结果写回 A 账号中。从 B 账号中把余额读出来。对 B 账号做加法操作。把结果写回 B 账号中。在这些操作中需要保证这些操作是一体的要么都成功做完,要么都不成功。 为了解决这种问题这也就是事务
诞生的背景。
ACID 特性
数据库事务拥有以下 4 大特性: 原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
1. 原子性
原子性是指事务是一个不可再分割的工作单元,事务中的操作要么都发生,要么都不发生。
简单来说就是操作成功了就成功了,一旦操作失败了就会放弃事务中已经执行的操作,回到事务刚开始的状态。
2. 一致性
一致性是指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。
一致性就是说不管操作是否成功,总的数据不会发生改变,即 A 给 B 转账,A 和 B 的总金额不发生变化。
3. 隔离性
多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。
如果 A 在转账 1 亿给 B(事务 1),同时 C 又在转账 3 亿给 A(事务 2),不管事务 1 和事务 2 谁先执行完毕,最终结果必须是 A 账户增加 2 亿,而不是 3 亿,B 增加 1 亿,C 减少 3 亿。
事务最复杂问题都是由事务隔离性引起的。完全的隔离性是不现实的,完全的隔离性(序列化)要求数据库同一时间只执行一条事务,这样会严重影响性能。
4. 持久性
这是最好理解的一个特性:持久性,意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。(完成的事务是系统永久的部分,对系统的影响是永久性的,该修改即使出现致命的系统故障也将一直保持)
隔离性和隔离级别
隔离性
隔离性是通过锁实现的,当一个事务对某项数据进行更新操作的时候就会对数据加锁,从而防止其他事务对该数据的影响,锁的级别一般有 3 种,全局锁,表锁,行锁,MySQL 中的 InnoDB 支持以上的三种锁,MyISAM 不支持行锁。锁获取的时机是事务执行的第一句才开始锁住数据的,并不是在数据开启的时候获取的,当事务提交成功后才会释放锁。
隔离级别
隔离级别有 4 种从低到高分别是读未提交(Read uncommitted)、读提交(Read committed)、重复读(Repeatable read)、序列化(Serializable)。
读未提交(Read uncommitted)
从名称中就可以看出,读 未提交,即一个事务可以读另一个事务未提交的数据,简单来说就是一个事务修改了某个数据当它还没提交的时候,另一个事务读取该数据的时候会读取到修改后的数据,即使修改的事务还没有提交。这种隔离级别解决了更新丢失,一事务写时其他事务可读不可写。
读提交(Read committed)
从名称也可以看出来,读 提交,一个事务要等另一个事务提交后才能读取数据,即事务期间独占数据,其他数据不能对该数据进行读写操作。这种级别解决了脏读和更新丢失,一事务写时其他事务不可读写。
重复读(Repeatable read)
这个从名称应该是无法看出来了,重复读即一个事务重复读的数据是相同的,就是当一个事务对数据进行读操作的时候其他事务不能对其进行修改但可读。这种隔离级别解决了脏读,更新丢失和不可重复读,一事务读时其他事务可读不可写。
序列化(Serializable)
这是最高的隔离级别,所有的事务只能一个接一个的执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
简单说一下事务的问题
- **更新丢失:**当事务 1 更新失败时会回滚数据,而在回滚之前事务 2 若成功更新数据,则事务 2 更新成功后的数据会被回滚覆盖,造成更新丢失。
- **脏读:**当事务 1 读取了事务 2 未提交的数据,而事务 2 若失败回滚数据了,则事务 1 读取到是数据就是无中生有的数据,即脏数据
- **不可重复读:**事务 1 读取某个数据后,事务 2 对其做了修改,当事务 1 再次读该数据时得到与前一次不同的值。
- **幻读:**事务 1 读取在读取某范围数据时,事务 2 插入一条数据,当事务 1 再次数据这个范围数据时就不一样了,出现了一些幻数据
简单整理一下各种隔离级别可避免的问题
<table> <tbody> <tr> <td></td> <td> <strong>更新丢失</strong> </td> <td> <strong>脏读</strong> </td> <td> <strong>不可重复读</strong> </td> <td> <strong>幻读</strong> </td> </tr> <tr> <td> <strong>读未提交(Read uncommitted)</strong> </td> <td>避免</td> <td></td> <td></td> <td></td> </tr> <tr> <td> <strong>读提交(Read committed)</strong> </td> <td>避免</td> <td>避免</td> <td></td> <td></td> </tr> <tr> <td> <strong>重复读(Repeatable read)</strong> </td> <td>避免</td> <td>避免</td> <td>避免</td> <td></td> </tr> <tr> <td> <strong>序列化(Serializable)</strong> </td> <td>避免</td> <td>避免</td> <td>避免</td> <td>避免</td> </tr> </tbody> </table>
MySQL事务隔离
https://blog.ixk.me/post/mysql-transaction-isolation许可协议
BY-NC-SA
本文作者
Otstar Lin
发布于
2019/09/05
转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!