事务是什么?
事务是数据库里最小处理单位的任务集合。可以是一条 SQL 语句,也可以是多个SQL的集合操作。它可以保证这些任务集合要么全部都执行完,要么都不执行,不会处于一个中间状态。
数据库是面向多线程多用户设计的,同一时刻会被多个程序读取或修改。对于并发操作,需要考虑对同一资源的访问控制。
而事务就是用来保证数据完整性的。这里的完整性包括逻辑上的完整性,即我们的预期执行结果;也包括了物理上的完整性,比如对于执行成功的事务即使重启了也不会丢失执行结果。
事务的使用
事务的整体流程主要涉及到几个状态点:
(1)开始事务:BEGIN TRANSACTION;
(2)提交事务:COMMIT;
(3)回滚事务:ROLLBACK;
一般的,当我们在执行一条 SQL 语句时就已经默认帮我们开启了事务。若执行成功,则会提交事务。此时的执行结果将会被持久化到硬盘上,即时重启也不会丢失执行结果。
回滚事务主要用于在批量执行 SQL 并且尚未提交事务时,允许撤回前面所有的 SQL 操作。
事务的 ACID
前面提到过,事务是用来保证数据完整性的,为了达到这个目的事务需要保证下面4个特性:
1、原子性(Atomicity)
一个事务是一个不可分割的单位,因此在一个事务里的所有操作要么全部生效,要么全部不生效。
2、一致性(Consistency)
也可以理解为是预期状态的正确性,即从一个正确的状态到另一个正确的状态,这里的状态往往是由业务来定义的。
比如转账中的一个扣钱一个加钱,是我们规定的一个数据流转,那么执行前的账户余额和执行成功后的账户余额就得满足我们的加减特性。只要满足了,就是达到一致性的。
3、隔离性(Isolation)
数据库是允许多个事务并发执行的,每个事务在执行时理想状态是互不影响。然而要达到这个效果就得串行执行事务,这样并发能力也会大大降低。
因此,为了兼顾执行效率,将互相影响的程度分为了4个隔离级别:
1)未提交读:
举个例子,当事务 A 对表1 进行更新操作后,有事务 B 读取了更新后的数据,后面又由于某种原因,事务A进行了回滚。
那么,对于事务 B 来讲就依赖了一个无效的回滚数据,从而后面所做出的决策,也不一定正确了,这就是所谓的脏读,即未提交读隔离级别,此级别数据一致性最差,但并发性最好。
2)已提交读:
如果想防止脏读,就需要等待其他事务提交后再进行读取操作。防止了无效的回滚情况,这就是已提交读隔离级别。
3)可重复读:
已提交读的隔离级别考虑到了数据回滚的无效性,却无法阻止事务的多次提交。
比如事务 A 不断的对表进行修改提交,那么事务 B 就会在不同的时间点读取到不同的数据,为了让事务 B 在执行期间读取的数据都是一致的,就有了可重复读的隔离级别。
保证了事务 B 在执行期间,其他事务不得进行修改操作。
4)可串行化:
可重复读隔离级别保证了事务执行期间读取的一致性。然而,并不包括插入、删除操作。
即会出现读取多数据或少数据的情况,这种现象叫做幻读。
为了解决幻读,只得进行串行化执行,才能互相不影响。而此时的事务并发性是最低的。
4、持久化(Durability)
提交的事务是永久性的,一旦事务执行成功,那么即时进行重启了,也是一样的执行结果(当然,前提是磁盘文件没有损坏)
总结
事务的 ACID 特性保证了数据的完整性,其中事务的隔离级别,数据一致性越高,并发能力就越差,需要我们在实际开发中根据实际的场景选择。例如日志记录的读取,即使是脏读对总体影响也是不大的。