mysql物理优化_mysql物理优化器代价模型分析【原创】

1.引言

mysql的sql server在根据where condition检索数据的时候,一般会有多种数据检索的方法,其会根据各种数据检索方法代价的大小,选择代价最小的那个数据检索方法。

比如说这个语句,where col1=x and col2=y and col3 >z ,同时存在inx_col1,inx_col2,inx_col3,inx_col1_col2_col3这四个索引,sql server要解决的问题有1)选择哪个索引、2)是索引range扫描还是ref扫描、3)table scan的方式是否可行。

mysql会根据以下几种数据检索策略选择代价最小的策略来从数据表中获取数据,1)各个索引的range scan代价2)各个索引的ref scan代价3)table scan的代价。如何计算这些代价,是本文详细说明的重点。

总代价cost = cpu cost + io cost。

2 .代价因子

mysql的代价因子在内存中有一份副本,由Server_cost_constants和SE_cost_constants两个类组成。这两个类的具体数据成员如下。

Mysql Server代价因子

Server_cost_constants {

m_row_evaluate_cost//行记录条件谓词评估代价

m_key_compare_cost //键值比较代价

m_memory_temptable_create_cost //内存临时表创建代价

m_memory_temptable_row_cost //内存临时表的行代价

m_disk_temptable_create_cost //磁盘临时表创建代价

m_disk_temptable_row_cost

}

存储引擎代价因子

SE_cost_constants{

m_memory_block_read_cost//从buffer pool中读取一个页面的代价

m_io_block_read_cost //从文件系统中读取一个页面的代价,buffer miss的场景

m_memory_block_read_cost_default

m_io_block_read_cost_default

}

mysql的代价因子在系统的持久化系统表中也有一份副本,对应mysql.server_cost和mysql.engine_cost两个表,这两个表中的字段与 内存中的类字段相同。DBA可以根据实际的硬件情况测试,测试出最适合的代价因子,然后update系统表中对应的字段。再然后执行flush OPTIMIZER_COSTS命令,将修改反应到内存中数据,这样新连接上来的mysql session会读取到内存中数据,然后以新的代价因子计算代价数。

代价因子如何根据实际的硬件环境与负载压力自适应地调整,是一个重要的研究课题。

3 .统计信息

sql server需要的统计信息是由存储引擎innodb提供的,调用innodb提供的api可以获取这些统计信息,本文的后半部分会罗列这些api。innodb的统计信息根据需要可以持久化到系统表中。mysql.innodb_table_stats和mysql.innodb_index_stats存储了表的统计信息和索引的统计信息。

mysql.innodb_table_stats表中字段说明

database_name 库名

table_name 表名

n_rows 表中的数据行数

clustered_index_size 聚集索引的页面数

sum_of_other_index_sizes 其他非主键索引的页面数

last_update 最后更新这张表的时间

mysql.innodb_index_stats表中字段说明

database_name 库名

table_name 表名

index_name 索引名

stat_name 统计项名称

stat_value 统计项值

sample_size 采样的页面数

last_update 最后更新这张表的时间

其中stat_name 统计项名称包括:

n_diff_pfxNN 为不同前缀列的cardinality,即不同前缀字段的 distinct value个数

n_leaf_page 索引叶子节点页面数目

size 索引页面数目

4.代价的计算公式

cpu代价计算

double row_evaluate_cost(doublerows)

{return rows * m_server_cost_constants->row_evaluate_cost();

}

table scan IO代价计算

Cost_estimate handler::table_scan_cost()

{double io_cost= scan_time() * table->cost_model()->page_read_cost(1.0);

}

ref and range scan IO代价计算

聚集索引扫描IO代价计算公式

Cost_estimate handler::read_cost(uint index, double ranges, doublerows)

{double io_cost= read_time(index, static_cast(ranges),

static_cast(rows)) *table->cost_model()->page_read_cost(1.0);

}

二级索引覆盖扫描(不需要回表)IO代价计算公式

Cost_estimate handler::index_scan_cost(uint index, double ranges, doublerows)

{double io_cost= index_only_read_time(index, rows) *table->cost_model()->page_read_cost_index(index, 1.0);

}

二级索引非覆盖扫描(需要回表)IO代价计算公式

min( table→cost_model()→page_read_cost(tmp_fanout), tab→worst_seeks )

估算读取pages个聚集索引页面所花费的代价,page数乘以代价因子

double Cost_model_table::page_read_cost(double pages)

估算读取pages个指定index索引页面所花费的代价数。

double Cost_model_table::page_read_cost_index(uint index, double pages)

5. innodb统计信息api

全表扫描聚集索引时,聚集索引(主键)占用的所有页面数

double ha_innobase::scan_time()

