MySql教程

Mysql常见面试题及解答《第一篇》

本文主要是介绍Mysql常见面试题及解答《第一篇》,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

前言

从我最开始学习Mysql,看过视频、读过很多相关的博客,后来开始看书,《Sql必知必会》、《高性能Mysql第三版》,也看过一些付费的专栏网站。很多内容看过很多遍,没有形成一个完整的思维导图结构,或者说没有很好地去记忆下自己所学的内容,经常是看了、学了之后就没有管了。偶尔记记笔记,也没有 形成体系,导致看过的内容都忘掉,然后又得重新去看一遍,浪费了很多时间,现在越来越理解做好笔记然后记好学过的东西,以及及时回顾的重要性。所有学习内容都是如此。
这里是我复习Mysql过程中记录的一些常见面试题和知识点,可供大家参考。

文章目录

  • 前言
  • 一、常见面试题
    • 1.索引对应的常见数据结构有哪些?各种数据结构优劣。
    • 2.InnoDB的索引模型是?
    • 3.自增主键的优势?
    • 4.什么场景适合业务字段做主键?
    • 5.如何避免长事务对业务的影响?
    • 6.联合索引的技巧
    • 7.为什么要重建索引?
    • 8.覆盖索引、最左前缀原则、索引下推 是什么?
    • 9.覆盖索引必须要覆盖所有的查询条件中的列吗?
    • 10.什么时候二级索引比主键索引还快?
    • 11.什么是视图,视图作用
    • 12.存储过程优缺点
    • 13.怎么让mysql的myisam引擎支持事务
    • 14.讲讲全局锁
    • 15.mysql自带的逻辑备份工具
    • 16..既然要全库只读,为什么不适用set global readonly=true 的方式呢?
    • 17.死锁监测
  • 二、常见知识点
    • 1.如果主库的binlog传来一个DDL语句会怎么样?
  • 总结


一、常见面试题

一些常见面试题,和总结。

1.索引对应的常见数据结构有哪些?各种数据结构优劣。

哈希表:哈希表这种结构适用于只有等值查询的场景,不适合区间查询。

有序数组:有序数组在等值查询和范围查询场景中的性能就都非常优秀。有序数组就是最好的数据结构了。但是,在需要更新数据的时候就麻烦了,你往中间插入一个记录就必须得挪动后面所有的记录,成本太高。所以,有序数组索引只适用于静态存储引擎

二叉查找树:查询和更新复杂度都为O(log(N)),

N叉数:(以 InnoDB 的一个整数字段索引为例,这个 N 差不多是 1200。这棵树高是 4 的时候,就可以存 1200 的 3 次方个值,这已经 17 亿了。考虑到树根的数据块总是在内存中的,一个 10 亿行的表上一个整数字段的索引,查找一个值最多只需要访问 3 次磁盘。其实,树的第二层也有很大概率在内存中,那么访问磁盘的平均次数就更少了。N 叉树由于在读写上的性能优点,以及适配磁盘的访问模式,已经被广泛应用在数据库引擎中了。)

2.InnoDB的索引模型是?

InnoDB 中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。又因为前面我们提到的,InnoDB 使用了 B+ 树索引模型,所以数据都是存储在 B+ 树中的。

3.自增主键的优势?

自增主键是指自增列上定义的主键,在建表语句中一般是这么定义的: NOT NULL PRIMARY KEY AUTO_INCREMENT。

插入新记录的时候可以不指定 ID 的值,系统会获取当前 ID 最大值加 1 作为下一条记录的 ID 值。也就是说,自增主键的插入数据模式,正符合了我们前面提到的递增插入的场景。每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂。

而有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高。

除了考虑性能外,我们还可以从存储空间的角度来看。假设你的表中确实有一个唯一字段,比如字符串类型的身份证号,那应该用身份证号做主键,还是用自增字段做主键呢?

由于每个非主键索引的叶子节点上都是主键的值。如果用身份证号做主键,那么每个二级索引的叶子节点占用约 20 个字节,而如果用整型做主键,则只要 4 个字节,如果是长整型(bigint)则是 8 个字节。

