MySQL 03 事务隔离:为什么你改了我还看不见?

事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在MySQL中,事务支持是在引擎层实现的,这也是InnoDB取代MyISAM的重要原因之一。

隔离性与隔离级别

事务的四大特性:原子性、一致性、隔离性、持久性。本文主要讨论隔离性。

当数据库上有多个事务同时执行的时候,可能出现脏读、不可重复读、幻读的问题,为了解决这些问题,就有了隔离级别的概念。

SQL标准的事务隔离级别包括以下四种:

  • 读未提交:一个事务没提交时,它做的变更就能被别的事务看到。

  • 读已提交:一个事务提交之后,它做的变更才会被其他事务看到。

  • 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。

  • 串行化:对于同一行记录。写会加写锁,读会加读锁,当出现锁冲突,后访问的事务必须等待前一个事务执行完成,才能继续执行。

下面举例说明隔离级别。假设有两个事务,其行为的时间顺序如下:

那么在不同的隔离级别下,事务A的查询结果为:

  • 读未提交:事务B的修改能被A看到,因此V1=V2=V3=2。

  • 读已提交:事务B的修改在提交后才能被A看到,因此V1=1,V2=V3=2。

  • 可重复读:事务A在执行过程中看到的数据,和事务A启动时看到的是一样的,因此V1=V2=1。由于之后事务A提交,因此V3=2。

  • 串行化:事务B想要修改时会被锁住,直到事务A提交。因此V1=V2=1,V3=2。

在实现上,数据库里会创建一个视图,访问时以视图的逻辑结果为准。在可重复读 隔离级别下,视图是在事务启动时创建的,整个事务存在期间都用同一个视图。在读已提交隔离级别下,每个SQL语句开始执行时都会创建一个视图。

事务隔离的实现

接下来,展开说明可重复读的实现。

在MySQL中,实际上每条记录在更新时都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到提前一个状态的值。假设一个值从1依次改成了2,3,4,那么回滚日志是下面这样的:

当前值是4,但是在查询时,不同时刻启动的事务有不同的read view,因此看到的值是不同的,并不一定是当前值。同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。

MVCC可以实现隔离性,从上面也能看出,即使现在有另外的事务正在将4改成5,这个事务也不会影响read view A、B、C对应的事务。

回滚日志不是一直保留的,当系统判断没有事务再需要用到这些回滚日志时,回滚日志就会被删除。具体来说,当系统里没有比这个回滚日志更早的read view时,就会删除,如果详细了解过MVCC机制,这句话等同于:系统中不存在任何read view的min_trx_id比回滚日志所属事务的trx_id小。

基于以上实现,我们建议尽量不要使用长事务,因为长事务会意味着系统里存在很老的事务视图,那么在该事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会占用大量存储空间。除此之外,长事务还占用锁资源,也可能拖垮整个库。

可以在infomation_schema库的innodb_trx表查询长事务,比如下面的语句,用于查找持续时间超过60秒的事务:

sql 复制代码
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

事务的启动方式

最后再讲讲事务的启动方式。MySQL事务启动方式有以下几种:

  • 显式启动。语句是begin或start transaction,配套的提交语句是commit,回滚语句是rollback。

  • set autocommit=0,该命令会将线程的自动提交关闭。意味着如果只执行一个select语句,这个事务就会启动,但不会主动提交。事务持续存在直到主动提交或回滚,或者连接断开。

有些客户端连接框架默认连接成功后就先执行set autocommit=0,这就导致接下来的查询都在事务中,可能会导致无意的长事务。因此,建议显式启动

参考资料:极客时间专栏《MySQL实战45讲》https://time.geekbang.org/column/intro/100020801?tab=catalog

相关推荐
问道飞鱼2 小时前
【数据库相关】TxSQL新增数据库节点步骤
数据库·mysql·txsql·新增节点
Ka1Yan3 小时前
MySQL索引优化
开发语言·数据结构·数据库·mysql·算法
程序猿 董班长6 小时前
springboot配置多数据源(mysql、hive)
hive·spring boot·mysql
且行志悠11 小时前
Mysql的使用
mysql
白鹭11 小时前
MySQL源码部署(rhel7)
数据库·mysql
星期天要睡觉13 小时前
MySQL 综合练习
数据库·mysql
JosieBook14 小时前
【数据库】MySQL 数据库创建存储过程及使用场景详解
数据库·mysql
处女座_三月14 小时前
改 TDengine 数据库的时间写入限制
数据库·sql·mysql
DemonAvenger15 小时前
MySQL与应用程序的高效交互模式:从基础到实战的最佳实践
数据库·mysql·性能优化
皆过客,揽星河16 小时前
mysql进阶语法(视图)
数据库·sql·mysql·mysql基础语法·mysql进阶语法·视图创建修改删除