📖
R‘Notes
  • 关于本仓库/网站
  • Note
    • Golang的知识碎片
      • 关于Golang的一些碎片知识
    • LeetCode
      • LCR 121. 寻找目标值 - 二维数组
      • LCR 125. 图书整理 II
      • LCR 127. 跳跃训练
      • LCR 143. 子结构判断
      • LCR 159. 库存管理 III
      • LCR 161. 连续天数的最高销售额
      • LCR 170. 交易逆序对的总数
      • LCR 174. 寻找二叉搜索树中的目标节点
      • LeetCode--1. 两数之和
      • LeetCode--10. 正则表达式匹配
      • LeetCode--1004. 最大连续1的个数 III
      • LeetCode--101. 对称二叉树
      • LeetCode--102. 二叉树的层序遍历
      • LeetCode--1027. 最长等差数列
      • LeetCode--103. 二叉树的锯齿形层序遍历
      • LeetCode--1035. 不相交的线
      • LeetCode--104. 二叉树的最大深度
      • LeetCode--1044. 最长重复子串
      • LeetCode--105. 从前序与中序遍历序列构造二叉树
      • LeetCode--106. 从中序与后序遍历序列构造二叉树
      • LeetCode--110. 平衡二叉树
      • LeetCode--111. 二叉树的最小深度
      • LeetCode--112. 路径总和
      • LeetCode--113. 路径总和 II
      • LeetCode--1137. 第 N 个泰波那契数
      • LeetCode--114. 二叉树展开为链表
      • LeetCode--1143. 最长公共子序列
      • LeetCode--115. 不同的子序列
      • LeetCode--1191. K 次串联后最大子数组之和
      • LeetCode--120. 三角形最小路径和
      • LeetCode--121. 买卖股票的最佳时机
      • LeetCode--1218. 最长定差子序列
      • LeetCode--122. 买卖股票的最佳时机 II
      • LeetCode--1220. 统计元音字母序列的数目
      • LeetCode--123. 买卖股票的最佳时机 III
      • LeetCode--124. 二叉树中的最大路径和
      • LeetCode--125. 验证回文串
      • LeetCode--128. 最长连续序列
      • LeetCode--1289. 下降路径最小和 II
      • LeetCode--129. 求根节点到叶节点数字之和
      • LeetCode--1301. 最大得分的路径数目
      • LeetCode--1312. 让字符串成为回文串的最少插入次数
      • LeetCode--134. 加油站
      • LeetCode--135. 分发糖果
      • LeetCode--136. 只出现一次的数字
      • LeetCode--138. 随机链表的复制
      • LeetCode--139. 单词拆分
      • LeetCode--14. 最长公共前缀
      • LeetCode--141. 环形链表
      • LeetCode--142. 环形链表 II
      • LeetCode--143. 重排链表
      • LeetCode--144. 二叉树的前序遍历
      • LeetCode--145. 二叉树的后序遍历
      • LeetCode--146. LRU 缓存
      • LeetCode--148. 排序链表
      • LeetCode--15. 三数之和
      • LeetCode--151. 反转字符串中的单词
      • LeetCode--152. 最大乘积子数组【DP】
      • LeetCode--153. 寻找旋转排序数组中的最小值
      • LeetCode--155. 最小栈
      • LeetCode--1584. 连接所有点的最小费用,最小生成树模板题
      • LeetCode--1594. 矩阵的最大非负积
      • LeetCode--16. 最接近的三数之和
      • LeetCode--160. 相交链表
      • LeetCode--162. 寻找峰值
      • LeetCode--165. 比较版本号
      • LeetCode--169. 多数元素
      • LeetCode--174. 地下城游戏
      • LeetCode--179. 最大数
      • LeetCode--1824. 最少侧跳次数
      • LeetCode--188. 买卖股票的最佳时机 IV
      • LeetCode--189. 轮转数组
      • LeetCode--19. 删除链表的倒数第 N 个结点,关于删除链表会遇见的指针问题
      • LeetCode--1964. 找出到每个位置为止最长的有效障碍赛跑路线
      • LeetCode--198. 打家劫舍
      • LeetCode--199. 二叉树的右视图
      • LeetCode--2. 两数相加
      • LeetCode--20. 有效的括号
      • LeetCode--200. 岛屿数量
      • LeetCode--206. 反转链表
      • LeetCode--207. 课程表
      • LeetCode--208. 实现 Trie (前缀树)
      • LeetCode--209. 长度最小的子数组
      • LeetCode--21. 合并两个有序链表,关于链表的复习
      • LeetCode--210. 课程表 II
      • LeetCode--213. 打家劫舍 II
      • LeetCode--2140. 解决智力问题
      • LeetCode--215. 数组中的第K个最大元素
      • LeetCode--22. 括号生成
      • LeetCode--221. 最大正方形
      • LeetCode--2218. 从栈中取出 K 个硬币的最大面值和
      • LeetCode--224. 基本计算器
      • LeetCode--225. 用队列实现栈
      • LeetCode--226. 翻转二叉树
      • LeetCode--2266. 统计打字方案数
      • LeetCode--227. 基本计算器 II
      • LeetCode--23. 合并 K 个升序链表【堆和分治】
      • LeetCode--230. 二叉搜索树中第 K 小的元素
      • LeetCode--2304. 网格中的最小路径代价
      • LeetCode--232. 用栈实现队列
      • LeetCode--2320. 统计放置房子的方式数
      • LeetCode--2321. 拼接数组的最大分数
      • LeetCode--233. 数字 1 的个数
      • LeetCode--234. 回文链表
      • LeetCode--236. 二叉树的最近公共祖先
      • LeetCode--239. 滑动窗口最大值,关于单调队列的复习
      • LeetCode--24. 两两交换链表中的节点
      • LeetCode--240. 搜索二维矩阵 II
      • LeetCode--2435. 矩阵中和能被 K 整除的路径
      • LeetCode--2466. 统计构造好字符串的方案数
      • LeetCode--25. K 个一组翻转链表
      • LeetCode--2533. 好二进制字符串的数量
      • LeetCode--256. 粉刷房子
      • LeetCode--2606. 找到最大开销的子字符串
      • LeetCode--265. 粉刷房子 II
      • LeetCode--2684. 矩阵中移动的最大次数
      • LeetCode--279. 完全平方数【动态规划】
      • LeetCode--283. 移动零
      • LeetCode--287. 寻找重复数
      • LeetCode--295. 数据流的中位数
      • LeetCode--297. 二叉树的序列化与反序列化
      • LeetCode--3. 无重复字符的最长子串
      • LeetCode--300. 最长递增子序列【DP+二分】
      • LeetCode--309. 买卖股票的最佳时机含冷冻期
      • LeetCode--31. 下一个排列
      • LeetCode--3186. 施咒的最大总伤害
      • LeetCode--32. 最长有效括号【栈和dp】
      • LeetCode--322. 零钱兑换
      • LeetCode--328. 奇偶链表
      • LeetCode--329. 矩阵中的最长递增路径
      • LeetCode--33. 搜索旋转排序数组【直接二分】
      • LeetCode--337. 打家劫舍 III
      • LeetCode--3393. 统计异或值为给定值的路径数目
      • LeetCode--34. 在排序数组中查找元素的第一个和最后一个位置
      • LeetCode--3418. 机器人可以获得的最大金币数
      • LeetCode--343. 整数拆分
      • LeetCode--347. 前 K 个高频元素
      • LeetCode--347. 前 K 个高频元素Golang中的堆(containerheap)
      • LeetCode--354. 俄罗斯套娃信封问题
      • LeetCode--377. 组合总和 Ⅳ
      • LeetCode--39. 组合总和
      • LeetCode--394. 字符串解码【栈】
      • LeetCode--395. 至少有 K 个重复字符的最长子串
      • LeetCode--4. 寻找两个正序数组的中位数
      • LeetCode--40. 组合总和 II
      • LeetCode--402. 移掉 K 位数字
      • LeetCode--41. 缺失的第一个正数
      • LeetCode--415. 字符串相加
      • LeetCode--416. 分割等和子集_494. 目标和【01背包】
      • LeetCode--42. 接雨水(单调栈和双指针)
      • LeetCode--426. 将二叉搜索树转化为排序的双向链表
      • LeetCode--43. 字符串相乘
      • LeetCode--437. 路径总和 III【前缀和】
      • LeetCode--44. 通配符匹配
      • LeetCode--440. 字典序的第K小数字
      • LeetCode--442. 数组中重复的数据
      • LeetCode--445. 两数相加 II
      • LeetCode--45. 跳跃游戏 II
      • LeetCode--450. 删除二叉搜索树中的节点
      • LeetCode--46. 全排列
      • LeetCode--460. LFU 缓存
      • LeetCode--468. 验证IP地址
      • LeetCode--470. 用 Rand7() 实现 Rand10()
      • LeetCode--474. 一和零
      • LeetCode--48. 旋转图像
      • LeetCode--498. 对角线遍历
      • LeetCode--5. 最长回文子串
      • LeetCode--50. Pow(x, n)
      • LeetCode--509. 斐波那契数
      • LeetCode--516. 最长回文子序列
      • LeetCode--518. 零钱兑换 II
      • LeetCode--529. 扫雷游戏题解C++广搜
      • LeetCode--53. 最大子数组和
      • LeetCode--54. 螺旋矩阵
      • LeetCode--540. 有序数组中的单一元素
      • LeetCode--543. 二叉树的直径
      • LeetCode--55. 跳跃游戏
      • LeetCode--556. 下一个更大元素 III
      • LeetCode--56. 合并区间
      • LeetCode--560. 和为 K 的子数组
      • LeetCode--572. 另一棵树的子树
      • LeetCode--59. 螺旋矩阵 II
      • LeetCode--61. 旋转链表
      • LeetCode--62. 不同路径
      • LeetCode--622. 设计循环队列
      • LeetCode--63. 不同路径 II
      • LeetCode--64. 最小路径和
      • LeetCode--646. 最长数对链
      • LeetCode--662. 二叉树最大宽度
      • LeetCode--673. 最长递增子序列的个数
      • LeetCode--678. 有效的括号字符串
      • LeetCode--679. 24 点游戏
      • LeetCode--69. x 的平方根
      • LeetCode--695. 岛屿的最大面积
      • LeetCode--7. 整数反转
      • LeetCode--70. 爬楼梯
      • LeetCode--704. 二分查找
      • LeetCode--712. 两个字符串的最小ASCII删除和
      • LeetCode--714. 买卖股票的最佳时机含手续费
      • LeetCode--718. 最长重复子数组
      • LeetCode--72. 编辑距离
      • LeetCode--739. 每日温度
      • LeetCode--74. 搜索二维矩阵
      • LeetCode--740. 删除并获得点数
      • LeetCode--746. 使用最小花费爬楼梯
      • LeetCode--75. 颜色分类
      • LeetCode--76. 最小覆盖子串
      • LeetCode--77. 组合
      • LeetCode--78. 子集
      • LeetCode--79. 单词搜索
      • LeetCode--790. 多米诺和托米诺平铺
      • LeetCode--8. 字符串转换整数 (atoi)
      • LeetCode--82. 删除排序链表中的重复元素 II
      • LeetCode--83. 删除排序链表中的重复元素
      • LeetCode--84. 柱状图中最大的矩形【单调栈】
      • LeetCode--85. 最大矩形
      • LeetCode--87. 扰乱字符串
      • LeetCode--88. 合并两个有序数组
      • LeetCode--887. 鸡蛋掉落
      • LeetCode--91. 解码方法
      • LeetCode--912. 排序数组
      • LeetCode--918. 环形子数组的最大和
      • LeetCode--92. 反转链表 II
      • LeetCode--93. 复原 IP 地址
      • LeetCode--931. 下降路径最小和
      • LeetCode--94. 二叉树的中序遍历
      • LeetCode--958. 二叉树的完全性检验
      • LeetCode--97. 交错字符串
      • LeetCode--98. 验证二叉搜索树
      • LeetCode--983. 最低票价
      • LeetCode--LCR 140. 训练计划 II
      • NC--311.圆环回原点
      • NC--36进制加法
      • 补充题1. 排序奇升偶降链表
    • Redis
      • Redis基础部分
      • 在用Docker配置Redis哨兵节点的时候出现的错误及其解决
    • SQL学习记录
      • SQL碎片知识
      • 系统
        • MySQL学习笔记1【DQL和DCL】
        • MySQL学习笔记2【函数/约束/多表查询】
        • MySQL学习笔记3【事务】
        • MySQL学习笔记4【存储引擎和索引】
        • MySQL学习笔记5【SQL优化/视图/存储过程/触发器】
        • MySQL学习笔记6【锁】
        • MySQL学习笔记7【InnoDB】
    • x86汇编
      • 学习汇编随手记
    • 微服务相关
      • Nacos与gRPC
      • 【Golangnacos】nacos配置的增删查改,以及服务注册的golang实例及分析
    • 手搓
      • Whalebox(仿Docker)的爆诞
    • 操作系统
      • MIT6.S081-lab1
      • MIT6.S081-lab2
      • MIT6.S081-lab3
      • MIT6.S081-lab3前置
      • MIT6.S081-lab4
      • MIT6.S081-lab4前置
      • MIT6.S081-lab5
      • MIT6.S081-lab5前置
      • MIT6.S081-lab7
      • MIT6.S081-lab7前置
      • MIT6.S081-lab8
      • MIT6.S081-lab8前置
      • MIT6.S081-lab9
      • MIT6.S081-环境搭建
    • 消息队列MQ
      • Kafka
    • 算法杂谈
      • 关于二分查找时的边界分类问题
    • 计组笔记
      • 计算机组成原理的学习笔记(1)--概述
      • 计算机组成原理的学习笔记(10)--CPU·其二 组合逻辑控制器和微程序
      • 计算机组成原理的学习笔记(11)--CPU·其三 中断和异常多处理器相关概念
      • 计算机组成原理的学习笔记(12)--总线和IO系统
      • 计算机组成原理的学习笔记(2)--数据的表示和运算·其一
      • 计算机组成原理的学习笔记(3)--数据表示与运算·其二 逻辑门和加减乘
      • 计算机组成原理的学习笔记(4)--数据的表示与运算·其三 补码的乘法以及原码补码的除法
      • 计算机组成原理的学习笔记(4)--数据的表示与运算·其三 补码的乘法以及原码补码的除法
      • 计算机组成原理的学习笔记(6)--存储器·其一 SRAMDRAMROM主存储器的初步认识
      • 计算机组成原理的学习笔记(7)--存储器·其二 容量扩展多模块存储系统外存Cache虚拟存储器
      • 计算机组成原理的学习笔记(8)--指令系统·其一 指令的组成以及数据寻址方式RISK和CISK
      • 计算机组成原理的学习笔记(9)--CPU·其一 CPU的基本概念流水线技术数据通路
    • 闲聊
      • 2025-03-23记
      • 大一上总结