显然,主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。
所以,从性能和存储空间方面考量,自增主键往往是更合理的选择。

4.什么场景适合业务字段做主键?

  1. 只有一个索引;

  2. 该索引必须是唯一索引 由于没有其他索引,所以也就不用考虑其他索引的叶子节点大小的问题。 这时候我们就要优先考虑“尽量使用主键查询”原则,直接将这个索引设置为主键,可以避免每次查询需要搜索两棵树。

5.如何避免长事务对业务的影响?

首先,从应用开发端来看:

确认是否使用了 set autocommit=0。这个确认工作可以在测试环境中开展,把 MySQL 的 general_log 开起来,然后随便跑一个业务逻辑,通过 general_log 的日志来确认。一般框架如果会设置这个值,也就会提供参数来控制行为,你的目标就是把它改成 1。

确认是否有不必要的只读事务。有些框架会习惯不管什么语句先用 begin/commit 框起来。我见过有些是业务并没有这个需要,但是也把好几个 select 语句放到了事务中。这种只读事务可以去掉。

业务连接数据库的时候,根据业务本身的预估,通过 SET MAX_EXECUTION_TIME 命令,来控制每个语句执行的最长时间,避免单个语句意外执行太长时间。(为什么会意外?在后续的文章中会提到这类案例)

其次,从数据库端来看:

监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 / 或者 kill;
Percona 的 pt-kill 这个工具不错,推荐使用;
在业务功能测试阶段要求输出所有的 general_log,分析日志行为提前发现问题;
如果使用的是 MySQL 5.6 或者更新版本,把 innodb_undo_tablespaces 设置成 2(或更大的值)。如果真的出现大事务导致回滚段过大,这样设置后清理起来更方便。

6.联合索引的技巧

总结:

1、覆盖索引:如果查询条件使用的是普通索引(或是联合索引的最左原则字段),查询结果是联合索引的字段或是主键,不用回表操作,直接返回结果,减少IO磁盘读写读取正行数据

2、最左前缀:联合索引的最左 N 个字段,也可以是字符串索引的最左 M 个字符

3、联合索引:根据创建联合索引的顺序,以最左原则进行where检索,比如(age,name)以age=1 或 age= 1 and name=‘张三’可以使用索引,单以name=‘张三’ 不会使用索引,考虑到存储空间的问题,还请根据业务需求,将查找频繁的数据进行靠左创建索引。

4、索引下推:like 'hello%’and age >10 检索,MySQL5.6版本之前,会对匹配的数据进行回表查询。

  1. 5.6版本后,会先过滤掉age<10的数据,再进行回表查询,减少回表率,提升检索速度

7.为什么要重建索引?

索引可能因为删除,或者页分裂等原因,导致数据页有空洞,重建索引的过程会创建一个新的索引,把数据按顺序插入,这样页面的利用率最高,也就是索引更紧凑、更省空间。

8.覆盖索引、最左前缀原则、索引下推 是什么?

  1. 覆盖索引:在索引上覆盖了我们的查询需求,可以直接提供查询结果,不需要回表。可以在高频请求上用到覆盖索引,不再需要回表查整行记录,减少树的搜索次数,显著提升查询性能。
  2. 最左前缀:索引项是按照索引定义里面出现的字段顺序排序的。B+树这种索引结构,可以利用索引的“最左前缀”,来定位记录。
    (如果既有联合查询,又有基于a、b 各自的查询呢?查询条件里面只有 b 的语句,是无法使用 (a,b)这个联合索引的,这时候你不得不维护另外一个索引,也就是说你需要同时维护 (a,b)、(b) 这两个索引。我们需要考虑的原则就是空间了)
  3. 索引下推: MySQL 5.6 引入的索引下推优化(index condition pushdown),
    可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。

9.覆盖索引必须要覆盖所有的查询条件中的列吗?

对。因为覆盖索引的目的就是”不回表“,
所以只有索引包含了where条件部分和select返回部分的所有字段,才能实现这个目的哦

10.什么时候二级索引比主键索引还快?

数据量很大,且使用了覆盖索引。
注:查询顺序和联合索引的顺序不一致是=时,优化器会自动优化

11.什么是视图,视图作用

