日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

庖丁解牛|图解 MySQL 8.0 优化器查询转换篇

發(fā)布時間:2024/8/23 数据库 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 庖丁解牛|图解 MySQL 8.0 优化器查询转换篇 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

簡介:?本篇介紹子查詢、分析表和JOIN的復雜轉(zhuǎn)換過程

一 ?背景和架構(gòu)

在《庖丁解牛-圖解MySQL 8.0優(yōu)化器查詢解析篇》一文中我們重點介紹了MySQL最新版本8.0.25關(guān)于SQL基本元素表、列、函數(shù)、聚合、分組、排序等元素的解析、設(shè)置和轉(zhuǎn)換過程,本篇我們繼續(xù)來介紹更為復雜的子查詢、分區(qū)表和JOIN的復雜轉(zhuǎn)換過程,大綱如下:

Transformation

  • remove_redundant_subquery_clause :Permanently remove redundant parts from the query if 1) This is a subquery 2) Not normalizing a view. Removal should take place when a query involving a view is optimized, not when the view is created.
  • remove_base_options:Remove SELECT_DISTINCT options from a query block if can skip distinct
  • resolve_subquery :Resolve predicate involving subquery, perform early unconditional subquery transformations
  • Convert subquery predicate into semi-join, or
  • Mark the subquery for execution using materialization, or
  • Perform IN->EXISTS transformation, or
  • Perform more/less ALL/ANY -> MIN/MAX rewrite
  • Substitute trivial scalar-context subquery with its value
  • transform_scalar_subqueries_to_join_with_derived:Transform eligible scalar subqueries to derived tables.
  • flatten_subqueries :Convert semi-join subquery predicates into semi-join join nests. Convert candidate subquery predicates into semi-join join nests. This transformation is performed once in query lifetime and is irreversible.
  • apply_local_transforms :
  • delete_unused_merged_columns : If query block contains one or more merged derived tables/views, walk through lists of columns in select lists and remove unused columns.
  • simplify_joins : Convert all outer joins to inner joins if possible.
  • prune_partitions :Perform partition pruning for a given table and condition.
  • push_conditions_to_derived_tables :Pushing conditions down to derived tables must be done after validity checks of grouped queries done by apply_local_transforms();
  • Window::eliminate_unused_objects:Eliminate unused window definitions, redundant sorts etc.

二 ?詳細轉(zhuǎn)換過程

1 ?解析子查詢(resolve_subquery)

解析條件中帶有子查詢的語句,做一些早期的無限制的子查詢轉(zhuǎn)換,包括:

  • 標記subquery是否變成semi-join

轉(zhuǎn)換判斷條件

  • 檢查OPTIMIZER_SWITCH_SEMIJOIN和HINT沒有限制
  • 子查詢是IN/=ANY和EXIST subquery的謂詞
  • 子查詢是簡單查詢塊而不是UNION
  • 子查詢無隱形和顯性的GROUP BY
  • 子查詢沒有HAVING、WINDOW函數(shù)
  • Resolve的階段是Query_block::RESOLVE_CONDITION和Query_block::RESOLVE_JOIN_NEST并且沒有用到最新的Hyper optimizer優(yōu)化器。
  • 外查詢塊可以支持semijoins
  • 至少要一個表,而不是類似"SELECT 1"
  • 子查詢的策略還沒有指定Subquery_strategy::UNSPECIFIED
  • 父查詢也至少有一個表
  • 父查詢和子查詢都不能有straight join
  • 父查詢塊不禁止semijoin
  • IN謂詞返回值是否是確定的,不是RAND
  • 根據(jù)子查詢判斷結(jié)果是否需要轉(zhuǎn)成true還是false以及是否為NULL,判斷是可以做antijoin還是semijoin
  • Antijoin是可以支持的,或者是semijoin
  • offset和limit對于semjoin是有效的,offset是從第一行開始,limit也不是0

設(shè)置Subquery_strategy::CANDIDATE_FOR_SEMIJOIN并添加sj_candidates

  • 標記subquery是否執(zhí)行時采用materialization方案
  • 如果不符合轉(zhuǎn)換semijoin,嘗試使用物化方式,轉(zhuǎn)換判斷條件
  • Optimzier開關(guān)subquery_to_derived=on
  • 子查詢是IN/=ANY or EXISTS謂詞
  • 子查詢是簡單查詢塊而不是UNION
  • 如果是[NOT] EXISTS,必須沒有聚合
  • Subquery謂詞在WHERE子句(目前沒有在ON子句實現(xiàn)),而且是ANDs or ORs的表達式tree
  • 父查詢塊支持semijoins
  • 子查詢的策略還沒有指定Subquery_strategy::UNSPECIFIED
  • 父查詢也至少有一個表,然后可以做LEFT JOIN
  • 父查詢塊不禁止semijoin
  • IN謂詞返回值是否是確定的,不是RAND
  • 根據(jù)子查詢判斷結(jié)果是否需要轉(zhuǎn)成true還是false以及是否為NULL,判斷是可以做antijoin還是semijoin
  • 不支持左邊參數(shù)不是multi-column子查詢(WHERE (outer_subq) = ROW(derived.col1,derived.col2))
  • 該子查詢不支持轉(zhuǎn)換為Derived table(m_subquery_to_derived_is_impossible)
  • 設(shè)置Subquery_strategy::CANDIDATE_FOR_DERIVED_TABLE并添加sj_candidates
  • 如果上面兩個策略無法使用,根據(jù)類型選擇transformer
  • Item_singlerow_subselect::select_transformer
  • 對于簡單的標量子查詢,在查詢中直接用執(zhí)行結(jié)果代替
select * from t1 where a = (select 1); => select * from t1 where a = 1;

Item_in_subselect/Item_allany_subselect::select_transformer->select_in_like_transformer

  • select_in_like_transformer函數(shù)來處理 IN/ALL/ANY/SOME子查詢轉(zhuǎn)換transformation
  • 處理"SELECT 1"(Item_in_optimizer)

  • 如果目前還沒有子查詢的執(zhí)行方式,也就是無法使用semijoin/antijoin執(zhí)行的子查詢,會做IN->EXISTS的轉(zhuǎn)換,本質(zhì)是在物化執(zhí)行和迭代式循環(huán)執(zhí)行中做選擇。IN語法代表非相關(guān)子查詢僅執(zhí)行一次,將查詢結(jié)果物化成臨時表,之后需要結(jié)果時候就去物化表中查找;EXISTS代表對于外表的每一條記錄,子查詢都會執(zhí)行一次,是迭代式循環(huán)執(zhí)行。子查詢策略設(shè)定為Subquery_strategy::CANDIDATE_FOR_IN2EXISTS_OR_MAT
  • 重寫single-column的IN/ALL/ANY子查詢(single_value_transformer)
oe $cmp$ (SELECT ie FROM ... WHERE subq_where ... HAVING subq_having) => - oe $cmp$ (SELECT MAX(...) ) // handled by Item_singlerow_subselect - oe $cmp$ \<max\>(SELECT ...) // handled by Item_maxmin_subselect ? fails=>Item_in_optimizer - 對于已經(jīng)是materialized方案,不轉(zhuǎn)換 - 通過equi-join轉(zhuǎn)換IN到EXISTS
  • 如果是ALL/ANY單值subquery謂詞,嘗試用MIN/MAX子查詢轉(zhuǎn)換
SELECT * FROM t1 WHERE a < ANY (SELECT a FROM t1); => SELECT * FROM t1 WHERE a < (SELECT MAX(a) FROM t1)

  • 不滿足上面,調(diào)用single_value_in_to_exists_transformer轉(zhuǎn)換IN到EXISTS
  • 轉(zhuǎn)換將要將子查詢設(shè)置為相關(guān)子查詢,設(shè)置UNCACHEABLE_DEPENDENT標識
  • 如果子查詢包含聚合函數(shù)、窗口函數(shù)、GROUP語法、HAVING語法,將判斷條件加入到HAVING子句中,另外通過ref_or_null_helper來區(qū)分NULL和False的結(jié)果,如需要處理NULL IN (SELECT ...)還需要封裝到Item_func_trig_cond觸發(fā)器中。
