<<深入理解mariadb和mysql>>之mysql执行计划分析学习记录

时间:2022-09-23 13:02:55

统计信息
只包括记录条数和索引唯一值个数

永久统计信息:
mysql 5.6
show index stats
或查看表innodb_table_stats和innodb_index_stats

create table   engine=innodb stats_persistent={DEFAULT|0|1}

为1表示统计信息保存在上面两个表中,为0表示不保存,用5.5前的管理方式,default时由下面参数来决定.

show global variables like 'innodb_stats_persistent';---由这个参数控制,默认为on,表示

每当遇到下列情形时,会重新收集:
新打开数据表时;
大量修改表的记录时(在数据表的全部记录中执行1/16的update、insert、delete时)
执行analyze tables时
执行show table status或show index from命令时
激活innodb监视器时

innodb_stats_on_metadata设置为on并执行show table status时

建表时,stats_auto_recalc为1表示自动收集  为0表示只能用analyze table收集,default时由参数 innodb_stats_auto_recalc(默认为on)值决定。
innodb_stats_persistent_sample_pages参数默认为20,表示执行analyze table时取20个页面收集,并将结果保存为永久统计信息
innodb_stats_transient_sample_pages 默认为8个,取8个页块,分析结果作为统计信息
mariadb:

使用综合统计信息:
table_stats、index_stats、column_stats

当use_stat_tables 为never时,使用永久统计信息
为preferably时,使用综合统计信息
为complementary,优先使用各引擎的统计信息,信息不足时,才使用综合信息;
当use_stat_tables 为never时,执行analyze table,只收集存储引擎的统计信息,为preferably或complementary时,才会同时收集引擎和综合统计信息。

analyze table用法:
analyze table tb1 persistent for columns (col1,col2) indexes (idx1,idx2);
analyze table tb1 persistent for columns (col1,col2) indexes ();
analyze table tb1 persistent for columns () indexes (idx1,idx2);
analyze table tb1 persistent for columns () indexes ();
analyze table tb1 persistent for all;


直方图:
 高度均衡直方图:先对所有列值 进行排序,再按相同的记录个数将其划分为几个组,然后取各组的最后一个值(排序后的最大值)保存到直方图

histogram_size默认为0,表示不使用直方图

使用直方图:
set histogram_size=20;
set use_stat_tables='preferably';
set histogram_type='double_prec_hb';


优化器连接优化参数:

optimizer_prune_level  默认为1,表示用Greedy检索算法,为0表示用exhaustive检索算法(n!种执行计划选择最优的一个)。

optimizer_search_depth=64  对greedy算法有用

查看具体时间使用:
set profiling=1
select ...
show profiles;
show profile cpu for query 1;

set session  optimizer_search_depth=10   ---当表比较多时,可以降低这个值 。

select_type列:

simple:需要union或子查询是的select
primary:需要union或含有子查询执行计划中,位于最外层的查询
union:由union组合成的查询中,除第一个外,第二个以后的所有查询都为union,由于有多个查询用union或union all进行联合,并创建临时表进行使用,所以union的第一个查询为DERIVED(

派生),若是在from后的第一个为派生
如:
select * from a where ...
uinon
select * from b where ...
dependent union和dependent subquery:当union作为子查询时,其中第二个union的select_type就是DEPENDENT UNION。
第一个子查询的select_type则是DEPENDENT SUBQUERY。当含 有DEPENDENT关键字的子查询时,外部查询先执行,子查询后执行,一般这种性能比较低.
dependent 表示union或union all受单位查询外部影响 ,若子查询需要使用外部查询中定义的列,则为dependent subquery.

如:
select * from a where a.id in (
select b.id from B where ....
union
select c.id from C where ....)

union result:包含union结果的数据表(临时表),因为不是一个查询,所以没有id值 。

subquery:除指from子句以外的子查询
derived:需要创建临时表,from子句的子查询

uncacheable subquery:
  subquery:不受外层查询影响 ,只执行一次,将结果缓存,后面每次使用缓存中的结果。
  dependent subquery:依赖外部查询列值为单位进行缓存

  下面三种无法使用缓存:
  a.子查询含有用户变更时,b.子查询含 用OT-DETERMINISTIC属性的存储列程时
  c.子查询使用每次调用有不同结果值的函数时(如uuid()或rand())

