Skip to content
YoosenYoosen
主页
使用指南
算法
github icon
  • 主页
    • C++ Tutorial

      • C++笔记
        • Vector
        • 算法

          • 算法笔记
          • 面试

            • 我的面试
              • MySQL
                • 1.0 MySQL 的内部构造,一般可以分为哪两个部分
                  • 1.1 一条 SQL 语句的执行过程
                    • 1.2 Order By 是如何工作的
                      • 1.3 SQL 语句中各关键字的执行顺序
                        • 1.4 说一说 Drop,Delete,Truncate 的共同点和区别
                          • 1.5 InnoDB 和 MyIsam 对比
                            • 1.6 Char 和 Varchar 的区别
                              • 2.0 B 树和 B+ 树有什么不同?
                                • 2.1 为什么用 B+ 树做索引
                                  • 2.2 为什么创建索引
                                    • 2.3 什么时候不需要加索引?
                                      • 2.4 索引失效
                                        • 2.5 为什么选择自增 id 做为主键
                                          • 2.6 聚集索引和非聚集索引的区别
                                            • 2.7 Hash 索引和 B+ 树索引的区别
                                              • 2.8 什么是最左前缀
                                                • 2.9 什么是索引下推,为什么要进行索引下推
                                                  • 2.10 联合索引是如何存储的
                                                    • 2.11 索引的优缺点
                                                      • 2.12 唯一索引和主键索引
                                                        • 2.13 索引的应用场景
                                                          • 2.14 什么是普通索引?什么是唯一索引?
                                                            • 3.0 redolog
                                                              • 3.1 binlog
                                                                • 4.0 事务的特性
                                                                  • 4.1 隔离级别
                                                                    • 4.2 什么是幻读,脏读,不可重复读?
                                                                      • 4.3 事务的几种特性是通过什么实现的?
                                                                        • 4.4 MySQL 如何保证一致性的
                                                                          • 4.5 如何保证原子性的
                                                                            • 4.6 如何保证持久性的
                                                                            • 5.0 MyIsam锁和InnoDB锁有什么不同?
                                                                              • 5.1 全局锁和表锁
                                                                                • 5.2 死锁和死锁检测
                                                                                  • 6.0 什么是MVCC
                                                                                    • 6.1 什么是当前读和快照读
                                                                                      • 6.2 MVCC实现了哪些隔离级别
                                                                                      • 7.0 MySQL优化
                                                                                        • 7.1 explain详解
                                                                                          • 7.2 MySQL慢查询优化
                                                                                            • 7.3 分库分表
                                                                                              • 8.0如何保证幂等性
                                                                                                • 8.1 分页查询
                                                                                                  • 8.2 如何分库分表
                                                                                                  • 操作系统
                                                                                                    • 计算机网络
                                                                                                    • 幻灯片页

                                                                                                      MySQL

                                                                                                      calendar icon2022年6月1日category icon
                                                                                                      • 面试
                                                                                                      timer icon大约 28 分钟

                                                                                                      此页内容
                                                                                                      • 1.0 MySQL 的内部构造,一般可以分为哪两个部分
                                                                                                      • 1.1 一条 SQL 语句的执行过程
                                                                                                      • 1.2 Order By 是如何工作的
                                                                                                      • 1.3 SQL 语句中各关键字的执行顺序
                                                                                                      • 1.4 说一说 Drop,Delete,Truncate 的共同点和区别
                                                                                                      • 1.5 InnoDB 和 MyIsam 对比
                                                                                                      • 1.6 Char 和 Varchar 的区别
                                                                                                      • 2.0 B 树和 B+ 树有什么不同?
                                                                                                      • 2.1 为什么用 B+ 树做索引
                                                                                                      • 2.2 为什么创建索引
                                                                                                      • 2.3 什么时候不需要加索引?
                                                                                                      • 2.4 索引失效
                                                                                                      • 2.5 为什么选择自增 id 做为主键
                                                                                                      • 2.6 聚集索引和非聚集索引的区别
                                                                                                      • 2.7 Hash 索引和 B+ 树索引的区别
                                                                                                      • 2.8 什么是最左前缀
                                                                                                      • 2.9 什么是索引下推,为什么要进行索引下推
                                                                                                      • 2.10 联合索引是如何存储的
                                                                                                      • 2.11 索引的优缺点
                                                                                                      • 2.12 唯一索引和主键索引
                                                                                                      • 2.13 索引的应用场景
                                                                                                      • 2.14 什么是普通索引?什么是唯一索引?
                                                                                                      • 3.0 redolog
                                                                                                      • 3.1 binlog
                                                                                                      • 4.0 事务的特性
                                                                                                      • 4.1 隔离级别
                                                                                                      • 4.2 什么是幻读,脏读,不可重复读?
                                                                                                      • 4.3 事务的几种特性是通过什么实现的?
                                                                                                        • 4.4 MySQL 如何保证一致性的
                                                                                                        • 4.5 如何保证原子性的
                                                                                                        • 4.6 如何保证持久性的
                                                                                                      • 5.0 MyIsam锁和InnoDB锁有什么不同?
                                                                                                      • 5.1 全局锁和表锁
                                                                                                      • 5.2 死锁和死锁检测
                                                                                                      • 6.0 什么是MVCC
                                                                                                        • 6.1 什么是当前读和快照读
                                                                                                        • 6.2 MVCC实现了哪些隔离级别
                                                                                                      • 7.0 MySQL优化
                                                                                                      • 7.1 explain详解
                                                                                                      • 7.2 MySQL慢查询优化
                                                                                                      • 7.3 分库分表
                                                                                                      • 8.0如何保证幂等性
                                                                                                      • 8.1 分页查询
                                                                                                      • 8.2 如何分库分表

                                                                                                      MySQL的面试题目

                                                                                                      # 1.MySQL 基础

                                                                                                      # 1.0 MySQL 的内部构造,一般可以分为哪两个部分

                                                                                                      可以分为服务层和存储引擎层两部分

                                                                                                      服务层:连接器,查询缓存,分析器,优化器,执行器等,提供了MySQL 大部分的核心服务功能,以及内置函数,所有的跨存储引擎的功能都在这一层实现。

                                                                                                      存储引擎负责数据的存储和提取。

                                                                                                      # 1.1 一条 SQL 语句的执行过程

                                                                                                      1.首先进行链接,链接时进行验证,保证安全性,查看执行权限

                                                                                                      2.查看缓存,是否有这条语句的执行缓存,如果有缓存则直接返回缓存

                                                                                                      3.连接器,分析器(这里查看语法是否有错误),优化器,执行器,搜索引擎

                                                                                                      这一块也是常考重点

                                                                                                      # 1.2 Order By 是如何工作的

                                                                                                      推荐阅读:“order by”是怎么工作的?open in new window

                                                                                                      # 1.3 SQL 语句中各关键字的执行顺序

                                                                                                      推荐阅读:关于sql和MySQL的语句执行顺序(必看)open in new window

                                                                                                      1. SELECT 
                                                                                                      2. DISTINCT <select_list>
                                                                                                      3. FROM <left_table>
                                                                                                      4. <join_type> JOIN <right_table>
                                                                                                      5. ON <join_condition>
                                                                                                      6. WHERE <where_condition>
                                                                                                      7. GROUP BY <group_by_list>
                                                                                                      8. HAVING <having_condition>
                                                                                                      9. ORDER BY <order_by_condition>
                                                                                                      10.LIMIT <limit_number>
                                                                                                      
                                                                                                      1
                                                                                                      2
                                                                                                      3
                                                                                                      4
                                                                                                      5
                                                                                                      6
                                                                                                      7
                                                                                                      8
                                                                                                      9
                                                                                                      10

                                                                                                      执行顺序

                                                                                                      FROM
                                                                                                      <表名> # 笛卡尔积
                                                                                                      ON
                                                                                                      <筛选条件> # 对笛卡尔积的虚表进行筛选
                                                                                                      JOIN <join, left join, right join...> 
                                                                                                      <join表> # 指定join,用于添加数据到on之后的虚表中,例如left join会将左表的剩余数据添加到虚表中
                                                                                                      WHERE
                                                                                                      <where条件> # 对上述虚表进行筛选
                                                                                                      GROUP BY
                                                                                                      <分组条件> # 分组
                                                                                                      <SUM()等聚合函数> # 用于having子句进行判断,在书写上这类聚合函数是写在having判断里面的
                                                                                                      HAVING
                                                                                                      <分组筛选> # 对分组后的结果进行聚合筛选
                                                                                                      SELECT
                                                                                                      <返回数据列表> # 返回的单列必须在group by子句中,聚合函数除外
                                                                                                      DISTINCT
                                                                                                      # 数据除重
                                                                                                      ORDER BY
                                                                                                      <排序条件> # 排序
                                                                                                      LIMIT
                                                                                                      <行数限制>
                                                                                                      
                                                                                                      1
                                                                                                      2
                                                                                                      3
                                                                                                      4
                                                                                                      5
                                                                                                      6
                                                                                                      7
                                                                                                      8
                                                                                                      9
                                                                                                      10
                                                                                                      11
                                                                                                      12
                                                                                                      13
                                                                                                      14
                                                                                                      15
                                                                                                      16
                                                                                                      17
                                                                                                      18
                                                                                                      19
                                                                                                      20
                                                                                                      21

                                                                                                      # 1.4 说一说 Drop,Delete,Truncate 的共同点和区别

                                                                                                      truncate drop delete 都表示删除,不过还是有一些不同

                                                                                                      Drop 会安全删除表,并清空内存,不会触发触发器,这个命令不能回滚。

                                                                                                      delete 用来删除表中的全部或者部分行,执行 delete 之后,可以删除或者撤销删除,内存不会被清空,会触动触发器,继续插入新值时,会继续之前的 id,可以是table 和 view

                                                                                                      truncate 不会触动触发器,会恢复内存,视图和索引不会产生影响,还会重置自增值。且只能针对表。

                                                                                                      推荐阅读:SQL语句中----删除表数据drop、truncate和delete的用法open in new window

                                                                                                      # 1.5 InnoDB 和 MyIsam 对比

                                                                                                      事务:InnoDB 支持事务,MyIsam 不支持事务

                                                                                                      索引:InnoDB 主键索引存的是数据,MyIsam存的是地址

                                                                                                      推荐阅读:MyISAM与InnoDB两者之间区别与选择,详细总结,性能对比open in new window

                                                                                                      # 1.6 Char 和 Varchar 的区别

                                                                                                      char 则是固定长度,长度设定几为几,不足会进行补齐

                                                                                                      varchar 则为可变长度,当不足长度时,则按照存储的长度。

                                                                                                      char 的效率高一些。

                                                                                                      推荐阅读:varchar与char有什么区别?open in new window

                                                                                                      # 2 索引

                                                                                                      # 2.0 B 树和 B+ 树有什么不同?

                                                                                                      因为B 中的每个节点都存储了key 和data,而B+树仅仅在叶子节点存储了key 和 data 也就是说B+树的数据都是存在叶子节点上的,这让我们使得非叶子节点可以存取更多的key,进而让B+树更加矮胖一些。

                                                                                                      因为索引树大部分情况下不能一次 I/O就读取到内存中的,树的深度越浅,查询的效率就越高一些,另外还有就是,B+树的叶子节点,是通过双向链表进行连接的,我们进行范围查询时效率更高,可以向前查找向后查找,另外B树的话,只能再次从根节点进行查询。

                                                                                                      image-20211014122519391

                                                                                                      image-20211014122617005

                                                                                                      # 2.1 为什么用 B+ 树做索引

                                                                                                      我们先来分析下使用其他数据结构作为索引时的缺点

                                                                                                      二叉搜索树

                                                                                                      当数据是单调递增或递减时,则会退化成链表

                                                                                                      AVL树

                                                                                                      因为维护二叉平衡树的开销比收益要大的多,我们作为索引的数据结构,更多的要求局部,而不是非常严格的平衡的红黑树。不过对于插入较少,查找较多的场景AVL的性能还是较高的。另外AVL树的每个节点只存储一个键值和数据。

                                                                                                      红黑树

                                                                                                      红黑树是一个弱平衡树,但是随着插入数据过多,查询数据时造成的I/O消耗也是巨大的,因为我们很多时候,一次查询并不能将所有数据全部存入内存中,深度过深的话,会加大I/O开销。

                                                                                                      B树和B+树的比较在上面

                                                                                                      推荐阅读:为什么MySQL数据库索引选择使用B+树? open in new window

                                                                                                      # 2.2 为什么创建索引

                                                                                                      通过创建索引能够大大加大我们的查询速度,比如我们寻找书中的知识,多是通过目录查找。

                                                                                                      也就是可以让数据有序化,将随机查找,转换为顺序查找,然后对于表的链接也有很大的帮助。

                                                                                                      唯一索引的话,还可以保证每一行的唯一性。

                                                                                                      常用的几种索引类型

                                                                                                      • 哈希索引:可以直接通过关键字查询到数据,键值对,指定查询效率更高
                                                                                                      • 数组索引:数组索引等值查询和范围查询效果较好,但是插入新数据的时候,需要做大量移动,降低性能
                                                                                                      • B+树索引:InnoDB的数据都是存储在B+树上的。每一个索引在InnoDB里面对应一棵B+树

                                                                                                      推荐阅读:为什么要建立索引?open in new window

                                                                                                      # 2.3 什么时候不需要加索引?

                                                                                                      • 数据量少的时候可以不需要加索引

                                                                                                      • 更新比较频繁的数据

                                                                                                      • 区分度较低的属性(性别)

                                                                                                      # 2.4 索引失效

                                                                                                      测试版本 8.0.26

                                                                                                      • where语句为 % a 时的模糊查询(不走)

                                                                                                      • 使用 a or b 进行查询时(a,b都有索引的定值查询时走,其余不走)索引合并

                                                                                                      • in (走)not in (不走)

                                                                                                      • is null 等字段(无记录,不走)

                                                                                                      • ≠ <> 等字段(不走)

                                                                                                      # 2.5 为什么选择自增 id 做为主键

                                                                                                      • 自增主键的插入数据模式,符合递增插入的场景。

                                                                                                      • 每次插入一条新记录,都是追加操作,不会挪动其他数据,也不会触发叶子节点的分裂。

                                                                                                      • 然后使用其他数据做主键,不容易保证有序插入,成本较高

                                                                                                      当我们只有一个索引的时候,然后该索引未唯一索引可以不使用自增id 做主键。

                                                                                                      推荐阅读:为理解MySQL(InnoDB)索引的设计原理,我编了个故事open in new window

                                                                                                      # 2.6 聚集索引和非聚集索引的区别

                                                                                                      • 聚集索引又称为主键索引,非聚集索引又称为二级索引
                                                                                                      • 主键索引每个表里只能有一个,二级索引可以有多个
                                                                                                      • 另外主键索引的存的值为元组,也就是数据,二级索引存的值为主键,获取数据还需要进行一次回表操作

                                                                                                      推荐阅读:聚集索引和非聚集索引的区别open in new window

                                                                                                      # 2.7 Hash 索引和 B+ 树索引的区别

                                                                                                      Hash索引不支持范围查询,B+树支持

                                                                                                      另外Hash索引等值查询效率会高一些

                                                                                                      B+树索引支持联合索引,比如(a,b),索引复用。

                                                                                                      B+树索引支持,order by 排序

                                                                                                      模糊查询时同样可以使用B+树索引。

                                                                                                      # 2.8 什么是最左前缀

                                                                                                      当我们创建组合索引时,我们会将访问最频繁的字段放到最前面,比如(a,b,c),我们进行数据查询时,可以不完全使用索引的全部定义,只要满足左前缀就可使用索引查询。

                                                                                                      创建这个索引就相当于创建了 a, ab, abc 三个索引。

                                                                                                      推荐阅读:MySQL 最左前缀原则open in new window

                                                                                                      # 2.9 什么是索引下推,为什么要进行索引下推

                                                                                                      查询时,如果满足最左前缀,则可以利用最左前缀原则进行查询。

                                                                                                      比如我们需要查找,名字姓张,年龄为 10 岁的孩子,如果我们此时创建了(名字,年龄)的联合索引,则可以查询到 名字和年龄对应的主键,然后再进行回表查询到数据。

                                                                                                      我们如果对索引进行下推的话,则可以对联合索引查询到的值进行过滤,删除掉符合名字但是不符合年龄的数据。减少回表次数。

                                                                                                      推荐阅读:Mysql:好好的索引,为什么要下推? open in new window

                                                                                                      # 2.10 联合索引是如何存储的

                                                                                                      image-20211012141519351

                                                                                                      很容易理解,key为联合属性,value 为ID

                                                                                                      # 2.11 索引的优缺点

                                                                                                      优点

                                                                                                      可以保证我们数据的唯一性

                                                                                                      查询速度更快,节省查询时间

                                                                                                      缺点

                                                                                                      创建索引和维护索引要耗费时间

                                                                                                      索引需要占物理空间,除了数据表占用数据空间之外,每一个索引还要占用一定的物理空间

                                                                                                      以表中的数据进行增、删、改的时候,索引也要动态的维护。

                                                                                                      # 2.12 唯一索引和主键索引

                                                                                                      聚集索引并不一定是唯一索引。
                                                                                                      主键是唯一的,所以创建了一个主键的同时,也就这个字段创建了一个唯一的索引,

                                                                                                      唯一索引实际上就是要求指定的列中所有的数据必须不同
                                                                                                      1 一个表的主键只能有一个,而唯一索引可以建多个。
                                                                                                      2 主键可以作为其它表的外键。
                                                                                                      3 主键不可为null,唯一索引可以为null。

                                                                                                      # 2.13 索引的应用场景

                                                                                                      保证幂等性时创建唯一索引

                                                                                                      搜索功能时可以使用索引

                                                                                                      范围查询时可以使用索引

                                                                                                      # 2.14 什么是普通索引?什么是唯一索引?

                                                                                                      普通索引:查询到第一个满足条件的记录后,继续向后遍历,直到第一个不满足条件的记录。

                                                                                                      唯一索引:查询到第一个满足条件的记录后,直接停止继续检索。

                                                                                                      这两个在查询时的性能差距是微乎其微。

                                                                                                      当更新时,如果更新的数据页是在内存中,则直接更新,如果不在内存中,则先存入 changebuffer中,后面访问该数据页时,再进行更新。这种适合存入后,短时间内不会访问的数据。

                                                                                                      唯一索引的情况则不需要使用 changebuffer这是因为,我们需要先判断唯一性,这个过程是要存入内存的,所以当验证成功之后,直接插入即可。

                                                                                                      1.Page在内存中,直接更新内存

                                                                                                      2.page没有在内存中,则在changepage区域,记录下我要往page2插入一行信息

                                                                                                      3.将上诉两个动作记入redo log中。

                                                                                                      另外我们每次查找数据并不一定要在磁盘中查找,可以在内存中直接查找。

                                                                                                      redo log 主要节省的是随机写磁盘的 IO 消耗(转成顺序写),而 change buffer 主要节省的则是随机读磁盘的 IO 消耗。

                                                                                                      # 3.持久化

                                                                                                      # MySQL持久化机制

                                                                                                      # 3.0 redolog

                                                                                                      redolog是innoDB特有的技术

                                                                                                      WAL技术:WAL技术的全称是write-Ahead logging ,他的关键字,就是先写粉板,等不忙的时候再也账本。此时的粉板就是redolog,账本也就是磁盘。

                                                                                                      注意:这个redolog 也是在磁盘里的,只不过其是顺寻I/O速度更快。

                                                                                                      InnoDB redolog的大小是固定大小的,比如一组可以为4个文件,每个1G,当redolog快要满的时候,则需要及性能存入磁盘。

                                                                                                      # 3.1 binlog

                                                                                                      binlog是在server层日志

                                                                                                      所有引擎都可以实现

                                                                                                      redolog是物理日志,记录在某个数据页上做了什么修改

                                                                                                      binlog是逻辑日志,比如某条记录的某个字段+1

                                                                                                      redolog是循环写的,binlog是可以追加写入的

                                                                                                      两阶段提交,是为了让两份日志之间逻辑一致,等两份日志,逻辑一致时,才进行提交。

                                                                                                      img

                                                                                                      图片来自IT届农民工

                                                                                                      推荐阅读:Mysql 中写操作时保驾护航的三兄弟! open in new window

                                                                                                      # 4.事务

                                                                                                      # 4.0 事务的特性

                                                                                                      原子性:要做就做完,要么就不做

                                                                                                      一致性:修改前后,不会破坏数据的完整性,无论转账成功是否,不会破坏两个人的账户总金额。

                                                                                                      持久性:修改完之后,永久有效

                                                                                                      隔离性:多个事务并发操作时,互不干扰井水不犯河水。

                                                                                                      # 4.1 隔离级别

                                                                                                      读未提交:其他事务未提交即可读

                                                                                                      读提交:提交后即可读

                                                                                                      可重复读:两次读的值一致

                                                                                                      串行化

                                                                                                      image-20211012143745523

                                                                                                      MySQL 默认的是,可重复读,Oracle 是读提交。

                                                                                                      隔离级别越低,事务请求的锁越少或保持锁的时间越短

                                                                                                      推荐阅读:MySQL 事务open in new window

                                                                                                      # 4.2 什么是幻读,脏读,不可重复读?

                                                                                                      事务A、B交替执行,事务A被事务B干扰到了,因为事务A读取到事务B未提交的数据,这就是脏读。

                                                                                                      在一个事务范围内,两个相同的查询,读取同一条记录,却返回了不同的数据,这就是不可重复读。

                                                                                                      事务A查询一个范围的结果集,另一个并发事务B往这个范围中插入/删除了数据,并静悄悄地提交,然后事务A再次查询相同的范围,两次读取得到的结果集不一样了,这就是幻读。

                                                                                                      # 4.3 事务的几种特性是通过什么实现的?

                                                                                                      隔离性:隔离性是通过加锁来实现的,

                                                                                                      其他三大特性主要是借助,redo log 和 undo log 来实现的。

                                                                                                      # 4.4 MySQL 如何保证一致性的

                                                                                                      这个从两个层面来讲,MySQL的一致性,是通过原子性,隔离性,持久性来实现的,也就是说一致性是目的,其他三种都是手段。

                                                                                                      # 4.5 如何保证原子性的

                                                                                                      原子性主要是通过undolog来实现的,因为这个日志里面记录的操作的相反操作,比如insert ,则我们可以通过查询回滚日志,来进行撤销操作。这也就让我们可以回滚事务。

                                                                                                      1)当你delete一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据 2)当你update一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作 3)当年insert一条数据的时候,就需要这条记录的主键,回滚的时候,根据主键执行delete操作 undo log记录了这些回滚需要的信息,当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。

                                                                                                      # 4.6 如何保证持久性的

                                                                                                      持久性,则是通过redolog来实现的,主要是使用了WAL技术,用户在执行一个操作的时候,会同时将这个操作的结果写到内存和redolog中。

                                                                                                      那为什么不直接写到磁盘上呢?我们如果每次都写到磁盘上则需要,每次都进行随机IO,而我们的redolog是顺序IO可以直接在后面追加,因为内存是易失的,所以我们通过 redolog 来进行过渡。

                                                                                                      redolog的大小是固定的,比如四个一组,每个redolog占用 1G内存。

                                                                                                      # 5.锁

                                                                                                      # 5.0 MyIsam锁和InnoDB锁有什么不同?

                                                                                                      MyIsam只支持表锁,一锁就锁住整个表,InnoDB的锁有表级锁和行级锁

                                                                                                      # 5.1 全局锁和表锁

                                                                                                      全局锁:我们通常在不支持事务的搜索引擎使用,我们使用FTWRL,但是我们需要注意的是,我们使用的时候,会让全库处于只读状态,这是非常恐怖的。

                                                                                                      表级锁:指令 lock tables read/wirte 这是表级锁,通过锁住整个表来保证,另一种表级锁是MDL锁,不需要显示使用,他是自动实现的,读锁之间不互斥,读写之间互斥,X锁和R锁

                                                                                                      行锁:因为行锁是在需要的时候才加上的,commit之后释放所以我们可以调整事务中,对某行的操作顺序,进而调整对操作较频繁的锁的持有时间。

                                                                                                      间隙锁:锁住间隙

                                                                                                      推荐阅读:关于MySQL常见的所有锁,已汇总整理完毕open in new window

                                                                                                      # 5.2 死锁和死锁检测

                                                                                                      对于有行锁的搜索引擎,我们就有可能出现死锁,因为其是commit之后才会事务才会释放锁。遇到死锁之后,有这样两种情况解决。

                                                                                                      1.设置超时时间,如果没有获取到锁的话, 直接放弃(默认是50秒)

                                                                                                      2.通过死锁检测,然后进行回滚某个事务。(主动死锁检测默认是开启的)

                                                                                                      可以通过deadlock—detect进行检测。但是这样也是有负担的,每个线程都需要进行检测,则就会增大延迟。

                                                                                                      推荐阅读:搜狐三面:说说你是怎么解决MySQL死锁问题的!open in new window

                                                                                                      # 6. Mysql 高可用

                                                                                                      # MVCC机制

                                                                                                      # 6.0 什么是MVCC

                                                                                                      事务的几种特性是,原子性,一致性,持久性,隔离性,然后主要的特性就是一致性,其他三种都是为了实现一致性而准备的。

                                                                                                      MVCC:多版本并发控制,这样每条记录就能够生成一个版本链,每个事务可以查看不同的版本。

                                                                                                      img

                                                                                                      图片来源:Yes的练级攻略

                                                                                                      # 6.1 什么是当前读和快照读

                                                                                                      当前读:当前读就是读和写之前,对其加锁,比如共享锁和排他锁,他们读到的是数据库的最新版本。

                                                                                                      快照读:就是在不加锁的select 语句里,但是此时的隔离级别不能是串行化调度,因为串行化调度会退化到当前读,快照读的出现就是为了实现并发控制,其实现就是基于MVCC,快照读读到的不一定是最新状态,有可能是历史状态。

                                                                                                      工作原理:事务在启动前会生成一个全库级别的快照,我们可能会想数据库的数据可是非常庞大的,全局数据快照是不是很夸张,数据表中的一行记录,其实可能有多个版本 (row),每个版本有自己的 row trx_id。

                                                                                                      # 6.2 MVCC实现了哪些隔离级别

                                                                                                      读提交和可重复读,读未提交的话,则是当前读,每次读到的都是最新的版本,然后串行化调度的话,则是通过加锁的方式进行读写,如果没有MVCC的话,则会在这两个隔离级别下产生冲突,产生脏读等。

                                                                                                      如果有了版本控制的话,对于没有提交的事务,我们读的时候则可以读之前的版本,这样就不会发生读写冲突了。

                                                                                                      另外并非是,真的存储多个版本,因为数据库的数据量太庞大了,如果每个版本都是一个新的快照,那根本存不下,其实这个是根据undolog来实现的,通过反向操作获取。

                                                                                                      这样看起来就是多个版本了。

                                                                                                      MVCC是如何实现的?

                                                                                                      当某个数据被存入时,比如 id = 1, name = XXX,此时不仅会存入这两个值,还会存入,两个隐藏字段,trx_id

                                                                                                      和 roll_pointer

                                                                                                      所以,InnoDB 可以通过,回滚日志来复原当前操作的反向操作。

                                                                                                      img

                                                                                                      另外需要注意的是,当某个插入型事务提交之后,对应的undolog则会被回收,因为没有人会再访问之前的数据了

                                                                                                      当某个事务执行更新操作时,则会有下面这种情况

                                                                                                      img

                                                                                                      图片来源:Yes的练级攻略

                                                                                                      另一个事务也执行更新操作时则会这样存储回滚日志

                                                                                                      img

                                                                                                      图片来源:Yes的练级攻略

                                                                                                      没错,修改型事务提交之后,不会被立刻删除,而是会追加,我们则可以根据undolog 访问之前的版本。好啦,版本的事到这我们就理解了,然后我们说一个Readview,这个就是用来判断哪个版本对哪个用户可见,哪个不可见的。

                                                                                                      # 读已提交隔离级别(不可重复读)

                                                                                                      这里有四个规则

                                                                                                      • creator_trx_id,当前事务ID。

                                                                                                      • m_ids,生成 readView 时还活跃的事务ID集合,也就是已经启动但是还未提交的事务ID列表。

                                                                                                      • min_trx_id,当前活跃ID之中的最小值。(最早开启的一个id)

                                                                                                      • max_trx_id,生成 readView 时 InnoDB 将分配给下一个事务的 ID 的值(事务 ID 是递增分配的,越后面申请的事务ID越大)

                                                                                                      对于可见版本的判断是从最新版本开始沿着版本链逐渐寻找老的版本,如果遇到符合条件的版本就返回。

                                                                                                      判断条件如下:

                                                                                                      • 如果当前数据版本的 trx_id ==  creator_trx_id 说明修改这条数据的事务就是当前事务,所以可见。

                                                                                                      • 如果当前数据版本的 trx_id < min_trx_id,说明修改这条数据的事务在当前事务生成 readView 的时候已提交,所以可见。

                                                                                                      • 如果当前数据版本的 trx_id 在 m_ids 中,说明修改这条数据的事务此时还未提交,所以不可见。

                                                                                                      • 如果当前数据版本的 trx_id >= max_trx_id,说明修改这条数据的事务在当前事务生成 readView 的时候还未启动,所以不可见(结合事务ID递增来看)。

                                                                                                      # 可重复读

                                                                                                      现在的隔离级别是可重复读。

                                                                                                      可重复读和读已提交的 MVCC 判断版本的过程是一模一样的,唯一的差别在生成 readView 上。

                                                                                                      上面的读已提交每次查询都会重新生成一个新的 readView ,而可重复读在第一次生成  readView 之后的所有查询都共用同一个 readView 。

                                                                                                      也就是说可重复读只会在第一次 select 时候生成一个 readView ,所以一个事务里面不论有几次 select ,其实看到的都是同一个 readView 。

                                                                                                      套用上面的情况,差别就在第二次执行select name where id 1,不会生成新的 readView,而是用之前的 readView,所以第二次查询时:

                                                                                                      • m_ids 还是为 [5,6],虽说事务 5 此时已经提交了,但是这个readView是在事务5提交之前生成的,所以当前还是认为这两个事务都未提交,为活跃的。

                                                                                                      • 此时 min_trx_id,为 5。

                                                                                                      (对于判断过程有点卡顿的同学可以再拉上去看看,判断版本的过程和读已提交一致)。

                                                                                                      所以在可重复级别下,两次查询得到的 name 都为 XX,所以叫可重复读。

                                                                                                      推荐阅读:一个 MVCC 和面试官大战 30 回合open in new window

                                                                                                      这篇文章非常牛批,强烈推荐

                                                                                                      # 7.优化

                                                                                                      # 7.0 MySQL优化

                                                                                                      建表优化

                                                                                                      • 尽量使用数字型字段,因为查询和链接时,只需比较一次,效率更快

                                                                                                      • 尽量使用varchar少使用char,变长字段空间少,更省空间

                                                                                                      • 对于区分度较低的索引,也就是有大量重复的索引,我们可以对其删除。

                                                                                                      查询优化

                                                                                                      • 少使用select * 语句

                                                                                                      • 尽量少使用模糊查询 %a

                                                                                                      • 不使用 not in ,in,这样会导致走全表索引

                                                                                                      • 不实用 a or b 方式进行查询,会使其不走索引

                                                                                                      • 尽量少使用子查询(因为需要创建临时表,还需要删除临时表)

                                                                                                      索引优化

                                                                                                      不要创建太多索引,多使用组合索引

                                                                                                      对经常 order by 的语句创建索引

                                                                                                      # 7.1 explain详解

                                                                                                      列名描述
                                                                                                      id在一个大的查询语句中每个SELECT关键字都对应一个唯一的id
                                                                                                      select_typeSELECT关键字对应的那个查询的类型
                                                                                                      table表名
                                                                                                      partitions匹配的分区信息
                                                                                                      type针对单表的访问方法
                                                                                                      possible_keys可能用到的索引
                                                                                                      key实际上使用的索引
                                                                                                      key_len实际使用到的索引长度
                                                                                                      ref当使用索引列等值查询时,与索引列进行等值匹配的对象信息
                                                                                                      rows预估的需要读取的记录条数
                                                                                                      filtered某个表经过搜索条件过滤后剩余记录条数的百分比
                                                                                                      Extra一些额外的信息

                                                                                                      id:一个select一个id,对于连接查询来说他们只有一个 select 但是因为需要使用两个表,所以会出现两行数据,但是是相同id。另外优化器会对子查询进行重写,将子查询变成连接查询,所以我们只需要看有几个id就可以判断是否进行了重写。

                                                                                                      如果id 为null则是创建的临时表。

                                                                                                      推荐阅读:explain | 索引优化的这把绝世好剑,你真的会用吗?open in new window

                                                                                                      # 7.2 MySQL慢查询优化

                                                                                                      先来说一下MySQL慢查询

                                                                                                      MySQL的慢查询,全名是慢查询日志 ,是MySQL中提供的一种日志记录,用来记录响应时间超过(等于的不会记录下来)阈值的SQL语句。

                                                                                                      设定值为long_query_time = 10,则会记录运行时间超过 10 秒的执行语句。

                                                                                                      需要手动开启

                                                                                                      有可能导致慢查询的原因

                                                                                                      • 索引未设计好

                                                                                                      可以现在备库上执行

                                                                                                      • 查询语句没有写好

                                                                                                      优化查询语句,有可能出现,没有命中索引的情况

                                                                                                      • 选错索引

                                                                                                      此时我们使用 force index 命令,强制使用索引

                                                                                                      • 脏页问题

                                                                                                      另一种还是我们上边所提到的刷脏页情况,只不过和写操作不同的是,是在读时候进行刷脏页的。

                                                                                                      是不是有点懵逼,别急,听我娓娓道来:

                                                                                                      为了避免每次在读写数据时访问磁盘增加 IO 开销,Innodb 存储引擎通过把相应的数据页和索引页加载到内存的缓冲池(buffer pool)中来提高读写速度。然后按照最近最少使用原则来保留缓冲池中的缓存数据。

                                                                                                      那么当要读入的数据页不在内存中时,就需要到缓冲池中申请一个数据页,但缓冲池中数据页是一定的,当数据页达到上限时此时就需要把最久不使用的数据页从内存中淘汰掉。但如果淘汰的是脏页呢,那么就需要把脏页刷到磁盘里才能进行复用。

                                                                                                      推荐阅读:最全 MySQL 优化方法,从此优化不再难open in new window

                                                                                                      # 7.3 分库分表

                                                                                                      垂直分表和水平分表

                                                                                                      垂直分表,就是将不常用的字端分到一个表中

                                                                                                      垂直分库:按照业务将表进行分类,分布到不同的数据库上面,每个库可以放在不同的服务器上,它的核心理念是专库专用。

                                                                                                      水平分表:是在同一个数据库内,把同一个表的数据按一定规则拆到多个表中 ,求模等就是

                                                                                                      水平分库:是把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器上。

                                                                                                      # 8.常考其他

                                                                                                      # 8.0如何保证幂等性

                                                                                                      幂等性概念:一次请求和多次请求造成的结果是一样的,不会因为多次点击而产生副作用。

                                                                                                      什么情况会又可能会造成幂等性

                                                                                                      • 多次点击 from 表单

                                                                                                      • 网络的情况不好,超时问题,多次提交

                                                                                                      • 消息队列出现超时问题

                                                                                                      MySQL中可能出现幂等性问题的语句

                                                                                                      1. insert操作,可能会出现重复值,但是自增 id 不一样

                                                                                                      2. update情况,update 直接修改不会,但是那种修改数量 +1减1的情况,则有可能造成。

                                                                                                      针对上面两种情况,我们应该如何处理

                                                                                                      高并发情况下保证幂等性

                                                                                                      1.对于insert操作,我们可以这样操作,先通过字段进行搜索,查看是否有该值。如果存在则执行 update或不进行操作,如果没有则 insert。

                                                                                                      2.我们可以通过悲观锁来实现,我们进行修改前先对其加锁,修改完成后再对其解锁,因为我们有insert 之前先判断的习惯,所以这样就能保证幂等性。

                                                                                                      3.使用乐观锁来实现,我们给数据加上版本号,修改前如果和第一次查询的版本号一致,则进行修改,同样可以保证幂等性。

                                                                                                      4.加唯一索引,我们可以通过唯一索引来解决,我们第一次可以添加成功,第二次的时候则不能添加成功。

                                                                                                      5.建立防重表,可以专门建立一个表,防止重复,这个表里存储的数据只有id和唯一索引的属性组,当我们能够插入防重表时,再通过防重表插入数据库。

                                                                                                      6.根据状态机这个就是订单生成的位置,通过订单状态来进行一个修改。如果我们第一次修改了状态之后,第二次就无法进行一个修改。

                                                                                                      7.通过分布式锁,也就是有缓存的情况,通过setnx来对缓存中数据加锁

                                                                                                      推荐阅读:高并发下如何保证接口的幂等性?open in new window

                                                                                                      # 8.1 分页查询

                                                                                                      MySQL的每个页的大小为16KB,每个页里面使用了数组进行二分查找,查找到目标ID所在的那一组,然后在组内遍历单链表进行查询。然后页(也就是B+树的节点)和页之间使用双向链表进行查询,但是我们的页太多时,则需要使用目录页来找到id所在页。

                                                                                                      img

                                                                                                      图片来自于:Yes的练级攻略

                                                                                                      当我们增加数据时,如果使用自增id来实现的话,则会是追加操作,只需在后面开辟新的页即可,但是如果使用的不是自增ID则可能出现这种情况,就是页分裂,该页满了,将该页的某些数据挤到下一页,然后造成下一页也满了,这就有可能造成多个页分裂的情况,导致性能损耗。所以我们建议使用自增id作为主键。

                                                                                                      # 8.2 如何分库分表

                                                                                                      按照列进行划分,也就是属性,可以将表进行分割。

                                                                                                      按照行id进行划分,取模等。

                                                                                                      edit icon编辑此页open in new window
                                                                                                      上次编辑于: 2022/9/7 03:26:35
                                                                                                      上一页
                                                                                                      我的面试
                                                                                                      下一页
                                                                                                      操作系统