1. 客户端发送一条查询给服务器
2. 服务器先检查查询缓存,如果命中缓存,则立即返回缓存结果,否则进入下一阶段
3. 服务器端进行SQL解析,预处理,再由优化器生成对应的执行计划
4. Mysql根据优化器生成的执行计划,调用存储引擎的API来执行查询
5. 将结果返回给客户端
MySQL的执行计划是在服务器层生成的,但是统计信息是在存储引擎层生成的!
优化器是数据库的一个核心子系统,你也可以把他理解为MySQL数据库中的一个核心模块或者一个核心功能模块。
优化器的目的是按照一定原则来得到她认为的目标SQL在当前情形下最有效的执行路径,优化器的目的是是为了得到目标SQL的执行计划
传统关系型数据库里面的优化器分为CBO和RBO两种。
RBO--- Rule_Based Potimizer 基于规则的优化器
RBO RBO所用的判断规则是一组内置的规则,这些规则是硬编码在数据库的编码中的,RBO会根据这些规则去从SQL诸多的路径中来选择一条作为执行计划(比如在RBO里面,有这么一条规则:有索引使用索引。那么所有带有索引的表在任何情况下都会走索引)所以,RBO现在被很多数据库抛弃(oracle默认是CBO,但是仍然保留RBO代码,MySQL只有CBO)
CBO---Cost_Based Potimizer 基于成本的优化器
CBO RBO最大问题在于硬编码在数据库里面的一系列固定规则,来决定执行计划。并没有考虑目标SQL中所涉及的对象的实际数量,实际数据的分布情况,这样一旦规则不适用于该SQL,那么很可能选出来的执行计划就不是最优执行计划了。
CBO在会从目标诸多的执行路径中选择一个成本最小的执行路径来作为执行计划。这里的成本他实际代表了MySQL根据相关统计信息计算出来目标SQL对应的步骤的IO,CPU等消耗。也就是意味着数据库里的成本实际上就是对于执行目标SQL所需要IO,CPU等资源的一个估计值。而成本值是根据索引,表,行的统计信息计算出来的。(计算过程比较复杂)
关于Cardinality的解释:
Cardinality是CBO特有的概念,它是指指定集合包含的记录数,说白了就是指定结果集的行数。Cardinality和成本值的估计息息相关,因为MySQL的指定结果集所消耗的io资源可以近似看做随着该结果集的递增而递增。
可选择率:
可选择率也是CBO特有的概念,它是指施加指定条件后返回结果集的记录数占未施加任何谓词条件的原始结果集的记录数的比率(谓词条件,可以理解为where等限定词进行限定)
selectivity(可选择率)=施加指定谓词条件后返回结果集的记录数/未施加指定谓词条件的结果集的记录数,我们可以通过可选择率的估计,来确定一个列上是否需要添加索引,实际上,MySQL的CBO也是通过可选择率来确定是否走索引,值越大在做表连接的时候,就越有机会选择这个索引。
对于Cardinality总结:
1. 列值代表的是此列中存储的唯一值的个数(如果此列为primary key 则值为记录的行数)
2. 列值只是个估计值,并不准确。
3. 列值不会自动更新,需要通过Analyze table来更新一张表或者mysqlcheck -Aa来进行更新整个数据库。
4. 列值的大小影响Join时是否选用这个Index的判断。
执行一次where的成本,可以通过查询当前会话的last_query_cost来计算当前的查询成本
mysql> select *from t1 where e="fish";
| e |
| fish |
| fish |
mysql> show status like '%last_query_cost%';
|Last_query_cost | 1.599000 |
这个结果表示,MySQL大概要做1.59页的随机查找才能完成查询,这是根据一系列的统计信息得来的。每个表或者索引的页面个数,索引的基数(Cardinality),索引和数据行的长度,索引分布,优化器在估算成本的时候不考虑缓存,他预设每次都需要一次磁盘IO。
1. 查询优化器使用统计信息为SQL选择执行计划
2. mysql没有数据直方图也无法手工删除统计信息
3. 在服务器层有查询优化器,却没有保存数据和索引统计信息。统计信息由存储引擎实现,不同的存储引擎会存储不同的统计信息
4. 统计信息分为索引的统计信息,表的统计信息
Analyze table收集表和索引统计信息,适用于MyISAM和InnoDB
对于INNODB存储引擎,在以下情况下,会重新收集统计信息:
1. 表第一次打开的时候
2. 表修改的行超过1/16 或者新插入20亿行的时候计算索引的统计信息
3. 执行show index或者查询information schema下的表
information_schema.TABLES
information_schema.STATISTICS
information_schema.PARTITIONS
information_schema.KEY_COLUMN_USAGE
information_schema.TABLE_CONSTRAINTS
information_schema.REFERENTIAL_CONSTRAINTS
information_schema.table_constraints
4. MySQL5.5与5.6版本的收集区别
在讨论收集之前,先看下5.5版本和5.6版本的关于统计信息配置的不同
MySQL 5.5版本统计信息
mysql> show variables like '%stats%';
结果如下:
innodb_stats_method nulls_equal
innodb_stats_on_metadata OFF
innodb_stats_sample_pages 8
myisam_stats_method nulls_unequal
对于参数的解析
innodb_stats_on_metadata
源码中,对于统计信息的收集,每个表维护一个stat_modified_counter 变量每次DML更新一行,此变量就+1,
当达到更新统计信息的阈值时,此重置为0,数据库层面,innodb_stats_on_metadata参数用来控制是否自动收集元数据信息,设置为false时不更新统计信息,在5.5中此参数默认开启;但是5.6中默认关闭,
innodb_stats_method
这个参数描述了在收集统计信息的时候,对于null的处理,这个参数可以影响到index的统计信息的对于Cardinality的处理。如果使用Nulls_unequal时,而且此时的null数量过多的话,是会影响查询性能的。
Nulls_equal: 所有Null都相等
Nulls_unequal: 所有Null互不相同
Nulls_ignored: 忽略Null
MySQL 5.6版本统计信息
下面我们来看一下5.6版本里面的关于统计信息的参数
mysql> show variables like '%_stats%';
innodb_stats_auto_recalc ON
innodb_stats_method nulls_equal
innodb_stats_on_metadata OFF
innodb_stats_persistent ON
innodb_stats_persistent_sample_pages 20
innodb_stats_sample_pages 8
innodb_stats_transient_sample_pages 8
myisam_stats_method nulls_unequal
innodb_stats_persistent
#是否持久化统计信息,默认打开
持久性的统计存储在mysql.innodb_table_stats和mysql.innodb_index_stats中
innodb_stats_persistent_sample_pages
#当打开innodb_stats_persistent选项时,这个设置才生效
innodb_stats_transient_sample_pages
#当关闭innodb_stats_persistent选项时生效,采样page数(尤其是后者)不应该设置的太大,否则会产生额外的IO开销,但也不应设置的太小,否则会导致查询计划不准确
innodb_stats_auto_recalc
#用于决定是否在表上存在大量更新时(超过10%的记录更新)重新计算统计信息。默认打开,如果关闭该选项,就需要在每次创建索引或者更改列之后,运行一次ANALYZE TABLE命令来更新统计信息,否则可能选择错误的执行计划。同样的,也可以在CREATE TABLE/ALTER TABLE命令中指定STATS_AUTO_RECALC值
如何查看统计信息
索引统计信息
Show index from table或information_schema.statistics表
表统计信息
Show table statuslike或information_schema.tables表
***要注意的是,如果生产环境上,大表大数据上,要谨慎做这些线上的数据统计分析,如果在大表上面查询性能会出现大幅度抖动。
select * from information_schema.tables where table_schema='test'\G;
*************************** 2. row ***************************
TABLE_CATALOG: def ---- 数据表登记目录
TABLE_SCHEMA: test ----所属数据库名字
TABLE_NAME: t2 ----表名称
TABLE_TYPE: BASE TABLE ----表类型(view|base table)
ENGINE: InnoDB ----使用的存储引擎类型
VERSION: 10 ------数据文件对应FRM的版本(默认10)
ROW_FORMAT: Compact -----行格式(compact|dynamic|fixed)
TABLE_ROWS: 7 ---行数
AVG_ROW_LENGTH: 2340 ---------平均行长度
DATA_LENGTH: 16384 ---------数据长度
MAX_DATA_LENGTH: 0 ---------最大数据长度
INDEX_LENGTH: 16384 ---------索引长度
DATA_FREE: 0 -----磁盘已经分配但是未使用
AUTO_INCREMENT: NULL ------自增
CREATE_TIME: 2016-03-18 15:26:55 ----创建时间
UPDATE_TIME: NULL ----更新时间
CHECK_TIME: NULL ---创建时间
TABLE_COLLATION: utf8_general_ci ----字符集
CHECKSUM: NULL ---校验和
CREATE_OPTIONS:
TABLE_COMMENT: ---表描述
从统计信息中,我们可以判断一个表的碎片的多少,看是否要进行碎片处理
[(data_length + Index_length) - rows *Avg_row_length] /1024/1024