MySQL之InnoDB存储引擎:事务之基本实践

事务作为数据库的一项重要特性,这里我们简单介绍下什么是事务,以及该如何使用

abstract.png

概述

定义

MySQL的操作使用相信大家都不陌生,即是通过一条条的SQL语句去完成的。而如果我们将一条或多条对数据库操作的SQL语句视作一个整体来看,即是所谓的Transaction事务。对于事务而言,其需要满足四个特性——ACID

Note

  • 对于MySQL数据库而言,并不是所有的存储引擎都支持事务。具体地,对于InnoDB、NDB存储引擎而言,其是支持事务的;但对于MyISAM存储引擎而言,是不支持事务的

ACID

Atomicity 原子性

事务的Atomicity原子性指的是,对于一个事务而言其是执行的最小单位,不可以被分割。具体地,对于一个事务中的若干条SQL语句而言,要么这些SQL语句全部不执行,要么这些SQL语句全部执行。而不允许在一个事务中出现只执行一部分SQL语句而另一部分SQL语句未执行的情况

这里我们以A给B的账户转账10元为例进行说明。对于这个转账操作来说,我们可以大致分为两个步骤

  1. 将A账户余额减去10
  2. 将B账户余额增加10

显然上述两个步骤要么全部执行了,要么全部不执行了。而其它任何的执行情况来说,即只执行步骤1或只执行步骤2,显然都是错误的

Consistency 一致性

数据库始终是对真实的现实世界进行描述的。故如果数据库中的数据能够满足现实世界的约束,即认为是符合Consistency一致性的。这里关于约束、规则的定义是主观的,比如上面转账的例子中,如果我们可以将 A、B两人账户的总额加在一起是不变 作为约束。还可以根据实际业务需要,比如根据账户类型借记卡还是信用卡,来考虑是否将 账户余额不允许为负值 作为约束。事实上对于MySQL而言,其语法也在一定程度上支持约束。比如NOT NULL非空约束

Isolation 隔离性

Isolation隔离性指的是多个事务并发执行时不应相互影响。同样还是以A给B转账为例。不同的是,这里假设两次转账操作是并发的。可以看到当两个转账事务按如下方式交错执行时,即会出现一些意外的情况

figure 1.png

A给B转了两次账,共计20元。结果B的账户上是多了20元,可是A的账户上却只少了10元。这种局面显然是不应该出现的

Durability 持久性

所谓Durability持久性是指当事务完成后对数据的修改应该落到磁盘中,即被持久化存储

状态

对于事务一个而言,其通常存在下面的几种状态

  • 活动状态

正在执行该事务中的数据库操作

  • 部分提交状态

该事务中的数据库操作虽然全部执行完了。但此时均只是发生在内存中,还未刷新同步到硬盘

  • 提交状态

处于部分提交状态的事务将数据同步刷新到硬盘

  • 失败状态

对处于活动状态部分提交状态的事务而言,如果发生意外(数据库出现错误、断电等)或主动停止当前事务,则该事务处于失败状态

  • 中止状态

根据事务的原子性可知,当事务处于失败状态时需要通过Rollback回滚将数据库数据恢复到执行事务之前的状态。当Rollback回滚操作完成后,该事务即为中止状态

上述各状态之间的转换关系,如下图所示

figure 2.png

基本使用

开启事务

下面语句均可开启一个事务。其中对于start transaction语句而言,其后面还可使用修饰符,若同时使用多个修饰符需通过逗号进行分隔。具体地,修饰符有以下几种:

  • READ ONLY:只读事务
  • READ WRITE:读写事务
  • WITH CONSISTENT SNAPSHOT:一致性读

如果不使用修饰符则默认为读写事务。值得一提的是,不可同时使用READ ONLY、READ WRITE修饰符

1
2
3
4
-- 开启事务
begin;
-- 开启事务
start transaction [修饰符];

提交事务

通过下面的SQL语句即可提交事务

1
2
-- 提交事务
commit

下图即为一个事务操作的基本流程,可以看到该事务中间只含有一条数据库操作。事实上,在事务中可以包含若干条数据库操作语句

figure 3.png

中止事务

在事务中,如果发现某个SQL语句输入错误了。可以通过下面的语句进行回滚来中止事务

1
rollback;

下图即为一个事务中途中止的示例。可以看到通过rollback回滚操作,即可将数据库数据恢复到该事务开始之前的状态。这里我们演示的是主动中止事务的情况。事实上,当事务在执行过程中发生了错误而无法继续执行时,事务会自动进行回滚

figure 4.png

保存点

在上面的例子中,我们在事务执行时由于手抖了把SQL语句中的 “电气工程” 写成 “电器工程”,就不得不进行全部回滚。然后又要从头执行事务中所有数据库操作,此举显然很麻烦。为此savepoint保存点应运而生,我们可以在事务执行过程中适当建立保存点。这样后续如果需要回滚就不必回滚到事务开启前最初的状态,而只需回滚到指定保存点所在的状态即可

保存点相关的SQL语句如下所示

1
2
3
4
5
6
-- 建立保存点
savepoint <保存点名称>;
-- 回滚到指定保存点,其中savepoint关键字可省略
rollback to [savepoint] <保存点名称>;
-- 删除指定保存点
release savepoint <保存点名称>;

下图即生动形象地反应了关于保存点的使用姿势

figure 5.png

自动提交

所谓自动提交指的是,如果我们不显式地通过 start transaction 或 begin 开启事务的话,则对于每条SQL语句而言均视为一个事务进行执行。该特性可通过系统变量autocommit进行使能控制,其默认为on。具体可通过下面的SQL语句查看、修改该系统变量

1
2
3
4
-- 查看系统变量 autocommit
show variables like 'autocommit';
-- 修改系统变量 autocommit
set autocommit = {on|off};

如果我们不想使用自动提交功能的话,具体有以下两种途径实现

  • 关闭autocommit自动提交

将系统变量autocommit修改为off以关闭自动提交功能。这样数据库会将多条SQL语句视为在同一个事务当中,直到我们显式地使用 commit提交事务 或 rollback回滚事务

  • 显式地通过start transaction 或 begin开启事务

当我们通过显式地通过start transaction 或 begin开启事务时,即使系统变量autocommit为on。自动提交的功能也会暂时被关闭。直到该事务被commit提交 或 rollback回滚

隐式提交

所谓隐式提交指的是,当我们通过显式地通过start transaction、begin开启事务 或 关闭了自动提交功能 后,如果遇到特殊类型的语句时既会导致当前事务被提交,效果等同于commit语句。这里就会常见的导致隐式提交的操作类型介绍下:

  • DDL类型的SQL语句。例如create、alter、drop等语句
  • 使用锁控制相关的语句。例如lock tables、unlock tables等语句
  • MySQL复制的语句。例如{start|stop|reset} slave、change master to等语句
  • 使用、修改mysql库中的表。例如{alter|create|drop|rename} user、grant、set password等语句
  • 当前事务还未提交或回滚,就又通过start transaction、begin开启一个新的事务。则上一个事务会被提交
  • 加载数据的操作。例如批量导入load data语句

参考文献

  1. MySQL是怎样运行的
0%