此页内容

Mysql

14030字约47分钟

Mysql

!> Mysql面试题重点围绕事务、隔离级别、锁、索引、存储引擎

考察点如下:

  • 数据库设计基础:掌握数据库设计中的基本范式,以及基础概念,例如表、视图、索引、外键、序列号生成器等,掌握数据库的数据类型的使用,清楚业务实体关系与数据库结构的映射。
  • 数据库隔离级别:掌握 MySQL 四种事务隔离级别的基础知识,并进一步了解 MVCC、Locking 等机制对于处理的进阶问题的解决;还需要了解不同索引类型的使用,甚至是底层数据结构和算法等。
  • SQL 优化:掌握基础的 SQL 调优技巧,至少要了解基本思路是怎样的,例如 SQL 怎样写才能更好利用索引、知道如何分析 SQL 执行计划等。
  • 数据库架构设计:掌握针对高并发等特定场景中的解决方案,如读写分离、分库分表等。

事务

隔离级别

索引

存储引擎

日志

性能优化

SQL语句

数据库语句:工人打工工资日结 一周一根金条 金条只可以被切分两次,怎么做

高可靠

数据库设计

参考

  1. 极客时间《Mysql实战45讲》
  2. 浅谈MySQL数据库中的锁与事务
  3. 透彻解读mysql的可重复读、幻读及实现原理
  4. MySQL 中事务的实现
  5. 阿里P8整理Mysql面试题答案,助你“脱颖而出”,吊打面试官!(建议收藏)
  6. 面试必刷:最有用的Mysql面试题,面试了无数公司总结的
  7. Mysql日志系统
  8. 面试问烂的 MySQL 四种隔离级别,看完吊打面试官!
  9. MySQL事务隔离级别和实现原理(看这一篇文章就够了!)
  10. 大明哥钉钉直播回放以及GitHub仓库的八股文
  11. 数据库考点
  12. MVCC只有RR和RC才会有
  13. 【133期】面试官:你说熟悉MySQL事务,那来谈谈事务的实现原理吧!
  14. Mysql 已读 未读_面试常问的MySQL事务ACID原理,你知道吗?
  15. 面试必备常见存储引擎与锁的分类,请查收

这种结果告诉我们其实在MySQL可重复读的隔离级别中并不是完全解决了幻读的问题(因为select查询的时候是在可重复读的情况下将会快照读,也就是直接复用之前的查询结果),而是解决了读数据情况下的幻读问题。而对于修改的操作依旧存在幻读问题(对于修改操作,将会是当前读,也就是执行修改操作的时候去数据库里面查询最新的结果,可能之前我们查询的是没有这一条记录,但是现在我们要修改的时候去查询,发现会有记录因此就会出现幻读的情况,所以这种基于修改的幻读情况在RR隔离级别是无法避免的,但是这只是对数据修改的操作(update、insert、delete)当前读产生的结果,他其实不是幻读)。

因此最后是如何解决幻读的呢?HEiF6Z


事务

在 MySQL 中,事务支持是在引擎层实现的,有的存储引擎支持事务,有的不支持事务。 MySQL是一个支持多引擎的系统,但并不是所有的引擎都支持事务。比如MySQL原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被 InnoDB 取代的重要原因之一

什么是事务

!>事务是什么:事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。

事务的特点或特性

类似问题: 数据库用的是什么?说说数据库事务的特性。 类似问题:ACID 是什么?具体介绍一下。

!> ACID:分别对应原子性atomicity,一致性consistency,隔离性isolation,持久性durability

  1. 原子性,是指一个事务操作不可分割,要么成功,要么失败,不能有一半成功一半失败的情况。
  2. 一致性:**是指这些数据在事务执行完成这个时间点之前,读到的一定是更新前的数据,之后读到的一定是更新后的数据,不应该存在一个时刻,让用户读到更新过程中的数据。**因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。
  3. 隔离性是指一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对正在进行的其他事务是隔离的,并发执行的各个事务之间不能互相干扰,这个有点儿像我们打网游中的副本,我们在副本中打的怪和掉的装备,与其他副本没有任何关联也不会互相影响。
  4. 持久性:一个事务被提交之后。它对数据库中数据的改变是持久的,后续的其他操作和故障都不会对事务的结果产生任何影响

mysql事务隔离级别对应的问题

  1. 脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
  2. 不可重复读(Non-repeatable read): 事务 A 先读取一条数据,然后执行逻辑的过程中,事务 B 更新了这条数据,事务 A 再读取时,发现数据不匹配,这个现象就是“不可重复读”。 不可重复读通常针对数据更新(UPDATE)操作。
  3. 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。幻读是针对数据插入(INSERT)操作来说的

“脏读”面试关注点:

  1. 脏读对应的隔离级别是“读未提交”,只有该隔离级别才会出现脏读。
  2. 脏读的解决办法是升级事务隔离级别,比如“读已提交”。

“不可重复读”面试关注点:

  1. 简单理解是两次读取的数据中间被修改,对应的隔离级别是“读未提交”或“读已提交”。
  2. 不可重复读的解决办法就是升级事务隔离级别,比如“可重复读”。

