0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

关于MySQL事务一个多小时的面试 太难了

数据分析与开发 来源:数据分析与开发 作者:数据分析与开发 2020-09-24 15:39 次阅读

迎面走来了一个风尘仆仆的身穿格子衫的男子,手里拿着一个MacBook Pro,看着那稀少的发量,和那从容淡定的眼神。

我心里一颤,我去,这是架构师,架构师来面我技术面,我心里顿时不淡定了,表面很稳实则心里慌得一批。

果然,他手里拿着我的简历,快速的扫了一下,然后用眼角余光看了一下我,上来就开问。

面试官:看你简历上说精通Mysql优化方法,你先来说说你对Mysql的事务的了解吧。

我心里喜了一下,这个简单啊,哥我可是北大(背大)的,再来面试之前,早就有准备的,二话不说,上去就是背。

我:好的,数据库的事务是指一组sql语句组成的数据库逻辑处理单元,在这组的sql操作中,要么全部执行成功,要么全部执行失败。

我:这里的一组sql操作,举个简单又经典的例子就是转账了,事务A中要进行转账,那么转出的账号要扣钱,转入的账号要加钱,这两个操作都必须同时执行成功,为了确保数据的一致性。

面试官:刚才你提到了数据一致性,你知道事务的特性吗?说说你的理解。

我:在Mysql中事务的四大特性主要包含:原子性(Atomicity)、一致性(Consistent)、隔离性(Isalotion)、持久性(Durable),简称为ACID。

我:原子性是指事务的原子性操作,对数据的修改要么全部执行成功,要么全部失败,实现事务的原子性,是基于日志的Redo/Undo机制。

我:一致性是指执行事务前后的状态要一致,可以理解为数据一致性。隔离性侧重指事务之间相互隔离,不受影响,这个与事务设置的隔离级别有密切的关系。

我:持久性则是指在一个事务提交后,这个事务的状态会被持久化到数据库中,也就是事务提交,对数据的新增、更新将会持久化到数据库中。

我:在我的理解中,原子性、隔离性、持久性都是为了保障一致性而存在的,一致性也是最终的目的。

心里暗自欢喜,背完了,平时背的多,面试就会说,幸好难不倒我。

面试官:刚才你说原子性是基于日志的Redo/Undo机制,你能说一说Redo/Undo机制吗?

啊哈?我都说了什么,不小心给自己埋了一颗大雷。不慌,哥脑子里还有货,假装若有所思的停了几十秒,接着背。

我:Redo/Undo机制比较简单,它们将所有对数据的更新操作都写到日志中。

我:Redo log用来记录某数据块被修改后的值,可以用来恢复未写入 data file 的已成功事务更新的数据;Undo log是用来记录数据更新前的值,保证数据更新失败能够回滚。

我:假如数据库在执行的过程中,不小心崩了,可以通过该日志的方式,回滚之前已经执行成功的操作,实现事务的一致性。

面试官:可以举一个场景,说一下具体的实现流程吗?

我:可以的,假如某个时刻数据库崩溃,在崩溃之前有事务A和事务B在执行,事务A已经提交,而事务B还未提交。当数据库重启进行 crash-recovery 时,就会通过Redo log将已经提交事务的更改写到数据文件,而还没有提交的就通过Undo log进行roll back。

面试官:之前你还提到事务的隔离级别,你能说一说吗?

我:可以的,在Mysql中事务的隔离级别分为四大等级,读未提交(READ UNCOMMITTED)、读提交 (READ COMMITTED)、可重复读 (REPEATABLE READ)、串行化 (SERIALIZABLE)。

我:读未提交会读到另一个事务的未提交的数据,产生脏读问题,读提交则解决了脏读的,出现了不可重复读,即在一个事务任意时刻读到的数据可能不一样,可能会受到其它事务对数据修改提交后的影响,一般是对于update的操作。

