最近后台好多小伙伴发私信咨询阿Q,问马上就是金九银十的面试黄金期了,该如何在三个月内突击一下,以便更好地通过面试呢?
阿Q的想法就是需要对自己掌握的知识进行归纳整理,系统的、分类的去复习相关的知识点。这样也能更好的把自己陌生的内容框出来,在暴漏自己短板的同时,更针对性的做准备。
为了节省大家的时间,阿Q决定帮大家先将 MySQL 面试相关的知识进行汇总整理,一改往日知识点东一棒槌西一榔头的问题。那么,你准备好迎接 MySQL 连环炮了嘛?
1、三大范式
2、DML 语句和 DDL 语句区别
3、主键和外键的区别
4、drop、delete、truncate 区别
5、基础架构
6、MyISAM 和 InnoDB 有什么区别?
7、推荐自增id作为主键问题
8、为什么 MySQL 的自增主键不连续
9、redo log 是做什么的?
10、redo log 的刷盘时机
11、redo log 是怎么记录日志的
12、什么是 binlog
13、binlog 记录格式
14、binlog 写入机制
15、redolog 和 binlog 的区别是什么
16、两阶段提交
17、什么是 undo log.
18、什么是 relaylog
19、索引
20、Hash 索引
21、B树和B+ 树
22、主键索引
23、二级索引
24、聚簇索引与非聚簇索引
25、回表
26、覆盖索引和联合索引
27、最左前缀匹配原则
28、索引下推
29、隐式转换
30、普通索引和唯一索引该怎么选择?
31、避免索引失效
32、建立索引的规则
33、事务极其特性
34、并发事务带来的问题
35、事务的隔离级别
36、MVCC
37、Mysql 中的锁
38、查询语句执行过程
39、更新语句执行过程
40、sql 优化
41、主从同步数据
42、主从延迟要怎么解决
43、为什么不要使用长事务
1NF(第一范式):属性(对应于表中的字段)不能再被分割,也就是这个字段只能是一个值,不能再分为多个其他的字段了。1NF 是所有关系型数据库的最基本要求 ,也就是说关系型数据库中创建的表一定满足第一范式。
2NF(第二范式):2NF 要求数据库表中的每个实例或行必须可以被惟一地区分,2NF 在 1NF 的基础上增加了一个列,这个列称为主键,非主属性都依赖于主键。
3NF(第三范式):3NF 在 2NF 的基础之上,要求每列都和主键列直接相关,而不是间接相关,即不存在其他表的非主键信息。
在开发过程中,并不一定要满足三大范式,有时候为了提高查询效率,可以在表中冗余其他表的字段。
一般来说:drop > truncate > delete
下图是 MySQL 的一个简要架构图,从下图你可以很清晰的看到客户端的一条 SQL 语句在 MySQL 内部是如何执行的。
MySQL 5.5 之前,MyISAM 引擎是 MySQL 的默认存储引擎,MySQL 5.5 版本之后,InnoDB 是 MySQL 的默认存储引擎。
MyISAM 只有表级锁,而 InnoDB 支持行级锁和表级锁,默认为行级锁。
具体的锁详情请参考阿Q的此篇文章:面试必备常见存储引擎与锁的分类,请查收
MyISAM 不提供事务支持,InnoDB 提供事务支持,实现了 SQL 标准定义的四个隔离级别,具有提交和回滚事务的能力。
InnoDB 默认使用的 REPEATABLE-READ(可重读)隔离级别是可以解决幻读问题发生的(基于 MVCC 和 Next-Key Lock)。
关于 MySQL 事务以及解决幻读的详细介绍,可以看看阿Q写的这篇文章:InnoDB 解决幻读的方案--LBCC&MVCC
MyISAM 不支持,而 InnoDB 支持。
MyISAM 不支持,而 InnoDB 支持。使用 InnoDB 的数据库在异常崩溃后,数据库重新启动的时候会保证数据库恢复到崩溃前的状态。这个恢复的过程依赖于 redo log 。
MyISAM 不支持,而 InnoDB 支持。
虽然 MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作为索引结构,但是两者的实现方式不太一样。
InnoDB 的性能比 MyISAM 更强大,不管是在读写混合模式下还是只读模式下,随着 CPU 核数的增加,InnoDB 的读写能力呈线性增长。MyISAM 因为读写不能并发,它的处理能力跟核数没关系。
InnoDB 和 MyISAM 性能对比
redo log(重做日志)是InnoDB存储引擎独有的,它让MySQL拥有了崩溃恢复能力。
比如 MySQL 实例挂了或宕机了,重启时,InnoDB存储引擎会使用redo log恢复数据,保证数据的持久性与完整性。
更新表数据的时候,如果发现 Buffer Pool 里存在要更新的数据,就直接在 Buffer Pool 里更新。然后会把“在某个数据页上做了什么修改”记录到重做日志缓存(redo log buffer)里,接着刷盘到 redo log 文件里。
InnoDB 存储引擎为 redo log 的刷盘策略提供了 innodb_flush_log_at_trx_commit 参数,它支持三种策略
innodb_flush_log_at_trx_commit 参数默认为 1 ,也就是说当事务提交时会调用 fsync(同步操作) 对 redo log 进行刷盘。
另外 InnoDB 存储引擎有一个后台线程,每隔1秒,就会把 redo log buffer 中的内容写到文件系统缓存(page cache),然后调用 fsync 刷盘。
redo log buffer占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动刷盘。
硬盘上存储的 redo log 日志文件不只一个,而是以一个日志文件组的形式出现的,每个的redo日志文件大小都是一样的。
比如可以配置为一组4个文件,每个文件的大小是 1GB,整个 redo log 日志文件组可以记录4G的内容。
它采用的是环形数组形式,从头开始写,写到末尾又回到头循环写,如下图所示。
所以,如果数据写满了但是还没有来得及将数据真正的刷入磁盘当中,那么就会发生「内存抖动」现象,从肉眼的角度来观察会发现 mysql 会宕机一会儿,此时就是正在刷盘了。
binlog 是归档日志,属于 Server 层的日志,是一个二进制格式的文件,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”。
不管用什么存储引擎,只要发生了表数据更新,都会产生 binlog 日志。它的主要作用就是数据备份、主从复制。
binlog会记录所有涉及更新数据的逻辑操作,属于逻辑日志,并且是顺序写。
binlog 日志有三种格式,可以通过binlog_format参数指定。
事务执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache写到binlog文件中。
因为一个事务的binlog不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为binlog cache。
我们可以通过binlog_cache_size参数控制单个线程 binlog cache 大小,如果存储内容超过了这个参数,就要暂存到磁盘(Swap)。
binlog 也提供了 sync_binlog 参数来控制写入 page cache 和磁盘的时机:
假设执行 sql 过程中写完 redo log 日志后,binlog 日志写期间发生了异常,会出现什么情况呢?
由于 binlog 没写完就异常,这时候 binlog 里面没有对应的修改记录。因此,之后用 binlog 日志恢复数据时,就会少这一次更新,最终数据不一致。
为了解决两份日志之间的逻辑一致问题,InnoDB 存储引擎使用两阶段提交方案。
将 redo log 的写入拆成了两个步骤 prepare 和 commit,这就是两阶段提交。使用两阶段提交后,写入 binlog 时发生异常也不会有影响,因为 MySQL 根据 redo log日志恢复数据时,发现 redo log 还处于 prepare 阶段,并且没有对应 binlog 日志,就会回滚该事务。
再看一个场景,redo log 设置 commit 阶段发生异常,那会不会回滚事务呢?
并不会回滚事务,虽然 redo log 是处于 prepare 阶段,但是能通过事务id找到对应的 binlog 日志,所以 MySQL 认为是完整的,就会提交事务恢复数据。
我们知道如果想要保证事务的原子性,就需要在异常发生时,对已经执行的操作(INSERT、DELETE、UPDATE)进行回滚,在 MySQL 中,恢复机制是通过回滚日志(undo log) 实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后再执行相关的操作。
每次对记录进行改动都会记录一条 undo log,每条 undo log 也都有一个DB_ROLL_PTR属性,可以将这些 undo log 都连起来,串成一个链表,形成版本链。
版本链的头节点就是当前记录最新的值。
relaylog 是中继日志,在主从同步的时候使用到,它是一个中介临时的日志文件,用于存储从 master 节点同步过来的 binlog 日志内容。
master 主节点的 binlog 传到 slave 从节点后,被写入 relay log 里,从节点的 slave sql 线程从 relaylog 里读取日志然后应用到 slave 从节点本地。
从服务器 I/O 线程将主服务器的二进制日志读取过来记录到从服务器本地文件,然后 SQL 线程会读取 relay-log 日志的内容并应用到从服务器,从而使从服务器和主服务器的数据保持一致。
索引其实是一种数据结构,能够帮助我们快速的检索数据库中的数据。
索引的作用就相当于书的目录。打个比方: 我们在查字典的时候,如果没有目录,那我们就只能一页一页的去找我们需要查的那个字,速度很慢。如果有目录了,我们只需要先去目录里查找字的位置,然后直接翻到那一页就行了。
哈希表是键值对的集合,通过键(key)即可快速取出对应的值(value),因此哈希表可以快速检索数据(接近 O(1))。
但是!哈希算法有个 Hash 冲突问题,也就是说多个不同的 key 最后得到的 index 相同。通常情况下,我们常用的解决办法是 链地址法。
链地址法就是将哈希冲突数据存放在链表中。就比如 JDK1.8 之前 HashMap 就是通过链地址法来解决哈希冲突的。不过,JDK1.8 以后 HashMap 为了减少链表过长的时候搜索时间过长引入了红黑树。
为了减少 Hash 冲突的发生,一个好的哈希函数应该“均匀地”将数据分布在整个可能的哈希值集合中。
既然哈希表这么快,为什么 MySQL 没有使用其作为索引的数据结构呢? 主要是因为 Hash 索引不支持顺序和范围查询。假如我们要对表中的数据进行排序或者进行范围查询,那 Hash 索引可就不行了,并且每次 IO 只能取一个。
数据表的主键列使用的就是主键索引,一种特殊的唯一索引。
在 MySQL 的 InnoDB 的表中,当没有显示的指定表的主键时,InnoDB 会自动先检查表中是否有唯一索引且不允许存在 null 值的字段,如果有,则选择该字段为默认的主键,否则 InnoDB 将会自动创建一个 6Byte 的自增主键。
二级索引又称为辅助索引,是因为二级索引的叶子节点存储的数据是主键。也就是说,通过二级索引,可以定位主键的位置。
唯一索引,普通索引,前缀索引等索引属于二级索引。
MySQL 中的全文索引,有两个变量,最小搜索长度和最大搜索长度,对于长度小于最小搜索长度和大于最大搜索长度的词语,都不会被索引。
聚簇索引即索引结构和数据一起存放的索引,并不是一种单独的索引类型。InnoDB 的主键索引的叶子节点中存放的就是数据行,所以它属于聚簇索引。
在 MySQL 中,InnoDB 引擎的表的 .ibd 文件就包含了该表的索引和数据,对于 InnoDB 引擎表来说,该表的索引(B+树)的每个非叶子节点存储索引,叶子节点存储索引和索引对应的数据。
非聚簇索引即索引结构和数据分开存放的索引,并不是一种单独的索引类型。二级索引(辅助索引)就属于非聚簇索引。MySQL 的 MyISAM 引擎,不管主键还是非主键,使用的都是非聚簇索引。
辅助索引是我们人为创建的索引,它的叶子节点中存放的是主键,当我们通过辅助索引查找到主键之后,再通过查找的主键去回表查找主键索引。
回表就是先通过数据库索引扫描出该索引树中数据所在的行,取到主键 id,再通过主键 id 取出主键索引数中的数据,即基于非主键索引的查询需要多扫描一棵索引树。
如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。指的是通过索引就能查询到我们所需要的数据,而不需要根据索引再去查询数据表中的数据( 回表),这样就减少了数据库的 io 操作,提高查询效率。
使用表中的多个字段创建索引,就是联合索引,也叫组合索引或复合索引。
最左前缀匹配原则指的是在使用联合索引时,MySQL 会根据联合索引中的字段顺序,从左到右依次到查询条件中去匹配,如果查询条件中存在与联合索引中最左侧字段相匹配的字段,则就会使用该字段过滤一批数据,直至联合索引中全部字段匹配完成,或者在执行过程中遇到范围查询,如 >、<、between 和 以%开头的like查询 等条件,才会停止匹配。
所以,我们在使用联合索引时,可以将区分度高的字段放在最左边,这也可以过滤更多数据。
索引下推(Index Condition Pushdown) 是 MySQL 5.6 版本中提供的一项索引优化功能,可以在非聚簇索引遍历过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录,减少回表次数。
当操作符与不同类型的操作数一起使用时,会发生类型转换以使操作数兼容。某些转换是隐式发生的。例如,MySQL 会根据需要自动将字符串转换为数字,反之亦然。以下规则描述了比较操作的转换方式:
当普通索引为条件时查询到数据会一直扫描,直到扫完整张表;
当唯一索引为查询条件时,查到该数据会直接返回,不会继续扫表;
普通索引会直接将操作更新到 change buffer 中,然后结束
唯一索引需要判断数据是否冲突
所以唯一索引更加适合查询的场景,普通索引更适合插入的场景。
索引失效也是慢查询的主要原因之一,常见的导致索引失效的情况有下面这些:
一个事情由n个单元组成,这n个单元在执行过程中,要么同时成功,要么同时失败,这就把n个单元放在了一个事务之中。举个简单的例子:在不考虑试题正确与否的前提下,一张试卷由多个题目构成,当你答完题交给老师的时候是将一整张试卷交给老师,而不是将每道题单独交给老师,在这里试卷就可以理解成一个事务。
事务的特性:
为了解决以上隔离性引发的并发问题,数据库提供了事务的隔离机制。
虽然 serializable 级别可以解决所有的数据库并发问题,但是它会在读取的每一行数据上都加锁,这就可能导致大量的超时和锁竞争问题,从而导致效率下降。所以我们在实际应用中也很少使用 serializable,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。
锁的粒度过大会导致性能的下降, MySQL 的 InnoDB 引擎下存在一种性能更优越的 MVCC 方法。
MVCC 是 Multi-Version Concurremt Control 的简称,意思是基于多版本的并发控制协议,通过版本号避免同一数据在不同事务间的竞争。它主要是为了提高数据库的并发读写性能,不用加锁就能让多个事务并发读写。
MVCC 的实现依赖于隐藏列、Undo log、 Read View 。
从上面对 SQL 标准定义了四个隔离级别的介绍可以看出,标准的 SQL 隔离级别定义里,REPEATABLE-READ(可重复读)是不可以防止幻读的。
但是 InnoDB 实现的 REPEATABLE-READ 隔离级别其实是可以解决幻读问题发生的,主要有下面两种情况:
InnoDB 存储引擎在分布式事务的情况下一般会用到 SERIALIZABLE 隔离级别。
关于 MySQL 事务以及解决幻读的详细介绍,可以看看阿Q写的这篇文章:InnoDB 解决幻读的方案--LBCC&MVCC
具体的锁详情请参考阿Q的此篇文章:面试必备常见存储引擎与锁的分类,请查收
select * from tb_student s where s.age='18' and s.name=' 张三 ';
查询语句的执行流程如下:权限校验(如果命中缓存)--->查询缓存--->分析器--->优化器--->权限校验--->执行器--->引擎
update tb_student A set A.age='19' where A.name=' 张三 ';
这条语句基本上也会沿着上一个查询的流程走,只不过执行更新的时候要记录日志,这就会引入日志模块了,MySQL 自带的日志模块是 binlog(归档日志) ,所有的存储引擎都可以使用,我们常用的 InnoDB 引擎还自带了一个日志模块 redo log(重做日志),我们就以 InnoDB 模式下来探讨这个语句的执行流程。
更新语句执行流程如下:分析器---->权限校验---->执行器--->引擎---redo log(prepare 状态)---> binlog --->redo log(commit状态)
对字段进行 null 值判断,
使用!=或<>
or 来连接条件(使用union all代替)
in 和 not in 也要慎用
不要使用模糊查询(可用全文索引)
减少表达式操作
函数操作
同步策略:
好看的皮囊千篇一律,有趣的灵魂万里挑一,让我们在冷漠的城市里相互温暖,我是阿Q,我们下期再见!
名称栏目:面试官:MySQL连环炮,你扛得住嘛?
文章起源:http://www.mswzjz.cn/qtweb/news21/422671.html
攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能