MySQL优化器cost计算
記錄MySQL 5.5上,優化器進行cost計算的方法。
第一篇: 單表的cost計算
數據結構:
1. table_share: 包含了表的元數據,其中索引部分:
key_info:一個key的結構體,代表一個索引,包含了:
例如:
(gdb) p (table->s->key_info->name) $16 = 0x8ca0ffbd "PRIMARY" (gdb) p (table->s->key_info->key_parts)$17 = 1 (gdb) p (table->s->key_info->rec_per_key)$18 = (ulong *) 0x8ca0ffe82. JOIN:
? ? mysql_select函數中,創建了 new JOIN(thd, fields, select_options, result)對象,包含了當前查詢的所有組件和各種轉換結果,其中 :
3. join_tab:
? ? 包含了一個table訪問的cost等一些信息,經過優化后,填充這個結構體
cost的計算方法
cost = cpu cost + io cost
統計信息
這里用到了兩部分統計信息:
1. server層的統計信息,保存在table_share中。包括:
2. innodb層的統計信息,包括:
主要函數調用
make_join_statistics:
--update_ref_and_keys: 添加可以使用的索引。
--get_quick_record_count:
----test_quick_select: 評估每一個join table查詢得到的記錄數,其中比較不同index的cost, 返回的記錄數,選擇最優的那個。
choose plan:選擇join的順序
實驗過程
CREATE TABLE `xpchild` (`id` int(11) NOT NULL,`name` varchar(100) DEFAULT NULL,`c1` int(11) DEFAULT NULL,`c2` int(11) DEFAULT NULL,PRIMARY KEY (`id`),KEY `xpchild_name` (`name`),KEY `xpchild_id_c1` (`id`,`c1`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1實驗1: 單值查詢
explain select * from xpchild where id =100;函數調用棧:
make_join_statistics:
? ? table->quick_condition_rows= table->file->stats.records;
? ?if (s->type == JT_SYSTEM || s->type == JT_CONST)
? ?s->found_records=s->records=s->read_time=1; s->worst_seeks=1.0;
結論:單key的查詢,join_tab的type=JT_CONST,索引record和read time都是1;最終得到的join->best_read=1.0;
實驗2: 范圍查詢
explain select * from xpchild where id > 100;
step1:update_ref_and_keys: 一共得到兩個possible index,
step2:test_quick_select
? ?
?1. 計算全表掃描的cost:
innodb全表掃描的io cost:
innodb的io cost: s->read_time=(ha_rows) s->table->file->scan_time();
innodb scan time:prebuilt->table->stat_clustered_index_size
等于innodb這張表的聚簇索引的page個數,本身innodb就是聚簇索引表,這里計算的io cost=16
(gdb) p s->read_time
$7 = 16
MySQL server的cpu cost:
scan_time= (double) records / TIME_FOR_COMPARE + 1;
(gdb) p scan_time
$16 = 1359.4000000000001
總的cost:read_time= (double) head->file->scan_time() + scan_time + 1.1;
(gdb) p read_time
$18 = 1376.5
?
繼續函數棧:
根據possible index,生成sel_tree;
get_best_group_min_max: 這里沒有使用到
get_key_scans_params:根據sel_tree找到更好的cost
?
2. 計算full index的cost
? ? ? find_shortest_key: 在覆蓋索引中選擇length最短的那個。
? ? ? get_index_only_read_time:這里如果有覆蓋索引(covering index)那么就會計算此覆蓋索引的cost。
? ? ? full index scan的計算方法:
? ? ?這里假設:一個塊中,只使用了一半的空間寫入數據,
? ? ?如果計算的key_read_time > read_time, 則read_time= key_read_time,從此不再使用全表掃描。
?
3. pk 索引計算的cost
進入get_key_scans_params函數:選擇比傳入的read_time小的cost的執行計劃,生成一個TRP_RANGE對象返回。
?
step1: 評估范圍掃描的記錄數(check_quick_select)
check_quick_keys:根據key,min,max值來評估記錄數,并把records記錄到table->quick_rows[key]中,以便后續需要。
(gdb) p *min_key $82 = 100 'd'ha_innobase::records_in_range: innodb引擎根據min和max值來評估記錄數。
? ? 計算方法:innodb對b_tree中范圍確定的page的個數和record_per_page進行計算,當評估>all_record/2時,就取all_record/2。
?
step 2: 計算cost
根據pk range估算的records=3396,調整table->quick_condition_rows從全表的6792到現在的3396。
計算cost:
io_cost = param->table->file->read_time(keynr,param->range_count,found_records);
found_read_time = cpu_cost + io_cost + 0.01 found_read_time = 683.74750000000006
這樣,通過pk掃描的cost遠小于前面第一階段的全表掃描的代價。
4. 計算普通索引的cost
? ? ?因為都可以使用前導列進行查詢,查詢的效率的差別在于非主鍵索引需要回到聚簇索引中查詢非索引列。
所以,innodb返回的found_rows=6556(包含了掃描索引xpchild_id_c1 和primary key的), 所以最終計算的cost大于使用pk的cost。
? ? ? 最終計算得到的cost=7868.21 遠高于pk索引的cost。
?
結論: 最終選擇了pk的索引進行range掃描
?
下一篇實驗待續:關聯查詢。
?
轉載于:https://www.cnblogs.com/xpchild/p/3770823.html
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的MySQL优化器cost计算的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用ultramon调整任务栏高度
- 下一篇: 更改Mysql数据库存储位置的具体步骤