[八股速成]mysql篇
前言
我之前整理过mysql超详细八股笔记:https://www.nowcoder.com/discuss/583297999409889280?sourceSSR=search,但是说实话因为这份这份八股资料过于详细,内容过于充实,给背记带来了很大的挑战,所以我准备再出一系列帖子,内容就是我根据自己的面试经历和网上的面经,去筛选八股里面哪些是最常被问到的问题把它们整理出来,这样也能省去大家自己整理和筛选的时间,大家可以在面试前一两个小时快速把这一系列最常问八股的帖子拿出来看看,临时抱佛脚的效果应该很好。后面这系列帖子我会放入专栏https://www.nowcoder.com/creation/manager/columnDetail/0ybvLm,欢迎大家订阅。最后我想说,速成虽好,但是还是建议有时间就去看看我详细的八股笔记帖子。
1.mysql基础
1.数据库的三大范式
第一范式1NF(保证原子性)
确保数据库表字段的原子性。如果一个表中的“地址”列包含了省、市和具体地址,这不符合1NF,因为“地址”可以继续拆分成更细致的部分。按1NF要求,应该将“地址”拆分为“省份”、“城市”和“具体地址”等列。
第二范式2NF(消除部分依赖)
首先要满足第一范式,另外包含两部分内容,一是表必须有一个主键;二是非主键列必须完全依赖于主键,而不能只依赖于主键的一部分。例如,在学生成绩表中,如果学号和课程编号组成联合主键,那么成绩这一非主键列应同时依赖于学号和课程编号,而不是仅依赖于其中之一。
第三范式3NF(消除传递依赖)
首先要满足第二范式,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。例如,如果一个表中包含学生信息和院校信息,其中学生所在院校依赖于学号,而院校地址和电话又依赖于所在院校,这就形成了传递依赖。按照3NF的要求,需要将表拆分为学生表和院校表,使每个属性都直接依赖于主键。
2NF和3NF的区别?
- 2NF解决主键部分依赖问题
- 3NF解决主键传递依赖问题
2.having和where区别?
- 二者作用的对象不同,
where
子句作用于表和视图,having
作用于组。 where
在数据分组前进行过滤,having
在数据分组后进行过滤。- 另一方面,
HAVING
子句中不能使用除了分组字段和聚合函数之外的其他字段 - 先执行where,然后group by 然后having
3.inner join,left join和right join的区别
类型 作用1 Join(Inner Join)内连接 查出两表完全匹配的部分。(交集)2 Left Join左连接 返回左表所有的行,右表返回匹配行,不匹配的返回NULL3 Right Join右连接 返回由表所有的行,左表返回匹配行,不匹配的返回NULL4 Full Join全连接 只要其中一个表存在匹配,则返回行
4.什么是sql注入?怎么解决?
SQL注入的原理是将SQL代码伪装到输入参数中,传递到服务器解析并执行的一种攻击手法。也就是说,在一些对SERVER端发起的请求参数中植入一些SQL代码,SERVER端在执行SQL操作时,会拼接对应参数,同时也将一些SQL注入攻击的“SQL”拼接起来,导致会执行一些预期之外的操作
如何解决SQL注入
- 严格的参数校验参数校验就没得说了,在一些不该有特殊字符的参数中提前进行特殊字符校验即可。
- PreparedStatement预编译防止SQL注入PreparedStatement具有预编译功能,以上述SQL为例使用PreparedStatement预编译后的SQL为:此时SQL语句结构已固定,无论"?"被替换为任何参数,SQL语句只认为where后面只有一个条件,当再传入 1001 or 1 = 1时当作一个整体,语句会报错,从而达到防止SQL注入效果
- mybatis中#{}防止SQL注入mybatis中#{}表达式防止SQL注入与PreparedStatement类似,都是对SQL语句进行预编译处理注意:#{} :参数占位符,可防sql注入${} :拼接替换符,不能防止SQL注入,一般用于传入数据库对象(如:数据库名称、表名)order by 后的条件
2.mysql优化
1.如何分析sql的性能(EXPLAIN
命令)
我们可以使用 EXPLAIN
命令来分析 SQL 的 执行计划 。
1.什么是执行计划
执行计划 是指一条 SQL 语句在经过 MySQL 查询优化器 的优化会后,具体的执行方式。
通过 EXPLAIN
的结果,可以了解到如数据表的查询顺序、数据查询操作的操作类型、哪些索引可以被命中、哪些索引实际会命中、每个数据表有多少行记录被查询等信息。
2.执行计划常用字段
MySQL 为我们提供了 EXPLAIN
命令,来获取执行计划的相关信息。
需要注意的是,EXPLAIN
语句并不会真的去执行相关的语句,而是通过查询优化器对语句进行分析,找出最优的查询方案,并显示对应的信息。
执行计划有12个字段组成,常用的有:
1.type表示查询表连接类型**,查看索引执行情况的一个重要指标。以下性能从好到坏依次:system > const > eq_ref > ref > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
- system:这种类型要求数据库表中只有一条数据,是
const
类型的一个特例,一般情况下是不会出现的。 - **const:通过一次索引就能找到数据,一般用于主键或唯一索引作为条件,这类扫描效率极高,,速度非常快。**基于主键或唯一索引唯一值查询,最多返回一条结果,比eq_ref略好。
- eq_ref:常用于主键或唯一索引扫描,一般指使用主键的关联查询
- ref : 常用于非主键和唯一索引扫描。
- ref_or_null:这种连接类型类似于
ref
,区别在于MySQL
会额外搜索包含NULL
值的行 - index_merge:使用了索引合并优化方法,查询使用了两个以上的索引。
- unique_subquery:类似于
eq_ref
,条件用了in
子查询 - index_subquery:区别于
unique_subquery
,用于非唯一索引,可以返回重复值。 - range:常用于范围查询,比如:between ... and 或 In 等操作
- index:全索引扫描(通过遍历整个索引树来获取数据行,而不是直接扫描整张表的数据。)
- ALL:全表扫描
2.possible_keys:表示 MySQL 执行查询时可能用到的索引。如果这一列为 NULL ,则表示没有可能用到的索引;这种情况下,需要检查 WHERE 语句中所使用的的列,看是否可以通过给这些列中某个或多个添加索引的方法来提高查询性能。
3.key:表示 MySQL 实际使用到的索引。如果为 NULL,则表示未用到索引。
4.key_len:表示 MySQL 实际使用的索引的最大长度;当使用到联合索引时,有可能是多个列的长度和。在满足需求的前提下越短越好。如果 key 列显示 NULL ,则 key_len 列也显示 NULL 。
5.rows:估算要找到所需的记录,需要读取的行数,这是一个估计值。
**6.extra:**这列包含了 MySQL 解析查询的额外信息,通过这些信息,可以更准确的理解 MySQL 到底是如何执行查询的。常见的值如下:
- Using filesort:在排序时使用了外部的索引排序,没有用到表内索引进行排序。
- Using temporary:MySQL 需要创建临时表来存储查询的结果,常见于 ORDER BY 和 GROUP BY。
- Using index:表明查询使用了覆盖索引,不用回表,查询效率非常高。
- Using index condition:表示查询优化器选择使用了索引条件下推这个特性。
- Using where:表明查询使用了 WHERE 子句进行条件过滤。一般在没有使用到索引的时候会出现。
- Using join buffer (Block Nested Loop):连表查询的方式,表示当被驱动表的没有使用索引的时候,MySQL 会先将驱动表读出来放到 join buffer 中,再遍历被驱动表与驱动表进行查询。
3.explain具体怎么分析一条慢sql?
如果一条sql执行很慢的话,我们通常会使用mysql自动的执行计划explain来去查看这条sql的执行情况,
比如在这里面可以通过key和key_len检查是否命中了索引,如果本身已经添加了索引,也可以判断索引是否有失效的情况,
第二个,可以通过type字段查看sql是否有进一步的优化空间,是否存在全索引扫描或全盘扫描,
第三个可以通过extra建议来判断,是否出现了回表的情况,如果出现了,可以尝试添加索引或修改返回字段来修复
2.怎么定位慢查询
2种方法·:
- 运维监控工具Slywalking
我们当时做压测的时候有的接口非常的慢,接口的响应时间超过了2秒以上,因为我们当时的系统部署了运维的监控系统Skywalking ,在展示的报表中可以看到是哪一个接口比较慢,并且可以分析这个接口哪部分比较慢,这里可以看到SQL的具体的执行时间,所以可以定位是哪个sql出了问题
- mysql慢查询日志 开启慢查询日志功能:慢查询日志是MySQL内置的一项功能,可以记录执行超过指定时间的SQL语句。在MySQL配置文件中设置slow_query_log=1以启用该功能,并设定long_query_time=2来规定只有执行时间超过2秒的SQL才会被记录。分析慢查询日志文件:通过查看日志文件中记录的SQL语句和相应的执行时间、扫描行数等指标,可以直观地识别出慢查询。一般情况下,慢查询日志会记录查询执行的时间、返回的行数以及检查的行数等重要信息。
3.导致MySQL慢查询有哪些原因?
- 索引使用不当或缺失: 索引未覆盖查询中使用的字段,或者索引被查询中的函数调用、范围查询或非等值条件所避开。索引选择性差,即索引不能有效地过滤掉大量行。
- 单表数据量太大
- 查询使用了临时表
- 表结构设计不佳: 过度规范化或反规范化可能导致额外的JOIN操作,增加查询复杂度。冗余数据或设计不良的外键关系。
- limit深度分页问题
- 复杂的查询语句: 包含大量的连接(JOINs)、子查询或者复杂的嵌套表达式,这些都会增加CPU和I/O负载。
4.慢sql的优化分析思路?
步骤如下:
1.查看慢查询日志记录,分析慢SQL
通过慢查询日志slow log,定位那些执行效率较低的SQL语句,重点关注分析
2.explain查看分析SQL的执行计划
当定位出查询效率低的SQL后,可以使用explain
查看SQL
的执行计划。
比如在这里面可以通过key和key_len检查是否命中了索引,如果本身已经添加了索引,也可以判断索引是否有失效的情况,
第二个,可以通过type字段查看sql是否有进一步的优化空间,是否存在全索引扫描或全盘扫描,
第三个可以通过extra建议来判断,是否出现了回表的情况,如果出现了,可以尝试添加索引或修改返回字段来修复
3.profile 分析执行耗时
explain
只是看到SQL
的预估执行计划,如果要了解SQL
真正的执行线程状态及消耗的时间,需要使用profiling
。开启profiling
参数后,后续执行的SQL
语句都会记录其资源开销,包括IO,上下文切换,CPU,内存
等等,我们可以根据这些开销进一步分析当前慢SQL的瓶颈再进一步进行优化。
4.Optimizer Trace分析详情
profile只能查看到SQL的执行耗时,但是无法看到SQL真正执行的过程信息,即不知道MySQL优化器是如何选择执行计划。这时候,我们可以使用Optimizer Trace
,它可以跟踪执行语句的解析优化执行的全过程。
大家可以查看分析其执行树,会包括三个阶段:
- join_preparation:准备阶段
- join_optimization:分析阶段
- join_execution:执行阶段
5.确定问题并采用相应的措施
最后确认问题,就采取对应的措施。
- 多数慢SQL都跟索引有关,比如不加索引,索引不生效、不合理等,这时候,我们可以优化索引。
- 我们还可以优化SQL语句,比如一些in元素过多问题(分批),深分页问题(基于上一次数据过滤等),进行时间分段查询
- SQl没办法很好优化,可以改用ES的方式,或者数仓。
- 如果单表数据量过大导致慢查询,则可以考虑分库分表
- 如果数据库在刷脏页导致慢查询,考虑是否可以优化一些参数,跟DBA讨论优化方案
- 如果存量数据量太大,考虑是否可以让部分数据归档
5.sql语句优化小技巧
SQL语句优化
- 查询时只返回必要的列,用具体的字段列表代替 select * 语句,因为要尽量用聚集索引防止回表查询
- SQL语句要避免造成索引失效的写法
- 尽量用union all代替union UNION 会进行去重处理,这会增加排序和比较的计算成本
- 避免在where子句中对字段进行表达式操作
- Join优化 能用inner join 就不用left join right join,如必须使用 一定要以小表为驱动,( 内连接会对两个表进行优化,优先把小表放到外边,把大表放到里边。left join 或 right join,不会重新调整顺序)inner join 内连接,只保留两张表中完全匹配的结果集;left
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏价格永远为19.9元! 不想当架构师的后端开发工程师不是好码农! 此专栏一方面用于存放我的架构设计学习笔记, 另外我会在本专栏加入一系列最常问八股问题帖子,内容就是我根据自己的面试经历和网上的面经,去筛选八股里面哪些是最常被问到的问题把它们整理出来,大家可以在面试前一两个小时快速把这一系列最常问八股的帖子拿出来看看,临时抱佛脚的效果应该很好