苍茫大地一剑尽挽破,何处繁华笙歌落。 难道这普天之下尽没我容纳之处?为什么这面试官总要与我争风相对,这不是难为人吗? mysql
每次经历痛领悟,总会悠然南山下,独自愁楚。记录心中一片大海,惟有独自努力,方能实现行业之俊杰。面试
一个阳光明媚得上午,迎面走来了一位身着雪白雪白得的白色衬衣,灰白色相间的休闲短裤,还给我露出了彰显男儿本色的黑色腿毛。手拿银色金属质感得 MacBook Pro,外加一双小白鞋。看着那稀少的发量,在灯光的照耀下,甚至还有点反光。透过那从容不迫的脸色。 sql
我心里不忍一颤,今天怕是要遇到人了。整整一个架构师的大叔呀。数据库
果不其然,他手里拿着个人简历,快速的扫了一下,而后用眼角余光看了一下我,像是在掂量着什么。上来开口即问。缓存
面试官: 看你简历描述精通 Mysql 优化方法,那你先说说你对Mysql的事务的了解吧。 安全
我吒吒辉心里独自平静了一些,这个不难,就是一个事务定义和执行得调用的方法。却不知后面的内容都是对我得严刑拷打。服务器
吒吒辉: 好呢,数据库的事务是指一组sql语句访问各类数据项的数据库逻辑处理单元,在这组 SQL 操做中,要么所有执行成功,要么所有执行失败。 微信
举个经典的例子就是转帐,事务 A 要进行转帐,那么,转出的帐号要扣钱,转入的帐号要加钱,这两操做要么同时执行成功,要么都所有执行失败。为得就是确保数据的一致性。 多线程
吒吒辉: 通常请求进入MySQL,都是根据不一样请求类型先在服务层内部作数据处理,而后在由后台线程把数据刷到磁盘中。从而避免频繁的读写磁盘。 架构
回答完毕后,本身笑笑的模一下头发,打理一下本身的发型,整理了一下脖子上得领结。
面试官:吆喝,这小子还有点料啊,敢在太岁头上动土,你还要反了天呀你。还在这欺负我头上的没头发。个小崽子。
面试官: 刚你提到了 MySQL 内部的工做,那你知道事务的特性吗? 数据是如何刷盘? 服务层数据如何修改? 这样得工做模式有什么好处? 怎么避免频繁写入? 读请求和写请求在服务层工做是同样的吗?
我心里一钝,这老秃驴,不要命啦,逮到这么问我。看来本身得正经点,感受空气中弥漫着杀气腾腾的气息。
尴尬的冲他笑了一笑。立马说到
吒吒辉: 在Mysql中事务的四大特性主要包含:
原子性(Atomicity)
一致性(Consistent)
隔离性(Isalotion)
持久性(Durable),简称为 ACID。由它们共同来保证数据的一致性。
面试官: 嗯,说说你对这四大特的理解?
吒吒辉:
原子性: 故名思议原子是最小的元素单位,因此一个事务是一个不可拆分的最小工做单元。整个事务操做要么所有执行成功,要么所有失败回滚。
因执行成功和失败都是状态的变化,若是只有执行效果只有一半,数据就会不完整,从而违背了原子的特色。
一致性: 指数据库从一个一致性的状态转换到另一个一致性的状态。要保证事务先后的状态要一致
隔离性:一个事务所作的修改在最终提交之前,对其余事务是不可见的。
持久性:一旦事务提交,则其所作的修改就会永久保存到磁盘上。是执行结果一直生效。
面试官: 那数据库底层是如何实现这四大特性的?
顿时,我望向他那张有点油腻的大脸,可他确实气定神闲的望着我,面相是多么的随和。此刻心里不断策马崩腾,第一个问题就给我整了这么多,后面可咋办? 心里感受悬了
吒吒辉:
undo log 主要记录数据的逻辑变化。因此须要将以前的操做都记录下来,而后在发生错误或执行事务回滚时才能保证回滚到以前的操做。
面试官: 那这个请求过程是怎么实现的?
好比当写请求为 insert 操做进入到数据库服务层时,会先在缓冲池下面的写缓冲中进行数据的写入,而后先把写请求的物理语言写入到 read log中,并记录一条删除当前语句的逻辑语句到 undo log中。 最后由 MySQL 后台线程按期刷新数据到磁盘里面。
可是这个写缓冲在5.5以前叫 插入缓冲,只针对 insert 优化; 如今对 update 和 delete更新操做都有效。如今叫作:写缓冲
面试官: 这个写缓冲有什么好处呢?
吒吒辉:
避免频繁磁盘I/O的写操做,若是没得这玩意,须要在磁盘里面读到数据,而后才能数据上面的修改。
但大多数场景下都是读多写少,若是在高并发的场景下,经过打包批次处理就能有效避免,在写操做处理时资源开销而影响到大量得读请求。
一致性: 由 Redo log 日志保证,也叫重作日志,是用来实现事务的持久性。该日志文件由两部分组成:重作日志缓冲(redo log buffer)以及重作日志文件(redo log),前者是在内存中,后者在磁盘中。
当事务提交以后会把全部修改信息都会存到该日志中。假设有个表叫作t1(id,username) 如今要插入数据(3,'吒吒辉')。
start transaction;
//数据写入 写缓冲中
insert into t1 (id,username) values (3,'吒吒辉')
// 生成 重作日志 id =3 , username = '吒吒辉'
commit;复制代码
面试官: 那这 redo log 有什么做用?
为了提高性能,不会把每次的修改都实时同步到磁盘,而是会先存到Boffer Pool(缓冲池)里头,把这个看成缓存来用。而后使用后台线程去作缓冲池和磁盘之间的同步。
面试官: 那若是缓冲池的数据还没来得急同步就宕机或断电了怎么办? 这样但是会致使丢部分已提交事务的修改信息!
吒吒辉: 因此引入redo log来记录已成功提交事务的修改信息,而且会把redo log持久化到磁盘,系统重启以后在读取redo log恢复最新数据
面试官: 能够举一个场景,怎么应用它们呢?
吒吒辉: 假如某个时刻数据库崩溃,在崩溃以前有相关事务在执行,一半已经提交,一半还未提交。当数据库重启进行 crash-recovery 时,就会经过 Redo log将已经提交事务的更改先载入到内存,而后再写到数据文件,而尚未提交的就经过 Undo log 进行 roll back。
面试官: 嗯,继续
此时,心里发毛。回答到这个程度,还面不改色,就一个继续打发我了?你可真是深造。
吒吒辉:
InnoDB的 MVCC ,是经过在每行记录的后面保存两个隐藏的列来实现的。这两个列, 一个保存了行的建立时间,一个保存了行的过时时间(或删除时间), 固然存储的并非实际的时间值,而是系统版本号。
面试官: 那不一样的SQL请求,MySQL 是如何实现得?
吒吒辉:
面试官: 那这个机制有什么好处?
吒吒辉:
它主要实现思想是经过数据多版原本作到读写分离。每一个请求给到不一样版数据来执行。从而实现不加锁读进而作到读写并行。
MVCC机制实现还须要依赖于 undo log与read view
undo log: undo log 中记录某行记录的多个版本的数据。
read view: 用来判断当前版本数据的可读性
面试官: 那你给我画个图看看
什么鬼,这玩意还画图。我用那水汪汪的大眼睛,直勾勾的看着他。只见他看口说到。
** 面试官:没明白吗? 就是用白板笔画出原理图解
吒吒辉: 我急忙应和到。额额额,好
吒吒辉: 就这样,每一个请求修改时就会对应一个版本。
面试官: 那这 MVCC 是如何保证多个版本数据得最终一致呢?
吒吒辉: 因数据操做为多个版本,因此它支持读写并行。
在请求进行事务操做前,会先入到读、写队列里面,而后再分别执行读写请求。若第一个写请求执行事务但未提交,但这时它就会拿到最新版本。
若是此时读请求进入也会直接执行,只不过读取数据时得系统版本就为未执行写请求的版本号,根据它来进行数据的读取。
这样在事务未进行提交前,全部读操做读取到的数据是同样的,由于它受系统当前版本控制。读取的都是同一个版本数据。
面试官: MySQL是多线程, 那同时有多个线程修改同一行数据,这种状况怎么处理的?
吒吒辉: 通常若是系统达到并发执行的状况,对用户请求得要求是很高的,每秒至少得几万请求,若有并发问题产生。
假如事务 A、B 都要执行 update 操做,事务A先 update 数据时,会先获取行锁,锁定数据,当事务 B 要进行 update 操做时,也会尝试获取该数据行的行锁,但此时已经被事务A占有,故事务B只能 wait。
吒吒辉: 如事务A,是个很费时的大SQL,长时间没释放锁。那么事务 B 就会等待超时。
查询全局等待事务锁超时时间
SHOW GLOBAL VARIABLES LIKE 'innodb_lock_wait_timeout';
面试官: 这个是在 update 的where后的条件是在有索引的状况下吧?
吒吒辉: 嗯,是的,由于 innodb 的行数只有索引才会触发,锁定的是行锁
面试官: 那没有索引的条件下,就没办法快速定位到数据行呀?
吒吒辉: 如果没有索引的条件下,就会退化为表锁。 而后获取全部行后,Mysql 再过滤符合条件的的行并释放锁。日后只有符合条件的行才持有锁。
吒吒辉: 这样就下降了并发度,而且性能开销也会很大。
吒吒辉: 而最后的一致性,其实是经过原子性、持久性、隔离性来实现的
此刻,感受本身多少放松了一些,心里有点小得意,感受达到了巅峰时刻。而后再拿着面前的矿泉水,饮了一口,顺便擦了下汗水。一切是那么得行云流水。然而面试官那脸仍是一沉不变,像是在酝酿着什么,这不又开始了。
面试官: 那请继续数据是如何刷盘? 这样得工做模式是怎么避免频繁读写得?
吒吒辉: 说到数据刷盘,首先得谈下 innodb 缓冲池。
buffer pool 主要由数据、索引、插入缓冲、自适应哈希索引、锁等组成。
不一样请求会根据 buffer pool里面对应部分数据来执行相关的操做。buffer pool中的数据会根据配置参数按期同步到磁盘中;只是这里刷盘的数据主要由 数据和日志 组成。
* 由 innodb_max_dirty_pages_pct 参数决定。在说到这个以前,得说说脏页。
脏页指存在于内存中的数据,但未同步到持久化存储里面,由于数据库的数据用页来读取,故此称之为脏页。
innodb_max_dirty_pages_pct 参数可动态调整,最小值为0, 最大值为99.99,默认值为 75。
根据缓冲池的大小和存储脏页的数量计算比例,若是知足了就调用后台线程把数据刷新到持久层当中里面。
这样作的好处就是合并其它数据页,从而提升写入的效率。
由 innodb_flush_log_at_trx_commit 变量来控制日志缓冲刷新频繁程度。
0:把日志缓冲写到日志文件,而且每秒钟刷新一次持久化存储,可是事务提交时不作任何事。
1: 将日志缓冲写到日志文件,而且每次事务提交都刷新到持久化存储。这是默认的(而且是最安全的)设置,该设置能保证不会丢失任何已经提交的事务,除非磁盘或者操做系统是“伪”刷新,即写入到磁盘缓冲。
2: 每次提交时把日志缓冲写到日志文件,可是并不刷新到持久化存储。InnoDB 每秒作一次刷新到持久化存储。通常也会选2,若是MySQL进程“挂了”,2 不会丢失任何事务。若是整个服务器“挂了”或者断电了,则仍是可能会丢失 1s 的事务。
为何上面把日志缓冲写到日志文件,还没到持久化存储呢?
由于在大部分操做系统中,把日志缓冲写到日志文件只是简单地把数据从 InnoDB 的内存缓冲转移到了操做系统的缓存,也是在内存里,并无真的把数据写到了持久化存储。因此还须要进行数据磁盘同步的刷写。
吒吒辉: 缓冲池是下降频繁得读写请求,这个得分状况来看:
面试官: 嗯嗯
瞬间感受被榨干了,怎么都这样出招,幸亏之前多少有点准备。突然感受本身身后开始滑落着汗滴。只见面试官看了看简历,脸上表情祥和,感受在端详着什么。。。。
面试官: 小伙,这今天就面到这里,我看你好像有点热呀,先回去等通知吧
感情你老这才开胃呀!还能这样整。行不行你到是给句花呗。让我流这么多汗,我得喝多少水才补得回来,这不虚都被你搞虚。瞬间炸裂开来
吒吒辉: 这样啊,那好吧。。。。
* 我命由我不禁天,只为那不甘心的,若有帮助,关注分享额
文章持续更新,可微信搜索「莲花童子哪吒 」第一时间阅读,还有在线技术答疑群聊