数据库事务

Jun 25, 2021


概述

事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。

事务的开始与结束可以由用户显式地控制。如果用户没有显式地定义事务,则由DBMS按默认的规定自动划分事务。


事务特点

事务具有原子性、一致性、独立性、隔离性及持久性等特点。

(1)原子性(Atomicity)整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被 回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

例子1:银行转账,从A账户转100元至B账户,分为两个步骤:

①从A账户减少100元

②转100元至B账户

这两步必须同时完成,要么都不完成。不可能A的钱转了,然后钱少了,但是B的钱却没有增多。

例子2:银行取钱

①银行卡减款

②拿到现金

不可能银行卡减少了,而钱却没拿到。这两步必须同时完成,要么都不完成。

(2)一致性(Consistency)在事务开始之前和事务结束以后,数据库数据的一致性约束没有被破坏。 例如:现有完整性约束A+B=100,如果一个事务改变了A,那么必须得改变B,使得事务结束后依然满足A+B=100,否则事务失败。

(3)隔离性(Isolation)数据库允许多个并发事务同时对数据进行读写和修改的能力,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。例如:现有有个交易是从A账户转100元至B账户,在这个交易事务还未完成的情况下,如果此时B查询自己的账户,是看不到新增加的100元的。

(4)持久性(Durability)事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多不同的级别。而且不可能有能做到100%的持久性策略(如果数据库本身就能做到真正的持久性,那么备份又怎么能增加持久性呢?)


标准的事务隔离级别

在数据库中标准的事务隔离级别有如下几种:

❶ 未提交读(READ-UNCOMMITTED),对应的异常线程是脏读(Dirty read)。

❷ 提交读(READ COMMITTED),对应的异常现象是不可重复读(Non-repeatable read)。

❸ 可重复读(REPEATABLE READ),对应的异常现象是幻读(Phantom read)。

❹ 可序列化(SERIALIZABLE)。

1.脏读

当一个事务被允许读取另一个事务修改但未提交的数据时,就会发生脏读(事务隔离级别为READ-UNCOMMITTED)。

如下表,事务2修改了一行记录,但是没有提交。然后事务1读取到了未提交的数据,如果事务2回滚其更改的数据或再次更新,那么事务1种看到的记录可能就是错误。

1

事务1读取到了age=27的记录,但是事务2执行了回滚操作,这时并不存id=1,age=27的记录。

2.不可重复读

当事务内相同的记录被检索两次,且两次得到的结果不同时,此现象称为不可重复读(事务隔离级别为 R。EAD COMMITTED)。

如下表,事务2对记录做了修改并提交成功,这意味着修改的记录对其他事务是可见的,因此事务1两次读取的age值不同。

2

3.幻读

在事务执行过程中,另一个事务将新记录添加到正在读取的事务中时,会发生幻读(事务隔离级别为REPEATABLE READ)。

当执行SELECT…WHERE语句时未对范围锁定,则可能会发生这种情况。幻读是不可重复的一种特殊情况,当事务1重复执行SELECT…WHERE语句时,在这期间事务2执行INSERT语句插入了满足where条件的新记录。

如下表:事务1执行两次,返回两组不同的记录。这里需要注意,在MySQL中增加了间隙锁防止幻读发生,所以在MySQL中事务隔离级别为REPREATABLE-READ,不会发生以上异常现象。

3


调整事务隔离级别

在MySQL中可以通过设置transaction_isolation参数来调整数据库事务隔离级别,默认的事务隔离级别是REPREATABLE-READ。但是为了避免发送锁等待,通常我们都将事务隔离级别设置为READ-COMMITTED

通过参数调整可以在配置文件中设置,或者使用命令行参数修改,例如修改事务隔离级别为READ-COMMITTED:

SET GLOBAL transaction_isolation = ‘READ-COMMITTED’


事务管理

在MySQL中默认是自动提交事务的,可以通过以下方式手动开启事务:

● 执行begin语句

● 执行start transaction 语句

● 设置autocommit = 0

可以通过以下4种方式提交事务

● 执行commit语句

● DDL语句隐式提交

● 自动提交(commit)

● 在事务内开启事务

事务的回滚方式如下:

● 执行rollback语句

● 因达到超时时间而发生语句级或者事务级的回滚

● 检测到死锁时发生回滚