精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

一條 SparkSQL 可歌可泣的一生

大數據
本文將帶領讀者踏上一段深入 Spark 內核的旅程,詳細剖析一條 Spark SQL 語句從被提交到最終執行結束所經歷的每一個關鍵階段。

在大數據處理領域,Apache Spark 憑借其卓越的性能、易用性和統一的計算引擎,已成為事實上的行業標準。而 Spark SQL 作為 Spark 生態系統中最為活躍和核心的模塊之一。提供了使用熟悉的 SQL 語言或 DataFrame/Dataset API 來處理結構化數據的強大能力。對于絕大多數 Spark 用戶而言,spark.sql("SELECT ...") 或 df.write.save(...) 是他們與 Spark 交互的起點。然而,在這看似簡單的一行代碼背后,隱藏著一個極其復雜、精密且高效的執行引擎。理解 Spark SQL 的全生命周期,不僅有助于我們編寫出性能更優的代碼,更能讓我們在面對性能瓶頸或疑難雜癥時,具備庖丁解牛般的診斷能力。

本文將帶領讀者踏上一段深入 Spark 內核的旅程,詳細剖析一條 Spark SQL 語句從被提交到最終執行結束所經歷的每一個關鍵階段。我們將從最上層的 API 入手,逐層深入,穿越邏輯計劃、物理計劃、執行計劃,直至最終的 Task 調度與執行,完整地揭示這條“數據之龍”是如何被 Spark 引擎一步步解析、優化、編譯并最終“馴服”的。

第一階段:入口與解析(Parsing)—— 將文本轉化為邏輯樹

一切的起點始于在 Spark Shell、JDBC/ODBC 服務或應用程序中提交的一條 SQL 語句。這個階段的核心任務是將人類可讀的、非結構化的 SQL 文本,轉化為 Spark 內部可以理解和處理的結構化數據表示形式——抽象語法樹(Abstract Syntax Tree, AST)。

1. Catalyst 的核心地位

Spark SQL 的強大能力源于其內置的查詢優化器——Catalyst。Catalyst 是一個基于 Scala 編寫的、可擴展的查詢優化框架,它采用函數式編程的思想,將整個查詢處理過程分解為一系列可組合、可重用的規則(Rules)。Catalyst 的設計哲學是“規則驅動”,這使得 Spark SQL 的優化過程既靈活又高效。

整個生命周期的第一步,就是由 Catalyst 的 Parser 模塊來完成的。

2. 詞法分析與語法分析

當 SparkSession.sql("SELECT name, age FROM people WHERE age > 30") 被調用時,SQL 字符串首先被傳遞給 SparkSession 的 sql 方法。該方法內部會調用 SessionState.sqlParser.parsePlan(sqlText)。

sqlParser 的具體實現通常是 SparkSqlParser,它繼承自 AbstractSqlParser。parsePlan 方法是解析的入口點。

解析過程分為兩個經典的編譯原理步驟:

(1) 詞法分析(Lexical Analysis):將輸入的字符流(SQL 字符串)分解成一系列有意義的“詞法單元”(Tokens)。例如,SELECT、name、>、30 等都會被識別為不同的 Token。Spark 使用 ANTLR4(Another Tool for Language Recognition)作為其詞法和語法分析器的生成器。ANTLR4 會根據預定義的 SQL 語法規則(.g4 文件)自動生成詞法分析器(Lexer)和語法分析器(Parser)。

(2) 語法分析(Syntax Analysis):語法分析器接收詞法分析器產生的 Token 流,并根據 SQL 的語法規則(上下文無關文法)來構建 AST。如果 SQL 語句不符合語法規則(例如,缺少 FROM 子句),則在此階段就會拋出 ParseException 異常。

3. 生成 Unresolved Logical Plan

ANTLR4 生成的原始 AST 是一個通用的、與 Spark 無關的樹形結構。SparkSqlParser 的職責就是遍歷這個 ANTLR AST,并將其轉換為 Spark Catalyst 內部定義的 LogicalPlan 樹。

此時生成的 LogicalPlan 被稱為 Unresolved Logical Plan。為什么叫“Unresolved”?因為在這個階段,Catalyst 對查詢中涉及的表、列等元數據信息一無所知。例如:

  • people 表是否存在?
  • people 表的 schema 是什么?它有哪些列?
  • name 和 age 列是否真的存在于 people 表中?
  • age 列的數據類型是什么?

這些問題在解析階段都無法回答。因此,UnresolvedLogicalPlan 中的 UnresolvedRelation("people") 和 UnresolvedAttribute("name") 等節點都只是占位符,等待后續階段去“解析”(Resolve)它們。

