本文介绍: 大家好,我是贺同学。金三银四到了,给大家整理一些数据库必知必会的面试题。基础相关1、关系型和非关系型数据库区别?关系型数据库的优点容易理解,因为它采用了关系模型组织数据可以保持数据的…

大家好,我是贺同学

金三银四到了,给大家整理一些数据库必知必会的面试题

基础相关

1、关系型和非关系型数据库的区别

关系型数据库的优点

非关系型数据库(NOSQL)的优点

2、详细说一下一条 MySQL 语句执行步骤

Server 层按顺序执行 SQL 的步骤为:

索引相关

3、MySQL 使用索引原因

根本原因

扩展

4、索引三种常见底层数据结构以及优缺点

三种常见索引底层数据结构:分别是哈希表、有序数组搜索树。

5、索引的常见类型以及它是如何发挥作用的?

根据叶子节点内容,索引类型分为主键索引和非主键索引。

6、MyISAM 和 InnoDB 实现 B 树索引方式区别什么

7、InnoDB 为什么设计 B+ 树索引?

两个考虑因素

为什么选择 B+ 树:

8、什么覆盖索引和索引下推?

覆盖索引:

索引下推:

9、哪些操作会导致索引失效

10、字符串加索引

日志相关

11、MySQL 的 change buffer什么

– 反过来,假设一个业务更新模式是写入之后马上会做查询,那么即使满足了条件,将更新记录change buffer,但之后由于马上要访问这个数据页,会立即触发 merge 过程。这样随机访问 IO 的次数不会减少,反而增加了 change buffer 的维护代价。

12、MySQL 是如何判断一行扫描数的?

13、MySQL 的 redo logbinlog 区别

09aa0f3431b3354843f515df0236224a.png

14、为什么需要 redo log

15、为什么 redo log 具有 crash-safe 的能力,是 binlog 无法替代的?

第一点:redo log 可确保 innoDB 判断哪些数据已经刷盘,哪些数据还没有

第二点:如果 redo log 写入失败说明此次操作失败事务也不可能提交

16、当数据库 crash 后,如何恢复未刷盘的数据到内存中?

根据 redo log 和 binlog 的两阶段提交,未持久化的数据分为几种情况:

17、redo log 写入方式

redo log包括两部分内容,分别是内存中的日志缓冲(redo log buffer)和磁盘上的日志文件(redo log file)。