我:可重复读解决了之前不可重复读和脏读的问题,但是由带来了幻读的问题,幻读一般是针对inser操作。

我:例如:第一个事务查询一个User表id=100发现不存在该数据行,这时第二个事务又进来了,新增了一条id=100的数据行并且提交了事务。

我:这时第一个事务新增一条id=100的数据行会报主键冲突,第一个事务再select一下,发现id=100数据行已经存在,这就是幻读。

面试官:小伙子你能演示一下吗?我不太会你能教教我吗?我电脑在这里,你演示我看一看。

男人的嘴骗人的鬼,我信你个鬼,你这糟老头子坏得很,出来装X总是要还的,只能默默含泪把它敲完。

我:首先创建一个User表,最为一个测试表,测试表里面有三个字段,并插入两条测试数据。

CREATE TABLE User ( id INT(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20), age INTDEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=gb2312; INSERT INTO `user` VALUES (1, 'zhangsan', 23); INSERT INTO `user` VALUES (2, 'lisi', 20);

我:在Mysql中可以先查询一下他的默认隔离级别,可以看出Mysql的默认隔离级别是REPEATABLE-READ。

我:先来演示一下读未提交,先把默认的隔离级别修改为READ UNCOMMITTED。

我:他设置隔离级别的语句中set global transaction isolation level read uncommitted,这里的global也可以换成session,global表示全局的,而session表示当前会话,也就是当前窗口有效。

我:当设置完隔离级别后对于之前打开的会话,是无效的,要重新打开一个窗口设置隔离级别才生效。

我:然后是开启事务,Mysql中开启事务有两种方式begin/start transaction,最后提交事务执行commit,或者回滚事务rollback。

我:在执行begin/start transaction命令,它们并不是一个事务的起点,在执行完它们后的第一个sql语句,才表示事务真正的启动 。

我:这里直接打开两个新的窗口,同时开启事务,在第一个窗口先update一个id=1的数据行name改为'非科班的科班',执行成功。

我:然后再第二个窗口执行两次的查询,分别是窗口一update之前的查询和update之后的查询。

我:第一个session产生的未提交的事务的状态就会直接影响到第二sesison,也就是脏读。

我:对于读提交也是一样的,开启事务后,第一个事务先执行查询数据,然后第二个session执行update操作,但是还没有commit,这是第一个session再次select,数据并没有改变,再第二个session执行commit之后,第一个session再次select就是改变后的数据了。

我:这样第一个事务的查询结果就会收到第二事务的影响,这个也就是产生不可重复读的问题。

面试官:小伙子你能画一下他执行的过程图吗?你讲的我有点乱,我还没有彻底明白。

我心里一万只什么马在飞过,欲哭无泪,这面试官真难伺候,说时迟那时快,从左屁股兜抽出笔,从右屁股兜拿出纸,开始画。

我:这个是读提交的时间轴图,读未提交的时间轴图,原理也一样的,第二个select的时候数据就已经改变了。

这是面试官拿过我的图看了一点,微微的点了点头,嘴角露出思思的笑意,我想你这糟老头子应该不会再刁难我了吧。

面试官:嗯,你接着演示你的可重复读吧。

我:嗯,好的,然后就是可重复读,和之前一样的操作。

我:将两个session开启为REPEATABLE READ,同时开启事务,在第一个事务中先select,然后在第二个事务里面update数据行,可以发现即使第二个事务已经commit,第一个事务再次select数据也还是没有改变,这就解决了不可重复读的问题。

我:这里有个不同的地方就是在Mysql中,默认的不可重复读个隔离级别也解决了幻读的问题。

我:从上面的演示中可以看出第一个事务中先select一个id=3的数据行,这条数据行是不存在的,返回Empty set,然后第二个事务中insert一条id=3的数据行并且commit,第一个事务中再次select的,数据也好是没有id=3的数据行。

我:最后的串行化,样式步骤也是一样的,结果也和Mysql中默认的个可重复读隔离级别的结果一样,串行化的执行流程相当于把事务的执行过程变为顺序执行,我这边就不再做演示了。

我:这四大等级从上到下,隔离的效果是逐渐增强,但是性能却是越来越差。

面试官:哦?性能越来越差?为什么会性能越来越差?你能说一说原因吗?

哎呀,我这嘴,少说一句会死啊,这下好了,这个得说底层实现原理了,从原来的假装若有所思,变成了真正得若有所思。

我:这个得从Mysq的锁说起,在Mysql中的锁可以分为分享锁/读锁(Shared Locks)、排他锁/写锁(Exclusive Locks)、间隙锁、行锁(Record Locks)、表锁。

我:在四个隔离级别中加锁肯定是要消耗性能的,而读未提交是没有加任何锁的,所以对于它来说也就是没有隔离的效果,所以它的性能也是最好的。

我:对于串行化加的是一把大锁,读的时候加共享锁,不能写,写的时候,加的是排它锁,阻塞其它事务的写入和读取,若是其它的事务长时间不能写入就会直接报超时,所以它的性能也是最差的,对于它来就没有什么并发性可言。

我:对于读提交和可重复读,他们俩的实现是兼顾解决数据问题,然后又要有一定的并发行,所以在实现上锁机制会比串行化优化很多,提高并发性,所以性能也会比较好。

我:他们俩的底层实现采用的是MVCC(多版本并发控制)方式进行实现。

面试官:你能先说一下先这几个锁的概念吗?我不是很懂,说说你的理解。

我:哦,好的,共享锁是针对同一份数据,多个读操作可以同时进行,简单来说即读加锁,不能写并且可并行读;排他锁针对写操作,假如当前写操作没有完成,那么它会阻断其它的写锁和读锁,即写加锁,其它读写都阻塞 。

我:而行锁和表锁,是从锁的粒度上进行划分的,行锁锁定当前数据行,锁的粒度小,加锁慢,发生锁冲突的概率小,并发度高,行锁也是MyISAM和InnoDB的区别之一,InnoDB支持行锁并且支持事务 。

我:而表锁则锁的粒度大,加锁快,开销小,但是锁冲突的概率大,并发度低。

我:间隙锁则分为两种:Gap Locks和Next-Key Locks。Gap Locks会锁住两个索引之间的区间,比如select * from User where id>3 and id<5 for update,就会在区间(3,5)之间加上Gap Locks。

我:Next-Key Locks是Gap Locks+Record Locks形成闭区间锁select * from User where id>=3 and id=<5 for update,就会在区间[3,5]之间加上Next-Key Locks。

面试官:那Mysql中什么时候会加锁呢?

我:在数据库的增、删、改、查中,只有增、删、改才会加上排它锁,而只是查询并不会加锁,只能通过在select语句后显式加lock in share mode或者for update来加共享锁或者排它锁。

面试官:你在上面提到MVCC(多版本并发控制),你能说一说原理吗?

我:在实现MVCC时用到了一致性视图,用于支持读提交和可重复读的实现。

我:在实现可重复读的隔离级别,只需要在事务开始的时候创建一致性视图,也叫做快照,之后的查询里都共用这个一致性视图,后续的事务对数据的更改是对当前事务是不可见的,这样就实现了可重复读。

我:而读提交,每一个语句执行前都会重新计算出一个新的视图,这个也是可重复读和读提交在MVCC实现层面上的区别。

面试官:那你知道快照(视图)在MVCC底层是怎么工作的吗?

我:在InnoDB 中每一个事务都有一个自己的事务id,并且是唯一的,递增的 。

我:对于Mysql中的每一个数据行都有可能存在多个版本,在每次事务更新数据的时候,都会生成一个新的数据版本,并且把自己的数据id赋值给当前版本的row trx_id。

面试官:小伙子你可以画个图我看看吗?我不是很明白。

我有什么办法呢?完全没办法,只能又从屁股兜里拿出笔和纸,迅速的画了起来,要是这次面试不过就血亏啊,浪费了我两张纸和笔水,现在的笔和纸多贵啊,只能豁出去了。

我:如图中所示,假如三个事务更新了同一行数据,那么就会有对应的三个数据版本。

我:实际上版本1、版本2并非实际物理存在的,而图中的U1和U2实际就是undo log,这v1和v2版本是根据当前v3和undo log计算出来的。

面试官:那对于一个快照来说,你知道它要遵循什么规则吗?

我:嗯,对于一个事务视图来说除了对自己更新的总是可见,另外还有三种情况:版本未提交的,都是不可见的;版本已经提交,但是是在创建视图之后提交的也是不可见的;版本已经提交,若是在创建视图之前提交的是可见的。

面试官:假如两个事务执行写操作,又怎么保证并发呢?

我:假如事务1和事务2都要执行update操作,事务1先update数据行的时候,先回获取行锁,锁定数据,当事务2要进行update操作的时候,也会取获取该数据行的行锁,但是已经被事务1占有,事务2只能wait。

我:若是事务1长时间没有释放锁,事务2就会出现超时异常 。

面试官:这个是在update的where后的条件是在有索引的情况下吧?

我:嗯,是的 。

面试官:那没有索引的条件下呢?没办法快速定位到数据行呢?

我:若是没有索引的条件下,就获取所有行,都加上行锁,然后Mysql会再次过滤符合条件的的行并释放锁,只有符合条件的行才会继续持有锁。

我:这样的性能消耗也会比较大。

面试官:嗯嗯

此时面试官看看手表一个多钟已经过去了,也已经到了饭点时刻,我想他应该是肚子饿了,不会继续追问吧,两人持续僵了三十秒,他终于开口了。

面试官:小伙子,现在时间也已经到了饭点了,今天的面试就到此结束吧,你回去等通知吧。

我:。。。。。。。。。。

责任编辑:xj

原文标题:我以为我对 MySQL 事务很熟,直到我遇到了阿里面试官

文章出处:【微信公众号:数据分析与开发】欢迎添加关注!文章转载请注明出处。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    6511

    浏览量

    87590
  • MySQL
    +关注

    关注

    1

    文章

    775

    浏览量

    26000

原文标题:我以为我对 MySQL 事务很熟,直到我遇到了阿里面试官

文章出处:【微信号:DBDevs,微信公众号:数据分析与开发】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    RTT5.0开启了软件定时器,运行大概4多小时就会出现定时器回调任务不再执行的问题怎么解决?

    我使用的是RTT5.0,开启了软件定时器,运行大概4多小时就会出现定时器回调任务不再执行,仿真打断点看,timer.c文件中的定时器线程_timer_thread_entry不再运行了。这可
    发表于 03-26 07:35

    阿里二面:了解MySQL事务底层原理吗

    MySQL 是如何来解决脏写这种问题的?没错,就是锁。MySQL 在开启一个事务的时候,他会将某条记录和事务做一个绑定。这个其实和 JVM 锁是类似的。
    的头像 发表于 01-18 16:34 163次阅读
    阿里二面:了解<b class='flag-5'>MySQL</b><b class='flag-5'>事务</b>底层原理吗

    mysql主从复制数据不一致怎么办

    不一致的原因和解决方法。 一、MySQL主从复制数据不一致的原因 网络延迟:主从之间的网络延迟导致从库在主库执行完并提交的操作之前拉取到的binlog可能不是最新的,从而导致数据不一致。 大事务:如果主库中存在大事务(比如大数据
    的头像 发表于 11-16 14:35 864次阅读

    redis与mysql如何保持数据一致性

    Redis是一款开源的高性能Key-Value存储系统,通常被用作缓存和数据存储。它支持多种数据结构,并提供了丰富的功能,如事务、发布订阅、Lua脚本等。Redis的内存数据库特性使得它能够提供极高的读写性能。 MySQL是一种关系型数据库管理系统(RDBMS),以其出
    的头像 发表于 11-16 11:27 606次阅读

    MYSQL事务的底层原理详解

    事务的实现机制上,MySQL 采用的是 WAL:Write-ahead logging,预写式日志,机制来实现的。
    的头像 发表于 11-15 10:10 227次阅读
    <b class='flag-5'>MYSQL</b><b class='flag-5'>事务</b>的底层原理详解

    FPGA是啥?太难了~

    不重要的工作。所以不需要有什么代码(姑且这么称呼)优劣之争。 2、FPGA是做不完的梦 FPGA开发的流程,就是通过verilog/VHDL等硬件描述语言,经过EDA工具编译、综合、布局布线成为下载
    发表于 11-03 10:29

    基于MySQL的锁机制

    在数据库系统中,为了保证数据的一致性和并发控制,锁机制发挥着至关重要的作用。尤其在关系型数据库MySQL中,其独特的锁机制设计更是赢得了许多开发者的喜爱。 本文将详细探讨MySQL的锁机制,包括
    的头像 发表于 09-30 11:16 495次阅读

    NineData:1小时迁移500GB的MySQL数据

    NineData 提供的数据复制同时包含了数据迁移和数据同步的能力,在不影响业务的前提下,提供了高效、稳定、可运维的大数据量迁移能力。经实测,在源及目标实例同城情况下,500GB的MySQL数据的迁移,只需1个小时,平均迁移速度142MB/s。
    的头像 发表于 09-05 11:34 337次阅读
    NineData:1<b class='flag-5'>小时</b>迁移500GB的<b class='flag-5'>MySQL</b>数据

    Arm事务性内存扩展概述

    本指南介绍事务性内存,它允许自动执行代码,而无需始终实现限制性能的同步方法。 ARM事务内存扩展(TME)是事务内存的ARM实现。 现代复杂系统在不止
    发表于 08-17 07:57

    MySQL运维常用脚本

    进入MySQL:启动MySQL Command Line Client(MySQL的DOS界面),直接输入安装时的密码即可。此时的提示符是:mysql>
    的头像 发表于 07-29 11:23 390次阅读

    MySQL三种日志讲解

    MySQL 日志包含了错误日志、查询日志、慢查询日志、事务日志、二进制日志等,如果存储引擎使用的是 InnoDB ,二进制日志(binlog)和事务日志(包括redo log和undo log) 是肯定绕不过去的,本篇接下来详细
    的头像 发表于 07-25 11:15 458次阅读
    <b class='flag-5'>MySQL</b>三种日志讲解

    常见的嵌入式C语言面试

    数组是最基本的数据结构,关于数组的面试题也屡见不鲜,本文罗列了一些常见的面试题,仅供参考。目前有以下18道题目。
    发表于 07-18 10:46 589次阅读

    mysql运维实用脚本序列~

    进入MySQL:启动MySQL Command Line Client(MySQL的DOS界面),直接输入安装时的密码即可。此时的提示符是:mysql>
    的头像 发表于 07-16 15:25 662次阅读

    为什么 MySQL 单表不能超过 2000 万行?

    最近看到一篇《我说 MySQL 每张表最好不要超过 2000 万数据,面试官让我回去等通知》的文章,非常有趣。 文中提到,他朋友在面试的过程中说,自己的工作就是把用户操作信息存到 MySQL
    的头像 发表于 06-29 16:48 346次阅读
    为什么 <b class='flag-5'>MySQL</b> 单表不能超过 2000 万行?

    Spring事务在哪几种情况下会不生效?

    日常开发中,我们经常使用到spring事务。最近星球一位还有去美团面试,被问了这么一道面试题: Spring 事务在哪几种情况下会不生效?
    的头像 发表于 05-10 17:53 564次阅读
    Spring<b class='flag-5'>事务</b>在哪几种情况下会不生效?