由 GitBook 提供支持
在本页
  • 1. 慢查询怎么处理?
  • 2. 为啥 MySQL 选择了 B+ 树而不是平衡二叉树或者 B 树?
  • 3. 聚簇索引和二级索引?
  • 4. MyISAM 和 InnoDB 的区别
  • 5. 为什么会有最左前缀原则?
  • 6. B+ 树里面的数据是怎么存储的?怎么查找的?
  • 7. 索引下推优化是什么?
  • 8. 什么时候适合/不适合用索引?
  • 9. 索引有什么优化的办法?
  1. Note
  2. SQL学习记录

SQL碎片知识

本篇笔记目的在于记录常见的 MySQL 相关的问题。

1. 慢查询怎么处理?

查看慢查询日志,通过 explain 分析慢查询的 SQL 语句,查看是否走的全表扫描,或者没有利用索引,随后添加合适的索引,同时查询需要遵循最左匹配原则。

另外也可以进行简化查询,只查询必要的字段,或者使用覆盖索引,或者减少通配符的使用;使用 JOIN 替代嵌套的 SELECT 减少子查询

如果发现这个查询确实走了索引,但是依旧很慢,可能需要分析锁。

2. 为啥 MySQL 选择了 B+ 树而不是平衡二叉树或者 B 树?