“幻读”面试关注点: 案例:事务 A 读了一次商品表,得到最后的 ID 是 3,事务 B 也同样读了一次,得到最后 ID 也是 3。接下来事务 A 先插入了一行,然后读了一下最新的 ID 是 4,刚好是前面 ID 3 加上 1,然后事务 B 也插入了一行,接着读了一下最新的 ID 发现是 5,而不是 3 加 1。这时,你发现在使用 ID 做判断或做关键数据时,就会出现问题,这种现象就像是让事务 B 产生了幻觉一样,读取到了一个意想不到的数据,所以叫幻读。当然,不仅仅是新增,删除、修改数据也会发生类似的情况。

要想解决幻读不能升级事务隔离级别到“可串行化”,那样数据库也失去了并发处理能力。

  1. 行锁解决不了幻读,因为即使锁住所有记录,还是阻止不了插入新数据。
  2. 解决幻读的办法是锁住记录之间的“间隙”,为此 MySQL InnoDB 引入了新的锁,叫间隙锁(Gap Lock),所以在面试中,你也要掌握间隙锁,以及间隙锁与行锁结合的 next-key lock 锁。SbCTR8

注意:MySQL 的 Innodb 引擎通过 MVCC 和 next-key lock已经解决了幻读问题

MVCC原理: 从多版本和锁机制来讲 Y2iiw9UWjZfI

undo log里面存储的数据都是我们操作的反向数据,比如我们这次是插入操作,那么我们就会存储一个delete在undo log里面,

事务隔离级别,对应的问题

类似问题:事务的隔离级别 类似问题:mysql 隔离级别 类似问题:mysql 的事务隔离级别有几种啊 !> 越往下效率越低

理解:SQL 标准定义了四个隔离级别:

  1. READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  2. READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  3. REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  4. SERIALIZABLE(可串行化): 最高的隔离级别,对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。如果 SQL 使用 WHERE 语句,还会获取区间锁。换句话说,事务 A 操作数据库时,事务 B 只能排队等待,因此性能也最低。。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。 mysql中默认使用可重读,oracle中默认使用读已提交

mysql事务的隔离级别以及分别如何实现?

!> 为了解决不可重复读,或者为了实现可重复读,MySQL 采用了 MVVC (多版本并发控制) 的方式。 实现:MVVC机制以及undo_log

隔离级别内容解决的问题以及出现的问题实现原理
READ-UNCOMMITTED(读取未提交)最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。读无锁。改启用行级共享锁
READ-COMMITTED(RC读取已提交)允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。读取:行级共享锁;改:行级排它锁
REPEATABLE-READ(可重复读)对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改可以阻止脏读和不可重复读,但幻读仍有可能发生。读取共享锁直到事务结束释放, 写排它锁直到事务结束释放
SERIALIZABLE(可串行化)最高的隔离级别,对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,该级别可以防止脏读、不可重复读以及幻读。读取表级共享锁直到事务结束释放。写表级排它锁直到事务结束释放

如何实现参考极客时间课程mysql实战45讲与https://blog.csdn.net/sanyuesan0000/article/details/90235335

读未提交实现:因为读不会加任何锁,所以写操作在读的过程中修改数据,所以会造成脏读。好处是可以提升并发处理性能,能做到读写并行。换句话说,读的操作不能排斥写请求。

读已提交实现: 为什么读已提交产生不可重复读:这跟 READ COMMITTED 级别下的MVCC机制有关系,在该隔离级别下每次 select的时候新生成一个版本号,所以每次select的时候读的不是一个副本而是不同的副本。在每次select之间有其他事务()或实例(都是在同一个事务)更新了我们读取的数据并提交了,那就出现了不可重复读。

因为读已提交是在每次select的时候会生成一个read-view,所以同一个事务select之间可能有其他事务修改了数据,或者自己这个事务也可能修改数据,因为自己的修改也是可以读取到的。

读的时候不加锁,写的时候会加写锁

可重读实现: 读的时候加读锁,写的时候加写锁

序列化实现:

事务的隔离有哪些,说一下每个隔离的区别,mysql默认使用哪个隔离,日常应用中应该使用已提交读还是可重复读

其他同上 !> 日常应用中推荐将隔离级别设计为已提交读

原因:

  1. https://blog.csdn.net/weixin_42144277/article/details/106916092
  2. https://www.cnblogs.com/fanguangdexiaoyuer/p/11323248.html#_label1

生产中使用RC隔离级别主要原因如下:

使用RR隔离级别,

  1. 如果存在间隙锁,就容易产生死锁
  2. 如果条件列未命中索引会引起锁表,其他事务只能读,不能写,影响性能。
  3. 还有在RC隔离级别下,半一致性读(semi-consistent)的特性增加了update操作的并发性。
  4. 4.1 mysql默认RR隔离级别是有历史原因的,mysql主从复制是基于binlog,在5.0以前binlog的格式(binlog_format)只支持statement格式(记录的是修改sql语句),这种格式在RC隔离级别下有BUG,因此mysql将RR隔离级别作为默认的隔离级别。 4.2 这个BUG大致是这样:一张表test有字段x并且是主键,x列有一行数据:1;sessionA设置隔离级别为RC,然后开启事务。sessionB设置隔离级别为RC,也开启事务。sessionA执行 delete from test where x >= 3;不提交。sessionB执行 insert into test select 2;sessionB执行commit;提交成功。然后sessionA执行commit;提交成功。此时在主(master)上执行 select * from test;可以查到x列有值为2。但是此时在从(slave)执行select * from test;查到是空的。这样就产生主从不一致的问题!原因其实就是,在master上执行的顺序为先删后插,而此时binlog为statement格式,他记录的顺序是先插后删,从(slave)同步的是binlog,因此主从执行的顺序不一致,就会出现主从不一致。 4.3 解决这个问题的方案有两种,1.就是设置隔离级别为RR,在该隔离级别下引入间隙锁,当sessionA执行delete时,会锁住间隙,sessionB执行insert时就会阻塞。2.将binlog的格式binlog_format改为row(记录的是每行实际数据的变更)。此格式是基于行的复制,自然就不会出现sql执行顺序不一样的问题!奈何row格式是在mysql5.1版本开始才引入的。因此由于历史原因,mysql将默认的隔离级别设为RR可重复读,保证主从复制不出问题。

