聊一聊 MySQL 中的事务及其实现原理
说到数据库,那就一定会聊到事务,事务也是面试中常问的问题,我们先来一个面试场景:
面试官:"事务的四大特性是什么?"
我:"ACID,即原子性(Atomicity)、隔离性(Isolation)、持久性(Durability)、一致性(Consistency)!"
面试官:"在 MySQL 数据库的 InnoDB 引擎是怎么实现这四大特性的?"
我:"这个...这个....,还真没有了解过哎"
面试官:"那我们就先这个吧,先回去吧,我们会通知你的~"
这可能是比较常见的面试场景了,你也许回答到了事务的四大特性,但是不一定知道他的实现原理。今天我们就来一起打卡事务的四大特性和实现原理,对于原理的实现,这篇文章只是粗略的介绍一下,更多的细节可以关注我后续的文章。
数据库的事务有四大特性:原子性、隔离性、永久性、一致性,下面将介绍这四大特性的定义和在 InnoDB 引擎中是怎么实现的。
原子性
定义
一次操作是不可分割的,要么全部成功,要么全部失败。比如我们的转账操作,不允许出款方成功,收款方失败这种情况,要么都成功,要么多失败,不可能出现中间状态。
实现
InnoDB 引擎使用 undo log(归滚日志)来保证原子性操作,你对数据库的每一条数据的改动(INSERT、DELETE、UPDATE)都会被记录到 undo log 中,比如以下这些操作:
- 你插入一条记录时,至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉就好了。
- 你删除了一条记录,至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了。
- 你修改了一条记录,至少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值就好了。
当事务执行失败或者调用了 rollback 方法时,就会触发回滚事件,利用 undo log 中记录将数据回滚到修改之前的样子。
更多关于 undo log 的信息,后面再单独开一篇文章打卡。
隔离性
定义
多个事务并发执行的时候,事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
实现
隔离性可能会引入脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)等问题,为了解决这些问题就引入了“隔离级别”的概念。
SQL 标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable):
- 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到。
- 读提交:一个事务提交之后,它做的变更才会被其他事务看到。
- 可重复读: 一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
- 串行化: 顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
SQL标准中规定,针对不同的隔离级别,并发事务可以发生不同严重程度的问题,具体情况如下:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 |
可能 | 可能 | 可能 |
读提交 |
不可能 | 可能 | 可能 |
可重复读 |
不可能 | 不可能 | 可能 |
串行化 |
不可能 | 不可能 | 不可能 |
上面就是几种隔离级别可能出现的并发问题,但是有必要说一下,你隔离得越严实,效率就会越低。
InnoDB 引擎是如何保证隔离性的?利用锁和 MVCC 机制。这里简单的介绍一下 MVCC 机制,也叫多版本并发控制,在使用 READ COMMITTD、REPEATABLE READ 这两种隔离级别的事务下,每条记录在更新的时候都会同时记录一条回滚操作,就会形成一个版本链,在执行普通的 SELECT 操作时访问记录的版本链的过程,这样子可以使不同事务的读-写、写-读操作并发执行,从而提升系统性能。
持久性
定义
事务一旦提交,它对数据库的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
实现
要保证持久性很简单,就是每次事务提交的时候,都将数据刷磁盘上,这样一定保证了安全性,但是要知道如果每次事务提交都将数据写入到磁盘的话,频繁的 IO 操作,成本太高,数据库的性能极低,所以这种方式不可取。
InnoDB 引擎是怎么解决的?InnoDB 引擎引入了一个中间层来解决这个持久性的问题,我们把这个叫做 redo log(归档日子)。
为什么要引入 redo log?redo log 可以保证持久化又可以保证数据库的性能,相比于直接刷盘,redo log 有以下两个优势:
- redo log体积小,毕竟只记录了哪一页修改了啥,因此体积小,刷盘快。
- redo log是一直往末尾进行追加,属于顺序IO。效率显然比随机IO来的快。
InnoDB 引擎是怎么做的?当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log 里面,并更新内存,这个时候更新就算完成了。当数据库宕机重启的时候,会将 redo log 中的内容恢复到数据库中,再根据 undo log和 binlog 内容决定回滚数据还是提交数据。
更多 redo log,后面我打算单独写一篇文章。
一致性
定义
一致性简单一点说就是数据执行前后都要处于一种合法的状态,比如身份证号不能重复,性别只能是男或者女,高考的分数只能在0~750之间,红绿灯只有3种颜色,房价不能为负的等等, 只有符合这些约束的数据才是有效的,比如有个小孩儿跟你说他高考考了1000分,你一听就知道他胡扯呢。数据库世界只是现实世界的一个映射,现实世界中存在的约束当然也要在数据库世界中有所体现。如果数据库中的数据全部符合现实世界中的约束(all defined rules),我们说这些数据就是一致的,或者说符合一致性的。
实现
要保证数据库的数据一致性,要在以下两个方面做努力:
- 利用数据库的一些特性来保证部分一致性需求:比如声明某个列为
NOT NULL
来拒绝NULL
值得插入等。 - 绝大部分还是需要我们程序员在编写业务代码得时候来保证。
以上就是我今天要分享的内容,希望这篇文章对你的学习或者工作有所帮助,感谢您的阅读,如果您觉得文章不错欢迎点赞+转发,感谢。
最后
目前互联网上很多大佬都有 MySQL 相关文章,如有雷同,请多多包涵了。原创不易,码字不易,还希望大家多多支持。若文中有所错误之处,还望提出,谢谢。
欢迎扫码关注微信公众号:「平头哥的技术博文」,和平头哥一起学习,一起进步。
聊一聊 MySQL 中的事务及其实现原理的更多相关文章
- MySql中的事务嵌套
1.Mysql中的事务必须是InnoDB.Berkeley DB引擎,myisam不支持. 2.Mysql是不支持嵌套事务的,开启了一个事务的情况下,再开启一个事务,会隐式的提交上一个事务. 3.My ...
- 【MySQL】漫谈MySQL中的事务及其实现
最近一直在做订单类的项目,使用了事务.我们的数据库选用的是MySQL,存储引擎选用innoDB,innoDB对事务有着良好的支持.这篇文章我们一起来扒一扒事务相关的知识. 为什么要有事务? 事务广泛的 ...
- 漫谈MySql中的事务
最近一直在做订单类的项目,使用了事务.我们的数据库选用的是MySql,存储引擎选用innoDB,innoDB对事务有着良好的支持.这篇文章我们一起来扒一扒事务相关的知识. 为什么要有事务? 事务广泛的 ...
- MySQL中Procedure事务编写基础笔记
原文:MySQL中Procedure事务编写基础笔记 目录: 一.PROCEDURE: 二.CREATE PROCEDURE基本语法: 三.PROCEDURE小进阶 3.1.基本的DECLARE语 ...
- MySql中的事务、JDBC事务、事务隔离级别
一.MySql事务 之前在Oracle中已经学习过事务了,这个东西就是这个东西,但是在MySql中用法还是有一点不同,正好再次回顾一下. 先看看MySql中的事务,默认情况下,每执行一条SQL语句,都 ...
- (转)漫谈MySql中的事务
最近一直在做订单类的项目,使用了事务.我们的数据库选用的是MySql,存储引擎选用innoDB,innoDB对事务有着良好的支持.这篇文章我们一起来扒一扒事务相关的知识. 为什么要有事务? 事务广泛的 ...
- 举例说明MySQL中的事务
一.场景导入 现在有一张仓库表,仓库表中记录了每一个物品的数量,还有一张用户表,用户购买产品,仓库表的产品数量减少,而用户拥有产品的数量增加. 但是如果仓库中的产品数量不足时怎么处理? 例子: #仓库 ...
- 【转】全面了解Mysql中的事务
为什么要有事务? 事务广泛的运用于订单系统.银行系统等多种场景.如果有以下一个场景:A用户和B用户是银行的储户.现在A要给B转账500元.那么需要做以下几件事: 1. 检查A的账户余额>500元 ...
- 在MySQL中设置事务隔离级别有2种方法:
在MySQL中设置事务隔离级别有2种方法: 1 在my.cnf中设置,在mysqld选项中如下设置 [mysqld] transaction-isolation = READ-COMMITTED 2 ...
随机推荐
- 逗号分隔的字符串转换为Python中的列表 split
将逗号分隔的字符串转换为Python中的列表 给定一个字符串: 它是由逗号分隔的几个值的序列: mStr = '192.168.1.1,192.168.1.2,192.168.1.3' 如何将字符 ...
- oralce函数 next_day(d1[,c1])
[功能]:返回日期d1在下周,星期几(参数c1)的日期 [参数]:d1日期型,c1为字符型(参数),c1默认为j(即当前日期) [参数表]:c1对应:星期一,星期二,星期三……星期日 [返回]:日期 ...
- Oracle dbms_random包的用法
1.dbms_random.value方法 dbms_random是一个可以生成随机数值或者字符串的程序包.这个包有initialize().seed().terminate().value().no ...
- oracle SELECT子句中避免使用 ‘ * ‘
当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用 ‘*’ 是一个方便的方法. 不幸的是,这是一个非常低效的方法. 实际上,ORACLE在解析的过程中, 会将’*’ 依次转换成所 ...
- 学习CSS预处理器Less
1.Sass与Less的区别:Sass与Less的区别 2.Less的使用:Less的使用 3.Less的相关网址:Less.js.Less中文网 Less的HSL函数-lighten():HSL函数 ...
- codeforces2B.The least round way 题解 动态规划/模拟
题目出处:http://codeforces.com/problemset/problem/2/B 题目描述 给你一个 \(n \times n\) 的二维数组,它包含的元素都是非负整数.你需要寻找一 ...
- POJ2976 题解 0/1分数规划入门题 二分
题目链接:http://poj.org/problem?id=2976 关于 0/1分数规划 参见 这篇博客 实现代码如下: #include <cstdio> #include < ...
- Android 设置TextView字体颜色
设置TextView字体的颜色其实很简单,尤其是直接在XML文件中,可以直接通过textColor属性指定颜色值,达到设置文本颜色的效果:那在代码中如何动态设置字体的颜色值呢? 接下来,介绍如何通过J ...
- Python--day69--ORM的F查询和Q查询
F查询和Q查询 F查询 在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较.如果我们要对两个字段的值做比较,那该怎么做呢? Django 提供 F() 来做这样的比较.F() 的实例可 ...
- 洛谷P2146 [NOI2015]软件包管理器 题解 树链剖分+线段树
题目链接:https://www.luogu.org/problem/P2146 本题涉及算法: 树链剖分: 线段树(区间更新及求和,涉及懒惰标记) 然后对于每次 install x ,需要将 x 到 ...