普通的二叉搜索树在顺序插入索引的时候会导致树退化成链表,这种情况下就相当于全表扫描。

而平衡二叉树或者红黑树性能差异并不大,但是由于每一个节点仅能存储一个索引,所以存储效率很低,在存储成千上万的索引时,树的高度很大,而每一次查找都是一次磁盘 I/O ,所以我们期望高度会比较低的数据结构。

B 树和 B+ 树的高度都比较低,为什么没有选择 B 树?B 树的数据是分布于整个树中的,除了叶子节点,还分布于内节点上面,不利于我们走范围查询,并且查询的速度波动很大,除此之外,这样的性质使得 B+ 树可以存放更多的索引,查询效率更高,磁盘的 I/O 也会更少。

而我们的 B+ 树,它的数据都存储在叶子节点上,而内节点都用于放索引,同时,我们的叶子节点相邻之间构成了双向链表,这种设计非常利于范围查询。 B+ 树存在大量的冗余节点(内节点)这就是的插入和删除的效率很高,树形结构的变化很小,而 B 树由于没有冗余节点,所以删除就会非常复杂,树形变化较大。

3. 聚簇索引和二级索引?

一张表只有一个聚簇索引,聚簇索引的叶子节点上存储着完整的数据,而二级索引存储的是对应的“主键”字段,当我们通过二级索引去查询数据时,就会先通过二级索引拿到主键信息,然后根据主键回到聚簇索引之中去查询具体的数据,也就是说,要执行两次 B+ 树查询,这种情况就是回表。