提交读和可重复读的实现原理

以下答案来自于极客时间的mysql实战45讲 在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。在“可重复读”隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。 在“读提交”隔离级别下,这个视图是在每个 SQL 语句开始执行的时候创建的。“读未提交”隔离级别下直接返回记录上的最新值,没有视图概念;而“串行 化”隔离级别下直接用加锁的方式来避免并行访问。

参考

事务的实现

实现:MVVC机制以及undo_log

简单讲,如果没有MVCC,当想要读取的数据被其他事务用排它锁锁住时,只能互斥等待;而这时MVCC可以通过提供历史版本从而实现读取被锁的数据的历史版本,从而避免了互斥等待。

InnoDB采用的MVCC实现方式是:在需要时,通过undo日志构造出历史版本

两个实现⽅式:

  1. LBCC(lock based concurrent control):基于锁的并发控制
  2. MVCC(multi-verson concurrent control):多版本的并发控制。 具体参考自己总结的笔记

如何实现参考极客时间课程mysql实战45讲与https://blog.csdn.net/sanyuesan0000/article/details/90235335

!> 类似问题: 如何实现事务 我就回答了MVCC + 一顿猛吹

事务的几个特性 2次 事务的特性以及对应的问题,如何保证的

!> 事务的特性以及对应的问题同上

那ACID靠什么保证的呢? 参考: https://blog.csdn.net/weixin_39524574/article/details/111693947

  1. 原子性:使用 undo log ,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql
  2. 持久性:由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,事务提交的3. 时候通过redo log刷盘,宕机的时候可以从redo log恢复
  3. 隔离性:使用锁以及MVCC,运用的优化思想有读写分离,读读并行,读写并行 一致性:通过回滚,以及恢复,和在并发环境下的隔离做到一致性。

mysql 的事务锁了解吗

至于数据库事务锁,分为悲观锁和乐观锁,“悲观锁”认为数据出现冲突的可能性很大,“乐观锁”认为数据出现冲突的可能性不大。那悲观锁和乐观锁在基于 MySQL 数据库的应用开发中,是如何实现的呢?

悲观锁一般利用 SELECT … FOR UPDATE 类似的语句,对数据加锁,避免其他事务意外修改数据。

乐观锁利用 CAS 机制,并不会对数据加锁,而是通过对比数据的时间戳或者版本号,实现版本判断。

那每一个事务隔离级别分别解决了什么问题呢

类似问题:那可重复读能解决什么 读未提交:出现脏读,不可重读,幻读问题 读已提交:解决脏读问题,出现不可重读,幻读问题 可重读:出现幻读问题,解决脏读,不可重读 序列化:解决脏读,不可重读,幻读问题

讲讲可重复读隔离级别

如何实现参考极客时间课程mysql实战45讲与https://blog.csdn.net/sanyuesan0000/article/details/90235335

事务隔离实现参考

事务相关:例如事务的隔离是如何实现的?事务是如何保证原子性?不同的事务看到的数据怎么就不一样了?难道每个事务都拷贝一份视图?MVCC 的实现原理(重要)等等。

事务隔离实现参考


索引

参考

参考

索引的结构以及概念

  1. 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。
  2. 索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B数及其变种B+数。
  3. 更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。

mysql索引的数据结构以及分类(B+树以及和B-树,红黑树,二叉树的优缺点,聚簇索引,非聚簇索引,hash索引的优缺点以及实现)

聚簇索引,是每一个叶子节点最终都保留了一整行的记录信息,不需要进行回表,直接可以查找到 但是非聚簇索引是每一个叶子节点都保留对应的主键值,通过主键进行回表来获取对应的记录信息,需要查两次

类似问题: 数据库索引结构是什么样的(我回答B+树,然后问对磁盘有什么优势夺命追问,我说降低IO次数,问为什么) 类似问题: 数据库使用b+树的好处 类似问题: b+树的特性,为啥要用到这 类似问题: B+ Tree 类似问题: 谈谈索引的数据结构?说了 B+树 类似问题: B+树和 B-树的区别以及优点? 类似问题: 为什么不用搜索树? 类似问题: B+树的特点 类似问题: mysql 索引 2次 类似问题:mysql索引结构。

