七、TCL
1 介绍
TCL:事务控制语言。
事物是由单独单元的一个或多个 SQL 语句组成,在这个单元中,每个 SQL 语句是相互依赖的。而整个单独单元作为一个不可分割的整体,如果单元中的某条 SQL 语句一旦执行失败或产生错误,整个单元将会回滚。所有受到影响的数据将返回到事务开始之前的状态;如果单元中的所有 SQL 语句都执行成功,则事务执行成功。
简单讲,事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。
事物的特点如下:
- 原子性(A:atomicity): 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么都执行,要么都回滚;
- 一致性(C:consistency):执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
- 隔离性(I:isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
- 持久性(D:durability): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
事物的原理:事务开启之后,所有的操作都会临时保存在事务日志中,事务日志只有在得到 commit
命令才会同步到数据库中,其他的任何情况都会清空事务日志( rollback
,断开连接)。
隐式事物:默认情况下,MySQL 中的事务是默认自动提交的,比如INSERT、DELETE语句,都可以看做开启了一个事物,自动提交。
显式事物:在需要原子性操作的时候,需要手动开启事务。
使用场景:转账、订单的生成、付款等。
2 事务的创建
主要介绍显示事物的创建。
前提:需要关闭隐式事物的自动提交
SET autocommit = 0;
创建事务的完整语法:
START TRANSACTION; # 开启事物
SQL 语句..;
SQL 语句..;
SQL 语句..;
...
COMMIT[ROLLBACK]; # 提交事务(COMMIT)或回滚事务(ROLLBACK)
示例:
# 张三向李四转账 500
SET autocommit = 0;
START TRANSACTION;
UPDATE account SET balance = balance - 500 WHERE username = '张三';
UPDATE account SET balance = balance + 500 WHERE username = '李四';
COMMIT;
3 隔离级别
对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题。比如脏读、幻读、不可重复读。
- 脏读(Drity Read):对于两个事务T1、T2,T1读取了已经被T2更新但是还没提交的字段。之后,如果T2回滚,T1读取的内容就是临时且无效的。
- 不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
- 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
为此,引入了数据库事务的隔离级别来避免事务的并发问题。
一个事务和其他事务隔离的程序称为隔离级别。数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程序,隔离级别越高,数据一致性就越好,但是并发性就越低。
为了满足事务的四大特性,数据库定义了4种不同的事务隔离级别,从低到高依次是:
- READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
- READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
- REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
下表展示了四种隔离级别的强度,会造成并发问题用√
表示,不会造成并发问题用×
表示:
隔离级别 | 脏读 | 不可重复读 | 幻影读 |
---|---|---|---|
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |
每启动一个 MySQL 客户端程序,就会获取一个单独的数据库连接。每个数据库连接都会有一个全局变量 @@tx_isolation ,表示当前的事务隔离级别。 设置隔离级别:
SET SESSION|GLOBAL TRANSACTION ISOLATION LEVEL 隔离级别名;
# SESSION表示设置当前连接的隔离级别
# GLOBAL表示设置系统隔离级别
查看当前的隔离级别:
SELECT @@tx_isolation;
Oracle 默认支持 2 种事务隔离级别:READ COMMITTED(读已提交)和SERIALIZABLE(串行化)。Oracle 默认的事务隔离级别是 READ COMMITTED(读已提交)。
MySQL 支持 4 种事务隔离级别。MySQL 默认的事务隔离级别是 REPEATABLE READ(可重复读)。
在分布式事物中,一般会使用最高的隔离级别SERIALIZABLE