当然,如果我们查询的数据已经包含在了索引之中,就无需回表查询,这种情况就是索引覆盖。

4. MyISAM 和 InnoDB 的区别

MyISAM 的索引是非聚集索引,数据和索引分离,每一个数据查询都需要经过回表的步骤,而 InnoDB 有聚集索引,通过主键检索的效率很高。

MyISAM 最细粒度的锁是表锁,而 InnoDB 支持行锁,并发更强。

MyISAM 不支持外键和事务。

5. 为什么会有最左前缀原则?

我们建立联合索引的时候,往往会有一个顺序,比如联合索引(name,age,email),我们的索引只有一个,如何根据这个联合索引去建立这唯一的索引呢?我们是先按照 name 排序,再按照 age,最后按照 email 来排序的顺序,所以如果我们直接使用 SELECT * FROM users WHERE age = 12 的时候,我们直接去寻找年龄为 12 的用户,但是此时我们并没有根据 name 去查找,而是直接根据 age,那么问题来了,如果我们真的去走这个联合索引,我们找到了一个 age = 12 的用户,但是这个是唯一的 age = 12 的用户吗?很有可能不是,我们根本不可能根据 age = 12 这个条件从这个联合索引中去找到所有的 age = 12 的用户,所以此时,只能走全表扫描,这就是最左前缀的由来。