索引的数据结构和具体存储引擎的实现有关,在MySQL中使用较多的索引有Hash索引,B+树索引,全文索引等,而我们经常使用的InnoDB存储引擎的默认索引实现为:B+树索引。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。

  1. B树索引:mysql通过存储引擎取数据,基本上90%的人用的就是InnoDB了,按照实现方式分,InnoDB的索引类型目前只有两种:BTREE(B树)索引和HASH索引。B树索引是Mysql数据库中使用最频繁的索引类型,基本所有存储引擎都支持BTree索引。通常我们说的索引不出意外指的就是(B树)索引(实际是用B+树实现的,因为在查看表索引时,mysql一律打印BTREE,所以简称为B树索引)
  2. B+tree性质:n棵子tree的节点包含n个关键字,不用来保存数据而是保存数据的索引。 所有的叶子结点中包含了全部关键字的信息,即指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。 所有的非终端结点可以看成是索引部分,结点中仅含其子数中的最大(或最小)关键字。 B+ 树中,数据对象的插入和删除仅在叶节点上进行。 B+树有2个头指针,一个是树的根节点,一个是最小关键码的叶节点。
  3. 哈希索引:简要说下,类似于数据结构中简单实现的HASH表(散列表)一样,当我们在mysql中用哈希索引时,主要就是通过Hash算法(常见的Hash算法有直接定址法、平方取中法、折叠法、除数取余法、随机数法),将数据库字段数据转换成定长的Hash值,与这条数据的行指针一并存入Hash表的对应位置;如果发生Hash碰撞(两个不同关键字的Hash值相同),则在对应Hash键下以链表形式存储。当然这只是简略模拟图。

别人的: 这个问题会涉及到mysql的存储数据结构,B+tree 多叉搜索树,有序,类似二叉搜索树(特性:中序遍历是有序的);curd的时间复杂度都是logN; 再谈谈mysql的索引结构,innodb引擎中分主键(聚簇索引)索引树和二级索引树; Innodb的主键索引树的叶子节点上存储行数据,非叶子结点只存储主键id,简单讲就是聚簇索引相当于物理内存地址(一级指针),查询快; 而二级索引树的每个节点只会存储索引字段值和主键id; 2种索引查询对比: 主键索引树:主键id=>叶子结点row数据 二级索引树: 二级索引树上找到对应的id=>主键索引树上的id=>主键索引树上的叶子结点行记录

上面“二级索引树=>主键索引树”的过程叫做“回表”,可以通过“索引覆盖”,“索引下推”优化

个人总结,可能不准确的地方

数据库中数据很多的时候如何处理?面试官提示了索引

索引越多越好么?

不一定,通常,通过索引查询数据比全表扫描要快。但是我们也必须注意到它的代价。 索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时,索引本身也会被修改。 这意味着每条记录的INSERT,DELETE,UPDATE将为此多付出4,5 次的磁盘I/O。 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。使用索引查询不一定能提高查询性能,索引范围查询(INDEX RANGE SCAN)适用于两种情况:

  1. 基于一个范围的检索,一般查询返回结果集小于表中记录数的30%
  2. 基于非唯一性索引的检索

B+树有什么好处

参考:https://zhuanlan.zhihu.com/p/140876416

  1. 由于B+树的内部节点只存放键,不存放值,因此,一次读取,可以在内存页中获取更多的键,有利于更快地缩小查找范围。
  2. B+树的叶节点由一条链相连,因此,当需要进行一次全数据遍历的时候,B+树只需要使用O(logN)时间找到最小的一个节点,然后通过链进行O(N)的顺序遍历即可。而B树则需要对树的每一层进行遍历甚至有可能需要回旋查找,这会需要更多的内存置换次数,因此也就需要花费更多的时间

那么对于mysql来说为什么采用b+树而不采用b树

自己说有效减少磁盘IO次数,可以提高

追问:B树同样也是一样的树高也可以做的很低呀,

B树占用内存比较大,B+树只是在叶子节点存放我们需要的数据

追问:占用很大内存怎么理解

B+树搜索经过路径中节点的时候,比如主键,只会保存主键的值,并不会保存其他列的记录信息,只是在最后到叶子节点的时候才会保存一整行的记录,所以B+树会比B树占用内存小,

追问:既然mysql索引是b+树,有一种特殊的索引是联合索引你知道么,

二叉树,b 树,b+树的区别

类似问题:对于数据结构里面,二叉树,二叉搜索树,B树,B+树,这四种树你能谈一下你的理解么 B树与B+树的区别 B树每个节点都存储数据,所有节点组成这棵树。B+树只有叶子节点存储数据(B+数中有两个头指针:一个指向根节点,另一个指向关键字最小的叶节点),叶子节点包含了这棵树的所有数据,所有的叶子结点使用链表相连,便于区间查找和遍历,所有非叶节点起到索引作用。

B树中叶节点包含的关键字和其他节点包含的关键字是不重复的,B+树的索引项只包含对应子树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。

B树中每个节点(非根节点)关键字个数的范围为m/2(向上取整)-1,m-1,并且具有n个关键字的节点包含(n+1)棵子树。B+树中每个节点(非根节点)关键字个数的范围为m/2(向上取整),m,具有n个关键字的节点包含(n)棵子树。

B+树中查找,无论查找是否成功,每次都是一条从根节点到叶节点的路径。

B树的优点

  1. B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速。