SELECT ... FROM t1 WHERE t1.b IN (SELECT <expr of SUM(t1.a)> FROM t2) => SELECT ... FROM t1 WHERE t1.b IN (SELECT <expr of SUM(t1.a)> FROM t2[trigcond] HAVING t1.b=ref-to-<expr of SUM(t1.a)>)

  • 如果子查詢不包含聚合函數(shù)、窗口函數(shù)、GROUP語法,會放在WHERE查詢條件中,當然如果需要處理NULL情況還是要放入HAVING子句(Item_func_trig_cond+Item_is_not_null_test)。
不需要區(qū)分NULL和FALSE的子查詢: ? SELECT 1 FROM ... WHERE (oe $cmp$ ie) AND subq_where ? 需要區(qū)分的子查詢: SELECT 1 FROM ...WHERE subq_where AND trigcond((oe $cmp$ ie) OR (ie IS NULL))HAVING trigcond(@<is_not_null_test@>(ie))
  • JOIN::optimize()會計算materialization和EXISTS轉(zhuǎn)換的代價進行選擇,設(shè)置m_subquery_to_derived_is_impossible = true
  • ROW值轉(zhuǎn)換,通過Item_in_optimizer,不支持ALL/ANY/SOME(row_value_transformer)
  • Item_in_subselect::row_value_in_to_exists_transformer
for (each left operand)create the equi-join conditionif (is_having_used || !abort_on_null)create the "is null" and is_not_null_test itemsif (is_having_used)add the equi-join and the null tests to HAVINGelseadd the equi-join and the "is null" to WHEREadd the is_not_null_test to HAVING
  • 沒有HAVING表達式
(l1, l2, l3) IN (SELECT v1, v2, v3 ... WHERE where) => EXISTS (SELECT ... WHERE where and(l1 = v1 or is null v1) and(l2 = v2 or is null v2) and(l3 = v3 or is null v3)[ HAVING is_not_null_test(v1) andis_not_null_test(v2) andis_not_null_test(v3)) ] <-- 保證不為NULL可以去掉HAVING
  • 有HAVING表達式
(l1, l2, l3) IN (SELECT v1, v2, v3 ... HAVING having) => EXISTS (SELECT ... HAVING having and(l1 = v1 or is null v1) and(l2 = v2 or is null v2) and(l3 = v3 or is null v3) andis_not_null_test(v1) andis_not_null_test(v2) andis_not_null_test(v3))

2 ?轉(zhuǎn)換的標量子查詢轉(zhuǎn)換成Derived Table(transform_scalar_subqueries_to_join_with_derived)

該特性是官方在8.0.16中為了更好的支持Secondary Engine(Heapwave)的分析下推,增強了子查詢的轉(zhuǎn)換能力。可以先直觀的看下轉(zhuǎn)換和不轉(zhuǎn)換的執(zhí)行計劃的不同:

root:test> set optimizer_switch = 'subquery_to_derived=off'; Query OK, 0 rows affected (0.00 sec) ? root:test> EXPLAIN SELECT b, MAX(a) AS ma FROM t4 GROUP BY b HAVING ma < (SELECT MAX(t2.a) FROM t2 WHERE t2.b=t4.b); +----+--------------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+ | 1 | PRIMARY | t4 | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 100.00 | Using temporary | | 2 | DEPENDENT SUBQUERY | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where | +----+--------------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+ 2 rows in set, 3 warnings (0.00 sec) ? root:test> set optimizer_switch = 'subquery_to_derived=on'; Query OK, 0 rows affected (0.00 sec) ? root:test> EXPLAIN SELECT b, MAX(a) AS ma FROM t4 GROUP BY b HAVING ma < (SELECT MAX(t2.a) FROM t2 WHERE t2.b=t4.b); +----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+ | 1 | PRIMARY | t4 | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 100.00 | Using temporary | | 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | Using where; Using join buffer (hash join) | | 2 | DERIVED | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | Using temporary | +----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+ 3 rows in set, 3 warnings (0.01 sec)
  • transform_scalar_subqueries_to_join_with_derived具體轉(zhuǎn)換的過程如下:
  • 首先從JOIN條件、WHERE條件、HAVING條件和SELECT list中收集可以轉(zhuǎn)換的標量子查詢(Item::collect_scalar_subqueries)。
  • 遍歷這些子查詢,判斷是否可以增加一個額外的轉(zhuǎn)換(transform_grouped_to_derived):把隱性的GROUP BY標量子查詢變成Derived Table。
SELECT SUM(c1), (SELECT SUM(c1) FROM t3) scalar FROM t1; 轉(zhuǎn)換為=> SELECT derived0.summ, derived1.scalar FROM (SELECT SUM(a) AS summ FROM t1) AS derived0LEFT JOIN(SELECT SUM(b) AS scalar FROM t3) AS derived1ON TRUE 執(zhí)行計劃如下: explain SELECT SUM(a), (SELECT SUM(c1) FROM t3) scalar FROM t1; +----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+ | 1 | PRIMARY | <derived3> | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL | | 1 | PRIMARY | <derived4> | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where; Using join buffer (hash join) | | 4 | DERIVED | t3 | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL | | 3 | DERIVED | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 100.00 | NULL | +----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
  • 收集唯一的聚合函數(shù)Item列表(collect_aggregates),這些Item將會被新的Derived Table的列代替。
  • 還需要添加所有引用到這些Item的fields,包括直接在SELECT列表的,Window函數(shù)參數(shù)、ORDER by、Partition by包含的,還有該查詢塊中ORDER BY的列,因為他們都會引動到Derived Table里。
  • 創(chuàng)建Derived Table需要的Query_expression/Query_block(create_query_expr_and_block)。
  • 添加Derived Table到查詢塊和top_join_list中。
  • 保留舊的子查詢單元塊,如果包含可以轉(zhuǎn)化的Derived的移到Derived Table下面的Query_block,如果不包含,保留到原來的子查詢塊中。
  • 將之前的聚合函數(shù)Item列表插入到Derived Table的查詢塊中。
  • 收集除GROUP AGG表達式中的列,由于這些fields已經(jīng)移動到Derived Table中,刪除不合理的fields引用。
  • 收集所有唯一的列和View的引用后,將他們加到新的Derived Table列表中。
  • 對新的新的Derived Table進行flatten_subqueries/setup_tables
  • 重新resolve_placeholder_tables,不處理進行轉(zhuǎn)換后的子查詢。
  • 處理Derived Table中,新加入的HAVING條件中的聚合函數(shù)Item,并通過Item_aggregate_refs引用到new_derived->base_ref_items而不是之前的父查詢塊base_ref_items。
  • 永久代替父查詢塊中的聚合函數(shù)列表,變成Derived Table的列,并刪除他們。
  • 之前保存和加入到Derived Table的唯一的列和View的引用,也要替換新的fields代替他們的引用。

  • 但目前不支持HAVING表達式中包含該子查詢,其實也是可以轉(zhuǎn)換的。
SELECT SUM(a), (SELECT SUM(b) FROM t3) scalar FROM t1 HAVING SUM(a) > scalar; 轉(zhuǎn)換為=> SELECT derived0.summ, derived1.scalar FROM (SELECT SUM(a) AS summ FROM t1) AS derived0LEFT JOIN(SELECT SUM(b) AS scalar FROM t3) AS derived1ON TRUE WHERE derived0.sum > derived1.scalar;
  • 接下來遍歷所有可以轉(zhuǎn)換的子查詢,把他們轉(zhuǎn)換成derived tables,并替換相應的表達式變成列(transform_subquery_to_derived)。
  • 生成derived table的TABLE_LIST(synthesize_derived)。
  • 將可以移動到derived table的where_cond設(shè)置到j(luò)oin_cond上。
  • 添加derived table到查詢塊的表集合中。
  • decorrelate_derived_scalar_subquery_pre
  • 添加非相關(guān)引用列(NCF)到SELECT list,這些條件被JOIN條件所引用,并且還有另外一個fields包含了外查詢相關(guān)的列,我們稱之為'lifted_where'
  • 添加COUNT(*)到SELECT list,這樣轉(zhuǎn)換的查詢塊可以進行cardinality的檢查。比如沒有任何聚合函數(shù)在子查詢中。如果確定包含聚合函數(shù),返回一行一定是NCF同時在GROUP BY列表中。
  • 添加NCF到子查詢的GROUP列表中,如果已經(jīng)在了,需要加到最后,如果發(fā)生GROUP BY的列由于依賴性檢查失敗,還要加Item_func_any_value(非聚合列)到SELECT list。對于NCF會創(chuàng)建 derived.field和derived.`count(field)` 。
  • 設(shè)置物化的一些準備(setup_materialized_derived)。
  • decorrelate_derived_scalar_subquery_post:
  • 創(chuàng)建對應的'lifted_fields'。
  • 更新JOIN條件中相關(guān)列的引用,不在引用外查詢而換成Derived table相關(guān)的列。
  • 代替WHERE、JOIN、HAVING條件和SELECT list中的子查詢的表達式變成對應的Derived Table里面列。