视图作为一张虚拟表,帮我们封装了底层与数据表的接口,方便我们重用sql语句。它相当于是一张表或多张表的数据结果集。视图的这一特点,可以帮我们简化复杂的 SQL 查询,比如在编写视图后,我们就可以直接重用它,而不需要考虑视图中包含的基础查询的细节。同样,我们也可以根据需要更改数据格式,返回与底层数据表格式不同的数据。

通常情况下,小型项目的数据库可以不使用视图,但是在大型项目中,以及数据表比较复杂的情况下,视图的价值就凸显出来了,它可以帮助我们把经常查询的结果集放到虚拟表中,提升使用效率。理解和使用起来都非常方便。

1.安全性:虚拟表是基于底层数据表的,我们在使用视图时,一般不会轻易通过视图对底层数据进行修改,即使是使用单表的视图,也会受到限制,比如计算字段,类型转换等是无法通过视图来对底层数据进行修改的,这也在一定程度上保证了数据表的数据安全性。同时,我们还可以针对不同用户开放不同的数据查询权限,比如人员薪酬是个敏感的字段,那么只给某个级别以上的人员开放,其他人的查询视图中则不提供这个字段。

2.简单清晰:视图是对 SQL 查询的封装,它可以将原本复杂的 SQL 查询简化,在编写好查询之后,我们就可以直接重用它而不必要知道基本的查询细节。同时我们还可以在视图之上再嵌套视图。这样就好比我们在进行模块化编程一样,不仅结构清晰,还提升了代码的复用率。

12.存储过程优缺点

好:首先存储过程可以一次编译多次使用。存储过程只在创造时进行编译,之后的使用都不需要重新编译,这就提升了 SQL 的执行效率。其次它可以减少开发工作量。将代码封装成模块,实际上是编程的核心思想之一,这样可以把复杂的问题拆解成不同的模块,然后模块之间可以重复使用,在减少开发工作量的同时,还能保证代码的结构清晰。还有一点,存储过程的安全性强,我们在设定存储过程的时候可以设置对用户的使用权限,这样就和视图一样具有较强的安全性。最后它可以减少网络传输量,因为代码封装到存储过程中,每次使用只需要调用存储过程即可,这样就减少了网络传输量。同时在进行相对复杂的数据库操作时,原本需要使用一条一条的 SQL 语句,可能要连接多次数据库才能完成的操作,现在变成了一次存储过程,只需要连接一次即可。

坏:它的可移植性差,存储过程不能跨数据库移植,比如在 MySQL、Oracle 和 SQL Server 里编写的存储过程,在换成其他数据库时都需要重新编写。其次调试困难,只有少数 DBMS 支持存储过程的调试。对于复杂的存储过程来说,开发和维护都不容易。此外,存储过程的版本管理也很困难,比如数据表索引发生变化了,可能会导致存储过程失效。我们在开发软件的时候往往需要进行版本管理,但是存储过程本身没有版本控制,版本迭代更新的时候很麻烦。最后它不适合高并发的场景,高并发的场景需要减少数据库的压力,有时数据库会采用分库分表的方式,而且对可扩展性要求很高,在这种情况下,存储过程会变得难以维护,增加数据库的压力,显然就不适用了。

13.怎么让mysql的myisam引擎支持事务

用lock table 来实现,但是这样只能实现串行化隔离级别,其它隔离都实现不了。

但是因为mysiam不支持崩溃恢复,所以即使用lock table硬实现,也是问题多多:ACID里面, 原子性和持久性做不到;

隔离性只能实现基本用不上的串行化;

一致性在正常运行的时候依赖于串行化,在异常崩溃的时候也不能保证。
所以这样实现的事务不要也罢。

14.讲讲全局锁

MySQL 提供了一个加全局读锁的方法,命令是 Flush tables with read lock (FTWRL)。当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。
全局锁的典型使用场景是,做全库逻辑备份。也就是把整库每个表都 select 出来存成文本。
以前有一种做法,是通过 FTWRL 确保不会有其他线程对数据库做更新,然后对整个库做备份。注意,在备份过程中整个库完全处于只读状态。
业务的更新不只是增删改数据(DML),还有可能是加字段等修改表结构的操作(DDL)。不论是哪种方法,一个库被全局锁上以后,你要对里面任何一个表做加字段操作,都是会被锁住的。

