mysql 5.7 hint_新特性解读 | MySQL 8.0 新增 HINT 模式
在開始演示之前,我們先介紹下兩個(gè)概念。
概念一,數(shù)據(jù)的可選擇性基數(shù),也就是常說的cardinality值。
查詢優(yōu)化器在生成各種執(zhí)行計(jì)劃之前,得先從統(tǒng)計(jì)信息中取得相關(guān)數(shù)據(jù),這樣才能估算每步操作所涉及到的記錄數(shù),而這個(gè)相關(guān)數(shù)據(jù)就是cardinality。簡單來說,就是每個(gè)值在每個(gè)字段中的唯一值分布狀態(tài)。
比如表t1有100行記錄,其中一列為f1。f1中唯一值的個(gè)數(shù)可以是100個(gè),也可以是1個(gè),當(dāng)然也可以是1到100之間的任何一個(gè)數(shù)字。這里唯一值越的多少,就是這個(gè)列的可選擇基數(shù)。
那看到這里我們就明白了,為什么要在基數(shù)高的字段上建立索引,而基數(shù)低的的字段建立索引反而沒有全表掃描來的快。當(dāng)然這個(gè)只是一方面,至于更深入的探討就不在我這篇探討的范圍了。
概念二,關(guān)于HINT的使用。
這里我來說下HINT是什么,在什么時(shí)候用。
HINT簡單來說就是在某些特定的場景下人工協(xié)助MySQL優(yōu)化器的工作,使她生成最優(yōu)的執(zhí)行計(jì)劃。一般來說,優(yōu)化器的執(zhí)行計(jì)劃都是最優(yōu)化的,不過在某些特定場景下,執(zhí)行計(jì)劃可能不是最優(yōu)化。
比如:表t1經(jīng)過大量的頻繁更新操作,(UPDATE,DELETE,INSERT),cardinality已經(jīng)很不準(zhǔn)確了,這時(shí)候剛好執(zhí)行了一條SQL,那么有可能這條SQL的執(zhí)行計(jì)劃就不是最優(yōu)的。為什么說有可能呢?
來看下具體演示
譬如,以下兩條SQL,A:select?*?from?t1?where?f1?=?20;B:select?*?from?t1?where?f1?=?30;
如果f1的值剛好頻繁更新的值為30,并且沒有達(dá)到MySQL自動(dòng)更新cardinality值的臨界值或者說用戶設(shè)置了手動(dòng)更新又或者用戶減少了sample page等等,那么對這兩條語句來說,可能不準(zhǔn)確的就是B了。
這里順帶說下,MySQL提供了自動(dòng)更新和手動(dòng)更新表cardinality值的方法,因篇幅有限,需要的可以查閱手冊。
那回到正題上,MySQL 8.0 帶來了幾個(gè)HINT,我今天就舉個(gè)index_merge的例子。
示例表結(jié)構(gòu):mysql>?desc?t1;+------------+--------------+------+-----+---------+----------------+|?Field??????|?Type?????????|?Null?|?Key?|?Default?|?Extra??????????|+------------+--------------+------+-----+---------+----------------+|?id?????????|?int(11)??????|?NO???|?PRI?|?NULL????|?auto_increment?||?rank1??????|?int(11)??????|?YES??|?MUL?|?NULL????|????????????????||?rank2??????|?int(11)??????|?YES??|?MUL?|?NULL????|????????????????||?log_time???|?datetime?????|?YES??|?MUL?|?NULL????|????????????????||?prefix_uid?|?varchar(100)?|?YES??|?????|?NULL????|????????????????||?desc1??????|?text?????????|?YES??|?????|?NULL????|????????????????||?rank3??????|?int(11)??????|?YES??|?MUL?|?NULL????|????????????????|+------------+--------------+------+-----+---------+----------------+7?rows?in?set?(0.00?sec)
表記錄數(shù):mysql>?select?count(*)?from?t1;+----------+|?count(*)?|+----------+|????32768?|+----------+1?row?in?set?(0.01?sec)
這里我們兩條經(jīng)典的SQL:SQL C:select?*?from?t1?where?rank1?=?1?or?rank2?=?2?or?rank3?=?2;SQL D:select?*?from?t1?where?rank1?=100??and?rank2?=100??and?rank3?=100;
表t1實(shí)際上在rank1,rank2,rank3三列上分別有一個(gè)二級索引。
那我們來看SQL C的查詢計(jì)劃。
顯然,沒有用到任何索引,掃描的行數(shù)為32034,cost為3243.65。mysql>?explain??format=json?select?*?from?t1??where?rank1?=1?or?rank2?=?2?or?rank3?=?2\G***************************?1.?row?***************************EXPLAIN:?{??"query_block":?{????"select_id":?1,????"cost_info":?{??????"query_cost":?"3243.65"????},????"table":?{??????"table_name":?"t1",??????"access_type":?"ALL",??????"possible_keys":?[????????"idx_rank1",????????"idx_rank2",????????"idx_rank3"??????],??????"rows_examined_per_scan":?32034,??????"rows_produced_per_join":?115,??????"filtered":?"0.36",??????"cost_info":?{????????"read_cost":?"3232.07",????????"eval_cost":?"11.58",????????"prefix_cost":?"3243.65",????????"data_read_per_join":?"49K"??????},??????"used_columns":?[????????"id",????????"rank1",????????"rank2",????????"log_time",????????"prefix_uid",????????"desc1",????????"rank3"??????],??????"attached_condition":?"((`ytt`.`t1`.`rank1`?=?1)?or?(`ytt`.`t1`.`rank2`?=?2)?or?(`ytt`.`t1`.`rank3`?=?2))"????}??}}1?row?in?set,?1?warning?(0.00?sec)
我們加上hint給相同的查詢,再次看看查詢計(jì)劃。
這個(gè)時(shí)候用到了index_merge,union了三個(gè)列。掃描的行數(shù)為1103,cost為441.09,明顯比之前的快了好幾倍。mysql>?explain??format=json?select?/*+?index_merge(t1)?*/?*?from?t1??where?rank1?=1?or?rank2?=?2?or?rank3?=?2\G***************************?1.?row?***************************EXPLAIN:?{??"query_block":?{????"select_id":?1,????"cost_info":?{??????"query_cost":?"441.09"????},????"table":?{??????"table_name":?"t1",??????"access_type":?"index_merge",??????"possible_keys":?[????????"idx_rank1",????????"idx_rank2",????????"idx_rank3"??????],??????"key":?"union(idx_rank1,idx_rank2,idx_rank3)",??????"key_length":?"5,5,5",??????"rows_examined_per_scan":?1103,??????"rows_produced_per_join":?1103,??????"filtered":?"100.00",??????"cost_info":?{????????"read_cost":?"330.79",????????"eval_cost":?"110.30",????????"prefix_cost":?"441.09",????????"data_read_per_join":?"473K"??????},??????"used_columns":?[????????"id",????????"rank1",????????"rank2",????????"log_time",????????"prefix_uid",????????"desc1",????????"rank3"??????],??????"attached_condition":?"((`ytt`.`t1`.`rank1`?=?1)?or?(`ytt`.`t1`.`rank2`?=?2)?or?(`ytt`.`t1`.`rank3`?=?2))"????}??}}1?row?in?set,?1?warning?(0.00?sec)
我們再看下SQL D的計(jì)劃:不加HINT,mysql>?explain?format=json?select?*?from?t1?where?rank1?=100?and?rank2?=100?and?rank3?=100\G***************************?1.?row?***************************EXPLAIN:?{??"query_block":?{????"select_id":?1,????"cost_info":?{??????"query_cost":?"534.34"????},????"table":?{??????"table_name":?"t1",??????"access_type":?"ref",??????"possible_keys":?[????????"idx_rank1",????????"idx_rank2",????????"idx_rank3"??????],??????"key":?"idx_rank1",??????"used_key_parts":?[????????"rank1"??????],??????"key_length":?"5",??????"ref":?[????????"const"??????],??????"rows_examined_per_scan":?555,??????"rows_produced_per_join":?0,??????"filtered":?"0.07",??????"cost_info":?{????????"read_cost":?"478.84",????????"eval_cost":?"0.04",????????"prefix_cost":?"534.34",????????"data_read_per_join":?"176"??????},??????"used_columns":?[????????"id",????????"rank1",????????"rank2",????????"log_time",????????"prefix_uid",????????"desc1",????????"rank3"??????],??????"attached_condition":?"((`ytt`.`t1`.`rank3`?=?100)?and?(`ytt`.`t1`.`rank2`?=?100))"????}??}}1?row?in?set,?1?warning?(0.00?sec)加了HINT,mysql>?explain?format=json?select?/*+?index_merge(t1)*/?*?from?t1?where?rank1?=100?and?rank2?=100?and?rank3?=100\G***************************?1.?row?***************************EXPLAIN:?{??"query_block":?{????"select_id":?1,????"cost_info":?{??????"query_cost":?"5.23"????},????"table":?{??????"table_name":?"t1",??????"access_type":?"index_merge",??????"possible_keys":?[????????"idx_rank1",????????"idx_rank2",????????"idx_rank3"??????],??????"key":?"intersect(idx_rank1,idx_rank2,idx_rank3)",??????"key_length":?"5,5,5",??????"rows_examined_per_scan":?1,??????"rows_produced_per_join":?1,??????"filtered":?"100.00",??????"cost_info":?{????????"read_cost":?"5.13",????????"eval_cost":?"0.10",????????"prefix_cost":?"5.23",????????"data_read_per_join":?"440"??????},??????"used_columns":?[????????"id",????????"rank1",????????"rank2",????????"log_time",????????"prefix_uid",????????"desc1",????????"rank3"??????],??????"attached_condition":?"((`ytt`.`t1`.`rank3`?=?100)?and?(`ytt`.`t1`.`rank2`?=?100)?and?(`ytt`.`t1`.`rank1`?=?100))"????}??}}1?row?in?set,?1?warning?(0.00?sec)
對比下以上兩個(gè),加了HINT的比不加HINT的cost小了100倍。
總結(jié)下,就是說表的cardinality值影響這張的查詢計(jì)劃,如果這個(gè)值沒有正常更新的話,就需要手工加HINT了。相信MySQL未來的版本會(huì)帶來更多的HINT。
總結(jié)
以上是生活随笔為你收集整理的mysql 5.7 hint_新特性解读 | MySQL 8.0 新增 HINT 模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 爬虫代理_python 爬
- 下一篇: ef6 mysql_VS2015 + E