MySQL学习过程中事务算是一个重点,这里对事务的相关知识点进行记录。
事务概述
事务就是一组原子性的数据库操作序列,或者说一个独立的工作单元。这个工作单元要么全部执行,要么全不执行。MySQL中事务可以是一条SQL语句,也可以是一组SQL语句。
MySQL中默认采用自动提交模式,若不显式开启事务,则一个
SELECT
、
INSERT
、
UPDATE
或
DELETE
语句皆被当作一个事务。若要显式创建事务,则需要在组成事务的一组SQL语句之前指定开始事务,即设置自动提交功能为禁用,并在之后指定结束事务,即回滚或提交。
SET autocommit=0;#设置自动提交功能为禁用/*SQL语句*/#ROLLBACK;回滚COMMIT;#提交
事务的四大特性
事务必须具备ACID四大特性,分别为:
- 原子性(Atomicity):事务所包含的所有操作要么全部提交成功,要么全部失败回滚。
- 一致性(Consistency):事务必须使数据库从一个一致性状态转换到另一个一致性状态。
- 隔离性(Isolation):一般而言一个事务所做的修改在最终提交以前对其他事务不可见。当然,事务间的隔离性受到隔离级别的影响。
- 持久性(Duration):一旦事务提交,所做的修改将永久保留在数据库中。
事务的并发问题
多个事务在同时运行时如果没有采用合适的隔离级别就会导致各种并发问题,主要有以下几种:
- 脏读(Dirty Read):事务A读取了事务B未提交的数据,然后事务B回滚后,事务A读到的数据就是临时且无效的脏数据。
- 不可重复读(Nonrepeatable Read):两次执行同样的查询可能读到不一样的结果。例如事务A在读取数据时,事务B对数据进行了修改并提交,导致事务A读到是数据前后不一致。
- 幻读(Phantom Read):当事务A在读取某个范围内的记录时,事务B在该范围内插入了新的记录,事务A再次读取时产生幻行(Phantom Row)。
事务的隔离级别
- 读未提交(READ UNCOMMITTED):最低的隔离级别,事务中的修改即使未提交,对其他事务也是可见的。会产生脏读、不可重复读、幻读问题。
- 读提交(READ COMMITTED):一个事务开始时,只能看见已提交的事务所做的修改。解决了脏读问题,不能解决不可重复读,幻读问题。
- 可重复读(REPEATABLE READ):保证了两次执行同样的查询可能读到的结果一致。可以解决脏读和不可重复度的问题,不能解决幻读问题。可重复读是MySQL默认是隔离级别。
- 串行化(SERIALIZABLE):最高的隔离级别,强制事务串行执行,在每一行都加锁。能够解决脏读、不可重复读、幻读问题。
操作示例
这里我创建了一个简单的数据表,记录动物数量。
表中初始条件如下。
首先查看默认的隔离级别,即可重复读。
之后简单验证该隔离级别能够解决脏读和不可重复度的问题,但不能解决幻读问题。
首先在两个终端开启事务,分别为事务A和事务B。
在事务A中把狗的数量改为4,但是没有提交。
之后在事务B进行查询,发现狗的数量没有改变,说明并没有产生脏读问题。
之后我们提交事务A的修改。
再次再事务B查询,发现仍未改变,说明没有出现不可重复度的问题。
最后事务B也提交。整个过程如下。
再在两个终端中都开启新的事务。为了方便还是称作事务A和事务B。
先在事务B查询数据,记住现在的状况,注意之前的修改已经提交,由于已经修改了狗的数量,显然查到的狗的数量就应该是4。
之后在事务A往表中插入新数据。
再回到事务B,这时查询表,发现数据并没有改变啊???
这时在终端B中把狗的数量再改一下,发现查询后结果也只是狗数量变了。
之后做全局的修改,查询一下才发现多了一行。
这是怎么回事呢?不是说好可重复读的隔离级别不能解决幻读的吗?为什么增加了一行再读也没有出现幻读问题?修改原有的数据再读也没有出现幻读问题?修改了全部行中的数据再查才发现出现了幻读问题?查阅相关技术博客,有人说MySQL可重复读隔离级别只解决了部分幻读问题,并不是完全解决了幻读的问题。行吧,由于MySQL学得不深所以我也无法解释这个问题。
整个过程如下。
尾声:事务作为MySQL的重点必须理解且牢记,之后要想深入,还得探究MySQL实现各个隔离级别的本质方法,对于这一点我目前还知之甚少,还是得深入学。