B+树的优点 所有的叶子结点使用链表相连,便于区间查找和遍历。B树则需要进行每一层的递归遍历。相邻的元素可能在内存中不相邻,所以缓存命中性没有B+树好。 b+树的中间节点不保存数据,能容纳更多节点元素。 B树和B+树的共同优点 考虑磁盘IO的影响,它相对于内存来说是很慢的。数据库索引是存储在磁盘上的,当数据量大时,就不能把整个索引全部加载到内存了,只能逐一加载每一个磁盘页(对应索引树的节点)。所以我们要减少IO次数,对于树来说,IO次数就是树的高度,而“矮胖”就是b树的特征之一,m的大小取决于磁盘页的大小。

InnoDB二级索引流程

主键和索引的区别

mysql索引类型有哪些

从数据结构的角度看,常用的索引可以分为full-text索引,hash索引,b+树索引:MjiiVj

在创建表时,InnoDB 存储引擎默认使用表的主键作为主键索引,该主键索引就是聚簇索引(Clustered Index),如果表没有定义主键,InnoDB 就自己产生一个隐藏的 6 个字节的主键 ID 值作为主键索引,而创建的主键索引默认使用的是 B+Tree 索引。

所以 B+Tree 相比于 B 树和二叉树来说,最大的优势在于查询效率。

那么问题来了,如果你当前查询数据时候,不是通过主键 ID,而是用商品编码查询商品,那么查询过程又是怎样的呢?

通过非主键(辅助索引)查询商品数据的过程

如果你用商品编码查询商品(即使用辅助索引进行查询),会先检索辅助索引中的 B+Tree 的 商品编码,找到对应的叶子节点,获取主键值,然后再通过主键索引中的 B+Tree 树查询到对应的叶子节点,然后获取整行数据。这个过程叫回表。

以上就是索引的实现原理。

如果你被问到“为什么 MySQL 会选择 B+Tree 当索引数据结构?”其实在考察你两个方面: B+Tree 的索引原理; B+Tree 索引相比于其他索引类型的优势。

我们刚刚已经讲了 B+Tree 的索引原理,现在就来回答一下 B+Tree 相比于其他常见索引结构,如 B 树、二叉树或 Hash 索引结构的优势在哪儿?

B+Tree 相对于 B 树 索引结构的优势

B+Tree 只在叶子节点存储数据,而 B 树 的非叶子节点也要存储数据,所以 B+Tree 的单个节点的数据量更小,在相同的磁盘 I/O 次数下,就能查询更多的节点。

另外,B+Tree 叶子节点采用的是双链表连接,适合 MySQL 中常见的基于范围的顺序查找,而 B 树无法做到这一点。

B+Tree 相对于二叉树索引结构的优势

对于有 N 个叶子节点的 B+Tree,其搜索复杂度为O(logdN),其中 d 表示节点允许的最大子节点个数为 d 个。在实际的应用当中, d 值是大于100的,这样就保证了,即使数据达到千万级别时,B+Tree 的高度依然维持在 3~4 层左右,也就是说一次数据查询操作只需要做 3~4 次的磁盘 I/O 操作就能查询到目标数据(这里的查询参考上面 B+Tree 的聚簇索引的查询过程)。

而二叉树的每个父节点的儿子节点个数只能是 2 个,意味着其搜索复杂度为 O(logN),这已经比 B+Tree 高出不少,因此二叉树检索到目标数据所经历的磁盘 I/O 次数要更多。

B+Tree 相对于 Hash 表存储结构的优势

我们知道范围查询是 MySQL 中常见的场景,但是 Hash 表不适合做范围查询,它更适合做等值的查询,这也是 B+Tree 索引要比 Hash 表索引有着更广泛的适用场景的原因。

至此,你就知道“为什么 MySQL 会选择 B+Tree 来做索引”了。在回答时,你要着眼于 B+Tree 的优势,然后再引入索引原理的查询过程(掌握这些知识点,这个问题其实比较容易回答)。

索引优化: 对于执行计划,参数有 possible_keys 字段表示可能用到的索引,key 字段表示实际用的索引,key_len 表示索引的长度,rows 表示扫描的数据行数。

这其中需要你重点关注 type 字段, 表示数据扫描类型,也就是描述了找到所需数据时使用的扫描方式是什么,常见扫描类型的执行效率从低到高的顺序为(考虑到查询效率问题,全表扫描和全索引扫描要尽量避免):

ALL(全表扫描);

index(全索引扫描);

range(索引范围扫描);

ref(非唯一索引扫描);

eq_ref(唯一索引扫描);

const(结果只有一条的主键或唯一索引扫描)。

总的来说,执行计划是研发工程师分析索引详情必会的技能(很多大厂公司招聘 JD 上写着“SQL 语句调优” ),所以你在面试时也要知道执行计划核心参数的含义,如 type。在回答时,也要以重点参数为切入点,再扩展到其他参数,然后再说自己是怎么做 SQL 优化工作的。

常见索引优化方法: 前缀索引优化

前缀索引就是用某个字段中,字符串的前几个字符建立索引,比如我们可以在订单表上对商品名称字段的前 5 个字符建立索引。使用前缀索引是为了减小索引字段大小,可以增加一个索引页中存储的索引值,有效提高索引的查询速度。在一些大字符串的字段作为索引时,使用前缀索引可以帮助我们减小索引项的大小。

但是,前缀索引有一定的局限性,例如 order by 就无法使用前缀索引,无法把前缀索引用作覆盖索引。

覆盖索引优化