15.mysql自带的逻辑备份工具

官方自带的逻辑备份工具是 mysqldump。当 mysqldump 使用参数–single-transaction 的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。而由于 MVCC 的支持,这个过程中数据是可以正常更新的。

你一定在疑惑,有了这个功能,为什么还需要 FTWRL 呢?一致性读是好,但前提是引擎要支持这个隔离级别。比如,对于 MyISAM 这种不支持事务的引擎,如果备份过程中有更新,总是只能取到最新的数据,那么就破坏了备份的一致性。这时,我们就需要使用 FTWRL 命令了。

所以,single-transaction 方法只适用于所有的表使用事务引擎的库。如果有的表使用了不支持事务的引擎,那么备份就只能通过 FTWRL 方法。这往往是 DBA 要求业务开发人员使用 InnoDB 替代 MyISAM 的原因之一。

16…既然要全库只读,为什么不适用set global readonly=true 的方式呢?

确实 readonly 方式也可以让全库进入只读状态,但我还是会建议你用 FTWRL 方式,主要有两个原因:

一是,在有些系统中,readonly 的值会被用来做其他逻辑,比如用来判断一个库是主库还是备库。因此,修改 global 变量的方式影响面更大,我不建议你使用。

二是,在异常处理机制上有差异。如果执行 FTWRL 命令之后由于客户端发生异常断开,那么 MySQL 会自动释放这个全局锁,整个库回到可以正常更新的状态。而将整个库设置为 readonly 之后,如果客户端发生异常,则数据库就会一直保持 readonly 状态,这样会导致整个库长时间处于不可写状态,风险较高。

17.死锁监测

当出现死锁以后,有两种策略:

一种策略是,直接进入等待,直到超时。这个超时时间可以通过参数 innodb_lock_wait_timeout 来设置。

另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑。

(在 InnoDB 中,innodb_lock_wait_timeout 的默认值是 50s,意味着如果采用第一个策略,当出现死锁以后,第一个被锁住的线程要过 50s 才会超时退出,然后其他线程才有可能继续执行。对于在线服务来说,这个等待时间往往是无法接受的。但是,我们又不可能直接把这个时间设置成一个很小的值,比如 1s。这样当出现死锁的时候,确实很快就可以解开,但如果不是死锁,而是简单的锁等待呢?所以,超时时间设置太短的话,会出现很多误伤。 所以,正常情况下我们还是要采用第二种策略,即:主动死锁检测,而且 innodb_deadlock_detect 的默认值本身就是 on。主动死锁检测在发生死锁的时候,是能够快速发现并进行处理的,但是它也是有额外负担的。)

所以,正常情况下我们还是要采用第二种策略,即:主动死锁检测,而且 innodb_deadlock_detect 的默认值本身就是 on。主动死锁检测在发生死锁的时候,是能够快速发现并进行处理的,但是它也是有额外负担的。

你可以想象一下这个过程:每当一个事务被锁的时候,就要看看它所依赖的线程有没有被别人锁住,如此循环,最后判断是否出现了循环等待,也就是死锁。

那如果是我们上面说到的所有事务都要更新同一行的场景呢?

每个新来的被堵住的线程,都要判断会不会由于自己的加入导致了死锁,这是一个时间复杂度是 O(n) 的操作。假设有 1000 个并发线程要同时更新同一行,那么死锁检测操作就是 100 万这个量级的。虽然最终检测的结果是没有死锁,但是这期间要消耗大量的 CPU 资源。因此,你就会看到 CPU 利用率很高,但是每秒却执行不了几个事务。

二、常见知识点

1.如果主库的binlog传来一个DDL语句会怎么样?


总结

以上就是今天要讲的内容,未完待续......![在这里插入图片描述](https://www.www.zyiz.net/i/ll/?i=20210612184648740.jpg?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MzIxOTI1NA==,size_16,color_FFFFFF,t_70#pic_center)
这篇关于Mysql常见面试题及解答《第一篇》的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!