MySQL 事务一览
MySQL 中的事务?
对 MySQL 来说,事务通常是一组包含对数据库操作的集合。在执行时,只有在该组内的事务都执行成功,这个事务才算执行成功,否则就算失败。MySQL 中,事务支持是在引擎层实现的,像 MySQL 原生的 MyISAM 引擎就不支持事务,这也是被 InooDB 取代的重要原因。
为什么要有事务呢,举个例子来说,你的账户有 100 元,现在想给朋友转账 100 元。其中就会包含两个很重要的操作,你的账户减 100 元,朋友账户多 100 元。由于转账过程中出现失败是很常见的,假设操作不包含在事务内,你的账户减钱操作成功,朋友账户加钱操作失败。就会出现,你的账户扣钱,对方没有收到钱的情况。
再比如,在发起转账操作时,由于系统需要进行像查询余额,计算,更新余额的操作,如果在等待时间内,又发起了转账操作,但目前更新余额的操作还没有成功,就会出现你的 100 元,可以给别人转账多次的情况,这对于银行来说是肯定不允许的。
对于一个事务来说通常要满足四个特性,也就是通常所说的 ACID:
Atomicity - 保证在一个工作单元(就是一组操作)中所有的操作都执行成功,否则的话当前这个事务就会失败,之前的操作都被会回滚。
Consistency - 保证一个事务被成功提交后,数据库的状态是从一致性状态变成另一个一致性状态。
Isolation - 保证每个事务中的操作时是独立的,对于其他事务没有影响。
Durability - 对于已经提交的事务,即使在数据库损坏的情况下,也不会造成数据的丢失和损坏。
MySQL 中的事务隔离是如何实现的?
当数据库中有多个事务同时执行时,就可能会出现脏读,幻读,不可重复读的问题,为了解决这些问题,就出现了"隔离级别"的概念。
事务隔离的问题:
脏读:事务 A 中访问了事务 B 中未提交的数据。这里 Transaction 1 读到了,Transaction 2 中未提交的年龄数据。
不可重复读:事务 A 中多次查询同一数据,但由于事务 B 在事务 A 两次查询中,修改了改数据的值,导致两次查询的结果不一样。下面 Transaction 1 中的两次查询查询结果是一致的,第二次读到的 age 已经被修改的内容。
幻读:通常发生在事务 B 对事务 A 正在读取的内容,添加或删除了一条数据。造成数据莫名出现或者消失的情况。这里 Transaction 1 中,两次查询的结果并不一致,第二次查询会多出一条记录。
事务的隔离级别:
隔离级别 | 解释 | 可能出现的问题 |
---|---|---|
读未提交 | 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。 | 脏读,不可重复读,幻读 |
读提交 | 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。 | 不可重复读,幻读 |
可重复读 | 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。 | 幻读 |
串行化 | 串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。 | 无 |
在 MySQL 中 RR 级别引入了间隙锁,解决了幻读的问题。
举一个实际的例子,来看一下这四种隔离级别对应的结果,假设表结构如下:
mysql> create table T(c int) engine=InnoDB;
insert into T(c) values(1);
此时发生的事务如下:
隔离级别 | 返回结果 |
---|---|
读未提交 | V1 是 2。事务 B 虽然还没有提交,但是结果已经被 A 看到了。因此,V2、V3 也都是 2 |
读提交 | V1 是 1,V2 的值是 2。事务 B 的更新在提交后才能被 A 看到。所以, V3 的值也是 2。 |
可重复读 | 则 V1、V2 是 1,V3 是 2。 V2 还是 1 的原因,需要遵循:事务在执行期间看到的数据前后必须是一致的。 |
串行化 | 事务 B 执行“将 1 改成 2”时,会被锁住。直到事务 A 提交后,事务 B 才可以继续执行。所以从 A 的角度看, V1、V2 值是 1,V3 的值是 2。 |
执行的效率会和执行的级别有关,隔离的越高,效率越低,需要在二者间寻找平衡。
事务隔离的应用场景?
读未提交:
- 这个基本没人会选择,连事务都构不成。
读提交:
- 一般互联网公司的隔离级别会选用这个,原因在:
- RR 级别下,存在间歇锁,出现死锁的几率比 RC 大的多。
- RR 级别下,条件列未命中索引会锁表。在 RC 级别下会锁行。
- RC 级别下,半一致性特性增加了 update 操作的并发性。
可重复读:
- 数据校验:假设你在管理一个个人银行账户表。一个表存了每个月月底的余额,一个表存了账单明细。这时候你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。这时使用“可重复读”隔离级别就很方便。事务启动时的视图可以认为是静态的,不受其他事务更新的影响。
串行化:
- 每次读操作都会加锁,性能不佳。
事务隔离的实现?
在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。
- 在可重复读时的隔离级别下,视图是在事务启动时创建的,整个事务存在期间都会用这个视图。
- 在读提交的隔离级别下,这个视图是在每个 SQL 语句开始执行时创建的。
- 在读未提交的隔离级别下,会直接返回记录的最新值,没有视图的概念。
- 串行化是直接通过加锁的方式来避免并行访问。
注意这个视图不是用于查询定义的虚拟表,而是在 InnoDB 中实现 MVCC 用到的一致性读视图(consistent read view),用于支持 RC 和 RR 隔离级别的实现。
可重复读的具体实现:
在 MySQL 中,实际上每条记录在更新时都会同时记录一条回滚操作。记录上的最新值,通过回滚都可以得到前一个状态的值。比如一个值 从 1 按照顺序,被修改成 2、3、4 ,就会在回滚日志中有如下的记录。
当前最新是 4,在查询这条记录时,不同时刻启动的事务会有不同的 read-view. 在视图 A B C 中,记录值为 1, 2, 4. 同一条记录可以存在多个版本,这就是数据库多版本并发控制(MVCC)。对于 read-view A 来说,要得到 1,就必须将当前值依次执行图中所有的回滚操作得到。假如,有另外一个事务将 4 改成 5,但对于视图 A B C 来说,事务是不冲突的。
回滚段的删除,为什么要避免使用长事务?
既然每一条记录都会更新是都会产生一条回滚操作记录,时间一长,肯定会占用大量的存储空间。那么系统会在什么时候删除这些回滚日志呢,就是在当前系统里不存在比该回滚日志更早的 read-view 时。
但如果系统里存在着很老的事务视图。由于这些事务可能会访问数据库里的任何数据,所以在事务提交之前,所有可能用到的回滚记录都必须保留,这就可能出现占用大量存储空间的情况。
在 MySQL 5.5 之前,回滚日志和数据字典一起放在 ibdata 文件里,即使长事务被提交,回滚段被清理,文件也不会变小。
并且长事务还占用锁资源,也可能拖垮整个库。
MySQL 中事务的启动方式?
显式启动事务:
# 使用 START TRANSACTION 或者 BEGIN 开启事务:
START TRANSACTION
[transaction_characteristic [, transaction_characteristic] ...]
transaction_characteristic: {
WITH CONSISTENT SNAPSHOT
| READ WRITE
| READ ONLY
}
BEGIN [WORK]
# 使用 COMMIT 来提交事务
COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
# 使用 ROLLBACK 来回滚事务
ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
隐式启动事务:
# 设置当前事务的是否自动提交
SET autocommit = {0 | 1}
autocommit 的讨论:
- 当 autocommit=0 时,事务启动后,不会自动关闭直到主动的输入 COMMIT 或者 ROLLBACK 语句,或者断开连接时,当前事务才结束。
- 一些客户端连接框架会默认连接成功后先执行一个 set autocommit=0 的命令。这就导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务。
- 当 autocommit=1 时,建议总是以这种方式启动事务。
- 如果担心多一次交互的问题,可以使用
commit work and chain
语法。
- 如果担心多一次交互的问题,可以使用
多一次交互的问题,如果采用 autocommit=0 的这种方式,不需要每次输入 begin ,减少了语句的交互次数。
查询长事务:
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60
避免长事务的方案:
- 在开发过程中,减少事务范围,少用长事务。如果无法避免,保证逻辑日志空间够用,并且支持动态日志空间的增长。
- 监控Innodb_trx表,发现长事务报警。
总结
在开始部分,介绍了 MySQL 中事务的概念,并回顾了事务的 ACID 的特性。接着探讨了事务隔离的可能出现的脏读,不可重复读以及幻读的问题,并给出了相应的解决方案-隔离级别。并分析了常见事务隔离的应用场景以及事务隔离的实现方式。
并在最后引出了回滚段的概念,以及为什么要避免使用长事务。并给出了开启事务的方法。
参考
MySQL 事务一览的更多相关文章
- Mysql事务探索及其在Django中的实践(二)
继上一篇<Mysql事务探索及其在Django中的实践(一)>交代完问题的背景和Mysql事务基础后,这一篇主要想介绍一下事务在Django中的使用以及实际应用给我们带来的效率提升. 首先 ...
- MySQL 事务
MySQL 事务主要用于处理操作量大,复杂度高的数据.比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成 ...
- mysql事务和并发控制
谈到事务,首先想到的问题是并发控制.比如两个用户同时操作数据库里面的一张表,一个正在读数据,一个正在删除数据,那么读数据的读出的结果究竟是多少?并发可以提高系统的性能,让多个用户同时操作一份数据,但为 ...
- MySQL事务学习-->隔离级别
MySQL事务学习-->隔离级别 6 事务的隔离级别 设置的目的 在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别. 数据库是要被广大客户所共享访问的,那么在数据库操作过程中 ...
- mysql 事务是专门用来管理insert,update,delete语句的,和select语句一点不相干
1.mysql 事务是专门用来管理insert,update,delete语句的,和select语句一点不相干 2.一般来说,事务是必须满足4个条件(ACID): Atomicity(原子性).Con ...
- php mysql事务
这里记录一下php操作mysql事务的一些知识 要知道,MySQL默认的行为是在每条SQL语句执行后执行一个COMMIT语句,从而有效的将每条语句独立为一个事务.但是,在使用事务时,是需要执行多条sq ...
- mysql事务问题
mysql事务: 若mysql 开启事务后START TRANSACTION ,不显示提交commit,则默认自动回滚,而不是默认自动提交.
- MYSQL事务和锁
mysql事务(一)—转载 2012年12月20日 ⁄ Mysql数据库, 技术交流 ⁄ 暂无评论 一. 什么是事务 事务就是一段sql 语句的批处理,但是这个批处理是一个atom(原子) ,不可分割 ...
- MySQL事务内幕与ACID
MySQL的事务实现严格遵循ACID特性,即原子性(atomicity),一致性(consistency),隔离性(isolation),持久性(durability).为了避免一上来就陷入对ACID ...
随机推荐
- 数据结构实验之图论七:驴友计划【迪杰斯特拉算法】(SDUT 3363)
分析:可以求简单的任意两点间最短距离的稍微变形,一个板子题. #include <iostream> #include <bits/stdc++.h> using names ...
- 使用java时报的一些错误
mysql-connector-java报not found的错误 ftp上传文件失败原因 1.mysql-connector-java报not found的错误 在网上查,很容易查找到java连接数 ...
- Java8函数式编程的宏观总结
1.java8优势通过将行为进行抽象,java8提供了批量处理数据的并行类库,使得代码可以在多核CPU上高效运行. 2.函数式编程的核心使用不可变值和函数,函数对一个值进行处理,映射成另一个值. 3. ...
- Spring家族主流成员介绍
摘 要:Spring 就像一个大家族,有众多衍生产品例如 Boot,Security,JPA等等.但他们的基础都是Spring 的 IOC 和 AOP,IOC提供了依赖注入的容器,而AOP解决了面向切 ...
- 一键分享QQ、微信、微博等
github上找到的,合并了一个二维码在线支持API,直接修改样式可用. 二维码API说明网址:http://www.liantu.com/pingtai/ <html> <head ...
- Delphi BASE64单元EncdDecd的修改
Delphi BASE64单元EncdDecd的修改 EncdDecd.pas两个函数声明: procedure EncodeStream(Input, Output: TStream);proced ...
- ArcGIS10.3_解决属性表中文乱码问题
借鉴前辈们解决ArcMap低版本属性表乱码的问题解决方法,勇敢的尝试了一下Pro中的解决方法,其实道理都一样.先来看看第一种方法:打开CMD,如果是ArcMap,输入如下命令: reg add HKE ...
- Python日志库logging总结-可能是目前为止将logging库总结的最好的一篇文章
在部署项目时,不可能直接将所有的信息都输出到控制台中,我们可以将这些信息记录到日志文件中,这样不仅方便我们查看程序运行时的情况,也可以在项目出现故障时根据运行时产生的日志快速定位问题出现的位置. 1. ...
- IIS7下搭建PHP(FastCgiModule)
windows2008和windows vista都可以安装IIS7 第一步: 下载软件, php官方网站:www.php.net(下载winfows版本) phpmyadmin官方网站:www.ph ...
- springboot之docker化
1.Docker安装 本人是centos7系统,安装也是按照官方文档进行安装.https://docs.docker.com/install/linux/docker-ce/centos/ ,即 1. ...