估算在聚集索引上,扫描rows条记录,需要读取的页面数

double ha_innobase::read_time(uint index, double ranges, double rows)

估算在指定keynr索引进行覆盖扫描(不需要回表),扫描records条记录,需要读取的索引页面数

double handler::index_only_read_time(uint keynr, double records)

估算指定keynr索引在范围(min_key,max_key)中的记录数量

ha_innobase::records_in_range(uint keynr, /*!< in: index number*/key_range*min_key, /*!< in: start key value of the

key_range *max_key) /*!< in: range end key val, may

)

估算聚集索引内存中页面数占其所有页面数的比率

double handler::table_in_memory_estimate()

估算二级索引内存中页面数占其所有页面数的比率

double handler::index_in_memory_estimate(uint keyno)

6.开启优化器跟踪

set session optimizer_trace="enabled=on";

explain your sqlselect * from information_schema.optimizer_trace;

7.优化器跟踪示例

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

"rows_estimation": [

{"table": "`tab`","range_analysis": {"table_scan": {"rows": 5,"cost": 4.1},"potential_range_indexes": [

{"index": "PRIMARY","usable": false,"cause": "not_applicable"},

{"index": "inx_clo2","usable": true,"key_parts": ["clo2","clo1"]

},

{"index": "inx_clo3","usable": true,"key_parts": ["clo3","clo1"]

},

{"index": "inx_clo2_clo3","usable": true,"key_parts": ["clo2","clo3","clo1"]

}

],"best_covering_index_scan": {"index": "inx_clo2_clo3","cost": 2.0606,"chosen": true},"setup_range_conditions": [

],"group_index_range": {"chosen": false,"cause": "not_group_by_or_distinct"},"analyzing_range_alternatives": {"range_scan_alternatives": [

{"index": "inx_clo2","ranges": ["hu <= clo2 <= hu"],"index_dives_for_eq_ranges": true,"rowid_ordered": true,"using_mrr": false,"index_only": false,"rows": 2,"cost": 3.41,"chosen": false,"cause": "cost"},

{"index": "inx_clo3","ranges": ["huan <= clo3 <= huan"],"index_dives_for_eq_ranges": true,"rowid_ordered": true,"using_mrr": false,"index_only": false,"rows": 1,"cost": 2.21,"chosen": false,"cause": "cost"},

{"index": "inx_clo2_clo3","ranges": ["hu <= clo2 <= hu AND huan <= clo3 <= huan"],"index_dives_for_eq_ranges": true,"rowid_ordered": true,"using_mrr": false,"index_only": true,"rows": 1,"cost": 1.21,"chosen": true}

],"analyzing_roworder_intersect": {"intersecting_indexes": [

{"index": "inx_clo2_clo3","index_scan_cost": 1,"cumulated_index_scan_cost": 1,"disk_sweep_cost": 0,"cumulated_total_cost": 1,"usable": true,"matching_rows_now": 1,"isect_covering_with_this_index": true,"chosen": true}

],"clustered_pk": {"clustered_pk_added_to_intersect": false,"cause": "no_clustered_pk_index"},"chosen": false,"cause": "too_few_indexes_to_merge"}

},"chosen_range_access_summary": {"range_access_plan": {"type": "range_scan","index": "inx_clo2_clo3","rows": 1,"ranges": ["hu <= clo2 <= hu AND huan <= clo3 <= huan"]

},"rows_for_plan": 1,"cost_for_plan": 1.21,"chosen": true}

}

}

]

},

{"considered_execution_plans": [

{"plan_prefix": [

],"table": "`tab`","best_access_path": {"considered_access_paths": [

{"access_type": "ref","index": "inx_clo2","rows": 2,"cost": 2.4,"chosen": true},

{"access_type": "ref","index": "inx_clo3","rows": 1,"cost": 1.2,"chosen": true},

{"access_type": "ref","index": "inx_clo2_clo3","rows": 1,"cost": 1.2,"chosen": false},

{"rows_to_scan": 1,"access_type": "range","range_details": {"used_index": "inx_clo2_clo3"},"resulting_rows": 1,"cost": 1.41,"chosen": false}

]

},"condition_filtering_pct": 40,"rows_for_plan": 0.4,"cost_for_plan": 1.2,"chosen": true}

]

},

{"attaching_conditions_to_tables": {"original_condition": "((`tab`.`clo2` = 'hu') and (`tab`.`clo3` = 'huan'))","attached_conditions_computation": [

],"attached_conditions_summary": [

{"table": "`tab`","attached": "(`tab`.`clo2` = 'hu')"}

]

}

},

{"refine_plan": [

{"table": "`tab`"}

]

}

]

View Code

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值