MySQL 每执行一条 DML 语句,会先把记录写入 redo log buffer(用户空间 ,再保存内核空间缓冲区 OS-buffer 中,后续某个时间点再一次性将多个操作记录写到 redo log file(刷盘) 。这种先写日志,再写磁盘的技术,就是WAL

007cb99d2696a4015ed7f4f34719c574.png

可以发现,redo log buffer写入到redo log file,是经过OS buffer中转的。其实可以通过参数innodb_flush_log_at_trx_commit进行配置参数值含义如下

18、redo log 的执行流程?

我们来看下Redo log的执行流程假设执行的 SQL 如下

update T set a =1 where id =666

28ea82b5fd43a8df97601897c0b15941.png

  1. MySQL 客户端请求语句 update T set a =1 where id =666,发往 MySQL Server 层。

  2. MySQL Server 层接收到 SQL 请求后,对其进行分析、优化、执行等处理工作,将生成的 SQL 执行计划发到 InnoDB 存储引擎层执行。

  3. InnoDB 存储引擎层将a修改为1的这个操作记录到内存中。

  4. 记录到内存以后会修改 redo log 的记录,会在添加一行记录,其内容是需要在哪个数据页上做什么修改

  5. 此后,将事务状态设置prepare ,说明已经准备提交事务了。

  6. 等到 MySQL Server 层处理完事务以后,会将事务的状态设置commit,也就是提交该事务。

  7. 收到事务提交请求以后,redo log 会把刚才写入内存中的操作记录写入到磁盘中,从而完成整个日志的记录过程。

19、binlog 的概念什么,起到什么作用, 可以保证 crash-safe 吗?

20、什么是两阶段提交

MySQL 将 redo log 的写入拆成了两个步骤prepare 和 commit,中间再穿插写入binlog,这就是”两阶段提交”。

eaeafc92c982d1b36f0ac8f02cc37fcc.png

而两阶段提交就是让这两个状态保持逻辑上的一致。redolog 用于恢复主机故障时的未更新的物理数据,binlog 用于备份操作。两者本身就是两个独立的个体,要想保持一致,就必须使用分布式事务的解决方案处理

为什么需要两阶段提交呢?

  • 如果不用两阶段提交的话,可能会出现这样情况

  • 先写 redo log,crash 后 bin log 备份恢复时少了一次更新,与当前数据不一致。

  • 先写 bin log,crash 后,由于 redo log 没写入,事务无效,所以后续 bin log 备份恢复时,数据不一致。

  • 两阶段提交就是为了保证 redo log 和 binlog 数据的安全一致性。只有在这两个日志文件逻辑高度一致了才能放心的使用。

在恢复数据时,redolog 状态commit说明 binlog 也成功,直接恢复数据;如果 redolog 是 prepare,则需要查询对应的 binlog事务是否成功,决定是回滚还是执行。

21、MySQL 怎么知道 binlog 是完整的?

一个事务的 binlog 是有完整格式的:

22、什么是 WAL 技术,有什么优点?

WAL,中文全称是 Write-Ahead Logging,它的关键点就是日志先写内存,再写磁盘。MySQL 执行更新操作后,在真正把数据写入到磁盘前,先记录日志

好处是不用每一次操作都实时把数据写盘,就算 crash 后也可以通过redo log 恢复,所以能够实现快速响应 SQL 语句

23、binlog 日志的三种格式

binlog 日志有三种格式

Statement格式

每一条会修改数据的 SQL 都会记录在 binlog 中

  • 优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能

  • 缺点:由于记录的只是执行语句,为了这些语句能在备库上正确运行,还必须记录每条语句在执行的时候的一些相关信息,以保证所有语句能在备库得到和在主库端执行时候相同的结果

Row格式

不记录 SQL 语句上下文相关信息,仅保存哪条记录被修改。

Mixed格式

实际上就是 Statement 与 Row 的结合。一般的语句修改使用 statment 格式保存 binlog,如一些函数statement 无法完成主从复制的操作,则采用 row 格式保存 binlog,MySQL 会根据执行的每一条具体的 SQL 语句来区分对待记录的日志形式。

24、redo log日志格式

9cfcc8a8fec028474b675c2b3590f2c1.png

redo log buffer (内存中)是由首尾相连的四个文件组成的,它们分别是:ib_logfile_1、ib_logfile_2、ib_logfile_3、ib_logfile_4。

25、原本可以执行得很快的 SQL 语句,执行速度却比预期的慢很多,原因是什么?如何解决

原因:从大到小可分为四种情况

  • MySQL 数据库本身被堵住了,比如系统网络资源不够。

  • SQL 语句被堵住了,比如表锁,行锁等,导致存储引擎不执行对应的 SQL 语句。

  • 确实是索引使用不当,没有走索引。

  • 表中数据的特点导致的,走了索引,但回表次数庞大。

解决

26、InnoDB 数据页结构

个数据页大致划分七个部分

数据相关

27、MySQL 是如何保证数据不丢失的?

  • 只要redolog 和 binlog 保证持久化磁盘就能确保MySQL异常重启后回复数据

  • 在恢复数据时,redolog 状态commit 则说明 binlog 也成功,直接恢复数据;如果 redolog 是 prepare,则需要查询对应的 binlog事务是否成功,决定是回滚还是执行。

28、误删数据怎么办?

DBA 的最核心工作就是保证数据的完整性,先要做好预防,预防的话大概是通过这几个点:

29、droptruncatedelete区别

30、在 MySQL 中有两个 kill 命令

kill 不掉的原因

  • kill命令被堵了,还没到位

  • kill命令到位了,但是没被立刻触发

  • kill命令被触发了,但执行完也需要时间

31、如何理解 MySQL 的边读边发

  • 如果客户端接受慢,会导致 MySQL 服务端由于结果发不出去,这个事务的执行时间会很长。

  • 服务端并不需要保存一个完整的结果集,取数据和发数据的流程都是通过一个 next_buffer 来操作的。

  • 内存的数据页都是在 Buffer_Pool中操作的。

  • InnoDB 管理 Buffer_Pool 使用的是改进的 LRU 算法,使用链表实现,实现上,按照 5:3 的比例把整个 LRU 链表分成了 young 区域old 区域

32、MySQL 的大表查询为什么不会爆内存?

  • 由于 MySQL 是边读变发,因此对于数据量很大的查询结果来说,不会再 server 端保存完整的结果集,所以,如果客户端读结果不及时,会堵住 MySQL 的查询过程,但是不会把内存打爆。

  • InnoDB 引擎内部,由于有淘汰策略,InnoDB 管理 Buffer_Pool 使用的是改进的 LRU 算法,使用链表实现,实现上,按照 5:3 的比例把整个 LRU 链表分成了 young 区域old 区域。对冷数据的全扫描,影响也能做到可控制。

33、MySQL 临时表的用法特性

34、MySQL 存储引擎介绍(InnoDB、MyISAM、MEMORY)

  • InnoDB 是事务型数据库的首选引擎,支持事务安全表 (ACID),支持行锁定外键。MySQL5.5.5 之后,InnoDB 作为默认存储引擎

  • MyISAM 基于 ISAM 的存储引擎,并对其进行扩展。它是在 Web、数据存储和其他应用环境下最常用的存储引擎之一。MyISAM 拥有较高的插入、查询速度,但不支持事务。在 MySQL5.5.5 之前的版本中,MyISAM 是默认存储引擎

  • MEMORY 存储引擎将表中的数据存储到内存中,为查询和引用其他表数据提供快速访问。

35、都说 InnoDB 好,那还要不要使用 MEMORY 引擎?

36、如果数据库误操作, 如何执行数据恢复?

数据库在某个时候误操作,就可以找到距离误操作最近的时间节点的bin log,重放到临时数据库里,然后选择误删的数据节点,恢复到线上数据库。

主从备份相关

37、MySQL 是如何保证主备同步

主备关系的建立:

  • 一开始创建主备关系的时候,是由备库指定的,比如基于位点的主备关系,备库说“我要从binlog文件A的位置P”开始同步,主库就从这个指定的位置开始往后发。

  • 而主备关系搭建之后,是主库决定要发给数据给备库的,所以主库有新的日志也会发给备库。

MySQL 主备切换流程

  • 客户读写都是直接访问A,而节点B是备库,只要将A的更新都同步过来,到本地执行就可以保证数据是相同的。

  • 当需要切换的时候就把节点换一下,A的节点B的备库

一个事务完整的同步过程:

38、什么是主备延迟

主库和备库在执行同一个事务的时候出现时间差的问题,主要原因有:

39、为什么要有多线程复制策略

40、MySQL 的并行策略哪些

41、MySQL的一主一备和一主多从有什么区别

在一主一备的双 M 架构里,主备切换只需要把客户流量切到备库;而在一主多从架构里,主备切换除了要把客户端流量切到备库外,还需要把从库接到新主库上。

42、主库出问题如何解决?

  • 基于位点的主备切换:存在找同步位点这个问题

  • MySQL 5.6 版本引入了 GTID,彻底解决了这个困难。那么,GTID 到底是什么意思,又是如何解决找同步位点这个问题呢?

  • GTID:全局事务 ID,是一个事务在提交的时候生成的,是这个事务的唯一标识;它由两部分组成,格式是:GTID=server_uuid:gno

  • 每个 MySQL 实例都维护了一个 GTID 集合用来对应“这个实例执行过的所有事务”。

  • 基于 GTID 的主备关系里,系统认为只要建立主备关系,就必须保证主库发给备库的日志是完整的。因此,如果实例 B 需要的日志已经不存在,A’就拒绝把日志发给 B。

43、MySQL 读写分离涉及到过期读问题的几种解决方案?

44、MySQL的并发链接并发查询有什么区别

性能相关

45、短时间提高 MySQL 性能的方法

  • 第一种方法:先处理掉那些占着连接但是不工作的线程。或者再考虑断开事务内空闲太久的连接。kill connection + id

  • 第二种方法:减少连接过程的消耗:慢查询性能问题在 MySQL 中,会引发性能问题的慢查询,大体有以下三种可能:索引没有设计好;SQL 语句没写好;MySQL 选错了索引(force index)。

46、为什么 MySQL 自增主键 ID 不连续?

  • 一键冲突

  • 事务回滚

  • 自增主键的批量申请

  • 深层次原因是:MySQL 不判断自增主键是否存在,从而减少加锁的时间范围和粒度,这样能保持更高的性能,确保自增主键不能回退,所以才有自增主键不连续。

  • 自增主键怎么做到唯一性?自增值加1来通过自增锁控制并发。

47、InnoDB 为什么要用自增 ID 作为主键?

  • 自增主键的插入模式,符合递增插入,每次都是追加操作,不涉及挪动记录,也不会触发叶子节点的分裂

  • 每次插入新的记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页。

  • 而有业务逻辑的字段做主键,不容易保证有序插入,由于每次插入主键的值近似于随机

  • 因此每次新纪录都要被插到现有索引页得中间某个位置, 频繁的移动分页操作造成了大量的碎片,得到了不够紧凑的索引结构,写数据成本较高。

48、如何最快的复制一张表?

49、grantflush privileges语句

  • grant语句会同时修改数据表和内存,判断权限的时候使用的内存数据,因此,规范使用是不需要加上 flush privileges 语句。

  • flush privileges 语句本身会用数据表的数据重建一份内存权限数据,所以在权限数据可能存在不一致的情况下再使用。

50、要不要使用分区表

  • 分区并不是越细越好。实际上,单表或者单分区的数据一千万行,只要没有特别大的索引,对于现在的硬件能力来说都已经是小表了。

  • 分区也不要提前预留太多,在使用之前预先创建即可。比如,如果是按月分区,每年年底时再把下一年度的 12 个新分区创建上即可。对于没有数据的历史分区,要及时的 drop 掉。

51、join 用法

52、MySQL 有哪些自增ID?各自场景是什么?

  • 表的自增 ID 达到上限之后,在申请值不会变化,进而导致联系插入数据的时候报主键冲突错误

  • row_id 达到上限之后,归 0 在重新递增,如果出现相同的 row_id 后写的数据会覆盖之前的数据。

  • Xid 只需要不在同一个 binlog 文件出现重复即可,理论上会出现重复值,但概率极小可忽略不计。

  • InnoDB 的 max_trx_id 递增值每次 MySQL 重启会保存起来。

  • Xid 是由 server 层维护的。InnoDB 内部使用 Xid,就是为了能够在 InnoDB 事务和 server 之间关联。但是,InnoDB 自己trx_id,是另外维护的。

  • thread_id 是我们使用中最常见的,而且也是处理得最好的一个自增 id 逻辑了。使用了insert_unique算法

53、Xid 在 MySQL 内部是怎么生成的呢?

MySQL 内部维护了一个全局变量 global_query_id,每次执行语句(包括select语句)的时候将它赋值给 Query_id,然后给这个变量加 1。如果当前语句是这个事务执行的第一条语句,那么 MySQL 还会同时把 Query_id 赋值给这个事务的 Xid。

global_query_id 是一个纯内存变量,重启之后就清零了。所以你就知道了,在同一个数据库实例中,不同事务的 Xid 也是有可能相同的。但是 MySQL 重启之后会重新生成新的 binlog 文件,这就保证了,同一个 binlog 文件里,Xid 一定是惟一的。

锁相关

54、说一下 MySQL 的锁

  • MySQL 在 server 层 和 存储引擎层 都运用了大量的锁

  • MySQL server 层需要讲两种锁,第一种是MDL(metadata lock) 元数据锁,第二种则 Table Lock 表锁。

  • MDL 又名元数据锁,那么什么是元数据呢,任何描述数据库的内容就是元数据,比如我们的表结构、库结构等都是元数据。那为什么需要 MDL 呢?

  • 主要解决两个问题:事务隔离问题;数据复制问题

  • InnoDB 有五种表级锁:IS(意向读锁);IX(意向写锁);S(读);X(写);AUTO-INC

  • 在对表进行select/insert/delete/update语句时候不会加表级锁

  • IS和IX的作用是为了判断表中是否有已经被加锁的记录

  • 自增主键的保障就是有 AUTO-INC 锁,是语句级别的:为表的某个列添加 AUTO_INCREMENT 属性,之后在插⼊记录时,可以不指定该列的值,系统会⾃动为它赋上单调递增的值。

  • InnoDB 4 种行级锁

  • RecordLock:记录锁

  • GapLock:间隙解决幻读;前一次查询不存在的东西在下一次查询出现了,其实就是事务A中的两次查询之间事务B执行插入操作被事务A感知

  • Next-KeyLock:锁住某条记录又想阻止其它事务在改记录前面间隙插入新纪录

  • InsertIntentionLock:插入意向锁;如果插入到同一行间隙中的多个事务未插入到间隙内的同一位置则无须等待

  • 行锁和表锁的抉择

55、什么是幻读?

值在同一个事务中,存在前后两次查询同一个范围的数据,第二次看到了第一次没有查询到的数据。

幻读出现的场景:

幻读带来的问题:

  • 对行锁语义的破坏

  • 破坏了数据一致性

解决:

  • 加间隙锁,锁住行与行之间的间隙,阻塞新插入的操作。

  • 带来的问题:降低并发度,可能导致死锁

其它为什么系列

56、为什么 MySQL 会抖一下?

57、为什么删除了表,表文件的大小还是没变?

  • 数据项删除之后 InnoDB 某个页 page A 会被标记为可复用

  • delete 命令把整个表的数据删除,结果就是,所有的数据页都会被标记为可复用。但是磁盘上,文件不会变小。

  • 经过大量增删改的表,都是可能是存在空洞的。这些空洞也占空间所以,如果能够把这些空洞去掉,就能达到收缩表空间的目的。

  • 重建表,就可以达到这样的目的。可以使用 alter table A engine=InnoDB 命令来重建表。

58、count(*)实现方式以及各种 count 对比

  • 对于 count(主键 id) 来说,InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层。server 层拿到 id 后,判断是不可能为空的,就按行累加。

  • 对于 count(1) 来说,InnoDB 引擎遍历整张表,但不取值。server 层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。单看这两个用法的差别的话,你能对比出来,count(1) 执行得要比 count(主键 id) 快。因为从引擎返回 id 会涉及到解析数据行,以及拷贝字段值的操作。

  • 对于 count(字段) 来说:如果这个“字段”是定义为 not null 的话,一行行地从记录里面读出这个字段,判断不能为 null,按行累加;如果这个“字段”定义允许为 null,那么执行的时候,判断到有可能是 null,还要把值取出来再判断一下,不是 null 才累加。也就是前面的第一条原则,server 层要什么字段,InnoDB 就返回什么字段。

  • 但是 count * 是例外,并不会把全部字段取出来,而是专门做了优化,不取值count(*) 肯定不是 null,按行累加。

  • 所以结论是:按照效率排序的话,count(字段)<count(主键 id)<count(1)≈count(※),所以建议尽量使用 count(*)。

59、orderby 排序内部原理

  • MySQL 会为每个线程分配一个内存(sort-buffer)用于排序该内存大小sort_buffer_size

  • 如果排序的数据量小于 sort_buffer_size,排序就会在内存中完成;

    内部排序分为两种

  • 全字段排序:到索引树上找到满足条件的主键ID根据主键ID去取出数据放到sort_buffer然后进行快速排序

  • rowid排序:通过控制排序的行数据的长度来让sort_buffer中尽可能多的存放数据

  • 如果数据量很大,内存中无法存下这么多,就会使用磁盘临时文件来辅助排序,称为外部排序;

  • 外部排序,MySQL会分为好几份单独的临时文件来存放排序后的数据,一般是磁盘文件中进行归并,然后将这些文件合并成一个大文件;

60、如何高效的使用 MySQL 显式随机消息

持续更新中。

参考

今天的唠嗑就到这里了。

我是小贺,我们下期再见。

271f2c175ee235f54464d76645867da6.gif

·················END·················

你好,我是 herongwei,一个精神小伙&amp;鹅厂程序猿,热爱编程,热爱生活,热爱分享,在平凡的人生中追求一点不平凡,欢迎关注,一起加油,点击下方名片,了解更多。

3244a9a18287428f77a8a5dda5c4a028.png

原文地址:https://blog.csdn.net/u013050857/article/details/123244085

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_31291.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注