覆盖索引是指 SQL 中 query 的所有字段,在索引 B+tree 的叶子节点上都能找得到的那些索引,从辅助索引中查询得到记录,而不需要通过聚簇索引查询获得。假设我们只需要查询商品的名称、价格,有什么方式可以避免回表呢?

我们可以建立一个组合索引,即商品ID、名称、价格作为一个组合索引。如果索引中存在这些数据,查询将不会再次检索主键索引,从而避免回表。所以,使用覆盖索引的好处很明显,即不需要查询出包含整行记录的所有信息,也就减少了大量的 I/O 操作。

联合索引

联合索引时,存在最左匹配原则,也就是按照最左优先的方式进行索引的匹配。比如联合索引 (userpin, username),如果查询条件是 WHERE userpin=1 AND username=2,就可以匹配上联合索引;或者查询条件是 WHERE userpin=1,也能匹配上联合索引,但是如果查询条件是 WHERE username=2,就无法匹配上联合索引。

另外,建立联合索引时的字段顺序,对索引效率也有很大影响。越靠前的字段被用于索引过滤的概率越高,实际开发工作中建立联合索引时,要把区分度大的字段排在前面,这样区分度大的字段越有可能被更多的 SQL 使用到。

区分度就是某个字段 column 不同值的个数除以表的总行数,比如性别的区分度就很小,不适合建立索引或不适合排在联合索引列的靠前的位置,而 uuid 这类字段就比较适合做索引或排在联合索引列的靠前的位置。

区分度公式:pRlbc9

总结: sRgeTB

如何提高索引的速度

innodb的索引有哪些

联合索引的使用原则

联合索引在b树,b+树的结构是怎么样的,查找过程你了解过么

查找过程可以理解为:最左匹配原则 参考

如何创建索引

数据库的索引

mysql索引

mysql 索引慢分析(线上开启slowlog,提取慢查询,然后仔细分析explain 中 tye字段以及extra字段,发生的具体场景及mysql是怎么做的,被表扬回答的不错)

了解Mysql中的索引:涉3树是这样的一颗多路查找树,其中每一个节点都具有两个孩子及到什么是B树,与二叉搜索树和平衡二叉树区别,以及B+树区别

二叉排序树(Binary Sort Tree),又称为二叉查找树,它或者是一颗空树,或者是具有下列性质的二叉树:

  1. 若它的左子树不为空,则左子树上所有节点的值均小于它的根节点的值
  2. 若它的右子树不为空,则右子树上所有节点的值均大于它的根节点的值
  3. 它的左右子树也分别为二叉排序树

平衡二叉树(self-Balancing Binary Search Tree,也叫做AVL树),是一种二叉排序树,其中每一个节点的左右子树的高度差至多等于1 前提首先是一颗二叉排序树,而后需要保持平衡特性所以就叫做平衡二叉树。

2-(我们称为2节点)或三个孩子(我们称为3节点)

一个2节点包含一个元素和两个孩子(或没有孩子),且与二叉排序树类似,左子树包含的元素小于该元素,右子树包含的元素大于该元素,不过与二叉排序树不同的是,这个2节点要么没有孩子,要么就有两个,不能只有一个孩子。

一个3节点包含一小一大两个元素和三个孩子(或没有孩子),一个3节点要么没有孩子,要么具有3个孩子,如果某个3节点有孩子的话,左子树包含小于较小元素的元素,右子树包含大于较大元素的元素,中间元素包含介于两元素之间的元素。

2-3-4树包含了4节点的使用,一个4节点包含小中大三个元素和四个孩子(或没有孩子),一个4节点要么没有孩子,要么具有4个孩子,如果某个4节点有孩子的话,左子树包含小于最小元素的元素,第二子树包含大于最小元素、小于第二元素的元素,第三子树包含大于第二元素,小于最大元素的元素,右子树包含大于最大元素的元素

B树是一种平衡的多路查找树,2-3树与2-3-4树都是B树的特例,节点最大的孩子数目称为B树的阶。因此,2-3树是3阶B树,2-3-4树是4阶B树 IMG_0379

B+树参照《大话数据结构》P352

数据库的索引

数据库覆盖索引

假设我们有张表,结构如下:

create table user( id int(11) not null, age int(11) not null, primary key(id), key(age) );

覆盖索引指的是在一次查询中,如果一个索引包含或者说覆盖所有需要查询的字段的值,我们就称之为覆盖索引,而不再需要回表查询。

而要确定一个查询是否是覆盖索引,我们只需要explain sql语句看Extra的结果是否是“Using index”即可。

以上面的user表来举例,我们再增加一个name字段,然后做一些查询试试。

explain select * from user where age=1; //查询的name无法从索引数据获取 explain select id,age from user where age=1; //可以直接从索引获取

数据库聚集索引和辅助索引

MySQL 的索引

后面的问题:为什么用 b+树 b 和 b+区别

何时使用索引

索引是如何实现的?多种引擎的实现区别?聚族索引,非聚族索引,二级索引,唯一索引、最左匹配原则等等(非常重要)


mysql的锁类型

参考 + 自己总结的mysql MVCC中的涉及到锁的笔记

行锁什么时候变表锁

  1. 事务的隔离级别为:可重复读时,如果有索引(包括主键索引),以索引列为条件更新数据,会存在间隙锁,行锁,页锁,而锁住一些行。如果没有索引,更新数据时会锁住整张表。
  2. 事务隔离级别为:串行化时,读写数据都会锁住整张表。(一次只能一个连接玩表)

