写点什么

原创 面试官:你说对 MySQL 事务很熟?那我问你 10 个问题

发布于: 2020 年 05 月 30 日
原创 面试官:你说对MySQL事务很熟?那我问你10个问题

大家好!我是 lemon 一个混迹一线互联网大厂,没有故事有点技术的程序员。


学习关系型数据库 MySQL 是很好的切入点,大部分人工作中用惯了 CRUD,对面试官刨根问底的灵魂拷问你还能对答如流吗?我们有必要了解一些更深层次的数据库基础原理。


整理了面试中,关于 MySQL 事务和存储引擎 10 个 FAQ(Frequently asked questions),你想知道的都在这里。


什么是事务?


事务就是「一组原子性的 SQL 查询」,或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询。如果其中有任何一条语句因为崩溃或其他原因无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。


事务控制语法知道吗?


BEGIN 或 START TRANSACTION 显式地开启一个事务;COMMIT / COMMIT WORK二者是等价的。提交事务,并使已对数据库进行的所有修改成为永久性的;ROLLBACK / ROLLBACK WORK。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;SAVEPOINT identifier 在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT;RELEASE SAVEPOINT identifier 删除一个事务的保存点;ROLLBACK TO identifier 把事务回滚到标记点;SET TRANSACTION 用来设置事务的隔离级别。InnoDB 存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE
复制代码


用通俗的语言说说你理解的事务


用银行业务举个栗子,用户 lemon 有两银行卡,一张是招商银行 CMBC 的工资卡,另一张是工商银行 ICBC 的储蓄卡,每月 5 号发工资都要把招行卡的 100 万转到建设银行储蓄卡账户。记住这里的银行缩写后面就是对应的数据表名称,你要记不住,我给你理一理。


招商银行(CMBC):“存么?白痴!”中国工商银行(ICBC): “爱存不存!”中国建设银行(CCB): “存?存不?”中国银行(BC): “不存!”中国农业银行(ABC): “啊,不存!”民生银行(CMSB):“存么?SB!"兴业银行(CIB):“存一百。”国家开发银行(CDB):“存点吧!”汇丰银行(HSBC):“还是不存!”
复制代码



这个转账的操作可以简化抽成一个事务,包含如下步骤:


  1. 查询 CMBC 账户的余额是否大于 100 万

  2. 从 CMBC 账户余额中减去 100 万

  3. 在 ICBC 账户余额中增加 100 万


以下语句对应创建了一个转账事务:


START TRANSACTION;SELECT balance FROM CMBC WHERE username='lemon';UPDATE CMBC SET balance = balance - 1000000.00 WHERE username = 'lemon';UPDATE ICBC SET balance = balance + 1000000.00 WHERE username = 'lemon';COMMIT;
复制代码


事务的 ACID 特性是什么?


ACID 其实是事务特性的英文首字母缩写,具体的含义是这样的:


  • 原子性(atomicity)

一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作。

  • 一致性(consistency)

数据库总是从一个一致性的状态转换到另外一个一致性的状态。在前面的例子中,一致性确保了,即使在执行第三、四条语句之间时系统崩溃,CMBC 账户中也不会损失 100 万,不然 lemon 要哭死因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。

  • 隔离性(isolation)

通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。在前面的例子中,当执行完第三条语句、第四条语句还未开始时,此时如果有其他人也准备给 lemon 的 CMBC 账户存钱,那他看到的 CMBC 账户里还是有 100 万的。

  • 持久性(durability)

一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。而且「不可能有能做到 100%的持久性保证的策略」否则还需要备份做什么。



什么是脏读、不可重复读、幻读?


脏读


在事务 A 修改数据之后提交数据之前,这时另一个事务 B 来读取数据,如果不加控制,事务 B 读取到 A 修改过数据,之后 A 又对数据做了修改再提交,则 B 读到的数据是脏数据,此过程称为脏读 Dirty Read。



不可重复读


一个事务内在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了变更、或者某些记录已经被删除了。



幻读


事务 A 在按查询条件读取某个范围的记录时,事务 B 又在该范围内插入了新的满足条件的记录,当事务 A 再次按条件查询记录时,会产生新的满足条件的记录(幻行 Phantom Row)


不可重复读与幻读有什么区别?


  • 不可重复读的重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的「数据不一样」。(因为中间有其他事务提交了修改)

  • 幻读的重点在于新增或者删除:在同一事务中,同样的条件,第一次和第二次读出来的「记录数不一样」。(因为中间有其他事务提交了插入/删除)


SQL 的四个隔离级别知道吗?具体是什么解决了什么问题说说看


SQL 实现了四个标准的隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。


各个隔离级别可以不同程度的解决脏读、不可重复读、幻读。隔离级别各有所长,没有完美的解决方案,脱离业务场景谈具体实施都是耍流氓。



MySQL 中哪些存储引擎支持事务?


MySQL 中 InnoDB 和 NDB Cluster 存储引擎提供了事务处理能力,以及其他支持事务的第三引擎。


什么是自动提交?


MySQL 默认采用自动提交AUTOCOMMIT模式。也就是说,如果不是显式地开始一个事务,则每个查询都被当作一个事务执行提交操作。


对于 MyISAM 或者内存表这些事务型的表,修改AUTOCOMMIT不会有任何影响。对这类表来说,没有COMMIT或者ROLLBACK的概念,也可以说是相当于一直处于AUTOCOMMIT启用的模式。


在事务中可以混合使用存储引擎吗?


尽量不要再同一个事务中使用多种存储引擎,MySQL 服务器层不管理事务,事务是由下层的存储引擎实现的。


