作者:微信小助手
发布时间:2022-06-09T23:05:22
总第503篇
2022年 第020篇
1 背景
2 基于代价的优化器介绍
2.1 SQL执行与优化器
2.2 代价模型介绍
2.3 基于代价的索引选择
2.4 基于代价的索引推荐思路
3 索引推荐实现
3.1 前置校验
3.2 提取关键列名
3.3 生成候选索引
3.4 数据采集
3.5 统计数据计算
3.6 候选索引代价评估
4 推荐质量保证
4.1 有效性验证
4.2 效果追踪
4.3 仿真环境
4.4 测试案例库
5 慢查询治理运营
5.1 过去-历史慢查询
5.2 现在-新增慢查询
5.3 未来-潜在慢查询
6 项目运行情况
7 未来规划
select * from sync_test1 where name like 'Bobby%'
,直接添加索引IX(
name
) 就可以取得不错的效果;但对于稍微复杂点的SQL,如
select * from sync_test1 where name like 'Bobby%' and dt > '2021-07-06'
,到底选择IX(
name
)、IX(
dt
)、IX(
dt,name
) 还是IX(
name,dt
),该方法也无法给出准确的回答。更别说像多表Join、子查询这样复杂的场景了。所以采用基于代价的推荐来解决该问题会更加普适,因为基于代价的方法使用了和数据库优化器相同的方式,去量化评估所有的可能性,选出的是执行SQL耗费代价最小的索引。
SQL select * from sync_test1 where name like 'Bobby%' and dt > '2021-07-06'
为例,我们看看MySQL优化器是如何根据代价模型选择索引的。首先,我们直接在建表时加入四个候选索引。
Create Table: CREATE TABLE `sync_test1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cid` int(11) NOT NULL,
`phone` int(11) NOT NULL,
`name` varchar(10) NOT NULL,
`address` varchar(255) DEFAULT NULL,
`dt` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `IX_name` (`name`),
KEY `IX_dt` (`dt`),
KEY `IX_dt_name` (`dt`,`name`),
KEY `IX_name_dt` (`name`,`dt`)
) ENGINE=InnoDB
mysql> explain select * from sync_test1 where name like 'Bobby%' and dt > '2021-07-06';
+----+-------------+------------+------------+-------+-------------------------------------+---------+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+-------+-------------------------------------+---------+---------+------+------+----------+------------------------------------+
| 1 | SIMPLE | sync_test1 | NULL | range | IX_name,IX_dt,IX_dt_name,IX_name_dt | IX_name | 12 | NULL | 572 | 36.83 | Using index condition; Using where |
+----+-------------+------------+------------+-------+-------------------------------------+---------+---------+------+------+----------+------------------------------------+
mysql> select * from INFORMATION_SCHEMA.OPTIMIZER_TRACE\G;
*************************** 1. row ***************************
TRACE: {
...
"rows_estimation": [
{
"table": "`sync_test1`",
"range_analysis": {
"table_scan": {
"rows": 105084,
"cost": 21628
},
...
"analyzing_range_alternatives": {
"range_scan_alternatives": [
{
"index": "IX_name",
"ranges": [
"Bobby\u0000\u0000\u0000\u0000\u0000 <= name <= Bobbyÿÿÿÿÿ"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": false,
"rows": 572,
"cost": 687.41,
"chosen": true
},
{
"index": "IX_dt",
"ranges": [
"0x99aa0c0000 < dt"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": false,
"rows": 38698,
"cost": 46439,
"chosen": false,
"cause": "cost"
},
{
"index": "IX_dt_name",
"ranges": [
"0x99aa0c0000 < dt"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": false,
"rows": 38292,
"cost": 45951,
"chosen": false,
"cause": "cost"
},
{
"index": "IX_name_dt",
"ranges": [
"Bobby\u0000\u0000\u0000\u0000\u0000 <= name <= Bobbyÿÿÿÿÿ"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": false,
"rows": 572,
"cost": 687.41,
"chosen": false,
"cause": "cost"
}
],
"analyzing_roworder_intersect": {
"usable": false,
"cause": "too_few_roworder_scans"
}
},
"chosen_range_access_summary": {
"range_access_plan": {
"type": "range_scan",
"index": "IX_name",
"rows": 572,
"ranges": [
"Bobby\u0000\u0000\u0000\u0000\u0000 <= name <= Bobbyÿÿÿÿÿ"
]
},
"rows_for_plan": 572,
"cost_for_plan": 687.41,
"chosen": true
}
...
}