select语句什么时候行锁什么时候变表锁

InnoDB 的锁,与索引类型,事务的隔离级别相关。InnoDB 到底是行锁还是表锁取决于你的 SQL 语句。如果查询没有命中索引,也将退化为表锁。InnoDB 的行锁是实现在索引上的,而不是锁在物理行记录上。所以如果访问没有命中索引,也无法使用行锁,将要退化为表锁。

参考:InnoDB 的 select 行锁还是表锁

mysql的锁

用什么语句实现数据库锁

innodb的一些特性,和Myisam的,行锁表锁

各种锁相关,例如表锁,行锁,间隙锁,共享锁,排他锁。这些锁的出现主要是用来解决哪些问题?(重要)

间隙锁:间隙锁是可重复读级别下才会有的锁,结合MVCC和间隙锁可以解决幻读的问题。我们还是以user举例,假设现在user表有几条记录

e data-draft-node="block" data-draft-type="table" data-size="normal" data-row-style="normal"> 当我们执行:

begin; select * from user where age=20 for update; begin; insert into user(age) values(10); #成功 insert into user(age) values(11); #失败 insert into user(age) values(20); #失败 insert into user(age) values(21); #失败 insert into user(age) values(30); #失败

只有10可以插入成功,那么因为表的间隙mysql自动帮我们生成了区间(左开右闭)

(negative infinity,10],(10,20],(20,30],(30,positive infinity)

由于20存在记录,所以(10,20],(20,30]区间都被锁定了无法插入、删除。

如果查询21呢?就会根据21定位到(20,30)的区间(都是开区间)。

需要注意的是唯一索引是不会有间隙索引的。


存储引擎

innodb的一些特性,和Myisam的,行锁表锁

mysql中innodb引擎

知道哪些存储引擎

存储引擎在MySQL这个系统架构的哪个位置

innodb与myisam的区别

hZkhr0

mysql的存储引擎


设计

数据表设计遵循的原则

数据库三大范式

  1. 第一范式:每个列都不可以再拆分。
  2. 第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
  3. 第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。 在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由。比如性能。事实上我们经常会为了性能而妥协数据库的设计。

一范式为什么要列不可分割

设计学生成绩系统,如何建表,如何建索引

数据库表是如何设计的


其他问题

数据库完整性的实现

主键

介绍mysql

数据库中有两个关联列,请问在搜索时,这两个关联列会怎样?

Mysql怎么去查询的,什么时候走索引,什么时候不走

Mysql了解什么,有没有搭过mysql、redis

数据库的部署怎么做

读写分离具体怎么做

数据库优化+缓存(缓存的不一致,binlog解析器),读写分离,分库分表

mysql怎么存储时间

mysql把邮戳转化为日常格式时间的函数

sql慢查询

如果一个表查询,插入等很慢,你怎么做?

mysql 分布式id(项目用到的)

sql语句:查询所有课程分数都大于或等于80分的学生姓名

写个sql语句,两张表,学生表,学生课程表,求平均分大于85的学生的姓名

