事务
定义
一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行
注意:在MySQL中只有使用了INNODB数据库引擎的数据库或表才支持事务
案例
生成订单或转账时,会同时发生的事情
-
订单生成
- 订单表里新增一条记录(起始为0)
UPDATE 订单表 SET items = 1 where id = 1;
- 库存表里减少一条商品记录(起始为100)
UPDATE 库存表 SET items = 99 where id = 1;
-
转账发生
- 张三的余额变成1500(起始2000)
UPDATE 表 张三的余额 = 1500 WHERE NAME = '张三';
- 郭襄的余额变成2500(起始2000)
UPDATE 表 郭襄的余额 = 2000 WHERE NAME = '郭襄';
事务的特性ACID
原子性:
- 一个事务不可再分割,要么都执行要么都不执行,不会结束在中间某个环节。
- 事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务出来没有执行过一样
一致性:
一个事务执行会使数据从一个一致性状态切换到另外一个一致状态,因此事务开始和事务结束后,数据库的完整性没有被破坏,这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性和后续数据库可以自发性地完成预定的工作。
隔离性:
- 一个事务的执行不受其他事务的干扰。
- 数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
- 事务隔离分为不同级别,包括已读未提交(Read Uncommitted)、已读已提交(Read committed)、可重复读(Repeatable read)和串行化(Serializable)
持久性:
一个事务一旦提交,则会永久的改变数据库的数据
语法
前提
UPDATE``DELETE``INSERT
语句都是默认开启自动提交的,可以通过语句来查看AUTOCOMMIT
状态
因此在需要进行生成订单或发生转账时,往往不止一条UPDATE
语句,为了保持事务的一致性,需要禁用自动提交模式
自动提交模式
禁用或启用当前会话的默认自动提交模式。
这以为着只要执行更新(修改)表的语句,MySQL就会将更新存储在磁盘上以使其永久化,无法回滚更改。
SET AUTOCOMMIT = 0 ;禁止自动提交
要为单个语句隐式禁用自动提交模式,就使用下列START TRANSACTION;
或BEGIN;
语句:
开始事务
自动提交保持禁用状态,直到使用COMMIT
提交或ROLLBACK
回滚事务,然后自动提交模式会恢复为先前的状态
这两者等价
START TRANSACTION;
BEGIN;
如果禁用了自动提交模式,也就是使用了SET AUTOCOMMIT = 0 ;
语句后会默认开启事务,上两句可以不写
提交事务
提交当前事务,使其更改永久化
COMMIT;
回滚事务
回滚当前事务,使其更改永久化
ROLLBACK;
案例演示
生成银行表
张三给王五转账500:
SET autocommit = 0;禁用自动提交,默认开启事务
BEGIN;
转账语句
UPDATE bank SET account = 1500 WHERE id = 1;
UPDATE bank SET account = 2500 WHERE id = 2;
COMMIT;提交事务
SELECT * FROM bank;
关于MySQL中隔离性
三种可能发生的问题
对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题:
脏读
对于两个事务T1、T2,T1读取了以及被T2更新但还没有提交的字段,之后,如果T2回滚,T1读取的内容就是临时且无效的
不可重复读
对于两个事务T1、T2,T1读取了一个字段,然后T2更新了该字段,之后,T1再次读取同一个字段,值就不一样了
幻读
对于两个事务T1、T2,T1从一个表中读取了一个字段,然后T2在该表中插入了一些新的行之后,如果T1再次读取同一个表,就会多出几行
区分:一般脏读和不可重复读是针对
UPDATE
语句,幻读是针对DELETE
和INSERT
语句
数据库提供的4种事务隔离级别
隔离级别 | 描述 |
---|---|
READ UNCOMMITTED 读未提交 | 允许事务读取并未被其他事务提交的变更,脏读、不可重复读和幻读的问题会出现 |
READ COMMITTED 读已提交 | 只允许事务读取已经被其他事务提交的变更,可以避免脏读,但不可重复读和幻读问题仍可以出现 |
REPEATABLE READ 可重复读 | 确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但幻读的问题仍存在 |
SERIALIZABLE 串行化 | 确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,所有并发问题都可以避免,但性能十分低下 |
案例演示
打开两个命令行,进行并行操作
注意 :我安装的mysql是8.0版本,之前版本的tx_isolation
变量改名为transaction_isolation
设置隔离级别的语法
SET SESSION TRANSACTION ISOLATION LEVEL XXX
隔离级别为READ UNCOMMITTED
修改隔离级别为READ UNCOMMITTED
在之前的案例演示的基础上,张三再给王五转账300,如果只执行了一条语句的情况下,在B事务中查看会话内容
脏读
如果A事务回滚,会出现脏读现象
不可重复读
如果A事务提交,在B事务中会出现不可重复读现象
幻读
如果事务A插入新的语句,在B事务中查看会出现幻读
隔离级别为READ COMMITTED
接下来,张三再给王五转账200
脏读
此隔离级别下,脏读的问题不会出现
不可重复读
不可重复读的问题仍会出现
幻读
幻读的问题仍会出现
隔离级别为REPEATABLE READ
此种隔离级别下不会出现脏读和不可重复读,但是会出现幻读
不可重复读
只有在事务B提交后再开事务查看事务A才会发生更改
幻读
在事务B中对bank表进行修改并提交后,在A事务中做出修改会出现幻读
隔离级别为SERILIZABLE
脏读、不可重复读和幻读的问题都不会出现
总结
事务的隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED 读未提交 | √ | √ | √ |
READ COMMITTED 读已提交 | × | √ | √ |
REPEATABLE READ 可重复读 | × | × | √ |
SERIALIZABLE 串行化 | × | × | × |
查看隔离级别
select @@transaction_isolation;
设置隔离级别
set session|global transaction isolation level 隔离级别
;
SAVE POINT
的使用
SET autocommit=0;
START TRANSACTION;
DELETE FROM bank WHERE id=2;
SAVEPOINT a;#设置保存点
DELETE FROM bank WHERE id=1;
ROLLBACK TO a;#回滚到保存点