6. B+ 树里面的数据是怎么存储的?怎么查找的?

B+ 树中的每个节点是以页为单位的,不论是叶子节点的数据还是内节点的索引,都是以页为单位的,都是一个数据页, MySQL 中的页的单位是 16 kb ,这也是和很多操作系统不一样的地方,这也是为什么 B+ 树可以存储比 B 树更多的索引,变得更矮胖,查询效率更高,磁盘 I/O 次数更少。

我们的索引在页中也是链表的形式串联的,但是会进行分组,每个组仅有几个元素,每个组都有一个目录,我们的目录会记录每个组的最后一个节点的数据和指针,而页目录可以帮助我们做二分查找,我们可以进行一次磁盘 I/O ,将这一页加载到内存中,然后通过二分查找,从而实现快速的查询,然后找到下一层节点的索引,进行下一步加载。

最后,我们进入到了叶子节点,也就是挂着数据的节点,每个行数据都是和对应的索引绑定在一起,也就是说,结构是一样的,还是通过链表绑在一起,我们可以先将叶子节点加载到磁盘中,然后通过页目录进行二分查找,最终找到对应的行。而在分组内,肯定只能实现遍历查找了。

这里可以引出另一个问题,**为什么是 B+ 树?**我们根据刚刚提到的,我们可以直接使用利用页目录进行二分查找,为什么不把所有的数据组织成一个链表呢?这是因为我们希望加载很方便,一次只需要加载一个页,便于维护,而我们的每个页只需要一个页目录就可以了,然而,一个页的数据量优先,不可能承受无限的页目录,所以我们需要多个页,这个时候又会出现一个问题,我们应该先在哪一个页进行二分?先将哪个页加载到磁盘?所以我们只能向上扩展一层作为上一层索引,一次来判断去加载哪一个页,而此时的结构,就和我们的 B+ 树很像了,随着数据的增长,我们的 B+ 树的高度也会长高,而为了数据检索更快,变高是不可避免的,这不就是我们刚刚说的模型吗?这就是 MySQL 使用 B+ 树的由来。