總結第一階段:提交的 SQL 文本被詞法和語法分析器轉換為一棵結構化的、但元數據信息缺失的邏輯計劃樹(Unresolved Logical Plan)。這是整個生命周期的基石,后續所有操作都基于這棵樹進行。

第二階段:分析(Analysis)—— 賦予邏輯計劃生命

解析階段產生的 Unresolved Logical Plan 是一個“空殼”。分析階段的任務就是通過查詢 Spark 的 Catalog(元數據目錄),為這個空殼注入真實的元數據信息,將其轉化為一棵“已解析”的、語義完整的 Analyzed Logical Plan。

1. Analyzer 與 Rule-Based Transformation

這個階段的核心執行者是 Analyzer。與 Parser 一樣,Analyzer 也遵循 Catalyst 的規則驅動范式。它內部維護了一個 RuleExecutor,其中包含了一系列預定義的分析規則(Analysis Rules)。

Analyzer 會反復應用這些規則到邏輯計劃樹上,直到樹的狀態穩定(即應用規則后樹不再發生變化)或者達到最大迭代次數。

2. 關鍵的分析規則

讓我們通過一個例子來看看 Analyzer 是如何工作的。假設我們有如下邏輯計劃:

Filter (age > 30)
  +- Project (name, age)
      +- UnresolvedRelation ("people")

Analyzer 會依次應用以下關鍵規則:

(1) ResolveRelations:這是最核心的規則之一。它會遍歷計劃樹,找到所有的 UnresolvedRelation 節點。對于 UnresolvedRelation("people"),Analyzer 會向當前 SessionState 的 Catalog 發起查詢。Catalog 是 Spark 管理所有數據庫、表、函數、視圖等元數據的中心。它可以是內置的 InMemoryCatalog(用于臨時視圖),也可以是外部的 HiveExternalCatalog(用于持久化的 Hive 表)。如果 people 表存在,ResolveRelations 規則會將其替換為一個具體的 LogicalRelation 節點,并附帶上該表的完整 schema 信息(例如,StructType(StructField("name", StringType, true), StructField("age", IntegerType, true)))。

(2) ResolveReferences:在表關系被解析后,下一步是解析列引用。ResolveReferences 規則會處理 Project 和 Filter 中的表達式。它會檢查 name 和 age 是否存在于剛剛解析出的 people 表的 schema 中。如果存在,就將 UnresolvedAttribute("name") 替換為一個綁定了具體數據類型和位置信息的 AttributeReference。同時,它還會處理表達式中的類型推斷。例如,在 age > 30 中,30 會被推斷為 IntegerType,以匹配 age 列的類型。

(3) CheckAnalysis:在所有引用都被解析后,Analyzer 會進行一次最終的合法性檢查。它會驗證計劃樹中的所有操作在語義上是否合法。例如,GROUP BY 子句中的列是否都出現在 SELECT 列表中(在非聚合查詢中),聚合函數的使用是否正確等。如果發現任何語義錯誤,會在此階段拋出 AnalysisException。

經過 Analyzer 的洗禮,我們的邏輯計劃就變成了:

Filter (age#1 > 30)
  +- Project (name#0, age#1)
      +- LogicalRelation (HadoopFsRelation, [name#0, age#1])

這里的 #0, #1 是 Catalyst 為每個 AttributeReference 分配的唯一 ID,用于在后續的優化和執行中精確地追蹤列。

總結第二階段:通過與 Catalog 交互并應用一系列分析規則,Unresolved Logical Plan 被賦予了真實的元數據和語義信息,成為一棵完整、合法、可被進一步處理的 Analyzed Logical Plan。

第三階段:邏輯優化(Logical Optimization)—— 智能的查詢重寫

現在我們有了一個語義正確的邏輯計劃,但這并不意味著它是最優的。編寫的 SQL 往往不是最高效的,可能存在冗余操作、低效的連接順序或可以利用的謂詞下推等優化機會。邏輯優化階段的任務就是應用一系列基于規則的優化(Rule-Based Optimization, RBO) 和 基于成本的優化(Cost-Based Optimization, CBO) 策略,對 Analyzed Logical Plan 進行等價變換,生成一個執行代價更低的 Optimized Logical Plan。

1. Optimizer 與優化規則

這個階段的主角是 Optimizer。和 Analyzer 一樣,它也是一個 RuleExecutor,內部包含了大量的優化規則。Spark 的優化規則庫非常豐富,涵蓋了從簡單常量折疊到復雜的連接重排序等各個方面。

2. 核心優化規則詳解

讓我們繼續以我們的例子為基礎,并稍作擴展,來展示一些關鍵的優化規則:

原始 Analyzed Plan:

Project (name)
  +- Filter (age > 30)
      +- Join (people, cities, people.city_id = cities.id)
          :- LogicalRelation (people, [name, age, city_id])
          +- LogicalRelation (cities, [id, city_name])

(1) 謂詞下推(Predicate Pushdown):

規則:PushDownPredicate

作用:將過濾條件下推到數據源或連接操作之前,盡早地減少數據量,從而節省后續操作的 I/O 和計算開銷。

變換:Filter (age > 30) 會被下推到 people 表的掃描操作之前。優化后的計劃變為:

Project (name)
  +- Join (Filter(age > 30) on people, cities, people.city_id = cities.id)
      :- Filter (age > 30)
      |   +- LogicalRelation (people, ...)
      +- LogicalRelation (cities, ...)

更進一步:如果 people 表是 Parquet、ORC 等支持謂詞下推的列式存儲格式,Spark 甚至可以將這個過濾條件下推到數據源讀取層,讓底層存儲引擎在讀取文件時就跳過不滿足條件的行組(Row Group)或條帶(Stripe),實現極致的 I/O 優化。

(2) 列裁剪(Column Pruning):

規則:ColumnPruning

作用:只讀取查詢最終需要的列,避免讀取無用的數據,節省 I/O 和內存。

變換:最終查詢只需要 name 列。通過分析計劃樹的依賴關系,Optimizer 會發現 cities 表的 city_name 列在整個查詢中從未被使用。因此,對 cities 表的掃描操作會被裁剪為只讀取 id 列。同時,people 表也只需要 name 和 city_id(用于連接),age 列在過濾后也不再需要。優化后:

Project (name#0)
  +- Join (...)
      :- Filter (age#1 > 30)
      |   +- LogicalRelation (people, [name#0, city_id#2]) // age#1 僅用于過濾,過濾后不再需要
      +- LogicalRelation (cities, [id#3]) // city_name 被裁剪

(3) 常量折疊(Constant Folding)與布爾表達式簡化:

規則:ConstantFolding, BooleanSimplification

作用:在編譯期就計算出可以確定的常量表達式,并簡化復雜的布爾邏輯。

例子:WHERE 1 = 1 AND age > 30 會被簡化為 WHERE age > 30。WHERE length("hello") > 3 會被直接計算為 WHERE true。

(4) 連接優化(Join Optimization):

規則:ReorderJoin, CostBasedJoinReorder (CBO)

作用:決定多表連接的最佳順序。RBO 通常采用啟發式策略(如將小表放在前面),而 CBO 則會利用表的統計信息(如行數、列的 NDV - Number of Distinct Values)來估算不同連接順序的代價,選擇最優方案。

(5) 子查詢優化:

規則:RewriteCorrelatedScalarSubquery 等

作用:將相關子查詢重寫為等價的連接(Join)操作,因為 Spark 的執行引擎對 Join 的優化和支持遠比對子查詢的要成熟和高效。

總結第三階段:Optimizer 通過應用一系列精心設計的規則,對邏輯計劃進行智能的、等價的重寫,旨在生成一個數據處理量最小、計算步驟最簡的 Optimized Logical Plan。這是 Spark SQL 性能卓越的關鍵所在。

第四階段:物理計劃生成(Physical Planning)—— 從邏輯到執行

經過邏輯優化后,我們得到了一個理論上最優的邏輯計劃。然而,邏輯計劃只描述了“做什么”(What to do),并沒有說明“怎么做”(How to do it)。物理計劃生成階段的任務就是將 Optimized Logical Plan 轉化為一個或多個具體的、可執行的 Physical Plan(也稱為 Spark Plan)。

1. SparkPlanner 與策略(Strategies)

這個階段的執行者是 SparkPlanner。與基于規則的 Analyzer 和 Optimizer 不同,SparkPlanner 采用的是基于策略(Strategy-Based) 的方法。它內部維護了一系列 Planning Strategies。每個 Strategy 負責將特定類型的 LogicalPlan 芚點轉換為一個或多個 PhysicalPlan 節點。

SparkPlanner 會遍歷 Optimized Logical Plan,對于每個節點,嘗試應用所有可用的 Strategy,直到找到一個能夠處理它的 Strategy 為止。

2. 核心策略與物理操作符

讓我們看看一些關鍵的 LogicalPlan 節點是如何被轉換的:

(1) LogicalRelation -> FileSourceScanExec / HiveTableScanExec:

策略:FileSourceStrategy, HiveTableScanning

作用:將對表的邏輯引用轉換為具體的物理掃描操作。FileSourceScanExec 用于讀取 Parquet、JSON、CSV 等通用文件格式,而 HiveTableScanExec 用于讀取 Hive 表。這些物理操作符內部封裝了與底層數據源交互的具體邏輯。

(2) Project -> ProjectExec:

策略:BasicOperators

作用:ProjectExec 是一個簡單的物理操作符,它接收上游的輸入行,并根據投影表達式計算出新的行。

(3) Filter -> FilterExec:

策略:BasicOperators

作用:FilterExec 對輸入的每一行應用謂詞表達式,只將滿足條件的行傳遞給下游。

(4) Join -> BroadcastHashJoinExec / ShuffledHashJoinExec / SortMergeJoinExec:

策略:JoinSelection

作用:這是最復雜的轉換之一。JoinSelection 策略會根據表的大小、數據的分布、配置參數(如 spark.sql.adaptive.enabled)以及是否存在廣播提示(Broadcast Hint)等因素,從多種物理連接實現中選擇最合適的一種。

  • BroadcastHashJoinExec:如果一張表足夠小(小于 spark.sql.autoBroadcastJoinThreshold),Spark 會將其廣播到所有 Executor 節點,然后在每個節點上與大表的分區進行本地 Hash Join。這是最高效的連接方式,因為它避免了昂貴的 Shuffle 操作。
  • ShuffledHashJoinExec:對兩張表都進行 Shuffle,按連接鍵分區,然后在每個分區內部構建 Hash 表進行 Join。
  • SortMergeJoinExec:對兩張表都進行 Shuffle 和排序,然后在每個分區內進行歸并式的 Join。這是 Spark 默認的連接策略,適用于大表之間的連接。

(5) Aggregate -> HashAggregateExec / SortAggregateExec:

策略:Aggregation

作用:同樣,根據數據特性和配置,選擇基于 Hash 表或基于排序的聚合實現。

3. 生成多個物理計劃與成本模型

值得注意的是,對于某些復雜的邏輯操作(尤其是 Join),SparkPlanner 可能會生成多個可行的物理計劃。例如,對于一個三表連接,可能有多種不同的連接順序和連接算法組合。

在早期版本的 Spark 中,Planner 會直接選擇第一個生成的計劃。但從 Spark 2.2 開始,引入了 Cost Model。Planner 會為每個候選的物理計劃估算一個執行成本(主要基于網絡 I/O 和磁盤 I/O 的估算),然后選擇成本最低的那個作為最終的物理計劃。

總結第四階段:SparkPlanner 將抽象的邏輯操作“翻譯”成具體的、可執行的物理操作符,并考慮了數據分布、資源消耗等因素,生成了最終的執行藍圖——Physical Plan。

第五階段:執行準備與代碼生成(Preparation & Code Generation)

物理計劃雖然已經很具體了,但它仍然是一個由 Scala 對象組成的樹形結構。直接解釋執行這個樹的效率會非常低下。為了追求極致的性能,Spark 在執行前會進行最后的準備工作,并利用 Whole-Stage Code Generation 技術將整個物理計劃(或其中連續的部分)編譯成高效的 Java 字節碼。

1. PrepareForExecution RuleExecutor

在物理計劃被提交給 Spark Core 的調度器之前,會經過一個名為 PrepareForExecution 的 RuleExecutor。它會應用一些最后的、與執行相關的規則:

  • InsertAdaptiveSparkPlan:如果啟用了自適應查詢執行(AQE),會在此處插入一個包裝節點,以便在運行時根據中間結果的統計信息動態調整后續的執行計劃。
  • PlanSubqueries:處理物理計劃中可能存在的子查詢。
  • EnsureRequirements:確保物理計劃滿足數據分布的要求。例如,如果一個 SortMergeJoinExec 要求輸入數據按連接鍵排序,而上游操作沒有提供這種排序,那么 EnsureRequirements 會插入一個 SortExec 操作符來滿足這個要求。

2. Whole-Stage Code Generation 的魔力

這是 Spark Tungsten 引擎的核心優化之一。其思想是,將物理計劃中連續的、不需要進行 Shuffle 或緩存的操作符(稱為一個 Codegen Stage)“融合”在一起,并生成一段單一的、高度優化的 Java 代碼。

優勢:

  • 消除虛函數調用:傳統的解釋執行模式下,處理每一行數據都需要調用一系列操作符的 processRow 虛函數,開銷巨大。代碼生成將這些調用內聯(Inline)成直接的 Java 代碼,消除了函數調用開銷。
  • 利用 CPU 寄存器:生成的代碼可以將頻繁訪問的中間變量保存在 CPU 寄存器中,而不是在內存中反復讀寫。
  • 循環展開與 SIMD 優化:JIT 編譯器可以對生成的循環代碼進行進一步優化,如循環展開(Loop Unrolling),甚至利用 CPU 的 SIMD(Single Instruction Multiple Data)指令集進行向量化計算。

例如,一個 FilterExec 后面跟著一個 ProjectExec 的組合,會被編譯成類似如下的 Java 代碼:

public void process() {
    while (input.hasNext()) {
        InternalRow row = input.next();
        // Filter condition: age > 30
        if (row.getInt(1) > 30) {
            // Project: create new row with name
            InternalRow newRow = new InternalRow(1);
            newRow.update(0, row.getString(0));
            output.write(newRow);
        }
    }
}

這段代碼直接、高效,沒有任何多余的抽象層。

總結第五階段:通過最后的執行準備和革命性的代碼生成技術,Spark 將物理計劃轉化為了可以直接在 JVM 上高效運行的機器指令,為最終的執行鋪平了道路。

第六階段:任務調度與執行(Task Scheduling & Execution)—— 數據的最終處理

至此,Spark SQL 引擎的工作基本完成,它將最終的、準備好的物理計劃(現在可以看作是一個 DAG - 有向無環圖)交給了 Spark Core 的 DAGScheduler。

1. DAGScheduler 與 Stage 劃分

DAGScheduler 是 Spark Core 的核心調度器。它的主要任務是:

劃分 Stage:根據物理計劃中的 ShuffleDependency(寬依賴)來劃分 Stage。Shuffle 操作(如 ShuffledHashJoinExec, SortExec 等)是 Stage 的邊界。一個物理計劃會被劃分為多個 Stage,它們之間構成一個 DAG。沒有 Shuffle 依賴的連續操作符屬于同一個 Stage。

提交 Job:將整個 DAG 作為一個 Job 提交給底層的 TaskScheduler。

2. TaskScheduler 與 Task 提交

TaskScheduler 負責與集群管理器(如 Standalone, YARN, Kubernetes)交互,獲取計算資源(Executor),并將具體的 Task 分發到各個 Executor 上執行。

  • 每個 Stage 會被拆分成多個 Task,Task 的數量通常等于該 Stage 最后一個 RDD 的分區數。
  • ResultTask:最后一個 Stage 中的 Task,它們負責將最終結果返回給 Driver。
  • ShuffleMapTask:非最后一個 Stage 中的 Task,它們負責計算中間結果,并將結果寫入本地磁盤(Shuffle Write),供下游 Stage 的 Task 讀取(Shuffle Read)。

3. Executor 上的執行

在 Executor 進程中,ExecutorBackend 會接收來自 Driver 的 Task,并將其交給 Executor 線程池中的一個線程來執行。

  • Task 的核心邏輯就是執行之前通過代碼生成得到的 Java 字節碼。
  • 它會從數據源(如 HDFS)讀取數據塊(Block),或者從本地磁盤讀取 Shuffle 數據。
  • 執行計算邏輯,處理每一行數據。
  • 對于 ShuffleMapTask,將結果按 Partitioner 規則寫入本地磁盤的 Shuffle 文件。
  • 對于 ResultTask,將最終結果序列化后通過網絡發送回 Driver。

4. 自適應查詢執行(AQE)的動態干預

如果啟用了 AQE(spark.sql.adaptive.enabled=true),那么在執行過程中,Spark 還會進行動態優化:

  • 動態合并 Shuffle Partitions:如果某些 Shuffle 分區的數據量很小,AQE 會在運行時將它們合并,以減少小文件和調度開銷。
  • 動態切換 Join 策略:在 Shuffle Read 階段,如果發現某個分區的數據量遠小于預期,AQE 可能會將原本計劃的 SortMergeJoin 動態切換為更高效的 BroadcastHashJoin。
  • 動態優化 Join 傾斜:自動檢測數據傾斜,并將傾斜的分區進行拆分和特殊處理。

AQE 使得 Spark SQL 的執行計劃不再是靜態的,而是能夠根據實際運行時的數據特征進行自我調整,進一步提升了性能和穩定性。

5. 結果返回與收尾

當所有的 ResultTask 都成功執行完畢,并將結果發送回 Driver 后,Driver 會將這些結果收集起來(對于 collect() 操作)或者寫入到指定的輸出位置(對于 write 操作)。最后,Spark 會清理本次查詢產生的臨時文件、釋放資源,并返回最終的結果或成功狀態。

總結第六階段:Spark Core 的調度系統接管了執行計劃,將其分解為可并行的 Task,在集群中調度執行。Executor 負責具體的計算工作,最終將結果匯聚并返回。AQE 的引入讓這個過程變得更加智能和動態。

結語

從敲下回車鍵提交一條 SQL 語句,到最終看到查詢結果,Spark SQL 完成了一次令人嘆為觀止的“變形記”。這條旅程穿越了 解析(Parsing)、分析(Analysis)、邏輯優化(Logical Optimization)、物理計劃生成(Physical Planning)、執行準備與代碼生成(Preparation & Codegen) 以及 任務調度與執行(Scheduling & Execution) 六大核心階段。

每一個階段都凝聚了 Spark 開發者們對查詢處理、分布式系統和性能優化的深刻理解。Catalyst 優化器的規則驅動架構、Tungsten 引擎的代碼生成技術、以及 AQE 的動態自適應能力,共同構成了 Spark SQL 強大性能的基石。

理解這個全生命周期,就如同掌握了一張 Spark SQL 的“藏寶圖”。它不僅能幫助我們寫出更高效的 SQL,更能讓我們在面對 EXPLAIN 命令輸出的計劃樹時,洞悉其背后的邏輯與意圖,從而成為一名真正駕馭 Spark 的數據工程師。

責任編輯:趙寧寧 來源: 大數據技能圈
相關推薦

2024-06-04 00:01:00

2019-08-18 23:26:25

物聯網操作系統IOT

2016-01-25 13:22:45

SparkSparkSQL數據分析

2015-04-23 08:51:53

2015-08-03 09:33:21

PH程序員一生

2016-08-24 11:13:30

2023-01-10 08:20:55

RocketMQ消息源碼

2021-08-06 22:43:54

中斷架構傳遞

2020-07-09 17:37:47

Linux網絡包中斷

2018-01-18 09:05:05

存儲數據包分層

2025-08-25 02:00:00

2010-04-13 16:57:01

2012-12-04 10:08:16

2025-06-04 08:20:30

2018-01-05 12:42:01

Lisa電腦蘋果Mac

2020-11-29 17:08:50

程序員IT

2015-03-24 13:39:08

IE

2025-05-19 08:43:00

2021-09-28 08:05:56

黑客網絡安全網絡攻擊

2021-08-30 05:47:12

MySQL SQL 語句數據庫
點贊
收藏

51CTO技術棧公眾號

91久久精品www人人做人人爽| 亚洲国产精品一区二区三区| 亚洲精品白虎| 亚洲天堂777| 精品在线99| 欧美午夜免费电影| 欧美xxxx吸乳| 丰满岳乱妇国产精品一区| 精品999网站| 亚洲欧美一区二区三区久久| 国产真实乱子伦| 成人av一区| 国产精品中文有码| 91国内精品久久| 永久免费毛片在线观看| 精品国产一级| 欧美午夜美女看片| 在线国产伦理一区| 手机福利小视频在线播放| 日韩国产欧美在线播放| 亚洲午夜电影在线| 亚洲japanese制服美女| 在线观看国产亚洲| 欧美一区电影| 精品国产乱码久久久久久蜜臀| 欧美国产亚洲一区| 精品99又大又爽又硬少妇毛片 | 亚洲精品视频中文字幕| 伊人成色综合网| 久草免费在线| www日韩大片| 亚洲资源在线看| 波多野结衣视频在线观看| 一区二区三区毛片免费| 日韩精品一区二区视频| 天天综合天天添夜夜添狠狠添| 成人在线免费观看黄色| 中文字幕成人av| 国产三区二区一区久久| 中文字幕在线观看欧美| 一本色道久久综合| 欧美wwwxxxx| 色欲AV无码精品一区二区久久| 香蕉成人app| 欧美日韩专区在线| 99999精品视频| 成人爽a毛片免费啪啪动漫| 中日韩av电影| 免费一区二区三区| 免费观看a视频| 美女视频免费一区| 日本精品久久电影| 国产精品第5页| 久久激情视频| 国产v综合v亚洲欧美久久| 国产污视频在线看| 欧美精品网站| 欧美成人一二三| 免费精品在线视频| 三上亚洲一区二区| 正在播放欧美视频| 小早川怜子久久精品中文字幕| jizz性欧美23| 精品少妇一区二区三区免费观看 | 女厕盗摄一区二区三区| 一区二区三区高清在线| 日韩 欧美 自拍| 91在线高清| 欧美国产日产图区| 欧美一区二区三区在线免费观看| 丰满熟妇人妻中文字幕| 国产成人一区在线| 147欧美人体大胆444| 国产乱淫av免费| 国产在线精品一区二区夜色| 91久久久精品| 中文字幕永久在线视频| 另类小说视频一区二区| 国产精品夜间视频香蕉| 亚洲影院在线播放| 久久永久免费| 国产精品视频yy9099| 中文在线资源天堂| 亚洲一级在线| 日韩av色在线| 国内av在线播放| 久久99精品网久久| 成人www视频在线观看| 国产三级小视频| 福利电影一区二区三区| 精选一区二区三区四区五区| 亚洲av毛片成人精品| 久久综合视频网| 日本中文不卡| 欧美高清视频| 亚洲综合一区二区三区| 日韩五码在线观看| 在线观看的黄色| 欧美性极品少妇| 亚洲综合伊人久久| 第四色中文综合网| 亚洲精品网站在线播放gif| 久久久久亚洲av无码a片| 色琪琪久久se色| 欧美黑人又粗大| 免费的毛片视频| 精品在线一区二区三区| 成人黄色在线观看| 亚洲欧美激情国产综合久久久| 99在线精品一区二区三区| 国产三区二区一区久久| 高清国产福利在线观看| 一区二区三区四区高清精品免费观看| 欧美日韩激情四射| 蜜臀国产一区| 欧美一级理论片| 精品无码一区二区三区 | 精品国产免费视频| 在线免费看黄视频| 欧美搞黄网站| 国产激情综合五月久久| 国产又大又粗又硬| 99久久精品国产导航| 日韩av电影免费观看| 四虎亚洲精品| 欧美日韩亚洲不卡| 女同性恋一区二区三区| 色婷婷一区二区三区| 久久久亚洲影院| 亚洲字幕av一区二区三区四区| 成人精品亚洲人成在线| 欧美亚洲一级二级| 高清电影在线观看免费| 欧美日精品一区视频| 日b视频在线观看| 久久精品一区二区不卡| 青青草成人在线| 精品国产免费无码久久久| 日本一区二区三区视频视频| 99精品视频播放| 日本在线中文字幕一区| 欧美激情视频一区| 97超碰人人草| 国产精品美女久久久久aⅴ| 日本精品一区在线观看| www.神马久久| 欧美精品在线第一页| 在线观看国产成人| 久久综合九色综合欧美就去吻| 无码人妻精品一区二区蜜桃网站| 欧美另类激情| 日韩精品视频在线播放| 日本三级理论片| 国产精品18久久久久| 伊人狠狠色丁香综合尤物| 欧美香蕉视频| 亚洲网站在线播放| 国产专区第一页| 99精品视频一区二区| 日韩av中文字幕第一页| 日本一区精品视频| 欧美xxxx18国产| 午夜精品无码一区二区三区| 中文字幕中文字幕在线一区 | av在线播放网址| 欧美视频四区| 国产精品大全| 成人福利影视| 亚洲娇小xxxx欧美娇小| 国产 欧美 日韩 在线| 大美女一区二区三区| www.欧美黄色| 精品精品国产毛片在线看| 美女视频黄免费的亚洲男人天堂| 一区二区三区免费在线视频| 中文字幕一区二区三区蜜月| 天天综合网久久| 天天操夜夜操国产精品| 91精品国产综合久久香蕉922| 在线看av的网址| 欧美精品在线一区二区| 强制高潮抽搐sm调教高h| 国产制服丝袜一区| 老司机激情视频| 99a精品视频在线观看| 97视频国产在线| 免费一级在线观看播放网址| 色天使色偷偷av一区二区| 成人性生交大片免费看无遮挡aⅴ| 欧美aⅴ一区二区三区视频| 一区二区三区四区| 日本在线一区二区三区| 久久久久久九九九| 日韩a在线观看| 欧美性一二三区| 日韩在线观看视频一区二区| 成人免费看的视频| 成人观看免费完整观看| 国产二区精品| 国产欧美日韩伦理| 日韩精选视频| 欧美美女操人视频| 黄色在线网站| 欧美三级乱人伦电影| 欧美成人精品欧美一级| 99久久久国产精品| www.国产视频.com| 校园激情久久| 美日韩精品免费视频| 亚洲自偷自拍熟女另类| 99久久国产综合精品成人影院| 国产另类自拍| 精品国产不卡一区二区| 国产精品久久久久久久久久久久久| 色噜噜狠狠狠综合欧洲色8| 在线观看亚洲视频| 亚洲av片一区二区三区| 日韩欧美色电影| 91片黄在线观看喷潮| 韩曰欧美视频免费观看| 久久国产一级片| 亚洲欧洲三级电影| 免费成人深夜天涯网站| 91在线porny国产在线看| 久久久久99人妻一区二区三区| 美女视频网站黄色亚洲| 久草综合在线观看| 一本久道久久综合婷婷鲸鱼| 久久男人资源站| 欧美一区免费| 国产在线拍揄自揄拍无码| 日韩中文字幕高清在线观看| 日韩一区不卡| 成人网18免费网站| 视频一区二区在线| 欧美日韩在线二区| 日韩jizzz| 欧美裸体在线版观看完整版| 欧美一区二区三区四区在线观看地址 | 欧美日韩高清丝袜| 久久一区二区三区四区| 大黑人交xxx极品hd| av在线不卡网| 水蜜桃av无码| 91亚洲精品乱码久久久久久蜜桃| 亚洲午夜久久久久久久久| 国产精品一卡二卡在线观看| 一级网站在线观看| 国产成人综合网站| 精品人妻伦一二三区久| 成人h动漫精品一区二区| 日本性生活一级片| 成人久久久精品乱码一区二区三区| 国产人成视频在线观看| 成人久久18免费网站麻豆| 亚洲av无码一区二区三区网址| av在线不卡观看免费观看| 久久久亚洲av波多野结衣| 久久久精品蜜桃| 免费看91的网站| 国产精品国产自产拍高清av王其| 黄色录像免费观看| 一区二区三区高清| 精品欧美一区二区三区免费观看| 色综合久久综合中文综合网| 国产91av在线播放| 欧美一区二区三区白人| 蜜桃久久一区二区三区| 日韩精品在线免费观看视频| 黄色软件在线| 久久夜色精品国产欧美乱| 牛牛精品在线视频| 欧洲中文字幕国产精品| 久久天天久久| 动漫一区二区在线| 九九免费精品视频在线观看| 亚洲激情一区二区三区| 激情综合视频| 天天爱天天操天天干| 国产精品自拍网站| 欧美 变态 另类 人妖| 国产精品高潮呻吟久久| 国产亚洲第一页| 91成人国产精品| 国产精品视频一二区| 亚洲激情国产精品| 亚洲精品传媒| 91豆花精品一区| 日韩精品一级毛片在线播放| 国产亚洲欧美一区二区| 日韩欧美三级| 国产91xxx| 韩国午夜理伦三级不卡影院| 国产ts丝袜人妖系列视频| 成人欧美一区二区三区视频网页| 日本少妇激情视频| 欧美日韩电影一区| 丝袜视频国产在线播放| 日韩在线观看免费高清| 黄在线观看免费网站ktv| 91精品国产综合久久香蕉922| 欧美亚洲国产日韩| 男女激烈动态图| 久久精品天堂| 人妻无码中文久久久久专区| 中文字幕一区二区视频| 波多野结衣不卡| 亚洲成人久久久| 看女生喷水的网站在线观看| 日韩美女中文字幕| 成人av动漫| 中文字幕第50页| 麻豆成人免费电影| 30一40一50老女人毛片| 亚洲午夜羞羞片| 国产高清第一页| 久久视频在线视频| www.26天天久久天堂| 久久综合一区二区三区| 在线观看的日韩av| 不卡的一区二区| 1024亚洲合集| 一区二区视频免费观看| 亚洲人成在线一二| 欧美久久天堂| 精品久久精品久久| 亚洲精选在线| 男人女人拔萝卜视频| 亚洲天天做日日做天天谢日日欢| 欧美视频xxxx| 中文字幕av一区二区| 在线一区视频观看| 天堂√在线观看一区二区| 性欧美videos另类喷潮| 亚洲天堂网一区二区| 懂色aⅴ精品一区二区三区蜜月| 亚洲精品视频专区| 久久久久久久久久婷婷| 亚洲综合网站| 日韩一级免费看| 成人免费毛片a| www..com国产| 亚洲男人第一网站| 欧美黑人粗大| 日韩少妇中文字幕| 久久99精品一区二区三区三区| 精品亚洲乱码一区二区| 在线综合亚洲欧美在线视频| 国精产品一区| www日韩av| 99精品视频免费全部在线| 91av在线免费| 一本色道久久综合亚洲91| 激情小视频在线观看| 国产精品视频专区| 婷婷成人基地| 中文字幕18页| 亚洲不卡一区二区三区| 三级在线播放| 国产精品美女网站| 亚洲女同一区| 插我舔内射18免费视频| 一本大道久久精品懂色aⅴ| 国产福利在线看| 亚洲已满18点击进入在线看片| 狠久久av成人天堂| 日本黄色网址大全| 欧美日本不卡视频| 国产丝袜在线播放| 欧美日韩一区在线观看视频| 日本不卡视频在线观看| 丁香花五月激情| 日韩成人激情在线| 91亚洲精品| 青青青在线观看视频| 久久影院电视剧免费观看| 国产一区二区小视频| 欧美国产在线电影| 国产成人久久| 被黑人猛躁10次高潮视频| 无吗不卡中文字幕| bbbbbbbbbbb在线视频| 成人午夜电影免费在线观看| 国产农村妇女精品一区二区| 亚洲 欧美 国产 另类| 亚洲成人激情在线观看| 日韩在线观看不卡| 日本国产中文字幕| 日本一区二区三区dvd视频在线| 国产日韩一级片| 国产成人福利视频| 欧美精品aa| 国产又粗又黄又猛| 亚洲精品国产精品国自产在线 | √最新版天堂资源网在线| 日韩在线观看电影完整版高清免费| 国产一区二区日韩精品| 亚洲成人av影片| 欧美日韩成人网| 日本高清免费电影一区|