下面圖解該函數(shù)的轉(zhuǎn)換過程和結(jié)果:

3 ?扁平化子查詢(flatten_subqueries)

該函數(shù)主要是將Semi-join子查詢轉(zhuǎn)換為nested JOIN,這個過程只有一次,并且不可逆。

  • 簡單來講步驟可以簡化理解為:
  • 創(chuàng)建SEMI JOIN (it1 ... itN)語以部分,并加入到外層查詢塊的執(zhí)行計劃中。
  • 將子查詢的WHERE條件以及JOIN條件,加入到父查詢的WHERE條件中。
  • 將子查詢謂詞從父查詢的判斷謂詞中消除。
  • 由于MySQL在一個query block中能夠join的tables數(shù)是有限的(MAX_TABLES),不是所有sj_candidates都可以做因此做flatten_subqueries 的,因此需要有優(yōu)先級決定的先后順序先unnesting掉,優(yōu)先級規(guī)則如下:
  • 相關(guān)子查詢優(yōu)先于非相關(guān)的
  • inner tables多的子查詢大于inner tables少的
  • 位置前的子查詢大于位置后的
subq_item->sj_convert_priority =(((dependent * MAX_TABLES_FOR_SIZE) + // dependent subqueries firstchild_query_block->leaf_table_count) *65536) + // then with many tables(65536 - subq_no); // then based on position
  • 另外,由于遞歸調(diào)用flatten_subqueries是bottom-up,依次把下層的子查詢展開到外層查詢塊中。
for SELECT#1 WHERE X IN (SELECT #2 WHERE Y IN (SELECT#3)) : ?Query_block::prepare() (select#1)-> fix_fields() on IN condition-> Query_block::prepare() on subquery (select#2)-> fix_fields() on IN condition-> Query_block::prepare() on subquery (select#3)<- Query_block::prepare()<- fix_fields()-> flatten_subqueries: merge #3 in #2<- flatten_subqueries<- Query_block::prepare()<- fix_fields()-> flatten_subqueries: merge #2 in #1
  • 遍歷子查詢列表,刪除Item::clean_up_after_removal標記為Subquery_strategy::DELETED的子查詢,并且根據(jù)優(yōu)先級規(guī)則設(shè)置sj_convert_priority。根據(jù)優(yōu)先級進行排序。
  • 遍歷排序后的子查詢列表,對于Subquery_strategy::CANDIDATE_FOR_DERIVED_TABLE策略的子查詢,轉(zhuǎn)換子查詢([NOT] {IN, EXISTS})為JOIN的Derived table(transform_table_subquery_to_join_with_derived)
FROM [tables] WHERE ... AND/OR oe IN (SELECT ie FROM it) ... => FROM (tables) LEFT JOIN (SELECT DISTINCT ie FROM it) AS derivedON oe = derived.ie WHERE ... AND/OR derived.ie IS NOT NULL ...
  • 設(shè)置策略為Subquery_strategy::DERIVED_TABLE
  • semijoin子查詢不能和antijoin子查詢相互嵌套,或者外查詢表已經(jīng)超過MAX_TABLE,不做轉(zhuǎn)換,否則標記為Subquery_strategy::SEMIJOIN策略。
  • 判斷子查詢的WHERE條件是否為常量。如果判斷條件永遠為FALSE,那么子查詢結(jié)果永遠為空。該情況下,調(diào)用Item::clean_up_after_removal標記為Subquery_strategy::DELETED,刪除該子查詢。
  • 如果無法標記為Subquery_strategy::DELETED/設(shè)置Subquery_strategy::SEMIJOIN策略的重新標記會Subquery_strategy::UNSPECIFIED繼續(xù)下一個。
  • 替換外層查詢的WHERE條件中子查詢判斷的條件(replace_subcondition)
  • 子查詢內(nèi)條件并不永遠為FALSE,或者永遠為FALSE的情況下,需要改寫為antijoin(antijoin情況下,子查詢結(jié)果永遠為空,外層查詢條件永遠通過)。此時將條件改為永遠為True。
  • 子查詢永遠為FALSE,且不是antijoin。那么將外層查詢中的條件改成永遠為False。
  • Item_subselect::EXISTS_SUBS不支持有聚合操作
  • convert_subquery_to_semijoin函數(shù)解析如下模式的SQL
  • IN/=ANY謂詞
  • 如果條件滿足解關(guān)聯(lián),解關(guān)聯(lián)decorrelate_condition
  • 添加解關(guān)聯(lián)的內(nèi)表表達式到 SELECT list
  • 收集FROM子句中的外表相關(guān)的 derived table或join條件
  • 去掉關(guān)聯(lián)標識UNCACHEABLE_DEPENDENT,更新used table
  • Derived table子查詢增加SELECT_DISTINCT標識
  • 轉(zhuǎn)換子查詢成為一個derived table,并且插入到所屬于的查詢塊FROM后(transform_subquery_to_derived)
  • 創(chuàng)建derived table及其join條件
  • 遍歷父查詢塊的WHERE,替換該子查詢的Item代替成derived table(replace_subcondition)
  • 遍歷排序后的子查詢列表,對于Subquery_strategy::CANDIDATE_FOR_SEMIJOIN策略的子查詢。
  • 判斷是否可以轉(zhuǎn)換為semijoin
  • 遍歷排序后的子查詢列表,對于Subquery_strategy::SEMIJOIN的子查詢,開始轉(zhuǎn)換為semijoin/antijoin(convert_subquery_to_semijoin)
  • convert_subquery_to_semijoin函數(shù)解析如下模式的SQL
  • IN/=ANY謂詞
SELECT ...FROM ot1 ... otNWHERE (oe1, ... oeM) IN (SELECT ie1, ..., ieMFROM it1 ... itK[WHERE inner-cond])[AND outer-cond][GROUP BY ...] [HAVING ...] [ORDER BY ...] =>SELECT ...FROM (ot1 ... otN) SJ (it1 ... itK)ON (oe1, ... oeM) = (ie1, ..., ieM)[AND inner-cond][WHERE outer-cond][GROUP BY ...] [HAVING ...] [ORDER BY ...]
  • EXISTS謂詞
SELECT ...FROM ot1 ... otNWHERE EXISTS (SELECT expressionsFROM it1 ... itK[WHERE inner-cond])[AND outer-cond][GROUP BY ...] [HAVING ...] [ORDER BY ...] =>SELECT ...FROM (ot1 ... otN) SJ (it1 ... itK)[ON inner-cond][WHERE outer-cond][GROUP BY ...] [HAVING ...] [ORDER BY ...]
  • NOT EXISTS謂詞
SELECT ...FROM ot1 ... otNWHERE NOT EXISTS (SELECT expressionsFROM it1 ... itK[WHERE inner-cond])[AND outer-cond][GROUP BY ...] [HAVING ...] [ORDER BY ...] =>SELECT ...FROM (ot1 ... otN) AJ (it1 ... itK)[ON inner-cond][WHERE outer-cond AND is-null-cond(it1)][GROUP BY ...] [HAVING ...] [ORDER BY ...]
  • NOT IN謂詞
SELECT ...FROM ot1 ... otNWHERE (oe1, ... oeM) NOT IN (SELECT ie1, ..., ieMFROM it1 ... itK[WHERE inner-cond])[AND outer-cond][GROUP BY ...] [HAVING ...] [ORDER BY ...] =>SELECT ...FROM (ot1 ... otN) AJ (it1 ... itK)ON (oe1, ... oeM) = (ie1, ..., ieM)[AND inner-cond][WHERE outer-cond][GROUP BY ...] [HAVING ...] [ORDER BY ...]
  • 查找可以插入semi-join嵌套和其生成的條件的位置,比如對于 t1 LEFT JOIN t2, embedding_join_nest為t2,t2也可以是nested,如t1 LEFT JOIN (t2 JOIN t3))
  • 生成一個新的semijoin嵌套的TABLE_LIST表
  • 處理Antijoin
  • 將子查詢中潛在的表合并到上述join表(TABLE_LIST::merge_underlying_tables)
  • 將子查詢的葉子表插入到當前查詢塊的葉子表后面,重新設(shè)置子查詢的葉子表的序號和依賴的外表。將子查詢的葉子表重置。
  • 如果是outer join的話,在join鏈表中傳遞可空性(propagate_nullability)
  • 將內(nèi)層子查詢中的關(guān)聯(lián)條件去關(guān)聯(lián)化,這些條件被加入到semijoin的列表里。這些條件必須是確定的,僅支持簡單判斷條件或者由簡單判斷條件組成的AND條件(Query_block::decorrelate_condition)
  • 判斷左右條件是否僅依賴于內(nèi)外層表,將其表達式分別加入到semijoin內(nèi)外表的表達式列表中(decorrelate_equality)
  • 解關(guān)聯(lián)內(nèi)層查詢的join條件(Query_block::decorrelate_condition)
  • 移除該子查詢表達式在父查詢的AST(Query_express::exclude_level)
  • 根據(jù)semi-join嵌套產(chǎn)生的WHERE/JOIN條件更新對應的table bitmap(Query_block::fix_tables_after_pullout)
  • 將子查詢的WHERE條件上拉,更新使用表的信息(Item_cond_and::fix_after_pullout())
  • 根據(jù)semijoin的條件列表創(chuàng)建AND條件,如果有條件為常量True,則去除該條件;如果常量為False,則整個條件都去除(Query_block::build_sj_cond)
  • 將創(chuàng)建出來的semijoin條件加入到外層查詢的WHERE條件中
  • 最后遍歷排序后的子查詢列表,對于沒有轉(zhuǎn)換的子查詢,對于Subquery_strategy::UNSPECIFIED的策略,執(zhí)行IN->EXISTS改寫(select_transformer),如果確實原有的子查詢已經(jīng)有替代的Item,調(diào)用replace_subcondition解析并把他們加入到合適的WHERE或者ON子句。
  • 清除所有的sj_candidates列表
  • Semi-join有5中執(zhí)行方式,本文并不介紹Optimizer和Execution過程,詳細可以參考引用文章中關(guān)于semijoin的介紹,最后引入下控制semijoin優(yōu)化和執(zhí)行的優(yōu)化器開關(guān),其中semijoin=on/off是總開關(guān)。
SELECT @@optimizer_switch\G *************************** 1. row *************************** @@optimizer_switch: ......materialization=on,semijoin=on,loosescan=on,firstmatch=on,subquery_materialization_cost_based=on,......
  • 下圖舉例說明該轉(zhuǎn)換過程:
SELECT * FROM t1 WHERE t1.a in (SELECT t2.c1 FROM t2 where t2.c1 > 0); => /* select#1 */ SELECT `t1`.`a` AS `a` FROM `t1` SEMI JOIN (`t2`) WHERE ((`t1`.`a` = `t2`.`c1`) and (`t2`.`c1` > 0)) 執(zhí)行計劃如下: explain SELECT * FROM t1 WHERE t1.a in (SELECT t2.c1 FROM t2 where t2.c1 > 0); +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------------------------------------+ | 1 | SIMPLE | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where; Start temporary | | 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where; End temporary; Using join buffer (hash join) | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------------------------------------+

4 ?應用當前查詢塊轉(zhuǎn)換(apply_local_transforms)

該函數(shù)在flattern subqueries之后,bottom-up調(diào)用,主要分幾個步驟:

刪除無用列(delete_unused_merged_columns)

如果查詢塊已經(jīng)刪除了一些derived tables/views,遍歷SELECT列表的列,刪除不必要的列

簡化JOIN(simplify_joins)

該函數(shù)會把Query_block中的top_join_list的嵌套join的簡化為扁平化的join list。嵌套連接包括table1 join table2,也包含table1, (table2, table3)這種形式。如果所示的簡化過程:

分區(qū)表的靜態(tài)剪枝(prune_partitions)

由于剪枝根據(jù)HASH/RANGE/LIST及二級分區(qū)都有不同,這里簡單介紹下剪枝過程,現(xiàn)有prune_partitions是在prepare和optimize階段會被調(diào)用,某些常量子查詢被評估執(zhí)行完。

struct TABLE {...... partition_info *part_info{nullptr}; /* Partition related information *//* If true, all partitions have been pruned away */bool all_partitions_pruned_away{false};...... }SQL tranformation phase SELECT_LEX::apply_local_transforms --> prune_partitions ? for example, select * from employee where company_id = 1000 ; ? SQL optimizer phase JOIN::prune_table_partitions --> prune_partitions ------> based on tbl->join_cond_optim() or JOIN::where_cond ? for example, explain select * from employee where company_id = (select c1 from t1);
  • 舉例下面RANGE剪枝的過程:
root:ref> CREATE TABLE R2 (-> a INT,-> d INT-> ) PARTITION BY RANGE(a) (-> PARTITION p20 VALUES LESS THAN (20),-> PARTITION p40 VALUES LESS THAN (40),-> PARTITION p60 VALUES LESS THAN (60),-> PARTITION p80 VALUES LESS THAN (80),-> PARTITION p100 VALUES LESS THAN MAXVALUE-> ); Query OK, 0 rows affected (0.09 sec) ? root:ref> Select * From R2 where a > 40 and a < 80;
  • 剪枝詳細過程如下:
  • 由于剪枝需要根據(jù)不同條件產(chǎn)生的pruning結(jié)果進行交集,因此剪枝過程中需要使用read_partitions這樣的bitmap來保存是否使用該對應分區(qū)。另外剪枝過程類似迭代判斷,因此引入了part_iterator來保存開始、結(jié)束和當前,以及對應需要獲取區(qū)間范圍的endpoint函數(shù)和獲取下一個值next的迭代器函數(shù)。這里巧妙的運用了指針,來兼容不同分區(qū)類型Hash/Range/List類型,如下圖所示:

  • 獲取join_cond或者m_where_cond的SEL_TREE紅黑樹(get_mm_tree)
  • 調(diào)用find_used_partitions來獲取滿足的分區(qū),對于SEL_TREE的每個區(qū)間(interval):1. 獲取區(qū)間的左右端點 2.從左邊繼續(xù)獲取下一個滿足的分區(qū),直到到右邊端點結(jié)束,每次調(diào)用完滿足條件的分區(qū)需要使用bitmap_set_bit設(shè)置該分區(qū)在part_info->read_partitions上的位點。
  • find_used_partitions是根據(jù)SEL_TREE的結(jié)構(gòu)進行遞歸,如圖從左到右遍歷next_key_part(and condition),然后再遍歷SEL_TREE的左右(也就是上下方向,or condition)深度遞歸。
(start)| $| Partitioning keyparts $ subpartitioning keyparts| $| ... ... $| | | $| +---------+ +---------+ $ +-----------+ +-----------+\-| par1=c1 |--| par2=c2 |-----| subpar1=c3|--| subpar2=c5|+---------+ +---------+ $ +-----------+ +-----------+| $ | || $ | +-----------+| $ | | subpar2=c6|| $ | +-----------+| $ || $ +-----------+ +-----------+| $ | subpar1=c4|--| subpar2=c8|| $ +-----------+ +-----------+| $| $+---------+ $ +------------+ +------------+| par1=c2 |------------------| subpar1=c10|--| subpar2=c12|+---------+ $ +------------+ +------------+| $... $ ? 例如第一行(par1=c1 and par2=c2 and subpar1=c3 and subpar2=c5)的遍歷的stack將是: in find_used_partitions(key_tree = "subpar2=c5") (***) in find_used_partitions(key_tree = "subpar1=c3") in find_used_partitions(key_tree = "par2=c2") (**) in find_used_partitions(key_tree = "par1=c1") in prune_partitions(...) 然后是繼續(xù)下面的條件,以此類推 or(par1=c1 and par2=c2 and subpar1=c3 and subpar2=c6) or(par1=c1 and par2=c2 and subpar1=c4 and subpar2=c8) or(par1=c2 and subpar1=c10 and subpar2=c12)
  • 下圖來展示了pruning的結(jié)構(gòu)和過程:

5 ?下推條件到Derived Table(push_conditions_to_derived_tables)

該函數(shù)將條件下推到derived tables,詳細見WL#8084 - Condition pushdown to materialized derived table。

root:test> set optimizer_switch = 'derived_merge=off'; // 關(guān)閉dervied_merge 測試下推能力 Query OK, 0 rows affected (0.00 sec) ? root:test> EXPLAIN FORMAT=tree SELECT * FROM (SELECT c1,c2 FROM t1) as dt WHERE c1 > 10; +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EXPLAIN | +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | -> Table scan on dt (cost=2.51..2.51 rows=1)-> Materialize (cost=2.96..2.96 rows=1)-> Filter: (t1.c1 > 10) (cost=0.35 rows=1)-> Table scan on t1 (cost=0.35 rows=1)| +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

過程如下:

  • 遍歷derived table列表,判斷是否可以下推(can_push_condition_to_derived),如果包括下面的情況則不能下推:
  • Derived table有UNION
  • Derived table有LIMIT
  • Derived table不能是outer join中的內(nèi)表,會導致更多NULL補償?shù)男?/li>
  • 不能是CTE包含的Derived table
  • 創(chuàng)建可以下推到的Derived table的where cond(Condition_pushdown::make_cond_for_derived)
  • 保留剩余不能下推的條件(Condition_pushdown::get_remainder_cond)
  • Top-down遞歸調(diào)用push_conditions_to_derived_tables