并且,如果对于二叉树,我们每次加载一页到内存,这个页对应的节点可能并不连续,可能导致多次磁盘IO,效率也很低下。相比之下 B+ 树稳定的最多三次磁盘 I/O ,性能和稳定性都更好。

7. 索引下推优化是什么?

对于我们的联合索引 index(a, b) ,在执行 SELECT * FROM USERS WHERE a > 1 AND b < 2 的时候,如果没有索引下推,则会先找到符合 a > 1 的数据,然后回表回到数据页去判断 'b < 2' 的条件,这就导致了有一些数据没能利用到第二个 b 索引进行过滤,所以在新的 MySQL 版本中,引入了索引下推,使得我们的 sql 语句在第一步索引检索期间,回表之前就可以通过 a 和 b 两个字段直接进行过滤,以此来减少回表操作。当然,前提是索引中包含对应的字段。

8. 什么时候适合/不适合用索引?

索引也有一些缺点:

  1. 占用物理空间。

  2. 创建和维护索引耗时,尤其是数据量很大的时候。

  3. 增删改的效率降低,因为 B+ 树需要进行动态维护。

在这些场景下,并不适合建立索引:

  1. 在 WHERE, ORDER BY,GROUP BY 中很少用到的字段,数据重复多的,比如性别这种字段,但是像 uid 这种唯一字段,或者商品编码比较适合建立索引。

  2. 表数据少的时候。

  3. 经常更新的字段,比如账户余额。(因为会频繁的更改,影响数据库性能)

9. 索引有什么优化的办法?

前缀索引优化

  • 前缀索引优化其实就是使用字符串的前几个字符建立索引,使用前缀索引可以避免使用整个很长的字符串进行索引,从而节省我们的索引的空间,使得可以在相同的磁盘空间中建立更多的索引,同时因为字符变短,检索也更快。但是需要你的前缀有一定的区分度。前缀索引可以用来优化模糊匹配和加速 WHERE 查询。

覆盖索引优化

  • 代表我们通过 SELECT 选择的字段直接在二级索引的叶子节点上就可以找到而无需回表操作。

主键索引自增:

  • 这样在插入数据时可以减少 B+ 树的自适应旋转,由于我们都是插入自增的数据,所以主键索引都是以追加的形式放在 B+ 树中,而减少了结构的变化导致的性能问题。而如果插入的是非自增主键,每次的索引值都是随机的,需要插入现有的数据到索引的中间,我们就不得不去复制移动其他的数据到另一个页,这种情况叫做页分裂,这也有可能造成大量的内存碎片,导致索引结构不紧凑,影响查询效率。

索引设置为 NOT NULL:

  • 虽然感觉没人会设置为 NULL,但是这样做有两个原因,第一是因为索引列存在 NULL 值导致优化器做选择的时候会更复杂,更难以优化,比如索引的统计和值都很复杂,因为是 NULL ,没人知道你是在干嘛。二是 NULL 没有意义,并且会占用物理空间。

防止索引失效:

  • 当我们使用左或者左右模糊匹配会导致索引失效,因为我们只能根据前缀去匹配字符串。

  • 使用函数也会导致索引失效,因为我们索引保存的是原始值,而不是经过函数计算的值。

  • 对于索引进行表达式计算也会导致索引失效,这里和使用函数差不多,比如:SELECT * FROM users WHERE id + 1 = 10 此时如果我们将 +1 移动到右侧,索引就可以生效了。

  • 值得一提的例子,MySQL 会自动把字符串转换为数字,如果我们的索引项是字符串的时候,我们传入一个数字,则会导致索引失效,因为此时是对索引项进行类型转换,也就是使用了函数,但是如果索引项是数字,我们传入字符串,索引并不会失效,因为此时是对我们传入的字符串进行类型转换,并没有对索引做操作。

  • 另外对于 OR 语句,如果两个字段其中一个不是索引字段,就会走全表扫描,这一点很容易理解。

  • 需要遵循最左匹配原则。

上一页SQL学习记录下一页系统

最后更新于1天前