materialized:
用于优化from子句或in查询中的子查询,子查询内容具体化为临时表,与derived类似.

table列:
<derived N> <union M,N>,表示临时表,数字表示执行计划中的id
materialized执行计划中table列会出现<subquery N>,表示将子查询结果具体化为临时表.

from子句的子查询必需要有别名。

type列:就是访问方式

1.system --访问仅有1条记录或没有记录的空表时,只适应myisam或memory引擎表中
2.const --通过主键或唯一键等值条件访问数据,有且只返回一行记录,相当于唯一索引扫描,常量化处理
3.eq_ref --多表连接中,第一次读取的表列值,作为读取第二张表的主键或唯一键等值检索条件,第二张表只返回一条记录
4.ref --使用等值条件检索,不保证返回一条记录。

执行计划中ref列中的const表示条件中的值是常量

5.fulltext---match ..against...条件查询
6.ref_or_null---是ref和is null的组合
7.unique_subquery --in查询中,in后面不返回重复值,所以不需要删除重复值
8.index_subquery --in查询或in(常数列表),in后面可能返回重复值 ,使用索引删除重复值
9.range ---范围扫描
10.index_merge ---使用两个以上的索引获得结果后,再将其进行合并
11.index --全索引扫描,排序时加上limit,获得较好的性能
12.all ---全表扫描

key_len列:
1.可以根据执行计划的ken_len来判断复合索引中字段是否有使用,计算规则如下:
 当索引字段为定长数据类型,比如char,int,datetime,需要有是否为空的标记,这个标记需要占用1个字节;对于变长数据类型,比如:varchar,除了是否为空的标记外,还需要有长度
信息,需要占用2个字节;同时还需要考虑表所使用的字符集,不同的字符集,gbk编码的为一个字符2个字节,utf8编码的一个字符3个字节,latin为1字节
2.单表排序时,order by后面字段顺序需要与复合索引中字段顺序一致时,才能用索引,符合索引的最左原则,且select后面字段需为索引字段的部分或全部。
3.order by 或group by 的字段不参与key_len长度计算。

integer类型:4字节
date类型:3字节  若同时定义了nullable列,则需加1为4


ref列:提供了哪种值作为参考条件,常数值显示为const,若为其它表的列值,则显示数据表名或列名

当为func时,表示经过排序或运算变换


rows:指处理查询时要从磁盘读取与检查多少条记录,不是返回记录数,估计值。

extra列:
 1.const row not found --虽然使用const访问方式读取了数据表,但若实际数据表中没有1条符合条件的记录,建议先向表中存储适当的测试数据,再查看执行计划.

2.distinct--表示去重
3.full scan on null key ---出现在col1 in (select col2 from ...)时,当col1为null时,就会发生全表扫描,建议在定义col1为非空或sql中加入where col1 is not null来避免。一
4.impossible having
 不存在满足having子句条件的记录时,会显示impossible having,出现这个,表示sql编写错误
5.impossible where --同impissible having一样
6.impossible where noticed after reading const tables --- 读数据表后,发现记录不存在
7.no matching min/max row ---查询中有mix和max,但没有符合条件的记录,返回空值 。
8.no matching row in const table ---以const方式访问连接中的数据表时,不存在符合条件的记录.
9.no tables used --不带有from子句的查询或from dual查询的计划中
10.not exists ---使用外部连接进行反连接的查询中的extra会出现not exists
11.range checked for each record(index map:n) ---在连接两个表的查询中,连接条件不是常量,而两个变量或两个字段,如where e2.emp_no>e1.emp_no
  index map:n中的n为16进制,换成二进制,对应位置为1表示备选索引在表中出现顺序.
12.scanned N database ---查看information_schema库的信息时,才会有
13. select tables optimizer away ---select只使用min()或max(),或者用group by访问min或max时,若无法使用合适的索引,就会按升序或降序只读取一个索引。
14.unique row not found--两个表的唯一键(含主键)执行外部连接查询,若外连接表中不存在一样的记录。
15.using filesort ---当没有索引时,就会出现,先读取数到排序缓存区,排序后返回到客户端
16 using index ---只读取索引内容,覆盖索引
17 using index for group-by --先用索引排序,然后再分组
18 using join buffer(block nested loop)、using join buffer(batched key access)
 读取驱动的表的记录放到临时的内存空间(join_buffer_size)
