文章目录
说到事务控制,先说一下数据库的事务是什么以及 MySQL 中我们必知的知识点。
1.事务的四大特性
数据库事务(Database Transaction) ,是指对数据库的一系列操作组成的逻辑工作单元(unit)。
并非任意的对数据库的操作序列都是数据库事务。数据库事务拥有以下四个特性,习惯上被称之为ACID特性。
(1)原子性(Atomicity)
事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
(2)一致性(Consistency)
事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
(3)隔离性(Isolation)
多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
(4)持久性(Durability)
已被提交的事务对数据库的修改应该永久保存在数据库中。
MySQL 中并非所有的数据库存储引擎都支持事务操作,比如 MyISAM 就不支持。所以,使用事务处理的时候一定要确定所操作的表示是否支持事务处理,可以通过查看建表语句来查看有没有指定事务类型的存储引擎。当然,事务处理是为了保障表数据原子性、一致性、隔离性、持久性。这些都是要消耗系统资源,要谨慎选择。
本文以数据库引擎 InnoDB 为例来演示命令行模式下事务的基本操作。
2.事务的隔离级别
在数据库操作中,为了有效保证并发读取数据的正确性,提出了事务隔离级别。
数据库是要被广大客户共享访问的,那么在数据库并发操作过程中很可能会出现一些不确定的情况。
(1)更新丢失(Update Lost)
两个事务都同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。
(2)脏读(Dirty Read)
读取未提交数据。
A 事务读取 B 事务尚未提交的数据,此时如果 B 事务发生错误并执行回滚操作,那么 A 事务读取到的数据就是脏数据。
(3)不可重复读(Non-repeatable Read)
前后多次读取,数据内容不一致。
A 事务在 B 事务开始前读和 B 事务结束后读的数据不一样,因为数据被事务 B 给修改了。
(4)幻读(Phantom Read)
一个行出现在查询结果集中,但不在较早查询的结果集中。
事务 A 在读取某个范围内的记录时,事务 B 在该范围内插入了新记录,事务 A 再次读取该范围内的记录时,会产生幻行。
幻读比不可重复读取更难防范,因为锁定第一个查询结果集中的所有行并不能阻止导致幻像出现的更改。
为了解决上面的问题,于是有了事务隔离。
3.事务隔离级别
事务隔离的级别从低到高有四个级别分别。
RU 级别:Read Uncommitted 读未提交。允许脏读、不可重复读、幻像
RC 级别:Read Committed 读已提交。允许不可重复读、幻像,不允许脏读
RR 级别:Repeatable Read 可重复读。允许幻读,不允许脏读、不可重复读和脏读。InnoDB 默认级别
S 级别:Serializable 。不允许脏读、不可重复读、幻读
注意:MySQL InnoDB 的 RR 级别和其他数据库是有区别的,不会造成幻读。
RU 级别
所有事务都可以读取未提交事务的执行结果,也就是允许脏读。但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读该事务增删改的数据。该隔离级别可以通过"排他写锁"实现。
RC 级别
允许不可重复读取,但不允许脏读取。这可以通过"瞬间共享读锁"和"排他写锁"实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
RR 级别
禁止不可重复读取和脏读取。这可以通过"共享读锁"和"排他写锁"实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。按照这种说法,是不会出现幻读的,MySQL的InnoDB的可重复读隔离级别和其他数据库的可重复读是有区别的,不会造成幻象读(phantom read)。
S 级别
提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。仅仅通过"行级锁"是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为 RC 级别。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
(1)查看全局和当前会话的事务隔离级别。
sql
# 查看全局
SELECT @@global.tx_isolation;
# 查看当前会话
SELECT @@session.tx_isolation;
SELECT @@tx_isolation;
show variables like 'tx_isolation';
(2)更改事务的隔离级别
sql
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
# 默认更改当前会话事务隔离级别
set tx_isolation='read-committed';
**注意:**不显示指明 session 和 global,默认的行为是带 session,即设置当前会话的事务隔离级别。如果使用 GLOBAL 关键字,为之后的所有新连接设置事务隔离级别。需要 SUPER 权限来做这个。MySQL 的 InnoDB 默认的事务隔离等级是 RR。
3.查看是否自动提交事务
MySQL 默认操作模式是自动提交模式(autocommit )。自动提交事务由会话变量 autocommit 控制,该变量只对当前会话有效。
sql
SELECT @@global.autocommit;
SHOW VARIABLES LIKE '%autocommit%';
环境变量 autocommit 是用来控制一条SQL语句提交后是否自动执行,默认值是1,表示在mysql命令行模式下每条增删改语句在键入回车后,都会立即生效,而不需要手动commit。我们可以把它关闭,关闭之后需要commit之后,SQL语句才会真正生效。
4.关闭和开启自动提交事务
- 关闭自动提交事务。
MySQL默认是自动提交事务的,关闭自动提交事务主要有两种方法。一种是临时关闭,只对当前会话有效。第二种是永久关闭,对所有会话有效。
第一种:临时关闭。
sql
# 关闭当前会话的自动提交事务
SET autocommit = 0;
这样之后,所有增删改语句,都必须使用 commit 之后,才能生效。
第二种:永久关闭。
在 MySQL 中,要永久地将自动提交事务模式设置为关闭,您可以通过在配置文件中进行设置,以便在每次启动 MySQL 服务器时都保持这个设置。
找到 MySQL 的配置文件。在大多数情况下,MySQL 的配置文件名为 my.cnf 或 my.ini,具体位置取决于您的操作系统和安装方式。
打开配置文件并找到 [mysqld] 部分,添加或修改下面的配置项。
[mysqld]
autocommit=0
保存,然后 MySQL 服务器即可生效。
- 开启自动提交事务。
如果需要,可以开启自动提交模式。
sql
SET autocommit=1;
或者将上面配置文件中的配置项autocommit=0
删除或设置为 1 即可。
5.事务执行的基本流程
首先创建一个测试数据表,建表语句如下:
sql
CREATE TABLE transaction_test(id int primary key)engine=InnoDB;
(1)开启一个事务。
sql
START TRANSACTION;
# 或
BEGIN;
(2)执行一系列增删改语句。
sql
INSERT INTO transaction_test VALUES(1);
(3)手动提交或者回滚。
事务回滚:
sql
ROLLBACK;
回滚后我们查看数据表中的数据。
sql
SELECT * FROM transaction_test;
Empty set (0.00 sec)
表中没有数据,回滚成功。
手动提交事务:
sql
COMMIT;
提交后,再 ROLLBACK 则不能回滚了,数据已经插入到数据表了。这里需要注意的是,在当前会话中,我们还没有手动 COMMIT 提交事务的时候,表中的数据已经被插入了,但对于其它会话,如果事务隔离级别是 read commited,那么在 COMMIT 之前,是查询不到新插入的记录的。
设置事务的保存点
sql
# 设置折返点
savepoit [pointname];
# 回滚至折返点
rollback to savepoint [pointname];
发生在保存点之前的事务被提交,之后的被忽略。
参考文献
MySQL 8.0 Reference Manual :: MySQL Glossary
15.7.2.1 Transaction Isolation Levels - MySQL