mysql基本架构
mysql总体可以分成serve层和存储引擎层,其中serve层完成大多数功能,存储引擎层负责数据的存储和提取,serve层主要有连接器,查询缓存,分析器,优化器,执行器,存储引擎是插件式的Mysql5.5.5InnoDB成为了默认的存储引擎。
- 连接器
连接器负责跟客户端建立连接,获取权限,维持和管理连接。
建立的连接有长连接和短连接两种,长连接连接成功后如果客户端有请求,则一直使用同一个连接。短连接则是指每次执行完成很少几次查询就断开连接。
连接建立比较复杂,尽量使用长连接。
长连接的问题
MySQL在执行过程中临时使用的内存是管理在连接对象里面的,这些资源会在连接断开的时候才释放。所以长连接积累下来,可能导致内存占用过大。
长连接的解决方法
- 定期断开长连接
- Mysql5.7后可以通过执行MySQL-reset-connection来重新初始化 资源(不需要重连和全权限验证)
- 查询缓存
MySQL拿到一个请求时就会先到查询缓存中查找(查询缓存以key-val形式),有的话会直接返回。MySQL 8.0被删掉了,原因在于只要对一个表更新,所有缓存都会被清除。
- 分析器
分析器先做词法分析,找出sql语句中每一个字符串对应的是什么(比如是表名称还是where条件内容)在做语法分析,判断SQL语句是否满足MySQL语法。
- 优化器
选择索引以及关联时选择各个表的连接顺序。
- 执行器
通过优化器知道了怎么做,进入执行器开始执行(先判断权限
日志模块
日志模块redo log
Write-Ahead Logging技术简称WAL,先写日志再写磁盘。
当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log里面,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做。innoDB的redo log是固定大小的.
有了redo log,InnoDB就可以保证即使数据库发生异常重启之前提交的记录都不会丢失,称为crash-safe
日志模块binlog
上面的redo log是InnoDB是引擎特有的日志,server层的日志称为binlog,binlog没有crash-safe
能力
两种日志的不同
- 物理日志(在那个数据页上做了修改)逻辑日志(给id=2这一行c加2)
- InnoDB引擎层实现,Mysql的server层实现
- 循环写,空间固定会用完 追加写不会覆盖以前的日志
两阶段提交
更新数据时,将数据读取到内存中,在redo log中进行记录,redo log处于prepare状态,告知执行器完成了,执行器生成这个操作的binlog,并写入磁盘,引擎将redo log改称提交.
id=2 c=0
不采用两阶段提交会产生的问题
原本c=0,update c=c+1
执行结果如下
先redo log后写bin log c=1 备份c=0
先binlog 后redo log c=0 备份c=1
分析:只有redo log有crash safe功能,所以先redo log就会让数据库得到正确的结果(只要写了就算崩溃也会恢复),后写数据库就不会得到正确的结果.binlog没有crash功能,先写就会保证备份正确,后写备份可能就不正确.
nodb_flush_log_at_trx_commit
这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。
sync_binlog
这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。
事务
ACID
事务的ACID 原子性,一致性,隔离性,持久性
- 原子性 操作是一个整体,要么全部成功,要么全部失败回滚,实现原理undo log, undo log是InnoDB里面的两个事务日志之一 。原子性体现在回滚上面。当事务对数据库进行修改的时候,innoDB就会生成undo log,记录sql执行的相关信息(执行相反的操作)。
- 一致性 一个事务执行之前和执行之后都必须处于一致性状态(转账,总和不变)
- 隔离性 并发访问数据库时,多个事务要相互隔离,写写操作通过锁,写读操作通过MVCC
- 持久性是指一个事务一但被提交了,那么对数据库中的数据的改变就是永久性的,通过redo log来实现。
事务的隔离性
事务的隔离级别:读未提交,读提交,可重复读,串行化
串行化:读加读锁,写加写锁,读写锁冲突时,后访问的事务必须等前一个事务执行完成.
MVCC
事务隔离的实现:每条记录在更新的时候都会同时记录一条回滚操作。同一条记录在系统中可以存在多个版本,这就是数据库的多版本并发控制(MVCC)。
MVCC主要是针对读已提交和可重复读。每一条数据除了本身需要存储的数据之外还有trx_id 事务id(自增的)和 roll pointer 回滚指针(回滚到上一个版本用的,之前的记录都保存到undo log日志里面。这就构成了版本链),利用ReadView在版本链里面去选择记录。就可以实现多版本并发控制。
读已提交和可重复读生成的时机是不同的,读已提交是每次执行select查询时都会生成一个readView,他是以每个select查询为单位的。可重复读生成readView是以事务为单位的。
ReadView的内容:
- m_ids 生成ReadView时当前系统中活跃的读写事务的事务id列表。
- min_trx_id 表示生成ReadView时最小的活跃事务的id也是m_ids中的最小值。
- max_trx_id表示生成ReadView时系统应该分配给下一个事务的id值
- creator_trx_id 表示生成ReadView事务的事务id。
ReadView的判断逻辑
class ReadView{
int []m_ids;
int min_trx_id;
int max_trx_id;
int creator_trx_id;
}
int trx_id;//要访问数据的事务id
//判断当前事务是不是可以访问该数据
public boolean judge(){
if(trx_id==creator_trx_id)return true;
if(trx_id<min_trx_id)return true;
if(trx_id>max_trx_id)return false;
for(int i=begin;i<end;i++)
if(m_ids[i]==trx_id)
return false;
return true;
}
/*
文字解释
1.如果访问的数据是当前事务修改的可以访问
2.如果访问的数据是在创建当前事务之前就已经提交了,也可以访问
3.如果访问的数据是没有提交的就不可以访问
4.如果访问的数据在活跃列表中证明没有提交不可以访问,如果没有查询到证明已经提交了可以访问
*/
事务并发产生的问题
脏读:事务 A 读取了事务 B 更新后的数据,但是事务 B 没有提交,然后事务 B 执行回滚操作,那么事务 A 读到的数据就是脏数据
不可重复读:事务 A 进行多次读取操作,事务 B 在事务 A 多次读取的过程中执行更新操作并提交,提交后事务 A 读到的数据不一致。
幻读:事务 A 将数据库中所有学生的成绩由 A -> B,此时事务 B 手动插入了一条成绩为 A 的记录,在事务 A 更改完毕后,发现还有一条记录没有修改,那么这种情况就叫做出现了幻读。
幻读
读分为当前读和快照读,快照读通过mvcc来实现,幻读通过间隙锁
幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。
- 事务的隔离级别为可重复读,且是当前读
- 幻读仅专指新插入的行
出现幻读的原因:
行锁只能锁住存在的行,针对新插入的行没有限定
存储引擎采用加间隙锁的方式来避免出现幻读。它锁的了行与行之间的间隙,能够阻塞新插入的操作 间隙锁的引入也带来了一些新的问题,比如:降低并发度,可能导致死锁。 注意,读读不互斥,读写/写读/写写是互斥的,但是间隙锁之间是不冲突的,间隙锁会阻塞插入操作 另外,间隙锁在可重复读级别下才是有效的。
索引
为什么索引快
计算机从磁盘获取数据,加载到内存期间,一般都要经历3个常规的耗时过程:
1、寻道(时间):确定要读的数据在哪个磁道耗费的时间
2、旋转延迟(时间):确定要读的数据在磁道上的哪个扇区耗费的时间
3、数据传输(时间):数据加载到内存耗费的时间
每次加载数据,我们称其为一次磁盘IO,每一次IO操作耗费时间 = 寻道 + 旋转延迟 + 数据传输(时间短暂,可以忽略不计)。
事实上实际加载数据到内存的时间非常短暂,一次IO操作主要的耗时来自寻道和旋转延迟。
总体来说,一般一次IO操作,耗时大概只有几ms。假如是4ms,虽然看起来很短暂,但是数据库百万级别的数据加载一遍,就需要4000s,对于一个系统来说,简直是毁灭级别的。
我们需要的就是减少磁盘IO的次数,这也是使用索引的意义所在。
选择B+树当索引数据结构的原因
hash比较费内存,二叉树红黑树,树的深度比较高,IO次数比较多,所以可以采取多路平衡树。
B-树和B+树
B-树由于节点上面需要存储数据,导致树的深度会变深,增加了IO次数
mysql中采取了B+树,B+树解决了回旋查找问题。
在 InnoDB 中,表都是根据主键顺序以索引的形式存放的,InnoDB 使用了 B+ 树索引模型,所以数据都是存储在 B+ 树中的。
为什么InnoDB中尽量使用自增主键
- 性能:B+树就会导致有一些页分裂和页合并。(自增主键每一条插入记录都是追加操作,不会触发叶子节点的分裂)
- 空间:用业务主键一般比自增主键长,主键长,普通索引的叶子节点就小,普通索引占据的空间就小
在kv场景下,可以不用自增主键(不然要回表)
覆盖索引
覆盖索引简单来说就是不需要回表就可以查询到所需要的数据,举个例子,有根据身份证查找姓名的高频请求(主键是一个自增id),可以通过建立身份证和姓名的联合索引来避免回表。
最左前缀原则
字符串和联合索引的时候按照这个来
索引下推
避免不必要的回表
索引的语句
#创建单列索引
CREATE INDEX index_name ON table_name(col_name);
#创建复合索引
CREATE INDEX index_name on table_name(col_name1,col_name2,...);
#创建唯一索引(单列)
CREATE UNIQUE INDEX index_name ON table_name(col_name);
#创建唯一索引多列
CREATE UNIQUE INDEX index_name on table_name(col_name,...);
#主键索引
alter table tbl_name add primary key(col_name);
锁
全局锁
MySQL 提供了一个加全局读锁的方法,命令是 Flush tables with read lock (FTWRL)(当你需要让整个库处于只读状态的时候,可以使用这个命令,)全局锁的典型使用场景是,做全库逻辑备份。
全库处于只读状态对于系统的影响?
主库上做备份:业务不能使用。
从库上做备份:binlog不能即使同步,主从延迟。
全库处于只读状态非常不好有什么别的方法做全库逻辑备份吗?
- 官方自带的逻辑备份工具是 mysqldump。当 mysqldump 使用参数
–single-transaction
的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。而由于 MVCC 的支持,这个过程中数据是可以正常更新的。但是必须要支持事务 set global readonly=true
,还不如使用全局锁。他和全局锁的区别在于如果采用这种方式,客户端发生异常,则数据库就会一直保持 readonly 状态,这样会导致整个库长时间处于不可写状态,风险较高。
表级锁
表级锁有两种,一种是表锁一种是元数据锁
- 表锁的语法是 lock tables … read/write,线程加上写锁后当前线程可以读写,其他线程读写操作都会被堵塞
线程加上写锁之后当前线程和其他线程都只能读
- 元数据锁MDL(metadata lock)MDL 不需要显式使用,在访问一个表的时候会被自动加上。
在 MySQL 5.5 版本中引入了 MDL,当对一个表做增删改查操作的时候,加 MDL 读锁;当要对表做结构变更操作的时候,加 MDL 写锁。
读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。因此,如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行。(所以假如有一个mdl写锁,恰好前面上了mdl读锁,那么就需要等待,后面的事务也需要全部等待,不可以读写,所以给频繁访问的表加字段还是比较危险的)
所以如果要变更一个热点表,比较理想的机制是,在alter table语句里面设置等待时间,如果能拿到写锁最好,拿不到也不要阻塞后面的业务语句。
ALTER TABLE tbl_name NOWAIT add column ...
ALTER TABLE tbl_name WAIT N add column ...
行锁
nnoDB支持行锁,MyISAM不支持行锁。
行锁就是针对数据表中行记录的锁。比如事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新。
使用or可能会让行锁升级为表锁 。
比如update a=1 or a=2,update a=3就会被堵塞
- 两阶段锁
在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
两阶段锁的反思总结:假如一个事务需要连续修改1,a,b行数据(不同事务a,b不一样)可以设计事务的顺序为a,b,1,把会被锁住的第一行放在最后面,这样就可以最大程度减少等待时间,从而提高了并发程度。
- 死锁
事务a:先锁第一行在锁第二行
事务b:先锁第二行在锁第一行
死锁:事务a锁完第一行没锁第二行的时候事务b锁住了第一行
处理死锁
- 通过
innodb_lock_wait_timeout
这个参数设置超时时间 - 将参数
innodb_deadlock_detect
设置为 on(默认值就是on),表示发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。
处理死锁的弊端
- 设置超时时间在一些交互式的系统中是不能够容忍的
- 死锁检测,每次加入都要判断自己与其他锁是否构成死锁,可以看成是个等差数列求和,也就是O(n^2),时间复杂度比较高,所以要么修改业务逻辑(比如原本在一个字段(1,a,b中的1)增加,现在将1放到多条记录上),要么控制并发度
范式
基本概念
- 候选码 可以推出表中的所有元素((b-c ac-b)ac是候选码,ab也是候选码 )
- 主属性 组成候选码的属性
- 非主属性 表关系中除了主属性剩余的属性
第一范式
第一范式是指数据库的每一列都是不可分割的基本数据项,强掉列的原子性。
不符合第一范式的例子
学生:姓名 学号 家庭成员 (家庭成员可能有多个)
第二范式
第二范式是在第一范式基础上消除了非主属性对候选码的部分函数依赖,简单的说是表中的非主属性必须完全依赖与主属性(不能指依赖候选键的一部分)
不符合第二范式的例子
订单表:订单编号,商品编号,商品名称,商品数量,订单金额,订单地址
第三范式
去掉了非主属性对于候选码的传递依赖
不符合第三范式的例子
博客表:博客id,分类id,博客创建时间,博客分类名称,博客浏览量
(应该将分类id放到另外一个表中)
BC范式
去掉了主属性的对于候选码部分依赖,或者传递依赖
举个例子
b-c ac-b
可以看出来ac和ab都是候选码,所以abc都是主属性,所以最低也是第三范式,但是存在c对于候选码ab的部分依赖所以不是bc范式
小问题
表数据删除一般,表文件大小不变?
- 原因
因为delete 命令其实只是把记录的位置,或者数据页标记为了“可复用”,但磁盘文件的大小是不会变的。也可以认为是一种逻辑删除,所以物理空间没有实际释放,只是标记为可复用。
- 表文件位置
在 MySQL 8.0 版本以前,表结构是存在以.frm 为后缀的文件里,表数据存储在.ibd文件中,MySQL 8.0 版本,则已经允许把表结构定义放在系统数据表中了。
参数innodb_file_per_table
off:表的数据放在系统共享表空间,也就是跟数据字典放在一起;on:每个 InnoDB 表数据存储在一个以 .ibd 为后缀的文件中。(也是默认值)
- 如何让表真正变小
重建表,消除表因为进行大量的增删改操作而产生的空洞,使用如下命令:
1:alter table t engine=InnoDB
2:optimize table t( 等于 recreate+analyze)。
3:truntace table t (等于drop+create)
- 空洞是什么?怎样产生的?
空洞就是那些被标记可复用但是还没被使用的存储空间。 使用delete命令删除数据会产生空洞,标记为可复用 插入新的数据可能引起页分裂,也可能产生空洞,修改操作,有时是一种先删后插的动作也可能产生空洞。需要注意的是页合并产生的空洞可以存储任何数据,数据删除产生的空洞还必须要在B+树的范围才可以。
NULL
- count(col)
col为null计数丢失
- count(distinct name,phone)
name或者phone为null都不会计数
- name !=‘hh’
name为null的也不会查询出来,该进 name!=‘hh’ or name isnull
- sum(num) where id>4
id | name |
---|---|
1 | 3 |
2 | 6 |
3 | 6 |
4 | null |
返回的是null而不是0
解决
select ifnull(sum(num),0) from t where id>4
- 查询时只能使用is null is not null或者ifnull 传统的= 或者!= <>都会失效
count(*)
- innodb和mylsam实现count(*)有什么不同,以及原因?
mylsam引擎将一个表的总行数存在了磁盘中,因此执行count(*)会直接返回这个数效率很高。(当然添加了where条件就不是这样了)
innodb引擎执行count(*)的时候,需要把数据一行行的读出来,让后累积计数。
innodb引擎为什么不采用mylsam引擎的方法呢?
由于MVCC的原因,即使是同一时间的查询,InnoDB该返回多少行也是不确定的,举个例子,原本100行,有三个会话
时刻 | 会话a | 会话b | 会话c |
---|---|---|---|
1 | 开始事务 | 开始事务insert into()提交 | |
2 | 开始事务 | ||
3 | insert into() | ||
4 | select * form tb 提交事务 | select * form tb 提交事务 | select * form tb 提交事务 |
可以看到在时刻4, a返回100,b返回102,c返回101.
可以看到即使是同一时刻,都会返回不同的行数。
- show table status命令显示行数可以吗?
不可以,这个命令是一个根据样本估算得出的统计值,官方文档说误差可能有40%到50%。
- 怎样快速求出count(*)?
可以保存在redis中(redis出现异常重启时在从数据库中读取数据),但是保存在redis中在下面的业务场景可能会有问题。
我有一个博客项目,首页需要同时展示最新的10条博客以及总的博客数目。
假如我把行数保存在数据库中可能出现问题
时刻 | 我自己写博客 | 浏览博客 |
---|---|---|
T1 | 插入一行数据 | |
T2 | 读取redis中的值,返回最新的10条博客 | |
T3 | redis数据加一 |
可以看到,浏览博客就会出现显示的最新10条博客正确,但是返回的条目数不一样,同理将redis计数加一和插入数据行会出现行数一样但是最新博客不对。
问题出现的原因以及解决方法
-
原因:插入数据以及增加数据应该是一条原子性的语句,但是我们却不是这么设计的。
-
解决方法:将数据库总条目放到数据库中存储,将其看成数据库的一个事务进行操作就可以解决了。
- 不同count的用法
count():innodb引擎是索引组织表,主键的索引是叶子节点,普通索引树叶子节点是主键值,因此普通索引树要比主键索引树小,mysql优化器会找到最小的那棵树来遍历。count( * ) innodb知道count()是取行数这个语义,做了一些其他优化
count(主键id)innodb引擎会遍历整张表,把每一行的id值取出来,返回给serve层,serve层判断是不是空,不为空的就按行返回。
count(1)innodb引擎遍历整张表,但不取值,serve层对于返回的每一行放进去数字“1”,判断1等不等于空(显然永远不等于),按行累加
count(字段)执行器需要判断这个字段是不是等于null,不是null才累加。
按照效率排序count(*)>count(1)>count(主键id)>count(字段)
mysql在不同时刻发生异常重启,是怎么保证数据的完整性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qjIETfyf-1649655846627)(C:\Users\laixi\AppData\Roaming\Typora\typora-user-images\image-20220402103355627.png)]
从上图就可以知道两阶段提交时每一个时刻出现崩溃时的处理方式。
mysql是怎么知道binlog是否完整的?mysql在5.6.2版本以后引入了binlog-checksum参数,用来验证binlog内容的正确性。
redo log和binlog是怎么关联起来的?因为redo log和binlog有一个共同的数据字段,叫做XID。
order by
- 全字段排序
全字段排序顾名思义就是将需要查询的所有字段和代排序字段放到一起进行排序,从原表中找到符合条件的行后,会将查询的所有字段和待排序字段放入到一个sort buffer中,将其中按照待排序字段排完序后直接返回就是结果。sort buffer是有大小的,假如排序的数据量大于这个值,就会将数据分成多份,使用外部排序来完成操作。全字段排序只对原表的数据读了一遍。
- rowid排序
rowid排序顾名思义就是将待排序字段和rowid放在一起进行排序,有参数max_length_for_sort_date,描述的是待排序的数据行最大是多少,假如超过了这个值,就会采用rowid排序算法,rowid算法会将待排序字段和主键id放到sort_buffer中,排序完成后再根据主键id来查找其他查询的元素,最后返回。对比全字段排序多了一次访问表的主键索引(其实这并不像普通的回表操作那么慢,因为已经加载进去内存了,不需要磁盘读取了)。
间隙锁和行锁合称next key lock,每个next key lock是前开后闭区间
sql执行顺序
SELECT DISTINCT
< select_list >
FROM
< left_table > < join_type >
JOIN < right_table > ON < join_condition >
WHERE
< where_condition >
GROUP BY
< group_by_list >
HAVING
< having_condition >
ORDER BY
< order_by_condition >
LIMIT < limit_number >
- 首先,对 SELECT 语句执行查询时,对
FROM
关键字两边的表执行连接,会形成笛卡尔积
- 然后对 FROM 连接的结果进行 ON 筛选
- 如果是
OUTER JOIN(left join、right join)
,那么这一步就将添加外部行 - 执行 WHERE 过滤器
- 根据 group by 字句中的列,会对记录进行分组操
- 紧跟着 GROUP BY 字句后面的是 HAVING
- 执行 SELECT 语句
- 应用 order by 子句
explain
id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|
- id
如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
- select_type
查询的类型,主要是用于区分普通查询、联合查询、子查询等复杂的查询
1、SIMPLE:简单的select查询,查询中不包含子查询或者union
2、PRIMARY:查询中包含任何复杂的子部分,最外层查询则被标记为primary
3、SUBQUERY:在select 或 where列表中包含了子查询
4、DERIVED:在from列表中包含的子查询被标记为derived(衍生),mysql或递归执行这些子查询,把结果放在临时表里
5、UNION:若第二个select出现在union之后,则被标记为union;若union包含在from子句的子查询中,外层select将被标记为derived
6、UNION RESULT:从union表获取结果的select
- table
查询的是哪个表
- type
访问类型,sql查询优化中一个很重要的指标,结果值从好到坏依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
- system 表只有一行记录(等于系统表)
- const:表示通过索引一次就找到了,const用于比较primary key 或者 unique索引。因为只需匹配一行数据,所有很快。如果将主键置于where列表中,mysql就能将该查询转换为一个const
- eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键 或 唯一索引扫描。
- ref:非唯一性索引扫描,返回匹配某个单独值的所有行。本质是也是一种索引访问,它返回所有匹配某个单独值的行,然而他可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体
- range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了那个索引。一般就是在where语句中出现了bettween、<、>、in等的查询。这种索引列上的范围扫描比全索引扫描要好。只需要开始于某个点,结束于另一个点,不用扫描全部索引
- index:Full Index Scan,index与ALL区别为index类型只遍历索引树。这通常为ALL块,应为索引文件通常比数据文件小。(Index与ALL虽然都是读全表,但index是从索引中读取,而ALL是从硬盘读取)
- ALL:Full Table Scan,遍历全表以找到匹配的行
- possible_keys
查询涉及到的字段上存在索引,则该索引将被列出,但不一定被查询实际使用
- key
实际使用的索引,如果为NULL,则没有使用索引。查询中如果使用了覆盖索引,则该索引出现在key列表中
- key_len
表示索引中使用的字节数,查询中使用的索引的长度(最大可能长度),并非实际使用长度,理论上长度越短越好。key_len是根据表定义计算而得的,不是通过表内检索出的
- ref
显示索引的那一列被使用了,如果可能,是一个常量const。
- rows
根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
- filtered
命中率
- Extra
不适合在其他字段中显示,但是十分重要的额外信息。
- Using filesort
mysql自己进行排序,不是从索引中顺序读取的。
- Using temporary
使用了临时表,常见于order by 和 group by
order by的row id排序会出现这种情况,当一行数字的字段值过大,就会提取出来排序字段和rowid排序,在回表查询
- Using index
在select中使用了索引覆盖
-
Using where
使用了where过滤
-
Using join buffer :
使用了链接缓存 -
Impossible WHERE:
where子句的值总是false,不能用来获取任何元祖 -
select tables optimized away:
在没有group by子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段在进行计算,查询执行计划生成的阶段即可完成优化 -
distinct:
优化distinct操作,在找到第一个匹配的元祖后即停止找同样值得动作