19 using sort_union、using union、using intersect、using sort_intersect
  都是index_merge操作的四种方式
  using sort_union和using union是or操作,后两个是and操作
  有sort的表示先排序操作
20 using temporary
  临时表使用
21 use where ---表示mysql层进行数据加工和过滤处理
22 using where with pushed condition ---适合NDB引擎,条件下推
23 deleting all rows ---删除所有记录
24 FirstMatch(tbl_name)---是将in子查询转换成exists的优化方式,tbl_name为外部表,表示从外部表读一条记录,再从子查询检索记录,直到匹配到记录为止
若子查询中一次就可找到符合条件的记录,则执行代价非常小,若不存在任何符合条件的记录,则执行代价变得非常高。
mairadb 10采用是materialized优化
25 loosescan(m..n)--用于in子查询中可能产生重复记录时
先使用using index scan访问方法读取子查询内容,然后删除重复记录时,要使用lossescan优化方法,不需要使用临时表。
使用方法:
  set optimizer_switch=default;
  set optimizer_switch='firstmatch=off';
  set optimizer_switch='materialization=off';
26 materialize、scan
 mysql 5.6和mariadb 10前几个版本的extra会显示,后期版本没有显示,但会在select_type中显示materialized,显示scan,表示不为具体化内容创建索引,走全表扫描,不显示scan
表示为具体化的临时表建索引,并将其用作lookup
27 start temporary、end temporary
优化子查询
先访问子查询,然后将与外部查询数据表连接后的结果存储到临时表,之后再删除重复记录。子查询表在执行计划中的extra显示start temporay,外部表显示end temporary。
28 using index condition
索引条件下推

29 rowid-ordered scan、key-ordered scan
MRR(多范围读)---先通过索引读取符合where条件的记录,然按主键排序,再从实际数据文件中读取其余数据列,减少随机IO

Mysql 5.6显示using MRR
mairaDB显示rowid-ordered scan和key-ordered scan
其中mysql 5.6的using MRR只相当于rowid-ordered scan
key-ordered scan是用于主键与连接的查询中,多表连接中,其中有一个表的连接字段为主键,对符合条件的记录按连接字段进行排序,然后再连接。

开启方法:
set optimizer_switch='mrr=on';
set optimizer_switch='mrr_sort_keys=on';
set join_cache_level=8;

4.3.11 explain extended(filtered列)
 filtered列表示过滤后剩下的记录数占读取总记录数的百分比,估计值

4.3.12 explain extended(附加优化器信息)
show warnings 查看优化器分析重组后的查询语句

4.3.13 explain partitions,分增加partitions列,显示查询使用的分区目录

4.4优化器提示
4.4.1 提示使用方法
create /*!32302 temporary */  table ...
当版本大于等于3.23.02时,执行create tmporary table,低于此版本,执行create table..
主版本取第一位数,次版本号取两位,修订版本号取两位。

4.4.2 straight_join

select straight_join ...

select /*!straight_join*/ ....

强制指定from后表按顺序连接。

或直接将inner join 改成straight_join


适用于普通表与临时表之间、普通表与普通表之间、临时表与临时表之间,这里临时表是批派生表或内嵌视图

4.4.3 use index/force index/ignore index
在表名或表别名后加use index()

4.4.4
sql_cache/sql_no_cache

与query_cache_type设置有关,
无提示时,只有等于1(on)时才缓存;
sql_cache时,等于1(on)或2(demand)时才缓存
sql_no_cache时,都不缓存

若能选择性的工作,当设置为2,再加上sql_cache提示时即可,
4.4.5 sql_calc_found_rows

当加了这个提示时,mairadb查找到limit指定条数的记录之后,还会继续检索,最终还是会返回limit指定记录数.
用found_rows()返回符合条件的记录数。

4.5 分析执行计划需要注意的事项

4.5.1 select_type
derived,注意临时表是否在磁盘中
dependent subquery --去除子查询对外部查询的依赖。

4.5.2 type列
all和index

4.5.3 key列
为空时,注意增加索引
4.5.4 rows列
rows列不受limit限制
若值比实际的记录数多时,检查索引使用情况
4.5.5 extra列

查询条件执行计划不佳:
range checked for each record(indedx map:N)
using filesort
using join buffer
using temporary
using where