一條數據HBase之旅,簡明HBase入門教程-Flush與Compaction

      網友投稿 1009 2022-05-29

      在閱讀本文之前,希望你已經閱讀過:

      一條數據的HBase之旅,簡明HBase入門教程-開篇

      一條數據的HBase之旅,簡明HBase入門教程-Write全流程

      前文回顧

      在閱讀本文之前,希望你已經閱讀過:

      一條數據的HBase之旅,簡明HBase入門教程-開篇

      一條數據的HBase之旅,簡明HBase入門教程-Write全流程

      前文回顧

      前文《一條數據的HBase之旅,簡明HBase入門教程:Write全流程》主要講了如下內容:

      1. 介紹HBase寫數據可選接口以及接口定義

      2. 通過一個樣例,介紹了RowKey定義以及列定義的一些方法,以及如何組裝Put對象

      3. 數據路由,數據分發、打包,以及Client通過RPC發送寫數據請求至RegionServer

      4. RegionServer接收數據以后,將數據寫到每一個Region中。寫數據流程先寫WAL再寫MemStore,這里展開了一些技術細節

      5. 簡單介紹了HBase權限控制模型

      至此,數據已經被寫入WAL與MemStore,可以說數據已經被成功寫到HBase中了。事實上,上篇文章講到的Flush流程是"簡化"后的流程,在2.0版本中,這里已經變的更加復雜。

      本文思路

      1. 回顧Flush&Compaction舊流程

      2. 介紹2.0版本的In-memory Flush & Compaction特性

      3. 介紹Compaction要解決的本質問題是什么

      4. 在選擇合理的Compaction策略之前,用戶應該從哪些維度調研自己的業務模型

      5. HBase現有的Compaction策略有哪些,各自的適用場景是什么

      6. Compaction如何選擇待合并的文件

      7. 關于Compaction更多的一些思考

      8. 本計劃列出所有的價值問題單,但篇幅文字受限。可以點擊"閱讀原文"鏈接參考文末References信息部分

      Flush&Compaction

      這是1.X系列版本以及更早版本中的Flush&Compaction行為:

      MemStore中的數據,達到一定的閾值,被Flush成HDFS中的HFile文件。

      但隨著Flush次數的不斷增多,HFile的文件數量也會不斷增多,那這會帶來什么影響?在HBaseCon 2013大會上,Hontonworks的名為《Compaction Improvements in Apache HBase》的演講主題中,提到了他們測試過的隨著HFile的數量的不斷增多對讀取時延帶來的影響:

      盡管關于Read的流程在后面文章中才會講到,但下圖可以幫助我們簡單的理解這其中的原因:

      圖中說明了從一個文件中指定RowKey為“66660000431^201803011300”的記錄,以及從兩個文件中讀取該行記錄的區別,明顯,從兩個文件中讀取,將導致更多的IOPS。這就是HBase Compaction存在的一大初衷,Compaction可以將一些HFile文件合并成較大的HFile文件,也可以把所有的HFile文件合并成一個大的HFile文件,這個過程可以理解為:將多個HFile的“交錯無序狀態”,變成單個HFile的“有序狀態”,降低讀取時延。小范圍的HFile文件合并,稱之為Minor Compaction,一個列族中將所有的HFile文件合并,稱之為Major Compaction。除了文件合并范圍的不同之外,Major Compaction還會清理一些TTL過期/版本過舊以及被標記刪除的數據。下圖直觀描述了舊版本中的Flush與Compaction流程:

      Flush

      在2.0版本中,Flush的行為發生了變化,默認的Flush,僅僅是將正在寫的MemStore中的數據歸檔成一個不可變的Segment,而這個Segment依然處于內存中,這就是2.0的新特性:In-memory Flush and Compaction ,而且該特性在2.0版本中已被默認啟用(系統表除外)。

      上文中簡單提到了MemStore的核心是一個CellSet,但到了這里,我們可以發現,MemStore的結構其實更加復雜:MemStore由一個可寫的Segment,以及一個或多個不可寫的Segments構成。

      MemStore中的數據先Flush成一個Immutable的Segment,多個Immutable Segments可以在內存中進行Compaction,當達到一定閾值以后才將內存中的數據持久化成HDFS中的HFile文件。

      看到這里,可能會有這樣的疑問:這樣做的好處是什么?為何不直接調大MemStore的Flush Size?

      如果MemStore中的數據被直接Flush成HFile,而多個HFile又被Compaction合并成了一個大HFile,隨著一次次Compaction發生以后,一條數據往往被重寫了多次,這帶來顯著的IO放大問題,另外,頻繁的Compaction對IO資源的搶占,其實也是導致HBase查詢時延大毛刺的罪魁禍首之一。而In-memory Flush and Compaction特性可以有力改善這一問題。

      那為何不干脆調大MemStore的大小?這里的本質原因在于,ConcurrentSkipListMap在存儲的數據量達到一定大小以后,寫入性能將會出現顯著的惡化。

      在融入了In-Memory Flush and Compaction特性之后,Flush與Compaction的整體流程演變為:

      關于Flush執行的策略

      一個Region中是否執行Flush,原來的默認行為是通過計算Region中所有Column Family的整體大小,如果超過了一個閾值,則這個Region中所有的Column Family都會被執行Flush。

      2.0版本中合入了0.89-fb版本中的一個特性:HBASE-10201/HBASE-3149:Make flush decisions per column family。

      2.0版本中默認啟用的Flush策略為FlushAllLargeStoresPolicy,也就是說,這個策略使得每一次只Flush超出閾值大小的Column Family,如果都未超出大小,則所有的Column Family都會被Flush。

      改動之前,一個Region中所有的Column Family的Flush都是同步的,雖然容易導致大量的小HFile,但也有好處,尤其是對于WAL文件的快速老化,避免導致過多的WAL文件。而如果這些Column Family的Flush不同步以后,可能會導致過多的WAL文件(過多的WAL文件會觸發一些擁有老數據的Column Family執行Flush),這里需要結合業務場景實測一下。如果每一行數據的寫入,多個Column Family的數據都是同步寫入的,那么,這個默認策略可能并不合適。

      Compaction

      在這個章節,我們繼續探討HBase Compaction,主要是想理清這其中所涉及的一些"道"與"術":

      "道":HBase Compaction要解決的本質問題是什么?

      "術":針對HBase Compaction的問題本質,HBase有哪些具體的改進/優秀實踐?

      Compaction會導致寫入放大

      我們先來看看Facebook在Fast 14提交的論文《Analysis of HDFS Under HBase: A Facebook Messages Case Study》所提供的一些測試結論(在我之前寫的一篇文章《從HBase中移除WAL?3D XPoint技術帶來的變革》已經提到過):

      在Facebook Messages業務系統中,業務讀寫比為99:1,而最終反映到磁盤中,讀寫比卻變為了36:64。WAL,HDFS Replication,Compaction以及Caching,共同導致了磁盤寫IO的顯著放大。

      雖然距離論文發表已經過去幾年,但問題本質并未發生明顯變化。尤其是在一個寫多讀少的應用中,因Compaction帶來的寫放大(Write Amplification)尤其明顯,下圖有助于你理解寫放大的原因:

      隨著不斷的執行Minor Compaction以及Major Compaction,可以看到,這條數據被反復讀取/寫入了多次,這是導致寫放大的一個關鍵原因,這里的寫放大,涉及到網絡IO與磁盤IO,因為數據在HDFS中默認有三個副本。

      Compaction的本質

      我們先來思考一下,在集群中執行Compaction,本質是為了什么?容易想到如下兩點原因:

      減少HFile文件數量,減少文件句柄數量,降低讀取時延

      Major Compaction可以幫助清理集群中不再需要的數據(過期數據,被標記刪除的數據,版本數溢出的數據)

      很多HBase用戶在集群中關閉了自動Major Compaction,為了降低Compaction對IO資源的搶占,但出于清理數據的需要,又不得不在一些非繁忙時段手動觸發Major Compaction,這樣既可以有效降低存儲空間,也可以有效降低讀取時延。

      而關于如何合理的執行Compaction,我們需要結合業務數據特點,不斷的權衡如下兩點:

      避免因文件數不斷增多導致讀取時延出現明顯增大

      合理控制寫入放大

      HFile文件數量一定會加大讀取時延嗎?也不一定,因為這里與RowKey的分布特點有關。我們通過列舉幾個典型場景來說明一下不同的RowKey分布,為了方便理解,我們先將一個Region的RowKey Range進一步劃分成多個Sub-Range,來說明不同的RowKey分布是如何填充這些Sub-Ranges的:

      下面的不同行代表不同的時間點(我們使用不斷遞增的時間點T1,T2,T3,T4,來描述隨著時間演變而產生的RowKey分布變化):

      分布A

      在這個Region中,某一個時間段只填充與之有關的一個Sub-Range,RowKey隨著時間的演進,整體呈現遞增趨勢。但在填充每一個Sub-Range的時候,又可能有如下兩種情形(以Sub-Range1的填充為例,為了區別于T1~T4,我們使用了另外4個時間點Ta~Td):

      分布A-a

      這種情形下,RowKey是嚴格遞增的。

      分布A-b

      這種情形下,RowKey在Sub-Range1的范圍內是完全隨機的。

      下面則是一種隨機RowKey的場景,也就是說,每一個時間點產生的數據都是隨機分布在所有的Sub-Range中的:

      分布B

      對于分布A-a來說,不同的HFile文件在RowKey Range(該HFile文件所涉及到的最小數據RowKey與最大數據RowKey構成的RowKey區間)上并不會產生重疊,如果要基于RowKey讀取一行數據,只需要查看一個文件即可,而不需要查看所有的文件,這里完全可以通過優化讀取邏輯來實現。即使不做Compaction,對于讀取時延的影響并不明顯(當然,從降低文件句柄數量,降低HDFS側的小文件數量的維度來考慮,Compaction還是有意義的)。

      對于分布B來說,如果有多個HFiles文件,如果想基于RowKey讀取一行數據,則需要查看多個文件,因為不同的HFile文件的RowKey Range可能是重疊的,此時,Compaction對于降低讀取時延是非常必要的。

      調研自己的業務模型

      在選擇一個合理的Compaction策略之前,應該首先調研自己的業務模型,下面是一些參考維度:

      1.寫入數據類型/單條記錄大小

      是否是KB甚至小于KB級別的小記錄?還是MB甚至更大的圖片/小文件數據?

      2.業務讀寫比例

      3.隨著時間的不斷推移,RowKey的數據分布呈現什么特點?

      4.數據在讀寫上是否有冷熱特點?

      是否只讀取/改寫最近產生的數據?

      5.是否有頻繁的更新與刪除?

      6.數據是否有TTL限制?

      7.是否有較長時間段的業務高峰期和業務低谷期?

      幾種Compaction策略

      HBase中有幾種典型的Compaction策略,來應對幾類典型的業務場景:

      它的設計初衷是,Major Compaction占用大量的IO資源,所以很多HBase用戶關閉了自動觸發的Major Compaction,改為手動觸發,因為Major Compaction依然會被用來清理一些不再需要的數據。

      隨著時間的推移,Major Compaction要合并的文件總Size越來越大,但事實上,真的有必要每一次都將所有的文件合并成一個大的HFile文件嗎?尤其是,不斷的將一些較老的數據和最新的數據合并在一起,對于一些業務場景而言根本就是不必要的。

      因此,它的設計思路為:

      將一個Region劃分為多個Stripes(可以理解為Sub-Regions),Compaction可以控制在Stripe(Sub-Region)層面發生,而不是整個Region級別,這樣可以有效降低Compaction對IO資源的占用。

      那為何不直接通過設置更多的Region數量來解決這個問題?更多的Region意味著會加大HBase集群的負擔,尤其是加重Region Assignment流程的負擔,另外,Region增多,MemStore占用的總體內存變大,而在實際內存無法變大的情況下,只會使得Flush更早被觸發,Flush的質量變差。

      新Flush產生的HFile文件,先放到一個稱之為L0的區域,L0中Key Range是Region的完整Key Range,當對L0中的文件執行Compaction時,再將Compaction的結果輸出到對應的Stripe中:

      HBase Document中這么描述Stripe Compaction的適用場景:

      Large regions. You can get the positive effects of smaller regions without additional overhead for MemStore and region management overhead.

      一條數據的HBase之旅,簡明HBase入門教程-Flush與Compaction

      Non-uniform keys, such as time dimension in a key. Only the stripes receiving the new keys will need to compact. Old data will not compact as often, if at all

      在我們上面列舉的幾種RowKey分布的場景中,分布A(含分布A-a,分布A-b)就是特別適合Stripe Compaction的場景,因為僅僅新寫入數據的Sub-Range合并即可,而對于老的Sub-Range中所關聯的數據文件,根本沒有必要再執行Compaction。

      Stripe Compaction關于Sub-Region的劃分,其實可以加速Region Split操作,因為有的情形下,直接將所有的Stripes分成兩部分即可。

      我們假設有這樣一種場景:

      新數據的產生與時間有關,而且無更新、刪除場景

      讀取時通常會指定時間范圍,而且通常讀取最近的數據

      在這種情形下,如果將老數據與新數據合并在一起,那么,指定時間范圍讀取時,就需要掃描一些不必要的老數據:因為合并后,數據按RowKey排序,RowKey排序未必與按照數據產生的時間排序一致,這使得新老數據交叉存放,而掃描時老數據也會被讀到。

      這是Date Tiered Compaction的設計初衷,Date Tiered Compaction在選擇文件執行合并的時候,會感知Date信息,使得Compaction時,不需要將新老數據合并在一起。這對于基于Time Range的Scan操作是非常有利的,因為與本次Scan不相關的文件可以直接忽略。

      什么是Time Range Based Scan?

      HBase的Scan查詢,通常都是指定RowKey的Range的,但HBase也支持這樣一類查詢:通過指定一個起始的Timestamp,掃描出所有的落在此Timestamp Range中的所有數據,這就是Time Range Based Scan。

      可以參考接口:Scan#setTimeRange(long minStamp, long maxStamp)

      Time Range Based Scan可以用來實現針對HBase數據表的增量數據導出/備份能力。

      容易想到,時序數據就是最典型的一個適用場景。但需要注意的是,如下場景并不適合使用Date Tiered Compaction:

      讀取時通常不指定時間范圍

      涉及頻繁的更新與刪除

      寫入時主動指定時間戳,而且可能會指定一個未來的時間戳

      基于bulk load加載數據,而且加載的數據可能會在時間范圍上重疊

      能否使用HBase來存儲MB級別的Blob(如圖片之類的小文件)數據?

      這是很多應用面臨的一個基礎問題,因為這些數據相比于普通的存儲于HBase中的結構化/半結構化數據顯得過大了,而如果將這些數據直接存儲成HDFS中的獨立文件,會加重HDFS的NameNode的負擔,再者,如何索引這些小文件也是一個極大的痛點。

      當然,也有人采用了這樣的方式:將多個小文件合并成HDFS上的一個大文件,這樣子可以減輕HDFS的NameNode的負擔,但需要維護每一個小文件的索引信息(文件名以及每一個小文件的偏移信息)。

      如果存這些這些小文件時,像普通的結構化數據/半結構化數據一樣,直接寫到HBase中,會有什么問題?這樣子多條數據可以被合并在較大的HFile文件中,減輕了NameNode的負擔,同時解決了快速索引的問題。但基于前面的內容,我們已經清楚知道了Compaction帶來的寫放大問題。試想一下,數MB級別的Blob數據,被反復多次合并以后,會帶來什么樣的影響?這對IO資源的搶占將會更加嚴重。

      因此,HBase的MOB特性的設計思想為:將Blob數據與描述Blob的元數據分離存儲,Blob元數據采用正常的HBase的數據存儲方式,而Blob數據存儲在額外的MOB文件中,但在Blob元數據行中,存儲了這個MOB文件的路徑信息。MOB文件本質還是一個HFile文件,但這種HFile文件不參與HBase正常的Compaction流程。僅僅合并Blob元數據信息,寫IO放大的問題就得到了有效的緩解。

      MOB Compaction也主要是針對MOB特性而存在的,這里涉及到數據在MOB文件與普通的HFile文件之間的一些流動,尤其是MOB的閾值大小發生變更的時候(即當一個列超過預設的配置值時,才被認定為MOB),本文暫不展開過多的細節。

      在HBase社區中,MOB特性(HBASE-11339)一直在一個獨立的特性分支中開發的,直到2.0版本才最終合入進來(華為的FusionInsight的HBase版本中,以及華為云的CloudTable的HBase版本中,都包含了完整的MOB特性)。

      就是默認的Compaction行為,2.0版本和舊版本中的行為沒有什么明顯變化。

      所謂的Default Compaction,具有更廣泛的適用場景,它在選擇待合并的文件時是在整個Region級別進行選擇的,所以往往意味著更高的寫IO放大。

      在實際應用中,應該結合自己的應用場景選擇合適的Compaction策略,如果前幾種策略能夠匹配自己的應用場景,那么應該是優選的(這幾個策略的質量狀態如何尚不好判斷,建議結合業務場景進行實測觀察),否則應該選擇Default Compaction。

      如果上述幾種Compaction策略都無法很好的滿足業務需求的話,用戶還可以自定義Compaction策略,因為HBase已經具備良好的Compaction插件化機制。

      如何選擇待合并的文件

      無論哪種Compaction策略,都涉及一個至關重要的問題:

      “如何選擇待合并的文件列表”

      Major Compaction是為了合并所有的文件,所以,不存在如何選擇文件的問題。

      選擇文件時,應該考慮如下幾個原則:

      1. 選擇合理的文件數量

      如果一次選擇了過多的文件: 對于讀取時延的影響時間范圍可能比較長,但Compaction產生的寫IO總量較低。

      如果一次選擇了較少的文件: 可能導致過于頻繁的合并,導致寫IO被嚴重放大。

      2. 選擇的文件在時間產生順序上應該是連續的,即應該遵循HBase的Sequence ID的順序

      這樣子,HBase的讀取時可以做一些針對性的優化,例如,如果在最新的文件中已經讀到了一個RowKey的記錄,那就沒有必要再去看較老的文件。

      在HBase中,有一種"歷史悠久"的選擇文件的策略,名為ExploringCompactionPolicy,即使在最新的版本中,該策略依然在協助發揮作用,它選擇文件的原理如下(下面的圖片和例子源自HBASE-6371):

      將HFile文件從老到新的順序排序,通常,舊文件較大,因為舊文件更可能是被合并過的文件。

      每一次需要從文件隊列中選取一個合適的開始文件位置,通過如下算法:

      f[start].size <= ratio * (f[start+1].size + ….. + f[end - 1].size)

      找到一個滿足條件的開始位置,以及滿足條件的文件組合。

      舉例:假設ratio = 1.0:

      1. 如果候選文件[1200, 500, 150, 80, 50, 25, 12, 10],則最終選擇出來的需要合并的文件列表為[150, 80, 50, 25, 12, 10]

      2. 如果候選文件[1200, 500, 150, 80, 25, 10],則選不出任何文件。

      每一次Minor Compaction所涉及的文件數目都有上下限。如果超過了,會減去一些文件,如果小于下限,則忽略此次Compaction操作。待選擇的文件也有大小限制,如果超出了預設大小,就不會參與Compaction。這里可以理解為:Minor Compaction的初衷只是為了合并較小的文件。另外,BulkLoad產生的文件,在Minor Compaction階段會被忽略。

      RatioBasedCompactionPolicy曾一度作為主力文件選擇算法沿用了較長的時間,后來,出現了一種ExploringCompactionPolicy,它的設計初衷為:

      RatioBasedCompactionPolicy雖然選擇出來了一種文件組合,但其實這個文件組合并不是最優的,因此它期望在所有的候選組合中,選擇一組性價比更高的組合,性價比更高的定義為:文件數相對較多,而整體大小卻較小。這樣,即可以有效降低HFiles數量,又可能有效控制Compaction所占用的IO總量。

      也可以這么理解它們之間的一些不同:

      RatioBasedCompactionPolicy僅僅是在自定義的規則之下找到第一個"可行解"即可,而ExploringCompactionPolicy卻在盡可能的去尋求一種自定義評價標準中的"最優解"。

      另外,需要說明的一點:ExploringCompactionPolicy在選擇候選組合時,正是采用了RatioBasedCompactionPolicy中的文件選擇算法。

      更多的一些思考

      Compaction會導致寫入放大,前面的內容中已經反復提及了很多次。在實際應用中,你是否關注過Compaction對于查詢毛刺的影響(查詢時延總是會出現偶發性的陡增)?

      關于Compaction的參數調優,我們可能看到過這樣的一些建議:盡可能的減少每一次Compaction的文件數量,目的是為了減短每一次Compaction的執行時間。這就好比,采用Java GC算法中的CMS算法時,每一次回收少量的無引用對象,盡管GC被觸發的頻次會增大,但可以有效降低Full GC的發生次數和發生時間。

      但在實踐中,這可能并不是一個合理的建議,例如,HBase默認的觸發Minor Compaction的最小文件數量為3,但事實上,對于大多數場景而言,這可能是一個非常不合理的默認值,在我們的測試中,將最小文件數加大到10個,我們發現對于整體的吞吐量以及查詢毛刺,都有極大的改進,所以,這里的建議為:Minor Compaction的文件數量應該要結合實際業務場景設置合理的值。另外,在實踐中,合理的限制Compaction資源的占用也是非常關鍵的,如Compaction的并發執行度,以及Compaction的吞吐量以及網絡帶寬占用等等。

      另外,需要關注到的一點:Compaction會影響Block Cache,因為HFile文件發生合并以后,舊HFile文件所關聯的被Cache的Block將會失效。這也會影響到讀取時延。HBase社區有一個問題單(HBASE-20045),試圖在Compaction時緩存一些最近的Blocks。

      在Facebook的那篇論文中,還有一個比較有意思的實踐:

      他們將Compaction下推到存儲層(HDFS)執行,這樣,每一個DateNode在本地合并自己的文件,這樣可以降低一半以上的網絡IO請求,但本地磁盤IO請求會增大,這事實上是用磁盤IO資源來換取昂貴的網絡IO資源。在我們自己的測試中也發現,將Compaction下推到HDFS側執行,能夠明顯的優化讀寫時延毛刺問題。

      總結

      本文基于2.0版本闡述了Flush與Compaction流程,講述了Compaction所面臨的本質問題,介紹了HBase現有的幾種Compaction策略以及各自的適用場景,更多是從原理層面展開的,并沒有過多講述如何調整參數的實際建議,唯一的建議為:請一定要結合實際的業務場景,選擇合理的Compaction策略,通過不斷的測試和觀察,選擇合理的配置,何謂合理?可以觀察如下幾點:

      寫入吞吐量能否滿足要求。隨著時間的推移,寫入吞吐量是否會不斷降低?

      讀取時延能否滿足要求。隨著時間的推移,讀取時延是否出現明顯的增大?

      觀察過程中,建議不斷的統計分析Compaction產生的IO總量,以及隨著時間的變化趨勢。2.0版本中盡管增加了一些與Compaction相關的Metrics信息,但關于Compaction IO總量的統計依然是非常不充分的,這一點可以自己定制實現,如果你有興趣,也完全可以貢獻給社區。

      本文轉載自微信公眾號【NoSql漫談】。

      NoSQL數據庫

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:虛擬機環境搭建之vagrant
      下一篇:MongoDB數據庫運維工具的使用
      相關文章
      亚洲日韩VA无码中文字幕| 国产亚洲午夜高清国产拍精品 | www.亚洲精品| 亚洲图片一区二区| 国产成人亚洲精品91专区高清| 亚洲一级高清在线中文字幕| 亚洲av无码成h人动漫无遮挡 | 亚洲日韩精品国产3区| 亚洲第一页中文字幕| 麻豆亚洲AV永久无码精品久久| 亚洲成a人片77777kkkk| 亚洲熟女一区二区三区| 中文字幕日韩亚洲| 亚洲人成亚洲人成在线观看 | 激情亚洲一区国产精品| 亚洲一卡2卡4卡5卡6卡在线99| 亚洲春色另类小说| 亚洲国产精品成人精品软件| 亚洲香蕉免费有线视频| 亚洲AV乱码久久精品蜜桃| 亚洲AV日韩精品久久久久| 亚洲AV无码国产丝袜在线观看| 亚洲AV无码一区二区三区系列 | 亚洲国产午夜福利在线播放| 亚洲国产成人AV网站| 亚洲精品永久在线观看| 亚洲 欧洲 视频 伦小说| 亚洲日韩AV无码一区二区三区人| 亚洲色欲色欱wwW在线| 亚洲剧情在线观看| 亚洲欧洲精品成人久久曰| 亚洲AV无码成人网站在线观看| 久久国产亚洲电影天堂| 图图资源网亚洲综合网站| 国产性爱在线观看亚洲黄色一级片| 亚洲欧洲日本在线观看 | 亚洲成AV人片天堂网无码| 精品亚洲成a人片在线观看少妇| 亚洲性69影院在线观看| 亚洲欧洲日韩国产一区二区三区 | 久久久无码精品亚洲日韩蜜桃 |