如果在事务中混合使用了事务型和非事务型的表(例如 InnoDB 和 MyISAM 表),在正常提交的情况下不会有什么问题。


但如果该事务需要回滚,非事务型的表上的变更就无法撤销,这会导致数据库处于不一致的状态,这种情况很难修复,事务的最终结果将无法确定。所以,为每张表选择合适的存储引擎非常重要。


MySQL 存储引擎类型有哪些?


最常用的存储引擎是 InnoDB 引擎和 MyISAM 存储引擎,InnoDB 是 MySQL 的默认事务引擎。


查看数据库表当前支持的引擎 :


show table status from 'your_db_name' where name='your_table_name'; 查询结果表中的`Engine`字段指示存储引擎类型。
复制代码


InnoDB 存储引擎的特点和应用场景?


InnoDB 是 MySQL 的默认「事务引擎」,被设置用来处理大量短期(short-lived)事务,短期事务大部分情况是正常提交的,很少会回滚。


更多 InnoDB 事务模型相关,参考 MySQL 官方手册,这里贴一下链接:https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-model.html


历史


现代 MySQL 版本中的 InnoDB 在历史上叫 InnoDB plugin,这个 MySQL 插件在 2008 年被开发出来,直到 2010 在 Oracle 收购了 Sun 公司后,发布的 MySQL5.5 才正式使用 InnoDB plugin 替代了旧版本的 InnoDB,至此 「备胎」成功转正成为 MySQL 的御用引擎而不再是插件,你看一个插件都这么努力。



特点


采用多版本并发控制(MVCC,MultiVersion Concurrency Control)来支持高并发。并且实现了四个标准的隔离级别,通过间隙锁next-key locking策略防止幻读的出现。


引擎的表基于聚簇索引建立,聚簇索引对主键查询有很高的性能。不过它的二级索引secondary index非主键索引中必须包含主键列,所以如果主键列很大的话,其他的所有索引都会很大。因此,若表上的索引较多的话,主键应当尽可能的小。另外 InnoDB 的存储格式是平台独立。


InnoDB 做了很多优化,比如:磁盘读取数据方式采用的可预测性预读、自动在内存中创建 hash 索引以加速读操作的自适应哈希索引(adaptive hash index),以及能够加速插入操作的插入缓冲区(insert buffer)等。


InnoDB 通过一些机制和工具支持真正的热备份,MySQL 的其他存储引擎不支持热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。


MyISAM 存储引擎的特点和应用场景?


MyISAM 是 MySQL 5.1 及之前的版本的默认的存储引擎。MyISAM 提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等,但 MyISAM 不「支持事务和行级锁」,对于只读数据,或者表比较小、可以容忍修复操作,依然可以使用它。


特性


MyISAM「不支持行级锁而是对整张表加锁」。读取时会对需要读到的所有表加共享锁,写入时则对表加排它锁。但在表有读取操作的同时,也可以往表中插入新的记录,这被称为并发插入。


MyISAM 表可以手工或者自动执行检查和修复操作。但是和事务恢复以及崩溃恢复不同,可能导致一些「数据丢失」,而且修复操作是非常慢的。


对于 MyISAM 表,即使是BLOBTEXT等长字段,也可以基于其前 500 个字符创建索引,MyISAM 也支持「全文索引」,这是一种基于分词创建的索引,可以支持复杂的查询。


如果指定了 DELAY_KEY_WRITE 选项,在每次修改执行完成时,不会立即将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入磁盘。这种方式可以极大的提升写入性能,但是在数据库或者主机崩溃时会造成「索引损坏」,需要执行修复操作。


InnoDB 与 MyISAM 对比


说了这么多估计看一眼也没记住,给你一张表,简单罗列两种引擎的主要区别,如下图。


其他存储引擎


MySQL 还支持其他一些存储引擎,比如 memory 引擎、NDB 集群引擎、CSV 引擎,由于这些引擎没有上述 InnoDB 和 MyISAM 常用,这里不作介绍,感兴趣可以去翻 MySQL 文档了解。这里同样给出官方链接:https://dev.mysql.com/doc/refman/5.7/en/storage-engines.html


再说两句


这一篇是 MySQL 基础篇,我力求用通俗易懂和图表结合的形式给大家梳理这块知识,越是基础和底层的知识越容易被考察掌握程度,以上知识点都可能成为面试中的一个考察点,相信看完对 MySQL 事务和存储引擎应该有一个比较完整的理解。


最后,感谢各位的阅读,文章的目的是分享对知识的理解,若文中出现明显纰漏也欢迎指出,我们一起在探讨中学习。


我是 lemon ,一个混迹互联网大厂的程序员。原创不易,各位的赞同是对我持续创作的最大支持。



文章每周持续更新,可以微信搜或扫码我的公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇)




发布于: 2020 年 05 月 30 日阅读数: 248
用户头像

公众号「后端技术学堂」作者 2020.05.16 加入

作者 lemon 是某一线互联网大厂高级工程师,从事编程工作多年,分享后端技术学习Linux/C++/Python/Go和后端组件、架构等方面的理解,用心写好每篇技术文章,敬请关注。

评论 (4 条评论)

发布
用户头像
能把工资减两个0吗?看着很虐
2020 年 05 月 31 日 10:01
回复
不可以
2020 年 06 月 02 日 23:31
回复
用户头像
一致性。少了“一”。

2020 年 05 月 30 日 15:54
回复
笔误,已修正。感谢指正。
2020 年 05 月 30 日 16:04
回复
没有更多了
原创 面试官:你说对MySQL事务很熟?那我问你10个问题