系列博客目录
文章目录
- 系列博客目录
- 前言
- [1. 优化](#1. 优化)
-
- [1.1 MySQL中,如何定位慢查询?](#1.1 MySQL中,如何定位慢查询?)
- [1.2 面试官接着问:那这个SQL语句执行很慢,如何分析 ( = 如何优化)呢?](#1.2 面试官接着问:那这个SQL语句执行很慢,如何分析 ( = 如何优化)呢?)
- [1.3 了解过索引吗?(什么是索引)](#1.3 了解过索引吗?(什么是索引))
- [1.4 继续问 索引的底层数据结构了解过吗?](#1.4 继续问 索引的底层数据结构了解过吗?)
- [1.5 什么是聚簇索引(聚集索引),什么是非聚簇索引(二级索引)? 知道什么是回表查询吗? 这两个问题关系很大](#1.5 什么是聚簇索引(聚集索引),什么是非聚簇索引(二级索引)? 知道什么是回表查询吗? 这两个问题关系很大)
- [1.6 知道什么叫覆盖索引吗?](#1.6 知道什么叫覆盖索引吗?)
- [1.7 MySQL超大分页怎么处理?(其实可以使用覆盖索引来解决)](#1.7 MySQL超大分页怎么处理?(其实可以使用覆盖索引来解决))
- [1.8 索引创建原则有哪些?](#1.8 索引创建原则有哪些?)
- [1.9 什么情况下索引会失效?](#1.9 什么情况下索引会失效?)
- [1.10 谈一谈你对sql的优化的经验](#1.10 谈一谈你对sql的优化的经验)
- 2.其他面试题
-
- [2.1 事务的特性是什么?可以详细说一下吗?](#2.1 事务的特性是什么?可以详细说一下吗?)
- [2.2 并发事务带来哪些问题?怎么解决这些问题呢?MSQL的默认隔离级别是?](#2.2 并发事务带来哪些问题?怎么解决这些问题呢?MSQL的默认隔离级别是?)
- [2.3 undo log和redo log的区别](#2.3 undo log和redo log的区别)
- [2.4 面试官接着上面问,事务中的隔离性是如何保证的呢?](#2.4 面试官接着上面问,事务中的隔离性是如何保证的呢?)
- [2.6 MySQL-主从同步原理](#2.6 MySQL-主从同步原理)
- [2.7 MySQL-22. 你们项目用过MySQL的分库分表吗?](#2.7 MySQL-22. 你们项目用过MySQL的分库分表吗?)
前言
项目中不可能不用数据库,mysql是最常见的数据库。这一篇分为两部分。
1. 优化
1.1 MySQL中,如何定位慢查询?
- 聚合查询
- 多表查询
- 表数据量过大查询
- 深度分页查询
表象:页面加载过慢、接口压测响应时间过长(超过1s)
那你如何知道是sql的问题,如果是sql的问题,是哪个sql的问题呢?
方案一:开源工具
调试工具:Arthas
运维工具:Prometheus、Skywalking (如下图所示,红框中是每个接口的执行时间,再下面是接口中每个方法以及sql语句的执行时间)
方案二:MySQL自带慢日志
慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志如果要开启慢查询日志,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:
bash
#开启MySQL慢日志查询开关
slow query log=1
#设置慢目志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询目志
long query time=2
配置完毕之后,通过以下指令重新启动MySQL服务器进行测试,查看日志文件中记录的信息
/var/lib/mysql/localhost-slow.log
.
面试文稿
MySQL中,如何定位慢查询?
候选人:
嗯~,我们当时做压测的时候有的接口非常的慢,接口的响应时间超过了2秒以上,因为我们当时的系统部署了运维的监控系统Skywalking,在展示的报表中可以看到是哪一个接口比较慢,并且可以分析这个接口哪部分比较慢,这里可以看到SQL的具体的执行时间,所以可以定位是哪个sql出了问题。
如果,项目中没有这种运维的监控系统,其实在MySQL中也提供了慢日志查询的功能,可以在MySOL的系统配置文件中开启这个慢日志的功能,并且也可以设置SQL执行超过多少时间来记录到一个日志文件中,我记得上一个项目配置的是2秒,只要SQL执行的时间超过了2秒就会记录到日志文件中,我们就可以在日志文件找到执行比较慢的SOL
1.2 面试官接着问:那这个SQL语句执行很慢,如何分析 ( = 如何优化)呢?
- 聚合查询
- 多表查询
- 表数据量过大查询
这三个都可以通过SQL执行计划 (来找到慢的原因)。
比如:可以采用EXPLAIN 或者 DESC命令获取 MySQL 如何执行 SELECT 语句的信息
我们需要掌握的字段如下,也是面试官喜欢问的字段:一共五个。
如果是Using index condition,说明索引的使用可以优化。
面试文稿
那这个SQL语句执行很慢,如何分析呢?
候选人:如果一条sql执行很慢的话,我们通常会使用mysql自动的执行计划explain来去查看这条sql的执行情况,比如在这里面可以通过key和key_len检查是否命中了索引,如果本身已经添加了索引,也可以判断索引是否有失效的情况,第二个,可以通过type字段查看sq!是否有进一步的优化空间,是否存在全索引扫描或全盘扫描,第三个可以通过extra建议来判断,是否出现了回表的情况,如果出现了可以尝试添加索引或修改返回字段来修复
1.3 了解过索引吗?(什么是索引)
定义:索引(index)是帮助MSQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构(B+树),这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
1.4 继续问 索引的底层数据结构了解过吗?
MySQL默认使用的索引底层数据结构是B+树。在聊B+树之前,我们先聊聊二叉树和B树。
二叉树会变坏,不行。红黑树如果加入节点过多,查找一个值,需要经过太多层级。有没有更好的数据结构呢,B树。
B-Tree,B树是一种多叉路衡查找树,相对于二叉树,B树每个节点可以有多个分支,即多叉以一颗最大度数(max-degree)为5(5阶)的b-tree为例,那这个B树每个节点最多存储4个key。
但是B树是矮胖树。
B+Tree是在BTree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构。非叶子节点不存数据,只存指针。数据都在叶子节点上。
B树与B+树对比:
- 磁盘读写代价B+树更低;经过非叶子节点时候,上面没有数据。
- 查询效率B+树更加稳定; 都要到叶子节点才能获取结果,所以比较稳定,变化不大。
- B+树便于扫库和区间查询。比如要查找6到34的数据,我们从根节点,找到6后,就可以根据双向指针,轻松找到34之间的数据。
面试文稿
-
了解过索引吗?(什么是索引)
候选人:嗯,索引在项目中非常常见,它是一种帮助MySQL高效获取数据的数据结构,主要用来提高数据检索效率,降低数据库的I/O成本。同时,索引列可以对数据进行排序,降低数据排序的成本,也能减少CPU的消耗。
-
索引的底层数据结构了解过吗?
候选人:MySQL的默认的存储引擎ImnoDB采用的B+树的数据结构来存储索引,选择B+树的主要的原因是:第一阶数更多,路径更短,第二个磁盘读写代价B+树更低,非叶子节点只存储指针,叶子阶段存储数据,第三是B+树便于扫库和区间查询,叶子节点是一个双向链表。
-
B树和B+树的区别是什么呢?
候选人:第一:在B树中,非叶子节点和叶子节点都会存放数据,而B+树的所有的数据都会出现在叶子节点,在查询的时候,B+树查找效率更加稳定
第二:在进行范围查询的时候,B+树效率更高,因为B+树都在叶子节点存储,并且叶子节点是一个双向链表
1.5 什么是聚簇索引(聚集索引),什么是非聚簇索引(二级索引)? 知道什么是回表查询吗? 这两个问题关系很大
不是所有字段都可以当聚集索引。
聚集索引选取规则:
- 如果存在主键,主键索引就是聚集索引。
- 如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引。
- 如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引。
举个详细的例子 如下:row代表一整行的数据,不仅是一个Id这种单个数据。
回表查询:因为二级索引中存放的只有一个Id,所以得再去聚集索引中找。
面试文稿
-
什么是聚簇索引什么是非聚簇索引?
候选人:聚簇索引是指数据与索引放在一起,B+树的叶子节点保存了整行数据,通常只有一个聚簇索引,一般是由主键构成。
非聚簇索引则是数据与索引分开存储,B+树的叶子节点保存的是主键值,可以有多个非聚簇索引,通常我们自定义的索引都是非聚簇索引。
-
知道什么是回表查询吗?面试官问到回表,需要把上面的问题也答出来。
候选人:回表查询是指通过二级索引找到对应的主键值,然后再通过主键值查询聚簇索引中对应的整行数据的过程。
1.6 知道什么叫覆盖索引吗?
覆盖索引是指查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到。
上面有两种索引,其中二级索引以name进行关联。
第一个是从聚集索引找,第二个是从二级索引找,但是二级索引包含主键值,所以也是覆盖索引。第三个是从二级索引找,但是不包含gender信息,所以不是覆盖索引,拿到Id值去聚集索引再进行查表,这就发生了回表查询。
面试文稿
知道什么叫覆盖索引吗?
候选人:覆盖索引是指在SELECT查询中,返回的列全部能在索引中找到,避免了回表查询,提高了性能。使用覆盖索引可以减少对主键索引的查询次数,提高查询效率。
1.7 MySQL超大分页怎么处理?(其实可以使用覆盖索引来解决)
在数据量比较大时,如果进行limit分页查询,在查询时,越往后,分页查询效率越低。
我们一起来看看执行limit分页查询耗时对比:
因为,当在进行分页查询时,如果执行limit 9000000,10,此时需要MySQL排序前9000010 记录,仅仅返回9000000-9000010的记录,其他记录丢弃,查询排序的代价非常大。
如何解决?
优化思路: 一般分页查询时,通过创建 覆盖索引 能够比较好地提高性能,可以通过覆盖索引 加子查询 形式进行优化
效果如下:
面试文稿
MySQL超大分页怎么处理?
候选人:超大分页通常发生在数据量大的情况下,使用LIMIT分页查询且需要排序时效率较低。可以通过覆盖索引和子查询来解决。先分页查询数据的id字段,确定了id之后,再用子查询来过滤,只查询这个id列表中的数据就可以了,因为查询id时使用的是覆盖索引,所以效率可以提升。
1.8 索引创建原则有哪些?
回答时候,先陈述自己在实际的工作中是怎么用的,比如有哪些索引,包括主键索引,唯一索引,然后根据自己的业务创建的索引(一般是复合索引)。
索引创建原则有哪些?
-
针对于数据量较大,且查询比较频繁的表建立索引。比如:我有个单表超过10万数据(增加用户体验)
-
针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。
-
尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
-
如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
-
尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。比如下面有三个字段,就是联合索引。
-
要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。(增删改的时候需要修改索引)
-
如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。
总结
索引创建原则有哪些?
候选人:创建索引的原则包括:
- 表中的数据量超过10万以上时考虑创建索引。重要
- 选择查询频繁的字段作为索引,如查询条件、排序字段或分组字段。重要
- 尽量使用复合索引,覆盖SQL的返回值。重要
- 如果字段区分度不高,可以将其放在组合索引的后面。
- 对于内容较长的字段,考虑使用前缀索引。
- 控制索引数量,因为索引虽然可以提高查询速度,但也会影响插入、更新的速度。重要
面试文稿
面试官:索引创建原则有哪些?
候选人:嗯,这个情况有很多,不过都有一个大前提,就是表中的数据要超过10万以上,我们才会创建索引,并且添加索引的字段是查询比较频繁的字段,一般也是像作为查询条件,排序字段或分组的字段这些。
还有就是,我们通常创建索引的时候都是使用复合索引来创建,一条sql的返回值,尽量使用覆盖索引,如果字段的区分度不高的话,我们也会把它放在组合索引后面的字段。
如果某一个字段的内容较长,我们会考虑使用前缀索引来使用,当然并不是所有的字段都要添加索引,这个索引的数量也要控制,因为添加索引也会导致新增改的速度变慢。
1.9 什么情况下索引会失效?
索引失效的情况有很多,可以说一些自己遇到过的,不要张口就得得得说一堆背诵好的面试题(适当的思考一下,回想一下,更真实)。
举例:给tb_seller创建联合索引,字段顺序:name,status,address。
那快速判断索引是否失效了呢? 执行计划explain
下面说几种索引失效的情况:
第一种:
第二种:
根据前面的两个字段 name,status 查询是走索引的,但是最后一个条件address 没有用到索引。
第三种:
第四种:
由于,在查询是,没有对字符串加单引号,MySQL的查询优化器,会自动的进行类型转换,造成索引失效。
第五种:
面试文稿
面试官:什么情况下索引会失效?
候选人:嗯,这个情况比较多,我说一些自己的经验,以前遇到过的
比如,索引在使用的时候没有遵循最左匹配法则,第二个是,模糊查询,如果%号在前面也会导致索引失效。如果在添加索引的字段上进行了运算操作或者类型转换也都会导致索引失效。
我们之前还遇到过一个就是,如果使用了复合索引,中间使用了范围查询,右边的条件索引也会失效
所以,通常情况下,想要判断出这条sql是否有索引失效的情况,可以使用explain执行计划来分析
1.10 谈一谈你对sql的优化的经验
可以谈很多方面。
- 表的设计优化
- 索引优化 参考优化创建原则和索引失效
- SQL语句优化
- 主从复制、读写分离
- 分库分表 后面有专门章节介绍
-
表的设计优化(参考阿里开发手册《嵩山版》)
(1)比如设置合适的数值(tinyint int bigint),要根据实际情况选择。(根据内容的长度选择不同的类型,节省成本,减小开销)。
(2)比如设置合适的字符串类型(char和varchar)char定长效率高,varchar可变长度,效率稍低。
-
SQL语句优化
(1) SELECT语句务必指明字段名称(避免直接使用select*)。
(2) SQL语句要避免造成索引失效的写法。
(3) 尽量用union all代替union union会多一次过滤,效率低。
(4)避免在where子句中对字段进行表达式操作。
(5) Join优化 能用inner join 就不用left join 或者right join,如必须使用的话,一定要以小表为驱动内连接会对两个表进行优化,优先把小表放到外边,把大表放到里边。left join 或 right join,不会重新调整顺序。
-
主从复制、读写分离
如果数据库的使用场景读的操作比较多的时候,为了避免写的操作所造成的性能影响 可以采用读写分离的架构。
读写分离解决的是,数据库的写入,影响了查询的效率。
面试文稿
面试官:sql的优化的经验
候选人:嗯,这个在项目还是挺常见的,当然如果直说sq!优化的话,我们会从这几方面考虑,比如建表的时候、使用索引、sql语句的编写、主从复制,读写分离,还有一个是如果量比较大的话,可以考虑分库分表
面试官:创建表的时候,你们是如何优化的呢?
候选人:这个我们主要参考的阿里出的那个开发手册《嵩山版》,就比如,在定义字段的时候需要结合字段的内容来选择合适的类型,如果是数值的话,像tinyint、int、bigint这些类型,要根据实际情况选择。如果是字符串类型,也是结合存储的内容来选择char和varchar或者text类型
面试官:那在使用索引的时候,是如何优化呢?
候选人:【参考索引创建原则 进行描述】
面试官:你平时对sql语句做了哪些优化呢?
候选人:嗯,这个也有很多,比如SELECT语句务必指明字段名称,不要直接使用select*,还有就是要注意SQL语句避免造成索引失效的写法;如果是聚合查询,尽量用union all代替union,union会多一次过滤,效率比较低;如果是表关联的话,尽量使用inner join,不要使用用left join或者 right join,如必须使用 一定要以小表为驱动。
2.其他面试题
2.1 事务的特性是什么?可以详细说一下吗?
先回忆一下事务的定义。
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
事务的特性就是ACID。
ACID是什么?可以详细说一下吗?
- 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
- 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
- 隔离性(lsolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
- 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。(也就是落盘的操作,即保存在磁盘之中)
面试文稿
面试官:事务的特性是什么?可以详细说一下吗?
候选人:嗯,这个比较清楚,ACID,分别指的是:原子性、一致性、隔离性、持久性;我举个例子:A向B转账500,转账成功,A扣除500元,B增加500元,原子操作体现在要么都成功,要么都失败在转账的过程中,数据要一致,A扣除了500,B必须增加500在转账的过程中,隔离性体现在A像B转账,不能受其他事务干扰在转账的过程中,持久性体现在事务提交后,要把数据持久化(可以说是落盘操作)。
2.2 并发事务带来哪些问题?怎么解决这些问题呢?MSQL的默认隔离级别是?
并发事务问题:脏读、不可重复读、幻读
隔离级别:读未提交、读已提交、可重复读、串行化
面试官并不会让你回答所有,你回答了哪个问题,面试官会追问你怎样解决这个问题。我们再回答隔离级别的时候,就又会被追问MySQL的默认隔离级别是什么。
事务并发问题
解决问题的方法
解决方案:对事务进行隔离 x表示可以解决
注意:事务隔离级别越高,数据越安全,但是性能越低。(串行化执行事务,性能肯定很低)
面试文稿
面试官:并发事务带来哪些问题?
候选人:我们在项目开发中,多个事务并发进行是经常发生的,并发也是必然的,有可能导致一些问题
- 第一是脏读,当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是"脏数据",依据"脏数据"所做的操作可能是不正确的。
- 第二是不可重复读:比如在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
- 第三是幻读(Phantomread):幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据接着另一个并发事务(T2)插入了一些数据时。在随后的査询中,第一个事务(T1)就会发现多了些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
面试官:怎么解决这些问题呢?MySQL的默认隔离级别是?
候选人:解决方案是对事务进行隔离,MySQL支持四种隔离级别,分别有:
- 第一个是,未提交读(readuncommitted)它解决不了刚才提出的所有问题,一般项目中也不用这个。
- 第二个是读已提交(read committed)它能解决脏读的问题的,但是解决不了不可重复读和幻读。
- 第三个是可重复读(repeatable read)它能解决脏读和不可重复读,但是解决不了幻读,这个也是mysql默认的隔离级别。
- 第四个是串行化(serializable)它可以解决刚才提出来的所有问题,但是由于让是事务串行执行的,性能比较低。所以,我们一般使用的都是mysql默认的隔离级别:可重复读
2.3 undo log和redo log的区别
- 缓冲池(buffer pool):主内存中的一个区域,里面可以缓存磁盘上经常操作的真实数据,在执行增删改査操作时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓存),以一定频率刷新到磁盘,从而减少磁盘10,加快处理速度
- 数据页(page):是InnoDB 存储引擎磁盘管理的最小单元,每个页的大小默认为 16KB。页中存储的是行数据。(每个页的数据分部分的放入缓冲池中)
如果我在缓冲池中的操作没有有效的存到磁盘中,那样就做不到数据的持久化了。
重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log file),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都(同步的)存到该日志文件中,用于在刷新脏页到磁盘,发生错误时,进行数据恢复使用。
undo log
回滚日志,用于记录数据被修改前的信息,作用包含两个:提供回滚 和 MVCC(多版本并发控制)。undo log和redo log记录物理日志不一样,它是逻辑日志。
可以认为当delete一条记录时,undolog中会记录一条对应的insert记录,反之亦然当update一条记录时,它记录一条对应相反的update记录。当执行roll back时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。
面试文稿
面试官:undolog和redolog的区别
候选人:好的,其中redo log日志记录的是数据页的物理变化,服务宕机可用来同步数据,而undo log不同,它主要记录的是逻辑日志,当事务回滚时,通过逆操作恢复原来的数据,比如我们删除一条数据的时候,就会在undolog日志文件中新增一条delete语句,如果发生回滚就执行逆操作;
redo log保证了事务的持久性,undolog保证了事务的原子性和一致性
2.4 面试官接着上面问,事务中的隔离性是如何保证的呢?
- 锁:排他锁(如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁)(比如我们之前的insert,delete等操作)
- mvcc:多版本并发控制
这时候会再问什么是mvcc?
解释-下MVCC
全称 Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突。
MVCC的具体实现,主要依赖于数据库记录中的隐式字段 、undo log日志 、readView。
MVCC如何解决下面图中问题。
-
记录中的隐藏字段
-
undo log 回滚日志
回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志。
当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。
而update、delete的时候,产生的undo log日志不仅在回滚时需要,mvcc版本访问也需要,不会立即被删除。
-
readview
ReadView(读视图)是 快照读 SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
- 当前读
读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:select ... lock in share mode(共享锁),select . for update、update、insert、delete(排他锁)都是一种当前读。 - 快照读
简单的selec(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。- Read Committed:每次select,都生成一个快照读。(没解决不可重复读,所以每次都是一个快照读)
- Repeatable Read:开启事务后第一个select语句才是快照读的地方。(别管查多少次都是相同的结果,都叫快照读)
上图中,m_ids是 3,4,5,因为事务2已经提交。然后min是3,max是5+1是6,creator是5。
不同的隔离级别,生成ReadView的时机不同
- READ COMMITTED :在事务中每一次执行快照读时生成ReadView。
- REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。
从记录中可以看出,最新的事务号是4,先用4从右边四个判断条件中进行判断,没有满足的话,根据历史链,查3,2,1。
面试文稿
面试官:事务中的隔离性是如何保证的呢?(你解释一下MVCC)
候选人:事务的隔离性是由锁和mvcc实现的。
其中mvcc的意思是多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,它的底层实现主要是分为了三个部分,第一个是隐藏字段,第二个是undo log日志,第三个是readView读视图。
隐藏字段是指:在mysql中给每个表都设置了隐藏字段,有一个是trx_id(事务id),记录每一次操作的事务id,是自增的;另一个字段是roll-pointer(回滚指针),指向上一个版本的事务版本记录地址。
undolog主要的作用是记录回滚日志,存储老版本数据,在内部会形成一个版本链,在多个事务并行探作某一行记录,记录不同事务修改数据的版本,通过roll_pointer指针形成一个链表。
readView解决的是一个事务查询选择版本的问题,在内部定义了一些匹配规则和当前的一些事务id判断该访问那个版本的数据,不同的隔离级别快照读是不一样的,最终的访问的结果不一样。如果是rc隔离级别,每一次执行快照读时生成ReadView,如果是rr隔离级别仅在事务中第一次执行快照读时生成ReadView,后续复用
2.6 MySQL-主从同步原理
这个问题被提到是因为你前面提到过,主从同步,或者你在简历上说过主从这个事情。如果一个项目上线了,一般都是主从的架构。
主库写数据的时候需要同步到从库中,那么主从是如何同步的呢,如何实现主从复制?
MySQL主从复制的核心就是二进制日志
二进制日志(BINLOG)记录了所有的 DDL(数据定义语言)语句和 DML(数据操纵语言)语句,但不包括数据査询(SELECT、SHOW)语句。
主库将数据的更改写入binlog文件,副库有一个IO线程专门从主库中读取binlog文件写入Relay log,然后SQL线程读取Relay log,把里面的命令重新执行一下,就可以保持主从库的数据一致。
复制分成三步
- Master 主库在事务提交时,会把数据变更记录在二进制
日志文件 Binlog 中。 - 从库读取主库的二进制日志文件 Binlog,写入到从库的
中继日志 Relay Log. - slave重做中继日志中的事件,将改变反映它自己的数据(反映:表示这些操作在从库执行后,最终结果是从库的数据内容、结构与主库一致。)
面试文稿
面试官:说一下主从同步的原理?
候选人:
嗯,好的。MySQL主从复制的核心就是二进制日志,二进制日志记录了所有的DDL语句和 DML语句。
具体的主从同步过程大概的流程是这样的:
- Master 主库在事务提交时,会把数据变更记录在二进制日志文件 Binlog 中。
- 从库读取主库的二进制日志文件 Binlog,写入到从库的中继日志 Relay Log。
- slave重做中继日志中的事件,将改变反映它自己的数据。
2.7 MySQL-22. 你们项目用过MySQL的分库分表吗?
先学习什么情况下需要分库分表,在学习如何具体的分库分表。
现在的主从库数据一样,主要解决访问的压力,解决不了海量数据存储的问题,现在就需要分库分表了,但不是一上来分库分表,是有前提的。
分库分表的时机:
- 前提,项目业务数据逐渐增多,或业务发展比较迅速(,数据增加很多,那样数据库就承受不住了,就需要分库分表)单表的数据量达1000W或20G以后。(因为达到了一定的量级,优化是解决不了问题的)。
- 优化已解决不了性能问题(主从读写分离、查询索引...)。
- IO瓶颈(磁盘IO、网络IO)、CP瓶颈(聚合查询、连接数太多)。
分库分表的策略(一共四种属于两种拆分):
总结
面试文稿
你们项目用过MySQL的分库分表吗?
候选人:我们采用微服务架构,每个微服务对应一个数据库,是根据业务进行拆分的,这个其实就是垂直拆分。
那你之前使用过水平分库吗?
候选人:使用过。当时业务发展迅速,某个表数据量超过1000万,单库优化后性能仍然很慢,因此采用了水平分库。我们首先部署了3台服务器和3个数据库,使用mycat进行数据分片。旧数据也按照ID取模规则迁移到了各个数据库中,这样各个数据库可以分摊存储和读取压力,解决了性能问题。