6-1 MySQL事务及相关特性
1. 前言
首先非常感谢读到第六章的同学,可能看到这章的目录你会产生一个疑问:前五章内容都是围绕C++程序设计讲述的有关内容,为什么第六章是讲MySQL数据库,而不是操作系统和计算机网络等内容呢?
我认为原因主要有两点:
-
- 专刊的篇幅有限,且作者的能力和精力有限。
-
- 为什么选择数据库?首先,我认为数据库原理的掌握是后台开发不可或缺的,应该深入学习MySQL数据库的实现原理、工作机制、产品特性并适动手实践。其次,对于操作系统和计算机网络的一些原理和特性介绍的资料较为全面和丰富,且在专刊的前五章中或多或少的有一些涉及和讲解。但我在这里需要提醒大家,操作系统和计算机网络相关的知识掌握也非常重要,需要同学们参考1-2节的知识图谱,主动去搜寻学习资料并掌握相关内容。
2. 事务
在关系型数据库中,事务通常是多个SQL指令的集合操作。如下图所示:事务的一个完整周期包括:begin-开启事物、commit-提交事务、rollback-回滚事务。
2.1 事务的四个特性
事务必须满足4个条件,也叫事务的4个特性(ACID):
- 1.原子性(Atomicity) 事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚到事务开始之前。MySQL的数据库管理系统通过回滚日志(undo log)来实现原子性,回滚日志记录着事务所执行的修改操作,若需要回滚则反向执行这些修改操作。
- 2.一致性(Consistency) 数据库在事务执行前后都保持一致性状态,一致性是对数据可见性的约束,保证在一个事务中的多次操作的数据中间状态对其他事务不可见的。这些中间状态,是过渡状态,与事务的开始状态和事务的结束状态是不一致的。
- 3.隔离性(Isolation) 数据库允许多个并发事务同时对数据进行读写和修改,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
- 4、持久性(Durability) 一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
需要强调的是: (1)原子性和一致性的侧重点不同:原子性关注状态,要么全部成功,要么全部失败,不存在部分成功的状态。而一致性关注数据的可见性,中间状态的数据对外部不可见,只有最初状态和最终状态的数据对外可见。 (2)只有满足一致性,事务的执行结果才是正确的:
- 在无并发的情况下,事务串行执行,隔离性一定能够满足。此时只要能满足原子性,就一定能满足一致性。
- 在并发的情况下,多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。
2.2 事务控制语句
在MySQL数据库的Innodb引擎下,使用以下语句操作和控制事务:
- 1.使用BEGIN,COMMIT,ROLLBACK来开启、提交和回滚事务 BEGIN——开始一个事务 ROLLBACK——事务回滚 COMMIT——事务提交
- 2.SAVEPOINT identifier, 允许在事务中创建一个保存点,在数据库事务处理中实现“子事务”(subtransaction),一个事务中可以有多个SAVEPOINT
- 3.ROLLBACK TO identifier 把事务回滚到保存点;
- 4.RELEASE SAVEPOINT identifier 删除一个事务的保存点
- 5.SELECT @@tx_isolation 查看当前事务的隔离级别
- 6.SET TRANSACTION 用来设置事务的隔离级别,InnoDB 存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE
- 7.SET AUTOCOMMIT=0 禁止自动提交。当AUTOCOMMIT=1时是自动提交的状态,也就是一条SQL语句执行完会默认执行一次COMMIT,也就不受我们自己事务命令的控制。因此,需要SET AUTOCOMMIT=0设置成禁止自动提交。
事务测试:
MySQL [(none)]> CREATE DATABASE TESTDB; // 创建数据库TESTDB
Query OK, 1 row affected (0.00 sec)
MySQL [(none)]> use TESTDB; // 选择数据库TESTDB
Database changed
MySQL [TESTDB]> CREATE TABLE transaction_test( pri_id int(5)) engine=innodb; // 创建表transaction_test 存储引擎指定为innodb
Query OK, 0 rows affected (0.05 sec)
MySQL [TESTDB]> desc transaction_test; // transaction_test表结构为一个pri_id字段,类型为int
+--------+--------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------+------+-----+---------+-------+
| pri_id | int(5) | YES | | NULL | |
+--------+--------+------+-----+---------+-------+
1 row in set (0.00 sec)
MySQL [TESTDB]> SET AUTOCOMMIT=0; // 禁止事务自动提交
Query OK, 0 rows affected (0.00 sec)
MySQL [TESTDB]> select * from transaction_test; // 当前为空表
Empty set (0.00 sec)
MySQL [TESTDB]> begin; // 开启事务
Query OK, 0 rows affected (0.00 sec)
MySQL [TESTDB]> insert into transaction_test value(0); // 插入元素0
Query OK, 1 row affected (0.00 sec)
MySQL [TESTDB]> insert into transaction_test value(1); // 插入元素1
Query OK, 1 row affected (0.00 sec)
MySQL [TESTDB]> commit; // 提交事务
Query OK, 0 rows affected (0.00 sec)
MySQL [TESTDB]> select * from transaction_test;
+--------+
| pri_id |
+--------+
| 0 |
| 1 |
+--------+
2 rows in set (0.00 sec)
MySQL [TESTDB]> begin; // 开启事务
Query OK, 0 rows affected (0.00 sec)
MySQL [TESTDB]> insert into transaction_test value(10); // 插入元素10
Query OK, 1 row affected (0.00 sec)
MySQL [TESTDB]> rollback; // 回滚事务
Query OK, 0 rows affected (0.00 sec)
MySQL [TESTDB]> select * from transaction_test; // 因为回滚所以元素10没有插入
+--------+
| pri_id |
+--------+
| 0 |
| 1 |
+--------+
2 rows in set (0.00 sec)
3. 隔离级别和并发一致性问题
ACID中的一致性描述的是一个最理想的事务应该怎样的,是一个强一致性状态,如果要做到这点,需要使用排它锁把事务排成一队,即串行化(Serializable)的隔离级别,这样性能就大大降低了。现实是骨感的,所以隔离性设置了不同隔离级别来打破一致性原则,来获取更好的性能。
3.1 读未提交(READ UNCOMMITTED)
读未提交(READ UNCOMMITTED)是低的隔离级别。在读未提交级别下,事务能够读取到其他事务“未提交”的数据,从而引发丢失修改、脏读和不可重复读的问题。
3.1.1
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
<p> C++工程师面试真题解析! </p> <p> 邀请头部大厂创作者<a href="https://www.nowcoder.com/profile/73627192" target="_blank">@Evila</a> 及牛客教研共同打磨 </p> <p> 助力程序员的求职! </p>