詳細圖解該過程如下:

三 ?綜述

兩篇文章重點介紹了下優(yōu)化器的基于規(guī)則的優(yōu)化部分,并沒有涉及更多的基于代價的優(yōu)化,可以看到對于直接運用規(guī)則優(yōu)化帶來執(zhí)行的加速,那么可以直接轉(zhuǎn)換,尤其是對于查詢結(jié)構(gòu)上面的變化類轉(zhuǎn)換,如merge_derived。對于運用規(guī)則優(yōu)化無法判斷是否帶來執(zhí)行的加速,那么優(yōu)化器會保留一些臨時結(jié)構(gòu),為后續(xù)的代價估算提供更多選擇,如IN/EXIST/Materialized轉(zhuǎn)換。當然還有一些,又改變查詢結(jié)構(gòu)又無法判定是否規(guī)則轉(zhuǎn)換帶來的執(zhí)行加速,MySQL目前還不支持。文章雖然詳盡,但無法覆蓋全部情況,也是為了拋磚引玉,還需要讀者自己通過調(diào)試的方法更進一步了解某一類SQL的具體過程。

四 ?參考資料

《MySQL 8.0 Server層最新架構(gòu)詳解》

《WL#13520: Transform correlated scalar subqueries》

《WL#8084 - Condition pushdown to materialized derived table》