sql语句:表【学生|科目|成绩】查询每个学生成绩最好的科目和成绩 ( 没完全写出来

mysql 和 mongodb 的区别,什么时候用 mysql,什么时候用 mongo(因为简历上 有写 mongodb)

给定一张表 user(id,name,age)

有这样一个查询 select id,name,age where name=.... 如何建立索引,并且使用到覆盖索引

一个教务系统,包括学生信息,课程信息,考试安排,成绩查询,绩点分析,你来设 计数据库表应该怎么设计?

用你刚刚设计的表来给我查询出全校所有高数在 85 分一以上的人的名字

char 和 varchar

SQL:userId(主播 Id) de..Id(设备 Id),怎么实现对主播与主播之间的重合数(共同关注数)

SQL,一个表 User,字段 userid,username,city 查询用户数量最多的三个城市。

MySQL 的自增主键和随机生成主键的区别,为什么自增主键效率高。

  1. 使用自增主键插入的时候可以避免我们b+树的页分裂。也就是说,自增主键的插入数据模式,正符合了我们前面提到的递增插入的场景。每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂。而有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高。
  2. 除了考虑性能外,我们还可以从存储空间的角度来看。假设你的表中确实有一个唯一字段,比如字符串类型的身份证号,那应该用身份证号做主键,还是用自增字段做主键呢?由于每个非主键索引的叶子节点上都是主键的值。如果用身份证号做主键,那么每个二级索引的叶子节点占用约 20 个字节,而如果用整型做主键,则只要 4 个字节,如果是长整型(bigint)则是 8 个字节。显然,主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。

所以,从性能和存储空间方面考量,自增主键往往是更合理的选择。

有没有什么场景适合用业务字段直接做主键的呢?还是有的。比如,有些业务的场景需求是这样的:

  1. 只有一个索引;
  2. 该索引必须是唯一索引。

你一定看出来了,这就是典型的 KV 场景。 由于没有其他索引,所以也就不用考虑其他索引的叶子节点大小的问题。 这时候我们就要优先考虑上一段提到的“尽量使用主键查询”原则,直接将这个索引设置为主键,可以避免每次查询需要搜索两棵树。

如何查看数据库表结构?

如何提高 SQL 查询效率,在语句方面上的优化?

一条 sql 语句是如何执行的?进行更新时又是怎么处理的?

在 InnoDB 存储引擎下,一条 update 语句在 MySQL 内部执行大概会经历下面五个步骤: 1、执行器先找引擎取 id=2 这一行数据,如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。 2、执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。 3、引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。 4、执行器生成这个操作的 binlog,并把 binlog 写入磁盘。 5、执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

问题:

  1. 第2步调用引擎接口将数据写到哪里?内存还是磁盘上保存的数据:内存上
  2. 如果事务出错,需要回滚,回滚到了哪一步(1,2,3,4,5哪个),回滚到了第2步,此时执行一个相反的命令(通过Undo log)

日志相关:redolog,binlog,undolog,这些日志的实现原理,为了解决怎么问题?日志也是非常重要的吧,面试也问的挺多。

redolog 用于日志重放,宕机时,可以保证数据未提交的数据,正常提交 undolog 用于 mvccc ,事务回滚 binlog 是 server 层的,其它两个是引擎层的.binlog同步和恢复数据,

日志只要看这3篇文章就可以了:

  1. https://mp.weixin.qq.com/s/zjtYYPbBNuhgAHtf1zxFrQ
  2. https://mp.weixin.qq.com/s/Eb95fGhyvLThOF9uhQYVDg
  3. https://mp.weixin.qq.com/s/rmQYMGfgyfLlo69l6SBbLQ

redolog,binlog,undolog

b站的表同步好像就是订阅的binlog undo原子性,redo持久性 Vs9wv3c7HbAqajP

mvcc只有RC 和 RR级别有,2eKqO4 读提交和可重复读是 mvcc RR级别通过 Next-Key锁防止幻读

大家一般用哪个级别?我看互联网都是RC 并发量大的用RC

OrJxSocG74IfqzW

RR通过Next-key锁解决幻读 bosXwVDyOLq7MUK

RR有next-key lock解决了幻读 = RR没幻读 next-key lock 是 行锁 + 间隙锁 R42PgZiXnCBEhcOodDUzTrCpuBPIgv8fFxAwiCnBrlXW5 RR下会产生幻读 但是 InnoDB和XtraDB通过MVCC解决幻读了 MVCC是解决不可重复读

GckayhpHrtlz2sm

数据库的主从备份、如何保证数据不丢失、如何保证高可用等等。

如何保证多个服务器的数据一致性

我能想到的思路是从分布式出发 1.先从 cap 理论将其 2.然后从常用的 mysql 的主从同步是最终一致性将其 3.可以的话从服务发现分析(go的etcd,consult,还有常用的zk) 4.从业务场景出发,是保证强一致性(秒杀业务),还是最终一致性(实时性要求不高的业务)

建立一张数据库表需要考虑哪些因素,索引的底层实现,是否了解过b+树之外的索引结构。

一条sql语句的执行过程:https://mp.weixin.qq.com/s/xVWq3u-rekmg6HNGKRncng

wQhav0BMOVqLPyad1P

这里有点疑问:Ap0z8A 所以是先写 redo log 设置为 prepare ,再写 binlog,之后提交事务设置redolog 为 commit。因为 Master thread 在提交事务和每秒都会将 redo log buffer 刷进 redo log 里面 提交事务时,先将 redo log 刷进盘,标识为 prepare,之后 binlog 刷盘,再 redo log commmit 设置,完成事务

没有整理的题目: 设计题:设计一个购物车,给出应该有哪些数据表,每个表包含哪些字段

3、 InnoDB 的索引类型

  1. 手撕数据库连接池(质疑我没有考虑高并发时的同步和效率问题)

针对每个连接维护一个线程,开销很大,如何优化(其实我也不懂,就瞎扯了 NIO……)

情景题:显示热门评论,按赞数倒序排序,写SQL(当场去世)

select 字段名与 select *的区别(索引失效)

索引为什么用B+树而不是二叉树(当场去世)

1.mysql 的主从(不了解)

sql:三句查询,要求建立索引来优化查询

mysql,innodb 和 myisam 引擎区别

怎么实现查表时候的内连接?

数据库事务回滚方法

MySQL 的存储引擎用的是什么?为什么选 InnoDB?

  1. MySQL 的聚簇索引和非聚簇索引有什么区别?

  2. B+树和二叉树有什么区别和优劣?

  3. 针对一个场景设计索引,考察的是联合索引与列选择性的知识

现有一个新的查询场景, 要怎么解决?

  1. 假如要查 A in () AND B in (), 怎么建索引?

  2. 查 A in () AND B in () 时, MySQL 是怎么利用索引的?

  3. 假如查询 A in (), MySQL 是针对 N 个值分别查一次索引, 还是有更好的操作?

第二轮就难很多。问了 innodb 怎么实现,(我乱猜了 hashtable,后来看了网上是 lru。。 难为小哥听我这个错到离谱的答案了)

然后 dict 怎么实现,我说了 hash key,然后每个 key 后面有 linked list,然后问我如果 key collision 太多怎么办,回 答扩容,怎么扩,我说 copy 到新的地方,然后 copy 的时候 mutex_lock。感觉答错 了。。但面试官也没接着说。(妈耶我真的好菜)

贡献者: cvenwu

No Pains, No Gains