1)原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败;
2)一致性(Consistent):事务结束后系统状态是一致的;
3)隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态;
4)持久性(Durable):事务完成后所做的改动都会被持久化,即使发生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。
【补充】关于事务,在面试中被问到的概率是很高的,可以问的问题也是很多的。首先需要知道的是,只有存在并发数据访问时才需要事务。当多个事务访问同一数据时,可能会存在5类问题,包括3类数据读取问题(脏读、不可重复读和幻读)和2类数据更新问题(第1类丢失更新和第2类丢失更新)。数据库事务隔离级别越高,并发副作用越小,相应的付出的代价就越高,因为事务隔离实质上就是使事务在一定程度上‘串行化’,这显然与‘并发’是矛盾的。
脏读、不可重复读和幻读实际上都是数据库读一致性问题,必须由数据库提供一定的隔离机制来解决,数据库实现事务隔离的方式,基本上可以分为以下两种
- 一种是在读数据前对其进行加锁,阻止其它事务对数据进行修改
- 不加任何锁,通过一定的机制生成一个数据请求时间点的一致性数据快照,并用这个快照来提供一定级别(语句级或事物级)的一致性读取,从用户的角度来看,好像数据库提供同一个数据的多个版本,因此,这种方式又被称为多版本并发控制简称MVCC
- 脏读(Dirty Read):A事务读取B事务尚未提交的数据并在此基础上操作,而B事务执行回滚,那么A读取到的数据就是脏数据。
时间 | 转账事务A | 取款事务B |
T1 |
| 开始事务 |
T2 | 开始事务 |
|
T3 |
| 查询账户余额为1000元 |
T4 |
| 取出500元余额修改为500元 |
T5 | 查询账户余额为500元(脏读) |
|
T6 |
| 撤销事务余额恢复为1000元 |
T7 | 汇入100元把余额修改为600元 |
|
T8 | 提交事务 |
|
- 不可重复读(Unrepeatable Read):事务A重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务B修改过了。
时间 | 转账事务A | 取款事务B |
T1 |
| 开始事务 |
T2 | 开始事务 |
|
T3 |
| 查询账户余额为1000元 |
T4 | 查询账户余额为1000元 |
|
T5 |
| 取出100元修改余额为900元 |
T6 |
| 提交事务 |
T7 | 查询账户余额为900元(不可重复读) |
|
- 幻读(Phantom Read):事务A重新执行一个查询,返回一系列符合查询条件的行,发现其中插入了被事务B提交的行。
时间 | 统计金额事务A | 转账事务B |
T1 |
| 开始事务 |
T2 | 开始事务 |
|
T3 | 统计总存款为10000元 |
|
T4 |
| 新增一个存款账户存入100元 |
T5 |
| 提交事务 |
T6 | 再次统计总存款为10100元(幻读) |
|
- 第1类丢失更新:事务A撤销时,把已经提交的事务B的更新数据覆盖了。
时间 | 取款事务A | 转账事务B |
T1 | 开始事务 |
|
T2 |
| 开始事务 |
T3 | 查询账户余额为1000元 |
|
T4 |
| 查询账户余额为1000元 |
T5 |
| 汇入100元修改余额为1100元 |
T6 |
| 提交事务 |
T7 | 取出100元将余额修改为900元 |
|
T8 | 撤销事务 |
|
T9 | 余额恢复为1000元(丢失更新) |
|
- 第2类丢失更新:事务A覆盖事务B已经提交的数据,造成事务B所做的操作丢失。
时间 | 转账事务A | 取款事务B |
T1 |
| 开始事务 |
T2 | 开始事务 |
|
T3 |
| 查询账户余额为1000元 |
T4 | 查询账户余额为1000元 |
|
T5 |
| 取出100元将余额修改为900元 |
T6 |
| 提交事务 |
T7 | 汇入100元将余额修改为1100元 |
|
T8 | 提交事务 |
|
T9 | 查询账户余额为1100元(丢失更新) |
|
数据并发访问所产生的问题,在有些场景下可能是允许的,但是有些场景下可能就是致命的,数据库通常会通过锁机制来解决数据并发访问问题,按锁定对象不同可以分为表级锁和行级锁;按并发事务锁定关系可以分为共享锁和独占锁,具体的内容大家可以自行查阅资料进行了解。
直接使用锁是非常麻烦的,为此数据库为用户提供了自动锁机制,只要用户指定会话的事务隔离级别,数据库就会通过分析SQL语句然后为事务访问的资源加上合适的锁,此外,数据库还会维护这些锁通过各种手段提高系统的性能,这些对用户来说都是透明的(就是说你不用理解,事实上我确实也不知道)。ANSI/ISO SQL 92标准定义了4个等级的事务隔离级别,如下表所示:
隔离级别 | 脏读 | 不可重复读 | 幻读 | 第一类丢失更新 | 第二类丢失更新 |
READ UNCOMMITED | 允许 | 允许 | 允许 | 不允许 | 允许 |
READ COMMITTED | 不允许 | 允许 | 允许 | 不允许 | 允许 |
REPEATABLE READ | 不允许 | 不允许 | 允许 | 不允许 | 不允许 |
SERIALIZABLE | 不允许 | 不允许 | 不允许 | 不允许 | 不允许 |
需要说明的是,事务隔离级别和数据访问的并发性是对立的,事务隔离级别越高并发性就越差。所以要根据具体的应用来确定合适的事务隔离级别,这个地方没有万能的原则。