222:cbo 和 rbo 的工作方式。
1.基于规则的优化方式(Rule-BasedOptimization,简称为RBO)
优化器在分析SQL语句时,所遵循的是Oracle内部预定的一些规则,对数据是不敏感的。它只借助少量的信息来决定一个sql语句的执行计划,包括:
1)sql语句本身
2)sql中涉及到的table、view、index等的基本信息
3)本地数据库中数据字典中的信息(远程数据库数据字典信息对RBO是无效的)
例如:我们常见的,当一个where子句中的一列有索引时去走索引。但是需要注意,走索引不一定就是优的,比如一个表只有两行数据,一次IO就可以完成全表的检索,而此时走索引时则需要两次IO,这时全表扫描(full table scan)的效率更优。
2.基于代价的优化方式(Cost-BasedOptimization,简称为CBO)
它是看语句的代价(Cost),通过代价引擎来估计每个执行计划所需的代价,该代价将每个执行计划所耗费的资源进行量化,CBO根据这个代价选择出最优的执行计划。一个查询所耗费的资源可分为三部分:I/O代价、CPU代价、NETWORK代价。I/O是指把数据从磁盘读入内存时所需代价(该代价是查询所需最主要的,所以在优化时一个基本原则就是降低I/O总次数);CPU代价是指处理内存中数据所需的代价,数据一旦读入内存,当我们识别出我们所要的数据后,会在这些数据上执行排序(sort)或连接(join)操作,这需要消耗CPU资源;对于访问远程节点来说,network代价的花费也是很大的。
优化器在判断是否用这种方式时,主要参照的是表及索引的统计信息。统计信息给出表的大小、有多少行、每行的长度等信息。这些统计信息起初在库内是没有的,是做analyze后才出现的,很多的时侯过期统计信息会令优化器做出一个错误的执行计划,因些应及时更新这些信息(dbms_stat.analyze)。
如星型连接排列查询,哈希连接查询,函数索引,和并行查询等一些技术都是基于CBD的。
223:mysql cbo 使用到的统计信息以及没有直方图带来的缺陷,如何解决这个缺陷。
MySQL的Query Tree 是通过优化实现DBXP 的经典数据结构和Tree 构造器而生成的一个指导完成一个Query 语句的请求所 需要处理的工作步骤,我们可以简单的认为就是一个的数据处理流程规划,只不过是以一个Tree 的数据 结构存放而已。通过Query Tree 我们可以很清楚的知道一个Query 的完成需要经过哪些步骤的处理, 每一步的数据来源在哪里,处理方式是怎样的。在整个DBXP 的Query Tree 生成过程中,MySQL 使用了 LEX 和YACC 这两个功能非常强大的语法(词法)分析工具。MySQL Query Optimizer 的所有工作都是基 于这个Query Tree所进行的。各位读者朋友如果对MySQL Query Tree 实现生成的详细信息比较感兴 趣,可以参考Chales A. Bell 的《Expert MySQL》这本书,里面有比较详细的介绍。
MySQLQuery Optimizer 并不是一个纯粹的CBO(Cost Base Optimizer),而是在CBO 的基础上增 加了一个被称为Heuristic Optimize(启发式优化)的功能。也就是说,MySQL Query Optimizer 在优 化一个Query 选择出他认为的最优执行计划的时候,并不一定完全按照系数据库的元信息和系统统计信息,而是在此基础上增加了某些特定的规则。其实我个人的理解就是在CBO 的实现中增加了部分 RBO(Rule Base Optimizer)的功能,以确保在某些特别的场景下控制Query 按照预定的方式生成执行 计划。
当客户端向MySQL 请求一条Query ,到命令解析器模块完成请求分类区别出是SELECT 并转发给 Query Optimizer 之后,Query Optimizer 首先会对整条Query 进行,优化处理掉一些常量表达式的预算,直接换算成常量值。并对Query 中的查询条件进行简化和转换,如去掉一些无用或者显而易见的条 件,结构调整等等。然后则是分析Query 中的Hint 信息(如果有),看显示Hint 信息是否可以完全 确定该Query 的执行计划。如果没有Hint 或者Hint 信息还不足以完全确定执行计划,则会读取所涉 及对象的统计信息,根据Query 进行写相应的计算分析,然后再得出最后的执行计划。
QueryOptimizer 是一个数据库软件非常核心的功能,虽然在这里说起来只是简单的几句话,但是在MySQL 内部,Query Optimizer 实际上是经过了很多复杂的运算分析,才得出最后的执行计划。对于 MySQL Query Optimizer 更多的信息,各位读者可以通过MySQLInternal 文档进行更为全面的了解。
CBOcost base optimizer mysql通过cbo挑选路径
基于成本的:
【1】考虑这个表有多少行
【2】这个列的选择性
工作方式:
【1】先根据规则库,过滤掉肯定不行的路径
【2】对于可能行的执行路径,计算每一个执行路径的成本
【3】选择一个最小的成本的路径作为执行计划
cbo解析的时候会用到:
【1】数据字典
【2】统计信息:表的行数和索引列的选择性
【3】mysql没有记录列值的倾斜程度(直方图:列上的每个值占多少行的分布直方图)5.7之前,如果一个值占了99%的行就说明倾斜程度很大,mysql8.0开始记录
针对不同的倾斜程度,强制执行不同的索引
224:学会使用 ue 批量编写脚本。
UltraEdit 是一套功能强大的文本编辑器,可以编辑文本、十六进制、ASCII 码,完全可以取代记事本(如果电脑配置足够强大),内建英文单字检查、C++ 及 VB 指令突显,可同时编辑多个文件,而且即使开启很大的文件速度也不会慢。
225:写一个定期批量收集统计信息的脚本 。
可以使用Analyeztable 命令定期刷新统计信息,然后使用Python连接数据库,利用查看统计信息的相关命令将数据保存在文件系统中或数据库中。
具体脚本略。
226:手工修改 rows 和 card 统计信息,引导 mysql 走索引。
rows:MySQL Query Optimizer通过系统收集到的统计信息估算出来的结果集记录条数;
cardinality:索引中唯一值的数目的估计值。通过运行ANALYZETABLE或myisamchk -a可以更新。基数根据被存储为整数的统计数据来计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时,MySQL使用该索引的机 会就越大。
使rows值减小和cardinality增大的方式引导MySQL使用索引。
227:计算表和索引的大小。
通过统计信息计算:
use information_schema;
SELECT
TABLE_NAME,
(DATA_LENGTH/1024/1024) as DataM ,
(INDEX_LENGTH/1024/1024) as IndexM,
((DATA_LENGTH+INDEX_LENGTH)/1024/1024) asAllM,
TABLE_ROWS
FROM
TABLES
WHERE
TABLE_SCHEMA = 'fj_db';
通过表和索引的状态计算:
SELECT
sum(stat_value) pages,
index_name,
sum(stat_value) * @@innodb_page_sizesize
FROM
mysql.innodb_index_stats
WHERE
table_name = 't'
AND database_name = 'test'
AND stat_description = 'Number of pagesin the index'
GROUP BY
index_name;
228:使用 like 来解决 mysql 数据倾斜导致执行计划不优秀的情况(个例)。
1. 对于字段特别的长的列不建议使用全字段匹配查询,会增大资源的消耗,使用like进行匹配可以达到同样的目的。
2. 使用like的情况下,‘%xxx’和’%xxx%’都不能使用索引,想使用索引的话只能使用‘xxx%’的方式进行匹配。
3. 如果必须要求前端使用模糊匹配的话,可以使用全文索引来处理。
229:使用 force、ignore 来手工解决数据倾斜问题。
强制索引MySQLFORCE INDEX:
SELECT * FROMTABLE1 FORCE INDEX (FIELD1) …
以上的SQL语句只使用建立在FIELD1上的索引,而不使用其它字段上的索引。
忽略索引IGNORE INDEX:
SELECT * FROMTABLE1 IGNORE INDEX (FIELD1, FIELD2) …
在上面的SQL语句中,TABLE1表中FIELD1和FIELD2上的索引不被使用。
关闭查询缓冲 SQL_NO_CACHE:
SELECTSQL_NO_CACHE field1, field2 FROM TABLE1;
有一些SQL语句需要实时地查询数据,或者并不经常使用(可能一天就执行一两次),这样就需要把缓冲关了,不管这条SQL语句是否被执行过,服务器都不会在缓冲区中查找,每次都会执行它。
MySQL forceIndex 强制索引:强制查询缓冲 SQL_CACHE
SELECT SQL_CALHE* FROM TABLE1;
如果在my.ini中的query_cache_type设成2,这样只有在使用了SQL_CACHE后,才使用查询缓冲。
优先操作HIGH_PRIORITY
HIGH_PRIORITY可以使用在select和insert操作中,让MySQL知道,这个操作优先进行。
SELECTHIGH_PRIORITY * FROM TABLE1;
滞后操作LOW_PRIORITY
LOW_PRIORITY可以使用在insert和update操作中,让MySQL知道,这个操作滞后。
updateLOW_PRIORITY table1 set field1= where field1= …
延时插入INSERT DELAYED
INSERT DELAYEDINTO table1 set field1= …
INSERT DELAYEDINTO,是客户端提交数据给MySQL,MySQL返回OK状态给客户端。而这是并不是已经将数据插入表,而是存储在内存里面等待排队。当MySQL有空余时,再插入。另一个重要的好处是,来自许多客户端的插入被集中在一起,并被编写入一个块。这比执行许多独立的插入要快很多。坏处是,不能返回自动递增的ID,以及系统崩溃时,MySQL还没有来得及插入数据的话,这些数据将会丢失。
强制连接顺序STRAIGHT_JOIN
SELECTTABLE1.FIELD1, TABLE2.FIELD2 FROM TABLE1 STRAIGHT_JOIN TABLE2 WHERE …
由上面的SQL语句可知,通过STRAIGHT_JOIN强迫MySQL按TABLE1、TABLE2的顺序连接表。如果你认为按自己的顺序比MySQL推荐的顺序进行连接的效率高的话,就可以通过STRAIGHT_JOIN来确定连接顺序。
MySQL forceIndex 强制索引:强制使用临时表 SQL_BUFFER_RESULT
SELECTSQL_BUFFER_RESULT * FROM TABLE1 WHERE …
当我们查询的结果集中的数据比较多时,可以通过SQL_BUFFER_RESULT.选项强制将结果集放到临时表中,这样就可以很快地释放MySQL的表锁(这样其它的SQL语句就可以对这些记录进行查询了),并且可以长时间地为客户端提供大记录集。
分组使用临时表SQL_BIG_RESULT和SQL_SMALL_RESULT
SELECTSQL_BUFFER_RESULT FIELD1, COUNT(*) FROM TABLE1 GROUP BY FIELD1;
一般用于分组或DISTINCT关键字,这个选项通知MySQL,如果有必要,就将查询结果放到临时表中,甚至在临时表中进行排序。SQL_SMALL_RESULT比起SQL_BIG_RESULT差不多,很少使用。
230:谨慎对待 analyze table 这个操作。
ANALYZE[NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name [, tbl_name] ...
1.分析后可以使系统得到准确的统计信息,使SQL正确的生成执行计划;
2.在分析期间,需要对表进行加一个读锁;
231:详细描述一下统计信息相关参数以及收集的策略。
如何收集统计信息
Analyze table收集表和索引统计信息,适用于MyISAM和InnoDB;
对于innodb表,还可以使用以下选项
1.表第一次打开的时候
2.表修改的行超过1/16或者20亿条
./row/row0mysql.c:row_update_statistics_if_needed
3.执行show index/table或者查询information_schema.tables/statistics表时
在访问以下表时,innodb表的统计信息可自动收集
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
innodb_stats_on_metadata参数用来控制此行为,设置为false时不更新统计信息
Innodb_stats_sample_pages每次收集统计信息时采样的页数,默认为8
每个表维护一个stat_modified_counter,每次DML更新1行就加1,直到满足阈值则自动收集统计信息,并把此值清0;
函数dict_update_statistics用于更新统计信息,但若有多个线程同时检测到阈值,会导致多次调用,浪费了系统资源;
可以直接修改代码,让dict_update_statistics对stat_modified_counter加锁,避免并发执行;http://dinglin.iteye.com/blog/1815392
5.6提供选项innodb_stats_persistent,默认on,将analyze table产生的统计信息保存于磁盘,直至下次analyze table为止,此举避免了统计信息动态更新,保证了执行计划的稳定,对于大表也节省了收集统计信息的所需资源;
除非当前sql执行计划不佳,否则不应经常analyze table收集统计信息
Innodb_stats_method和myisam_stats_method
计算统计信息时,拥有相同key prefix的行算作一个value group(类似oracle索引中的num_distinct,其值越多意味着索引选择性越好),average group size是非常重要的指标,即平均一个索引值返回的表行数,主要有两个用途:
1估算每次ref access要读取多少行
2 估算一个partial join要产生多少行 (…) join tab on tab.key = expr
由此可知,averagegroup size越高则索引选择性越低,表基数即value group数量计算公式为N/S(N:表行数 S:average group size),可通过show index查看
除了主键,索引不可避免的会遇到Null(对于<=>操作符,NULL和Non-null被同等对待,而Null = Null则会返回false),mysql将NULL视作无穷小;
收集统计信息时,为了灵活的处理Null,InnoDB/MyISAM各引入一个参数Innodb_stats_method/myisam_stats_method,分别三个候选值:nulls_equal/nulls_unequal/nulls_ignored(其中innod_stats_method只有全局变量)
Nulls_equal:所有Null都相等,即算作一个value group;若Null过多则会导致average group size偏大
Nulls_unequal:所有Null互不相同,每个算作一个value group;如果non-null group size过大且null数量过多,此设置会拉低整体的average group size,可能导致滥用索引
Nulls_ignored:忽略Null
对于已经收集的统计信息,无法分辨其采用了那种方式;对于非InnoDB/MyISAM表,只有一种收集方式,即nulls_equal;
手工收集统计信息需要调用analyze table,但若表自上次analye至今没有任何改动,即便调用此命令实际也不会收集统计信息,需先让统计信息过期(插入一行再删除即可)
Mysql也可自动收集,诸如bulkinsert/delete以及某些alter table语句均会触发
如何查看统计信息
Show index fromtable或查看information_schema.statistics表
Show tablestatus或information_schema.tables表