《WL#2980: Subquery optimization: Semijoin》

  • WL#3740: Subquery optimization: Semijoin: Pull-out of inner tables
  • WL#3741: Subquery optimization: Semijoin: Duplicate elimination strategy
  • WL#3750: Subquery optimization: Semijoin: First-match strategy
  • WL#3751: Subquery optimization: Semijoin: Inside-out strategy

《WL#4389: Subquery optimizations: Make IN optimizations also handle EXISTS》

《WL#4245: Subquery optimization: Transform NOT EXISTS and NOT IN to anti-join》

《WL#2985: Perform Partition Pruning of Range conditions》
《MySQL · 源碼分析 · Semi-join優(yōu)化執(zhí)行代碼分析》
《MySQL·源碼分析·子查詢優(yōu)化源碼分析》《Optimizing Subqueries, Derived Tables, View References, and Common Table Expressions》

原文鏈接
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。?

總結(jié)

以上是生活随笔為你收集整理的庖丁解牛|图解 MySQL 8.0 优化器查询转换篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

欧美激情综合色 | 亚洲五月六月 | 亚洲天堂网在线观看视频 | 欧美日韩国产亚洲乱码字幕 | 精品综合久久 | 日韩av图片 | 国产成人在线观看免费 | 国产小视频免费在线网址 | 成 人 黄 色 视频 免费观看 | 国产一级电影网 | 在线观看久草 | 国产成人精品女人久久久 | 国产亚洲片 | 超碰人人国产 | 亚洲一级片 | 日本中文字幕在线视频 | 欧美精品黑人性xxxx | 天天色天天操天天爽 | 六月丁香婷婷在线 | 久99久精品 | av片在线观看 | 欧美另类激情 | 99久久精品国产一区 | 成人国产精品久久久久久亚洲 | 久久精品一区八戒影视 | 久草网站在线观看 | 欧美少妇xxxxxx | 青草视频免费观看 | 国产精品99久久久精品免费观看 | 天天草综合 | 免费v片 | 91桃色在线观看视频 | 91视频在线自拍 | 久久天| 免费看的黄色 | 国产最顶级的黄色片在线免费观看 | 亚洲一级片在线看 | 麻豆国产视频下载 | 免费在线观看一区 | 久久香蕉一区 | 91看成人| www.色午夜 | 黄色成人av网址 | 久久久首页 | 欧美性网站 | 欧美黑人性爽 | 久久天天躁夜夜躁狠狠85麻豆 | 69av国产 | 久久久久亚洲精品成人网小说 | 成年人网站免费观看 | 在线观看免费一区 | 在线视频app | 日韩精品免费在线播放 | 日韩在线电影一区二区 | 五月天中文字幕 | 欧美一二三区在线播放 | 男女激情免费网站 | 亚洲专区 国产精品 | 在线网址你懂得 | 综合天天色 | 免费福利片 | 国产区精品视频 | 久久久久久网站 | 91在线操| 黄色大全在线观看 | 久久久久久亚洲精品 | 操操综合| 免费在线观看av网址 | 激情久久网 | 一区二区三区免费看 | 一二三精品视频 | 亚洲视频播放 | 蜜臀av性久久久久蜜臀aⅴ四虎 | 久久综合狠狠狠色97 | 国产一区二区免费看 | 成人av动漫在线 | 国产亚洲欧美精品久久久久久 | 国内精品久久久久久久久久久久 | 久久黄色免费 | 精品欧美乱码久久久久久 | 国产女v资源在线观看 | 欧美坐爱视频 | 国产午夜激情视频 | 日韩大片免费观看 | 五月天综合网 | 涩涩在线 | 久久草在线视频国产 | 九九在线播放 | 在线网址你懂得 | 9热精品| 日韩av在线一区二区 | 国产中文字幕视频在线观看 | 欧美男男激情videos | 亚洲乱码一区 | 91视频久久久 | 中文字幕资源网在线观看 | 最近中文字幕视频完整版 | 天天射天天操天天色 | 国产精品视频久久 | 久久午夜精品 | 91免费视频网站在线观看 | 久久精品国产成人精品 | 午夜色站| 久草在线最新视频 | 国产一级片久久 | 中文字幕丰满人伦在线 | 久久久精品欧美一区二区免费 | 99精品久久99久久久久 | 国产中文字幕一区 | 国产精品热 | 久久精品免费 | 又黄又爽又色无遮挡免费 | 成人羞羞免费 | 日韩av中文在线 | 免费高清在线观看电视网站 | 黄色www在线观看 | a一片一级 | a在线一区 | 九九热精品国产 | 日韩视频免费观看高清 | 久久超碰网 | 久久综合色播五月 | 国内免费的中文字幕 | 爱干视频 | 欧美综合久久 | 久久久久久久影院 | 天天操天天干天天爱 | 日韩欧美国产免费播放 | 在线观看视频国产 | 久久久久女人精品毛片九一 | 狠狠干网 | 成人黄色av网站 | 精品毛片一区二区免费看 | 亚洲精品在线网站 | 麻豆久久久 | 精品久久五月天 | 国产99久久久久久免费看 | 狠狠操夜夜 | 男女精品久久 | 国产精品爽爽久久久久久蜜臀 | 精品日韩在线一区 | 久久久亚洲精品 | 亚洲精品一区二区三区高潮 | 黄色com | 亚洲精品xxx | 亚洲最新在线 | 波多野结衣日韩 | 日韩精品久久久久久久电影99爱 | 中文区中文字幕免费看 | 国产在线精品区 | 色婷婷综合视频在线观看 | 亚洲视屏一区 | 久久激情视频 久久 | 免费人成网 | 欧亚日韩精品一区二区在线 | 日韩理论电影在线 | 久久久久久电影 | 国产精品白浆 | 欧美国产日韩一区二区三区 | 中文字幕在线观看2018 | 夜夜操综合网 | 欧美日韩一区二区三区在线免费观看 | 五月开心六月伊人色婷婷 | 国产精品日韩久久久久 | www黄色软件 | www久草 | 免费网站在线观看成人 | 久久99亚洲网美利坚合众国 | 成人黄色电影在线播放 | 99免费在线播放99久久免费 | 亚洲天堂激情 | 日韩在线免费看 | 不卡电影一区二区三区 | 亚洲精品免费在线观看视频 | 一级黄色在线视频 | 96av视频 | 九九99| 天天干人人 | 黄色av大片 | 精品国产视频在线观看 | 欧美男同网站 | 在线观看免费版高清版 | 九九热免费观看 | 亚洲国产高清在线 | a√天堂资源 | 婷婷在线免费视频 | 欧美日韩视频免费看 | 伊人成人精品 | 国语对白少妇爽91 | 国产999在线观看 | 亚洲精品视频中文字幕 | 国产精品一区久久久久 | 99久久久国产精品免费99 | 在线一二三区 | 久久天堂亚洲 | 婷婷色在线播放 | 国产精品 中文字幕 亚洲 欧美 | 亚洲一区二区视频 | 又黄又爽又刺激 | 五月天电影免费在线观看一区 | 91刺激视频 | 国产精品免费视频网站 | 欧美日韩中文在线观看 | 激情小说网站亚洲综合网 | 午夜精品一区二区国产 | 激情影院在线 | 综合久久久久久 | www.天天射 | www免费视频com━ | 成人精品一区二区三区电影免费 | 国产精品久久久久久久久毛片 | 久久精彩免费视频 | 波多野结衣精品视频 | 久久久精品亚洲 | 日韩精品中文字幕在线观看 | 日韩三级视频在线观看 | 国产一区二区三区在线 | 高清av中文字幕 | 欧美成人视 | 热久久电影 | 狠狠网| 久久精品国产精品 | 国产一区私人高清影院 | 黄色软件在线观看 | 最新日韩中文字幕 | 九九九在线观看 | 日韩高清国产精品 | 久久久久国产精品免费网站 | 精品欧美乱码久久久久久 | 日韩av网址在线 | 51精品国自产在线 | 国产伦理久久精品久久久久_ | 亚洲少妇影院 | 91传媒在线| 在线国产高清 | 人人澡视频| 日b视频国产 | 天天舔天天射天天操 | 国内丰满少妇猛烈精品播 | 人人爽人人爽人人片av | 日韩欧美国产成人 | 欧美精品久久久久久久久久丰满 | 免费一级片在线 | 久久久精品国产一区二区电影四季 | 不卡中文字幕av | 992tv在线成人免费观看 | 91传媒在线 | 粉嫩aⅴ一区二区三区 | 国内毛片毛片 | 98久9在线 | 免费 | 日韩成人高清在线 | 五月天中文字幕 | 在线观看亚洲国产 | 91av免费看 | 国产成人a亚洲精品v | 九九免费在线观看 | 国产成人黄色网址 | 欧美成年人在线观看 | 日韩女同一区二区三区在线观看 | 久久99精品国产99久久 | 亚洲一二三在线 | 福利视频午夜 | 色香com.| 成人免费在线观看电影 | 婷婷色综合色 | 中文乱幕日产无线码1区 | 国产伦精品一区二区三区在线 | 婷婷在线不卡 | 99在线视频网站 | 夜夜操夜夜干 | 欧美少妇bbwhd | 最新极品jizzhd欧美 | 国内成人精品视频 | 一区二区中文字幕在线播放 | 日韩精品免费一区二区三区 | 天天色天天艹 | 日韩精品视频免费在线观看 | 香蕉视频一级 | 狠狠狠狠狠干 | 亚洲一区免费在线 | 五月综合色婷婷 | 国产精品对白一区二区三区 | 黄色大片入口 | 国产精品亚洲片在线播放 | 国产九九精品视频 | 狠狠色伊人亚洲综合网站色 | 香蕉网在线播放 | 日韩精品免费在线视频 | 国产亚洲婷婷免费 | 午夜精品一二区 | 最近免费观看的电影完整版 | 色吧av色av | 国产亚洲精品久久 | 人人搞人人干 | 国产精品av在线 | av先锋中文字幕 | 99在线观看精品 | 久久九精品 | 99精品福利 | 国产手机在线播放 | 中文字幕日韩有码 | 欧美十八 | 激情久久一区二区三区 | 日韩在线观看一区二区三区 | 中文在线最新版天堂 | www.狠狠操.com | 2018精品视频| 99成人免费视频 | 狠狠狠色丁香婷婷综合久久五月 | 国产99久 | 日韩a在线看 | 色婷婷伊人 | 99视频在线精品免费观看2 | 亚洲 欧洲 国产 日本 综合 | 国产午夜精品久久久久久久久久 | 国产午夜精品久久久久久久久久 | 午夜精品久久久久久久久久久久久久 | 免费大片黄在线 | 五月天天色 | 日韩av在线小说 | 狠狠干网| 91精品国产三级a在线观看 | 久久久国产精品一区二区三区 | 国产成人三级在线 | 成人免费观看完整版电影 | 久草视频在线免费看 | 91精品综合在线观看 | 麻豆 videos | 最近的中文字幕大全免费版 | 欧美日韩高清一区二区 | 久久久国产精华液 | 人人干狠狠操 | 成人国产电影在线观看 | 在线观看精品视频 | 久久精品免费播放 | 6699私人影院 | 探花视频在线版播放免费观看 | 欧美a在线免费观看 | 国产精品久久一卡二卡 | 久久九九网站 | 中文字幕在线免费看 | 欧美一区二区伦理片 | 中国一级片在线播放 | 国产精品黑丝在线观看 | 亚洲视频专区在线 | 欧美久久久久久久久中文字幕 | 国产特级毛片aaaaaa | 操操操日日日干干干 | 欧美福利在线播放 | 国产精品专区在线观看 | 日日日干 | 国产精品久久久久一区二区 | 99久久日韩精品免费热麻豆美女 | 久草免费在线视频观看 | 伊人五月天综合 | 精品a级片 | av免费电影在线 | 天天射天天 | 九九视频免费在线观看 | 国产精品久久人 | 久草在线视频免费资源观看 | 精品国产福利在线 | 日韩网站在线播放 | 精品免费观看 | 香蕉日日 | 在线观看视频精品 | 日韩三级免费观看 | 91成年人网站 | 亚洲精品在线观看网站 | 欧美a级一区二区 | 正在播放国产精品 | 国产精品电影一区二区 | 最新国产精品拍自在线播放 | 香蕉视频国产在线观看 | 免费在线视频一区二区 | 国产日韩在线看 | 日韩欧美在线国产 | 免费国产一区二区视频 | 日本爱爱免费 | 国产福利精品视频 | 成人黄视频 | www.在线观看av | a v在线观看 | 激情综合亚洲精品 | 国产视频99 | 久久免费av| 一区二区三区中文字幕在线观看 | 依人成人综合网 | www免费视频com━ | 成av在线 | 精品中文字幕在线 | 五月婷av | 久久精品国产精品亚洲精品 | 日韩免费视频 | 婷婷色在线播放 | 亚洲国产精品视频在线观看 | 日韩视频一区二区三区 | 精品国产1区2区3区 国产欧美精品在线观看 | 国产精品国产三级在线专区 | 91丨九色丨国产丨porny精品 | 日日躁夜夜躁aaaaxxxx | 久久亚洲福利视频 | 欧美日韩国产一二 | 91欧美精品 | 正在播放国产精品 | 免费视频在线观看网站 | 韩日色视频 | 成年人在线观看免费视频 | 国产精品99久久久久久宅男 | 午夜av在线播放 | 97人人模人人爽人人喊网 | 日韩资源视频 | 一级黄色在线免费观看 | 五月天综合色 | 国产精品久久久久久久久久久免费看 | 日韩理论片| 91爱在线 | 久久综合色一综合色88 | 手机看片 | 国产精品情侣视频 | 亚洲成人网av| 国产美腿白丝袜足在线av | 91成人精品一区在线播放 | 人人草网站 | 亚洲激情在线 | 国产毛片久久久 | 免费在线观看亚洲视频 | 欧美一级黄色视屏 | 久久香蕉国产 | 国产精品影音先锋 | 日韩在线观看一区 | 国产精品嫩草在线 | 精品国产一区二区三区日日嗨 | 粉嫩aⅴ一区二区三区 | 日韩网站在线免费观看 | 免费看搞黄视频网站 | 中文字幕不卡在线88 | 亚洲人成在线电影 | 亚洲精品国产精品国 | 午夜精品久久久久久中宇69 | 久久精品123 | 欧美精品久久人人躁人人爽 | 久久综合99 | 97操操| 精品一区二区影视 | 欧美精品一级视频 | 日韩啪啪小视频 | www.av在线.com | 综合在线亚洲 | 免费观看第二部31集 | 日韩网站一区二区 | 黄色电影小说 | 99麻豆久久久国产精品免费 | 午夜精品久久一牛影视 | 97超在线| 99热这里只有精品国产首页 | 国产一区二区影院 | 久久九九九九 | 色噜噜在线观看 | 国产精品成人av久久 | 日本性生活一级片 | 国产超碰在线 | 久久免费的精品国产v∧ | 欧美日韩在线视频一区二区 | 久久综合色一综合色88 | 久久亚洲专区 | www.xxx.性狂虐 | 美女黄色网在线播放 | 久久精品福利视频 | 日日干网址 | 91麻豆精品国产91久久久使用方法 | av日韩av | 日韩在线免费不卡 | 精品视频免费在线 | 亚洲欧美一区二区三区孕妇写真 | 成人动漫视频在线 | 91在线入口 | 国产在线看| 不卡的av电影在线观看 | 麻豆精品91 | 丝袜护士aⅴ在线白丝护士 天天综合精品 | 欧美极品xxx| 国产精品原创av片国产免费 | 狠狠色丁香婷婷综合最新地址 | 国产精品99久久99久久久二8 | 有码中文字幕 | 毛片888| 亚洲国产精品500在线观看 | 天堂av免费看 | 九九久| 伊人伊成久久人综合网站 | 国产精品免费在线播放 | 丁香六月网 | 日日草夜夜操 | 99免费精品视频 | 中文免费在线观看 | 色香蕉网| 国产小视频在线观看免费 | 在线中文字幕观看 | 日韩精品视频久久 | 丁香花在线观看视频在线 | 久久综合色天天久久综合图片 | 午夜精品久久一牛影视 | 免费网站v | 国产视频 久久久 | 丝袜一区在线 | 999成人免费视频 | 亚洲免费观看在线视频 | 99免费在线| 中文字幕av免费在线观看 | 亚洲精品国产品国语在线 | 精品一区二区三区四区在线 | 欧美性生交大片免网 | 国产在线国偷精品产拍 | 久久国产精品影视 | 欧美成天堂网地址 | 欧美日韩在线观看一区二区三区 | 99草在线视频 | 中文字幕在线影视资源 | 国产精品99精品 | 中文字幕乱码一区二区 | 久久99久久99精品免观看软件 | 人人插人人草 | 日韩精品中文字幕在线 | 欧美国产不卡 | 亚洲国产精品免费 | 亚洲夜夜网 | 操天天操 | 五月婷婷激情网 | 色999五月色| 国产精品原创在线 | 波多野结衣视频一区 | 天天干,天天草 | 精品黄色在线 | 中文字幕在线看视频 | 国产福利精品一区二区 | 国产二区视频在线观看 | 国产精品久久久久一区二区 | 精品一区二区在线观看 | 精品视频久久 | 天堂av官网 | 午夜视频一区二区 | 成人精品在线 | 波多野结衣在线观看视频 | 中文字幕激情 | 中文字幕日本电影 | 亚洲天堂网在线播放 | 国产精品久久久久四虎 | 久久久久黄色 | 韩国av永久免费 | 久久精品女人毛片国产 | 99精品久久只有精品 | 欧美日韩1区 | 久久激情小说 | 在线观看岛国 | 久久久国产精品亚洲一区 | 日本中文乱码卡一卡二新区 | 欧美久久久久久久 | 国产中文自拍 | 夜夜视频 | 国产黄免费看 | 免费三级大片 | 奇米777777 | 婷婷国产v亚洲v欧美久久 | 久久精品五月 | 欧洲一区精品 | 婷婷日| 久久99这里只有精品 | 久久久久久中文字幕 | av丁香花| 成人综合日日夜夜 | 91视频久久 | 国产精品丝袜久久久久久久不卡 | 2022中文字幕在线观看 | 久久精品com | 国产福利91精品一区二区三区 | 久久毛片高清国产 | 国产精品美女免费视频 | 免费成人在线电影 | 91av中文| 综合久久综合久久 | 精品久久国产 | 日韩理论片在线 | 亚洲免费精彩视频 | 成年人免费看片网站 | 狠狠干.com | 911免费视频| 精品久久久久免费极品大片 | 欧美电影黄色 | 成人免费网站在线观看 | 91精品999| 亚洲精品综合在线观看 | 国产高清一区二区 | 人人爱夜夜操 | 97在线免费观看视频 | 久久香蕉电影网 | 玖玖视频精品 | 国内精品久久久久久久 | 日韩久久一区二区 | 久久看片网站 | 91精品久久久久久粉嫩 | 伊人婷婷久久 | av在线播放网址 | 91人人澡人人爽人人精品 | 国产在线毛片 | 欧美成年网站 | 国产一级一级国产 | 天天操天天干天天爱 | 手机看片中文字幕 | 国产日产精品久久久久快鸭 | 欧美一级日韩三级 | 97在线免费视频 | 精品久久久久久亚洲综合网站 | 亚洲精品在线电影 | 999久久久久久久久6666 | 欧美精品亚洲精品 | 欧美精品久久人人躁人人爽 | 免费看片成年人 | 亚洲成人资源 | 手机在线看永久av片免费 | 中文字幕中文中文字幕 | 免费av在线播放 | av在线精品 | 久草视频中文 | 91久久黄色| 一区二区三区在线不卡 | 国产日本亚洲 | 最新久久免费视频 | 久久久久国产一区二区三区 | 国产1级视频 | www91在线观看 | 国产自偷自拍 | 中文字幕一区二区三区久久 | 狠狠躁日日躁狂躁夜夜躁 | 国产亚洲视频在线观看 | 97**国产露脸精品国产 | 国产 日韩 在线 亚洲 字幕 中文 | 国产一区在线观看免费 | 97人人网 | 中文成人字幕 | 去看片 | 精品久久久成人 | 狠狠干夜夜爽 | 成人禁用看黄a在线 | 国产手机在线精品 | 亚洲专区视频在线观看 | 蜜臀av性久久久久av蜜臀三区 | 亚洲人成网站精品片在线观看 | 国产高清视频在线播放一区 | 午夜三级福利 | 久热只有精品 | 久久艹在线 | 国产精品a久久 | 丁香婷婷激情 | 午夜精品婷婷 | 日韩激情久久 | 96av视频| 亚洲欧美视频在线播放 | 精品三级av | 日韩欧美在线不卡 | 国产日韩视频在线观看 | 天天干,天天射,天天操,天天摸 | 超碰精品在线观看 | www.超碰97.com| 91精品国产成人观看 | 在线观看成人网 | www狠狠操 | 亚洲国产97在线精品一区 | 成年人免费av网站 | 久久成人国产精品免费软件 | 国产色啪 | 亚洲 精品在线视频 | 久久人人97超碰com | 毛片网站免费在线观看 | 91人人澡人人爽 | 久久久国产一区二区三区四区小说 | www.成人久久 | 欧产日产国产69 | 久99久在线 | 日韩视频在线不卡 | 欧美日本日韩aⅴ在线视频 插插插色综合 | 国产精品美女视频网站 | 亚洲在线观看av | 日韩电影久久久 | 亚洲全部视频 | 日本性xxxxx| 日韩免费观看视频 | 色综合天天色 | 国产精品v欧美精品 | 久久久久国产免费免费 | 国产手机在线视频 | 97人人人人 | 日韩一区二区在线免费观看 | 91在线亚洲 | 黄色网www| 激情视频免费在线 | 亚洲国产成人久久综合 | 亚洲人成网站精品片在线观看 | 日韩大片在线看 | 日韩在线一二三区 | 亚洲国产精品成人精品 | 色网站国产精品 | 久久精品中文视频 | 国产91综合一区在线观看 | 992tv成人免费看片 | 在线观看91久久久久久 | www.久久婷婷 | 久久免费看毛片 | av免费黄色 | 黄色一级在线观看 | 日本高清dvd | 91免费在线播放 | 午夜私人影院 | 亚洲欧美日本A∨在线观看 青青河边草观看完整版高清 | www.在线观看视频 | 久久精品在线免费观看 | 精品国产免费人成在线观看 | 色婷婷久久 | 天天插综合网 | 久久黄色免费观看 | 最近2019中文免费高清视频观看www99 | 久久综合五月 | 国产精品永久久久久久久www | 福利一区视频 | 日韩av一区二区三区四区 | 欧美黄网站 | 国产精品理论在线观看 | 综合色中色 | 97夜夜澡人人爽人人免费 | 中文资源在线官网 | 国产精品入口传媒 | 911免费视频 | 天天操天天操天天操天天操天天操天天操 | 六月激情网 | 99热超碰| 亚洲精品一区二区18漫画 | 久草免费资源 | 久久综合久久综合九色 | 亚洲精品成人网 | 久草免费福利在线观看 | 午夜久久 | 国产精品麻豆99久久久久久 | 国产午夜三级一区二区三 | 色婷久久 | 三级av在线播放 | 国产小视频在线观看免费 | 国产综合在线观看视频 | 99热只有精品在线观看 | 中文字幕在线看视频国产中文版 | a久久免费视频 | 免费观看第二部31集 | 91传媒91久久久 | 一区二区三区在线免费观看 | av成人免费在线 | 久99久中文字幕在线 | www视频在线免费观看 | 国产中文字幕在线视频 | 天堂激情网 | 久草视频在线观 | 久久久久久久久久久久99 | 91福利视频网站 | 超碰人人干人人 | 国产成人亚洲精品自产在线 | 欧美va天堂在线电影 | 麻豆国产精品永久免费视频 | 国产成人精品久 | 99精品一级欧美片免费播放 | 免费在线国产视频 | 毛片网站观看 | 天天躁日日躁狠狠 | 激情大尺度视频 | 亚洲精品福利在线 | 激情网色 | 国产精品久久一区二区无卡 | 久久在线精品视频 | 国产一级免费在线观看 | www.狠狠插.com| 国产高清久久久 | 精品伊人久久久 | 亚洲国产一二三 | 中文字幕中文字幕在线中文字幕三区 | 69久久99精品久久久久婷婷 | 中文字幕高清 | 青青草在久久免费久久免费 | 一二三区av| 色婷婷啪啪免费在线电影观看 | 91麻豆精品国产91久久久使用方法 | 九九九九九九精品 | 国产精品九色 | 91成人免费在线视频 | 亚洲人片在线观看 | 国产黄在线播放 | 天天翘av | 综合黄色网 | 香蕉在线视频播放网站 | 日韩性色| 日韩欧美91 | 国产亚洲精品久久久久久移动网络 | 色综合婷婷 | 久久久久国产a免费观看rela | 亚洲高清激情 | 黄色小说免费观看 | 欧美激情第一区 | 国产99久久久精品视频 | 激情小说 五月 | 日韩在线国产精品 | 日韩一区视频在线 | 人人干人人超 | 午夜一级免费电影 | 丁香在线| 九九精品视频在线看 | 久久免费国产精品 | 免费看的视频 | 亚洲毛片久久 | 亚洲激情 在线 | 干天天| 99久久精品免费看国产一区二区三区 | 日韩欧美视频 | 999免费视频| 亚洲欧洲精品一区二区 | 午夜三级理论 | 欧美小视频在线观看 | 最新成人在线 | www日韩欧美| 国产亚洲精品久久久久久久久久久久 | 69亚洲乱| 亚洲一区二区三区毛片 | 黄色最新网址 | 蜜桃视频日韩 | 亚洲最大免费成人网 | 久久精品视频免费播放 | 亚洲三级影院 | 久久 在线 | 中文字幕国产一区二区 | 欧美综合国产 | 四虎影视成人精品国库在线观看 | 亚洲清纯国产 | 波多野结衣久久精品 | 中文字幕高清在线播放 | 日韩高清 一区 | 在线观看不卡的av | 欧美视频国产视频 | 制服丝袜在线 | 久久精品站 | 久久精品久久久精品美女 | 免费午夜av | 亚洲欧美日韩国产一区二区 | 深爱五月激情五月 | 在线成人免费av | 国产成人黄色片 | 天天做日日做天天爽视频免费 | 久久综合亚洲鲁鲁五月久久 | 在线天堂中文www视软件 | 国产麻豆精品久久一二三 | 久久黄视频 | www.久艹 | 日日天天 | 在线观看成人网 | 欧美一区免费观看 | 天天色欧美 | 亚洲婷婷在线视频 | 亚洲二级片 | 久久久久免费精品国产小说色大师 | 中文字幕视频三区 | 国产精品毛片久久 | 国产99久久久国产精品 | 深爱五月激情五月 | 天天综合网天天综合色 | 精品国产乱码一区二区三区在线 | 欧美一区日韩一区 | 亚洲成人第一区 | 久综合网 | 亚洲国产精品激情在线观看 | 超碰97在线资源站 | 中文字幕av免费 | 久久久国产精品久久久 | 欧美精品久久久久久 | av网站手机在线观看 | 美女久久久久 | 91精品国产综合久久久久久久 | 亚洲精品在线观看不卡 | 99在线视频精品 | 欧美一级在线观看视频 | 精品一区av| 在线播放你懂 | 91人人爽久久涩噜噜噜 | av在线免费在线观看 | 香蕉视频在线观看免费 | 精品99久久久久久 | 国产一区二区三精品久久久无广告 | 日韩在线观看视频一区二区三区 | 国产在线高清 | 九九久久视频 | 一级成人网 | 日本黄色大片儿 | 伊人久久国产精品 | 亚洲一区视频在线播放 | 国产视 | 国产视频欧美视频 | 亚洲黄色在线 | 91网在线| 探花国产在线 | 国产成人三级在线观看 | 99久久99久久精品免费 | 亚洲精品大片www | 国产精品破处视频 | 欧美性色黄大片在线观看 | 日韩激情在线 | 日日天天 | 中文字幕永久 | 91av资源网 | 国产精品初高中精品久久 | 精品久久久久久国产偷窥 | 国内精品久久久久久 | 成 人 黄 色 视频免费播放 | 免费视频久久久 | 在线观看日韩视频 | 成人av片在线观看 | 亚洲精品国产成人av在线 | www欧美色 | 天天操天天射天天添 | 久久久精品视频网站 | 成人蜜桃网 | 日韩欧美极品 | 欧美日韩国产精品久久 | 国产精品福利av | 亚洲欧美激情精品一区二区 | 欧美大荫蒂xxx | 久久精品中文字幕少妇 | 高清av在线免费观看 | 午夜精品久久久久久久久久 | 亚洲黄色片| 六月婷操 | 超碰.com| 国产成人久久av | 国产 日韩 在线 亚洲 字幕 中文 | 日本精品一 | 在线a视频免费观看 | 国产字幕在线观看 | 国产在线不卡一区 | 狠狠色丁香婷婷综合橹88 | 黄色一及电影 | www.狠狠插.com| 在线观看亚洲国产 | 日韩精品视频免费在线观看 | 久久综合九色 | 久久久网址 | 日韩三级成人 | 国产视频在线播放 | 精品一二区| 国外调教视频网站 | 99精品免费 | 日日爽天天 | 天天玩天天干 | 成人99免费视频 | 亚洲一级影院 | 国产在线观看a | 亚洲精选久久 | 欧美亚洲精品在线观看 | 日本中文字幕视频 | 久久乐九色婷婷综合色狠狠182 | 99精品视频在线观看播放 | 精品96久久久久久中文字幕无 | 成年人免费在线观看网站 | 日日夜夜天天 | av中文字幕免费在线观看 | 波多野结衣在线播放一区 | 久久综合久久综合久久综合 | 国产精品久久嫩一区二区免费 | 国产在线欧美日韩 | 免费高清在线观看成人 | 五月的婷婷 | 国产精品综合久久 | 99视频精品全部免费 在线 | 亚洲伊人第一页 | 欧美亚洲成人xxx | 98久9在线 | 免费 | 干av在线 | 就要干b| 亚洲欧美偷拍另类 | 久久香蕉电影网 | 波多野结衣视频一区二区三区 | 日韩午夜av电影 | 蜜臀av性久久久久蜜臀aⅴ流畅 | av黄色一级片 | 特黄特色特刺激视频免费播放 | 黄色软件视频大全免费下载 | 麻豆成人精品 | 久久亚洲精品国产亚洲老地址 | 国产精品第一视频 | 亚洲精品视频免费在线观看 | 欧美一二三区播放 | 亚洲韩国一区二区三区 | 91免费网站在线观看 | 天天操福利视频 | 国产精品久久久久久久免费观看 | 九色视频网 | 久草精品视频在线看网站免费 |