MySQL事务

MySQL事务

一、数据库事务基础

1.1.什么是事务

简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在 MySQL 中,事务支持是在引擎层实现的。

比如 MySQL 原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被 InnoDB 取代的重要原因之一

1.2.事务隔离级别

当数据库上有多个事务同时执行的时候,可能出现以下问题

  1. 脏读(dirty read)
  2. 不可重复读(non-repeatable read)
  3. 幻读(phantom read)

为了解决这些问题,就有了隔离级别的概念

SQL 标准的事务隔离级别包括:

  1. 读未提交(read uncommitted):一个事务还没提交时,它做的变更就能被别的事务看到。
  2. 读提交(read committed):一个事务提交之后,它做的变更才会被其他事务看到。
  3. 可重复读(repeatable read):一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。 一个事务未提交变更对其他事务也是不可见的。
  4. 串行化(serializable ):顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

举个例子:

在不同的隔离级别下,V1、V2、V3的值分别是:

隔离级别V1V2V3说明
读未提交222虽然B事务还没提交,但是做的更改事务A也可以看到
读提交122事务B只有提交了,结果才能被事务A看到
可重复读112事务在执行期间看到的数据前后必须是一致的
串行化112事务B在执行“1改成2”的时候,会被锁住,等到事务A提交完成之后,才继续执行

通过以下命令查看数据库的默认隔离级别:

mysql> show variables like 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |  // 可重复读
+-----------------------+-----------------+
1 row in set (0.01 sec)

为了实现可重复度读,MYSQL使用的机制是:每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。

因此,对于长事务,这个回滚操作的记录就会很长,所以不建议使用长事务。

1.3.启动事务的方式

  1. 显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback。不过注意,begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作 InnoDB 表的语句,事务才真正启动。如果要马上启动一个事务,可以使用 start transaction with consistent snapshot 这个命令。

  2. 执行一条SQL语句,例如update t set k=k+1 where id=1, 本身这就是一个事务,相当于隐式地执行了begin和commit。不过,如果设置了 set autocommit=0,此时这个线程的自动提交关掉。只要执行了一条语句(不管是查询还是插入更新),这个事务就启动了,而且并不会自动提交。这个事务持续存在直到主动执行 commit/rollback 语句,或者断开连接。

1.4.查询长事务语句

查询持续时间超过60s的事务

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

二、MVVC与事务隔离

2.1.什么是MVCC

MVCC(Multi-Version Concurrency Control)是为了实现事务的隔离性,通过数据的版本号,避免同一数据在不同事务间的竞争。

2.2.transaction id与row trx_id

事务开始的时候,都会向InnoDB的事务系统申请一个递增的transaction id。同时,而每行数据也都是有多个版本的(MVCC机制)。每次事务更新数据的时候,都会生成一个新的数据版本并且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够拿到旧的版本数据。

下图是一个同一行数据4个版本的例子,此时最新的版本是V4

图中的三个虚线箭头,就是 undo log;

V1、V2、V3 并不是物理上真实存在的,每次需要的时候根据当前版本和 undo log 计算出来的。比如,需要 V2 的时候,就是通过 V4 依次执行 U3、U2 算出来。

也正是这种设计,让每个事务启动的时候,都有个全局的快照(基于整个库)。

2.3.可重复读和当前读

在 MySQL 里,有两个视图的概念:

  1. 一个是用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建视图的语法是 create view … ,查询方法与表一样。

  2. 另一个则是InnoDB 在实现 MVCC 时用到的一致性读视图,即 consistent read view,用于支持 RC(Read Committed,读提交)和 RR(Repeatable Read,可重复读)隔离级别的实现。InnoDB默认的事务隔离级别是可重复读,以下主要讨论这种级别。

在事务里面,一般就是查询和更新语句,对于查询语句,使用可重复读。读到的数据是事务启动前的数据。也就是事务执行期间,看到的数据保持一致。对于更新数据,都是先读后写的,而这个读,只能读当前的值(读提交)。

记住下面这两条原则就比较好分析问题了:

  • 对于可重复读,查询只承认在事务启动前就已经提交完成的数据;
  • 对于当前读,总是读取已经提交完成的最新版本;

接下来看一个例子:

mysql> CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `k` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);

  • 对于事务A,最终查询到的结果就是1。因为事务里面只有一个查询语句,使用可重复读。
  • 事务B查询到的结果是3,因为当执行update语句的时候,使用读提交,此时拿到的数据,是事务C执行完成后的数据,这样也才能保证数据是最新的。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值