SQL语句执行顺序
- 案例
首先,我们先看下如上SQL的执行顺序,如下:
1.首先执行 FROM
子句, 从学生成绩表中组装数据源的数据。
2.执行 WHERE
子句, 筛选学生成绩表中所有学生的数学成绩不为 NULL 的数据 。
3.执行 GROUP BY
子句, 把学生成绩表按 “班级” 字段进行分组。
4.计算 avg
聚合函数, 按找每个班级分组求出 数学平均成绩。
5.执行HAVING
子句, 筛选出班级 数学平均成绩大于 75 分的。
6.执行SELECT
语句,返回数据,但别着急,还需要执行后面几个步骤。
7.执行 ORDER BY
子句, 把最后的结果按 “数学平均成绩” 进行排序。
8.执行LIMIT
,限制仅返回3条数据。结合ORDER BY 子句,即返回所有班级中数学平均成绩的前三的班级及其数学平均成绩。
SQL一些容易遇到的问题
- 可以先通过where 过滤,再进行分组
- 聚合函数 出现在select 或者 having
- group by 可以有多个字段
- on 是首要筛选的条件,where对此进行虚表过滤。on优先级最高。
- having过滤条件在聚合函数之后。也就意味着可以过滤聚合函数
- 遇到一些不正常走索引的可以使用
force index()
来强制走索引 - explain 查看执行计划,
- show profiles 查慢查询日志(set profiling = 1;set long_query_time = 0; )
一条SQL是如何执行的
MyIsam 与 Innodb 区别
- MyIsam 不支持事务,不支持行锁
- MyIsam 表的行数都会自己做存储(查MyIsam 表的行数的时候,会比较快)
- innodb
主键索引
存储整行
的数据;其他二级索引存储【索引数据与主键ID】 - Innodb 的二级索引 存的是【索引数据与主键ID】,通过主键索引来查询到这个行数据
更新操作详解
1.更新之前会先进行查询
2.拿到行数据之后,用执行器调用存储引擎的接口,进行更新操作。
3.更新的操作会设计一些日志WAL
Write Ahead Log简称WAL,在分布式存储系统中的元数据更新中应用得十分广泛。WAL的主要意思是说在将元数据的变更操作写入到持久稳定的db之前,先预先写入到一个log中,然后再由另外的操作将log apply到外部的持久db里去。这种模式会减少掉每次的db写入操作,尤其当系统要处理大量的transaction操作的时候,WAL的方式相比较于实时同步db的方式有着更高的效率。
日志分类
redo log
- 空间有限,只有4G
bin log
- 不是环形的
- 磁盘上的全量日志
- 只要磁盘够大,你只管追加操作就行了
MySQL如何做到崩溃恢复
- 奔溃提交涉及到:两阶段提交
- 顺序:
1.首先记录一个redo log
2.in-> redo prepare -> out 其中一个状态崩溃了都无法恢复 - bin log 可日志回放
MySQL执行计划
- Extra:Using index 表示走了覆盖索引,不需要回表
主键索引存储结构
二级索引存储结构
联合索引存储结构
- 比较占用空间
优化案例
- 强制走索引
- 走联合索引
- 拒绝回表,小表驱动大表(翻页)
数据库的优化,大多数都在优化IO
索引下推(ICP)
https://www.bilibili.com/video/BV12V4y157yi/?spm_id_from=333.999.0.0&vd_source=0722208728783f1453516ee9e0d6ca3f
- 在不使用ICP的情况下,在使用非主键索引(又叫普通索引或者二级索引)进行查询时,存储引擎通过索引检索到数据,然后回表,判断数据是否符合条件 。
- 索引下推在非主键索引上的优化,可以有效减少回表的次数,大大提升了查询的效率。
- ICP特点:先判断索引列是否满足条件,不满足则忽略。
在开始之前先先准备一张用户表(user)
其中主要几个字段有:id、name、age、address
。
建立联合索引(name,age)
。
假设有一个需求,要求匹配姓名第一个为陈的所有用户,sql语句如下:
SELECT * from user where name like '陈%'
根据 “最佳左前缀” 的原则,这里使用了联合索引(name,age)进行了查询,性能要比全表扫描肯定要高。
问题来了,如果有其他的条件呢?
假设又有一个需求,要求匹配姓名第一个字为陈。
年龄为20岁的用户,此时的sql语句如下:
SELECT * from user where name like '陈%' and age=20
这条sql语句应该如何执行呢?
下面对Mysql5.6之前版本和之后版本进行分析。
Mysql5.6之前的版本
5.6之前的版本是没有索引下推这个优化的,因此执行的过程如下图:
会忽略age这个字段,直接通过name进行查询,在(name,age)这课树上查找到了两个结果,id分别为2,1,然后拿着取到的id值一次次的回表查询,因此这个过程需要回表两次。
Mysql5.6及之后版本
5.6版本添加了索引下推这个优化,执行的过程如下图:
InnoDB并没有忽略age这个字段,而是在索引内部就判断了age是否等于20,对于不等于20的记录直接跳过,因此在(name,age)这棵索引树中只匹配到了一个记录,此时拿着这个id去主键索引树中回表查询全部数据,这个过程只需要回表一次。
explain
关于 in 的顺序问题 解决方案
MVCC
https://www.bilibili.com/video/BV1864y1976i/?spm_id_from=333.337.search-card.all.click&vd_source=0722208728783f1453516ee9e0d6ca3f
Mvcc的实现主要是由隐藏的三个字段
,undolog
, read view
来实现的。
隐藏字段
undoLog
流程步骤:
- 事务1插入数据
- 事务2更新name
- 事务3更新age
Read view
流程:
具体的比较规则:
总结
全表扫描
多次IO
索引
创建索引,相当于是给表中的字段创建了一个
目录
索引文件
innodb
:数据和索引存储在一个文件MyIsam
: 数据和索引分开存
索引的分类(5)
- 主键索引
- 普通索引
- 唯一索引
- 联合索引 (建议不要超过5个列)
- 全文索引(MyIsam)
数据结构-树
- 平衡二叉树(动态的二叉搜索树)
-
红黑树
-
B树
-
B+树
Update 锁
- 如果一个字段上是有索引的,那么在一个事务下会开启行锁
- 没有索引则开启表锁
- 所以在update的时候,尽量走索引的列
- 它是根据索引加的锁,锁的是索引。