【GreatSQL优化器-03】查询开销估算
一、cost和read_time介绍
GreatSQL的优化器在创建执行计划的时候是根据每张表的行数和数据分布以及读数据硬盘消耗等信息来判断先查询哪张表后查询哪张表,要不要使用索引,这些表资源信息就被称为cost,俗称为"开销"。在这之前已经执行了update_ref_and_keys
(参考【GreatSQL优化器-02】)和extract_const_tables
(参考【GreatSQL优化器-01】),拿到了const tables
信息和表的keyuse_array
索引信息,这里就开始计算单张表扫描的开销,做一个初步的估计,用来给后面的choose_table_order()
搜索最佳表顺序提供原始数据信息。
优化器通过estimate_rowcount
函数初步计算单表开销,这个函数最后会计算出3个重要的数据。
名称 | 说明 | 计算公式 |
---|---|---|
found_records | 表的总行数 | tab->table()->file->stats.records |
read_time | 读取所有数据需要的开销 | io_cost + cpu_cost + import_cost |
worst_seeks | 扫描全表需要的最差开销 | find_worst_seeks(tab->table(), tab->found_records, tab->read_time)根据上面2项计算得出 |
下面用一个简单的例子来说明这三个数字怎么查看:
greatsql> CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT,date1 DATETIME);
greatsql> INSERT INTO t1 VALUES (1,10,'2021-03-25 16:44:00.123456'),(2,1,'2022-03-26 16:44:00.123456'),(3,4,'2023-03-27 16:44:00.123456'),(5,5,'2024-03-25 16:44:00.123456');
greatsql> CREATE TABLE t2 (cc1 INT PRIMARY KEY, cc2 INT);
greatsql> INSERT INTO t2 VALUES (1,3),(2,1),(3,2),(4,3),(5,15);
greatsql> CREATE INDEX idx1 ON t1(c2);
greatsql> CREATE INDEX idx2 ON t1(c2,date1);
greatsql> CREATE INDEX idx2_1 ON t2(cc2);
greatsql> SET optimizer_trace = 'enabled=ON' ;
greatsql> SELECT * FROM t2,t1,(select 10) t3 where t1.c1=t2.cc2;
+-----+------+----+------+---------------------+----+
| cc1 | cc2 | c1 | c2 | date1 | 10 |
+-----+------+----+------+---------------------+----+
| 3 | 2 | 2 | 1 | 2022-03-26 16:44:00 | 10 |
| 1 | 3 | 3 | 4 | 2023-03-27 16:44:00 | 10 |
| 4 | 3 | 3 | 4 | 2023-03-27 16:44:00 | 10 |
| 2 | 1 | 1 | 10 | 2021-03-25 16:44:00 | 10 |
+-----+------+----+------+---------------------+----+
> SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
{
"rows_estimation": [
{
"table": "`t2`", -- 按照上面查询顺序t2放第一个
"table_scan": {
"rows": 5, 因为t2非const表,因此这里显示的是所有行
"cost": 0.25, 这个是t2查询5条的开销,
"worst_seeks": 2 这里是另外加上去的,为了查看方便
}
},
{
"table": "`t1`", 按照上面查询顺序t1放第二个
"table_scan": {
"rows": 4, 因为t1非const表,因此这里显示的是所有行
"cost": 0.25,
"worst_seeks": 2 这里是另外加上去的,为了查看方便
}
},
{
"table": " `t3`", 按照上面查询顺序t3放第三个
"rows": 1,
"cost": 1, ※这里有疑问,实际的read_time=worst_seeks=0.25,但是代码用了固定值1
"worst_seeks": 0.25 这里是另外加上去的,为了查看方便
"table_type": "system", 因为t3是const表,因此这里显示的system
"empty": false
}
]
},
附表:代价系数
代价系数 | 值 | 说明 |
---|---|---|
ROW_EVALUATE_COST | 0.1 | 扫描一行需要的开销 |
KEY_COMPARE_COST | 0.05 | 比较row id需要的开销 |
MEMORY_TEMPTABLE_CREATE_COST | 1.0 | 创建临时表的开销,等于读10行 |
MEMORY_TEMPTABLE_ROW_COST | 0.1 | 读或写一行到临时表 |
DISK_TEMPTABLE |