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

掌握它才說明你真正懂Elasticsearch

原創
開發 架構 開發工具
Elasticsearch 基于 Lucene,隱藏其復雜性,并提供簡單易用的 Restful API接口、Java API 接口。所以理解 ES 的關鍵在于理解 Lucene 的基本原理。

【51CTO.com原創稿件】Elasticsearch 基于 Lucene,隱藏其復雜性,并提供簡單易用的 Restful API接口、Java API 接口。所以理解 ES 的關鍵在于理解 Lucene 的基本原理。

Lucene 簡介

Lucene 是一種高性能、可伸縮的信息搜索(IR)庫,在 2000 年開源,最初由鼎鼎大名的 Doug Cutting 開發,是基于 Java 實現的高性能的開源項目。

Lucene 采用了基于倒排表的設計原理,可以非常高效地實現文本查找,在底層采用了分段的存儲模式,使它在讀寫時幾乎完全避免了鎖的出現,大大提升了讀寫性能。

核心模塊

Lucene 的寫流程和讀流程如下圖所示:

 

圖 1:Lucene 的寫流程和讀流程

其中,虛線箭頭(a、b、c、d)表示寫索引的主要過程,實線箭頭(1-9)表示查詢的主要過程。

Lucene 中的主要模塊及模塊說明如下:

  • analysis:主要負責詞法分析及語言處理,也就是我們常說的分詞,通過該模塊可最終形成存儲或者搜索的最小單元 Term。
  • index 模塊:主要負責索引的創建工作。
  • store 模塊:主要負責索引的讀寫,主要是對文件的一些操作,其主要目的是抽象出和平臺文件系統無關的存儲。
  • queryParser 模塊:主要負責語法分析,把我們的查詢語句生成 Lucene 底層可以識別的條件。
  • search 模塊:主要負責對索引的搜索工作。
  • similarity 模塊:主要負責相關性打分和排序的實現。

核心術語

下面介紹 Lucene 中的核心術語:

  • Term:是索引里最小的存儲和查詢單元,對于英文來說一般是指一個單詞,對于中文來說一般是指一個分詞后的詞。
  • 詞典(Term Dictionary,也叫作字典):是 Term 的集合。詞典的數據結構可以有很多種,每種都有自己的優缺點。

比如:排序數組通過二分查找來檢索數據:HashMap(哈希表)比排序數組的檢索速度更快,但是會浪費存儲空間。

FST(finite-state transducer)有更高的數據壓縮率和查詢效率,因為詞典是常駐內存的,而 FST 有很好的壓縮率,所以 FST 在 Lucene 當前版本中有非常多的使用場景,也是默認的詞典數據結構。

  • 倒排序(Posting List):一篇文章通常由多個詞組成,倒排表記錄的是某個詞在哪些文章中出現過。
  • 正向信息:原始的文檔信息,可以用來做排序、聚合、展示等。
  • 段(Segment):索引中最小的獨立存儲單元。一個索引文件由一個或者多個段組成。在 Luence 中的段有不變性,也就是說段一旦生成,在其上只能有讀操作,不能有寫操作。

Lucene 的底層存儲格式如下圖所示,由詞典和倒排序兩部分組成,其中的詞典就是 Term 的集合:

 

圖 2:Lucene 的底層存儲格式

詞典中的 Term 指向的文檔鏈表的集合,叫做倒排表。詞典和倒排表是 Lucene 中很重要的兩種數據結構,是實現快速檢索的重要基石。

詞典和倒排表是分兩部分存儲的,在倒排序中不但存儲了文檔編號,還存儲了詞頻等信息。

在上圖所示的詞典部分包含三個詞條(Term):Elasticsearch、Lucene 和 Solr。詞典數據是查詢的入口,所以這部分數據是以 FST 的形式存儲在內存中的。

在倒排表中,“Lucene”指向有序鏈表 3,7,15,30,35,67,表示字符串“Lucene”在文檔編號為3、7、15、30、35、67的文章中出現過,Elasticsearch 和 Solr 同理。

檢索方式

在 Lucene 的查詢過程中的主要檢索方式有以下四種:

①單個詞查詢

指對一個 Term 進行查詢。比如,若要查找包含字符串“Lucene”的文檔,則只需在詞典中找到 Term“Lucene”,再獲得在倒排表中對應的文檔鏈表即可。

②AND

指對多個集合求交集。比如,若要查找既包含字符串“Lucene”又包含字符串“Solr”的文檔,則查找步驟如下:

  • 在詞典中找到 Term “Lucene”,得到“Lucene”對應的文檔鏈表。
  • 在詞典中找到 Term “Solr”,得到“Solr”對應的文檔鏈表。
  • 合并鏈表,對兩個文檔鏈表做交集運算,合并后的結果既包含“Lucene”也包含“Solr”。

③OR

指多個集合求并集。比如,若要查找包含字符串“Luence”或者包含字符串“Solr”的文檔,則查找步驟如下:

  • 在詞典中找到 Term “Lucene”,得到“Lucene”對應的文檔鏈表。
  • 在詞典中找到 Term “Solr”,得到“Solr”對應的文檔鏈表。
  • 合并鏈表,對兩個文檔鏈表做并集運算,合并后的結果包含“Lucene”或者包含“Solr”。

④NOT

指對多個集合求差集。比如,若要查找包含字符串“Solr”但不包含字符串“Lucene”的文檔,則查找步驟如下:

  • 在詞典中找到 Term “Lucene”,得到“Lucene”對應的文檔鏈表。
  • 在詞典中找到 Term “Solr”,得到“Solr”對應的文檔鏈表。
  • 合并鏈表,對兩個文檔鏈表做差集運算,用包含“Solr”的文檔集減去包含“Lucene”的文檔集,運算后的結果就是包含“Solr”但不包含“Lucene”。

通過上述四種查詢方式,我們不難發現,由于 Lucene 是以倒排表的形式存儲的。

所以在 Lucene 的查找過程中只需在詞典中找到這些 Term,根據 Term 獲得文檔鏈表,然后根據具體的查詢條件對鏈表進行交、并、差等操作,就可以準確地查到我們想要的結果。

相對于在關系型數據庫中的“Like”查找要做全表掃描來說,這種思路是非常高效的。

雖然在索引創建時要做很多工作,但這種一次生成、多次使用的思路也是非常高明的。

分段存儲

在早期的全文檢索中為整個文檔集合建立了一個很大的倒排索引,并將其寫入磁盤中,如果索引有更新,就需要重新全量創建一個索引來替換原來的索引。

這種方式在數據量很大時效率很低,并且由于創建一次索引的成本很高,所以對數據的更新不能過于頻繁,也就不能保證實效性。

現在,在搜索中引入了段的概念(將一個索引文件拆分為多個子文件,則每個子文件叫做段),每個段都是一個獨立的可被搜索的數據集,并且段具有不變性,一旦索引的數據被寫入硬盤,就不可修改。

在分段的思想下,對數據寫操作的過程如下:

  • 新增:當有新的數據需要創建索引時,由于段段不變性,所以選擇新建一個段來存儲新增的數據。
  • 刪除:當需要刪除數據時,由于數據所在的段只可讀,不可寫,所以 Lucene 在索引文件新增一個 .del 的文件,用來專門存儲被刪除的數據 id。

當查詢時,被刪除的數據還是可以被查到的,只是在進行文檔鏈表合并時,才把已經刪除的數據過濾掉。被刪除的數據在進行段合并時才會被真正被移除。

  • 更新:更新的操作其實就是刪除和新增的組合,先在.del文件中記錄舊數據,再在新段中添加一條更新后的數據。

段不可變性的優點如下:

  • 不需要鎖:因為數據不會更新,所以不用考慮多線程下的讀寫不一致情況。
  • 可以常駐內存:段在被加載到內存后,由于具有不變性,所以只要內存的空間足夠大,就可以長時間駐存,大部分查詢請求會直接訪問內存,而不需要訪問磁盤,使得查詢的性能有很大的提升。
  • 緩存友好:在段的聲明周期內始終有效,不需要在每次數據更新時被重建。
  • 增量創建:分段可以做到增量創建索引,可以輕量級地對數據進行更新,由于每次創建的成本很低,所以可以頻繁地更新數據,使系統接近實時更新。

段不可變性的缺點如下:

  • 刪除:當對數據進行刪除時,舊數據不會被馬上刪除,而是在 .del 文件中被標記為刪除。而舊數據只能等到段更新時才能真正地被移除,這樣會有大量的空間浪費。
  • 更新:更新數據由刪除和新增這兩個動作組成。若有一條數據頻繁更新,則會有大量的空間浪費。
  • 新增:由于索引具有不變性,所以每次新增數據時,都需要新增一個段來存儲數據。當段段數量太多時,對服務器的資源(如文件句柄)的消耗會非常大,查詢的性能也會受到影響。
  • 過濾:在查詢后需要對已經刪除的舊數據進行過濾,這增加了查詢的負擔。

為了提升寫的性能,Lucene 并沒有每新增一條數據就增加一個段,而是采用延遲寫的策略,每當有新增的數據時,就將其先寫入內存中,然后批量寫入磁盤中。

若有一個段被寫到硬盤,就會生成一個提交點,提交點就是一個用來記錄所有提交后的段信息的文件。

一個段一旦擁有了提交點,就說明這個段只有讀的權限,失去了寫的權限;相反,當段在內存中時,就只有寫數據的權限,而不具備讀數據的權限,所以也就不能被檢索了。

從嚴格意義上來說,Lucene 或者 Elasticsearch 并不能被稱為實時的搜索引擎,只能被稱為準實時的搜索引擎。

寫索引的流程如下:

  • 新數據被寫入時,并沒有被直接寫到硬盤中,而是被暫時寫到內存中。Lucene 默認是一秒鐘,或者當內存中數據量達到一定階段時,再批量提交到磁盤中。

當然,默認的時間和數據量的大小是可以通過參數控制的。通過延時寫的策略,可以減少數據往磁盤上寫的次數,從而提升整體的寫入性能,如圖 3。

  • 在達到出觸發條件以后,會將內存中緩存的數據一次性寫入磁盤中,并生成提交點。
  • 清空內存,等待新的數據寫入,如下圖所示。

 

圖 3:Elasticsearch 寫索引

從上述流程可以看出,數據先被暫時緩存在內存中,在達到一定的條件再被一次性寫入硬盤中,這種做法可以大大提升數據寫入的書單。

但是數據先被暫時存放在內存中,并沒有真正持久化到磁盤中,所以如果這時出現斷電等不可控的情況,就會丟失數據,為此,Elasticsearch 添加了事務日志,來保證數據的安全。

段合并策略

雖然分段比每次都全量創建索引有更高的效率,但是由于在每次新增數據時都會新增一個段,所以經過長時間的的積累,會導致在索引中存在大量的段。

當索引中段的數量太多時,不僅會嚴重消耗服務器的資源,還會影響檢索的性能。

因為索引檢索的過程是:查詢所有段中滿足查詢條件的數據,然后對每個段里查詢的結果集進行合并,所以為了控制索引里段的數量,我們必須定期進行段合并操作。

但是如果每次合并全部的段,則會造成很大的資源浪費,特別是“大段”的合并。

所以 Lucene 現在的段合并思路是:根據段的大小將段進行分組,再將屬于同一組的段進行合并。

但是由于對于超級大的段的合并需要消耗更多的資源,所以 Lucene 會在段的大小達到一定規模,或者段里面的數據量達到一定條數時,不會再進行合并。

所以 Lucene 的段合并主要集中在對中小段的合并上,這樣既可以避免對大段進行合并時消耗過多的服務器資源,也可以很好地控制索引中段的數量。

段合并的主要參數如下:

  • mergeFactor:每次合并時參與合并的最少數量,當同一組的段的數量達到此值時開始合并,如果小于此值則不合并,這樣做可以減少段合并的頻率,其默認值為 10。
  • SegmentSize:指段的實際大小,單位為字節。
  • minMergeSize:小于這個值的段會被分到一組,這樣可以加速小片段的合并。
  • maxMergeSize:若有一段的文本數量大于此值,就不再參與合并,因為大段合并會消耗更多的資源。

段合并相關的動作主要有以下兩個:

  • 對索引中的段進行分組,把大小相近的段分到一組,主要由 LogMergePolicy1 類來處理。
  • 將屬于同一分組的段合并成一個更大的段。

在段合并前對段的大小進行了標準化處理,通過 logMergeFactorSegmentSize 計算得出。

其中 MergeFactor 表示一次合并的段的數量,Lucene 默認該數量為 10;SegmentSize 表示段的實際大小。通過上面的公式計算后,段的大小更加緊湊,對后續的分組更加友好。

段分組的步驟如下:

①根據段生成的時間對段進行排序,然后根據上述標準化公式計算每個段的大小并且存放到段信息中,后面用到的描述段大小的值都是標準化后的值,如圖 4 所示:

 

圖 4:Lucene 段排序

②在數組中找到段,然后生成一個由段的標準化值作為上限,減去 LEVEL_LOG_SPAN(默認值為 0.75)后的值作為下限的區間,小于等于上限并且大于下限的段,都被認為是屬于同一組的段,可以合并。

③在確定一個分組的上下限值后,就需要查找屬于這個分組的段了,具體過程是:創建兩個指針(在這里使用指針的概念是為了更好地理解)start 和 end。

start 指向數組的第 1 個段,end 指向第 start+MergeFactor 個段,然后從 end 逐個向前查找落在區間的段。

當找到第 1 個滿足條件的段時,則停止,并把當前段到 start 之間的段統一分到一個組,無論段的大小是否滿足當前分組的條件。

如圖 5 所示,第 2 個段明顯小于該分組的下限,但還是被分到了這一組。

 

圖 5:Lucene 段分組

這樣做的好處如下:

  • 增加段合并的概率,避免由于段的大小參差不齊導致段難以合并。
  • 簡化了查找的邏輯,使代碼的運行效率更高。

④在分組找到后,需要排除不參加合并的“超大”段,然后判斷剩余的段是否滿足合并的條件。

如圖 5 所示,mergeFactor=5,而找到的滿足合并條件的段的個數為 4,所以不滿足合并的條件,暫時不進行合并,繼續找尋下一個組的上下限。

⑤由于在第 4 步并沒有找到滿足段合并的段的數量,所以這一分組的段不滿足合并的條件,繼續進行下一分組段的查找。

具體過程是:將 start 指向 end,在剩下的段中尋找大的段,在找到大的值后再減去 LEVEL_LOG_SPAN 的值,再生成一下分組的區間值。

然后把 end 指向數組的第 start+MergeFactor 個段,逐個向前查找第 1 個滿足條件的段:重復第 3 步和第 4 步。

⑥如果一直沒有找到滿足合并條件的段,則一直重復第 5 步,直到遍歷完整個數組,如圖 6 所示:

 

圖 6:Lucene 段分組二

⑦在找到滿足條件的 mergeFactor 個段時,就需要開始合并了。但是在滿足合并條件的段大于 mergeFactor 時,就需要進行多次合并。

也就是說每次依然選擇 mergeFactor 個段進行合并,直到該分組的所有段合并完成,再進行下一分組的查找合并操作。

⑧通過上述幾步,如果找到了滿足合并要求的段,則將會進行段的合并操作。

因為索引里面包含了正向信息和反向信息,所以段合并的操作分為兩部分:

  • 一個是正向信息合并,例如存儲域、詞向量、標準化因子等。
  • 一個是反向信息的合并,例如詞典、倒排表等。

在段合并時,除了需要對索引數據進行合并,還需要移除段中已經刪除的數據。

Lucene 相似度打分

我們在前面了解到,Lucene 的查詢過程是:首先在詞典中查找每個 Term,根據 Term 獲得每個 Term 所在的文檔鏈表;然后根據查詢條件對鏈表做交、并、差等操作,鏈表合并后的結果集就是我們要查找的數據。

這樣做可以完全避免對關系型數據庫進行全表掃描,可以大大提升查詢效率。

但是,當我們一次查詢出很多數據時,這些數據和我們的查詢條件又有多大關系呢?其文本相似度是多少?

本節會回答這個問題,并介紹 Lucene 最經典的兩個文本相似度算法:基于向量空間模型的算法和基于概率的算法(BM25)。

如果對此算法不太感興趣,那么只需了解對文本相似度有影響的因子有哪些,哪些是正向的,哪些是逆向的即可,不需要理解每個算法的推理過程。但是這兩個文本相似度算法有很好的借鑒意義。

Elasticsearch 簡介

Elasticsearch 是使用 Java 編寫的一種開源搜索引擎,它在內部使用 Luence 做索引與搜索,通過對 Lucene 的封裝,提供了一套簡單一致的 RESTful API。

Elasticsearch 也是一種分布式的搜索引擎架構,可以很簡單地擴展到上百個服務節點,并支持 PB 級別的數據查詢,使系統具備高可用和高并發性。

核心概念

  • Elasticsearch 的核心概念如下:
  • Cluster:集群,由一個或多個 Elasticsearch 節點組成。
  • Node:節點,組成 Elasticsearch 集群的服務單元,同一個集群內節點的名字不能重復。通常在一個節點上分配一個或者多個分片。
  • Shards:分片,當索引上的數據量太大的時候,我們通常會將一個索引上的數據進行水平拆分,拆分出來的每個數據庫叫作一個分片。

在一個多分片的索引中寫入數據時,通過路由來確定具體寫入那一個分片中,所以在創建索引時需要指定分片的數量,并且分片的數量一旦確定就不能更改。

分片后的索引帶來了規模上(數據水平切分)和性能上(并行執行)的提升。每個分片都是 Luence 中的一個索引文件,每個分片必須有一個主分片和零到多個副本分片。

  • Replicas:備份也叫作副本,是指對主分片的備份。主分片和備份分片都可以對外提供查詢服務,寫操作時先在主分片上完成,然后分發到備份上。

當主分片不可用時,會在備份的分片中選舉出一個作為主分片,所以備份不僅可以提升系統的高可用性能,還可以提升搜索時的并發性能。但是若副本太多的話,在寫操作時會增加數據同步的負擔。

  • Index:索引,由一個和多個分片組成,通過索引的名字在集群內進行標識。
  • Type:類別,指索引內部的邏輯分區,通過 Type 的名字在索引內進行標識。在查詢時如果沒有該值,則表示在整個索引中查詢。
  • Document:文檔,索引中的每一條數據叫作一個文檔,類似于關系型數據庫中的一條數據通過 _id 在 Type 內進行標識。
  • Settings:對集群中索引的定義,比如一個索引默認的分片數、副本數等信息。
  • Mapping:類似于關系型數據庫中的表結構信息,用于定義索引中字段(Field)的存儲類型、分詞方式、是否存儲等信息。Elasticsearch 中的 Mapping 是可以動態識別的。

如果沒有特殊需求,則不需要手動創建 Mapping,因為 Elasticsearch 會自動根據數據格式識別它的類型,但是當需要對某些字段添加特殊屬性(比如:定義使用其他分詞器、是否分詞、是否存儲等)時,就需要手動設置 Mapping 了。一個索引的 Mapping 一旦創建,若已經存儲了數據,就不可修改了。

  • Analyzer:字段的分詞方式的定義。一個 Analyzer 通常由一個 Tokenizer、零到多個 Filter 組成。

比如默認的標準 Analyzer 包含一個標準的 Tokenizer 和三個 Filter:Standard Token Filter、Lower Case Token Filter、Stop Token Filter。

Elasticsearch 的節點的分類如下:

①主節點(Master Node):也叫作主節點,主節點負責創建索引、刪除索引、分配分片、追蹤集群中的節點狀態等工作。Elasticsearch 中的主節點的工作量相對較輕。

用戶的請求可以發往任何一個節點,并由該節點負責分發請求、收集結果等操作,而并不需要經過主節點轉發。

通過在配置文件中設置 node.master=true 來設置該節點成為候選主節點(但該節點不一定是主節點,主節點是集群在候選節點中選舉出來的),在 Elasticsearch 集群中只有候選節點才有選舉權和被選舉權。其他節點是不參與選舉工作的。

②數據節點(Data Node):數據節點,負責數據的存儲和相關具體操作,比如索引數據的創建、修改、刪除、搜索、聚合。

所以,數據節點對機器配置要求比較高,首先需要有足夠的磁盤空間來存儲數據,其次數據操作對系統 CPU、Memory 和 I/O 的性能消耗都很大。

通常隨著集群的擴大,需要增加更多的數據節點來提高可用性。通過在配置文件中設置 node.data=true 來設置該節點成為數據節點。

③客戶端節點(Client Node):就是既不做候選主節點也不做數據節點的節點,只負責請求的分發、匯總等,也就是下面要說到的協調節點的角色。

其實任何一個節點都可以完成這樣的工作,單獨增加這樣的節點更多地是為了提高并發性。

可在配置文件中設置該節點成為數據節點:

  1. node.master=false 
  2. node.data=false 

④部落節點(Tribe Node):部落節點可以跨越多個集群,它可以接收每個集群的狀態,然后合并成一個全局集群的狀態。

它可以讀寫所有集群節點上的數據,在配置文件中通過如下設置使節點成為部落節點:

  1. tribe: 
  2.   one: 
  3.     cluster.name: cluster_one 
  4.   two: 
  5.     cluster.name: cluster_two 

因為 Tribe Node 要在 Elasticsearch 7.0 以后移除,所以不建議使用。

⑤協調節點(Coordinating Node):協調節點,是一種角色,而不是真實的 Elasticsearch 的節點,我們沒有辦法通過配置項來配置哪個節點為協調節點。集群中的任何節點都可以充當協調節點的角色。

當一個節點 A 收到用戶的查詢請求后,會把查詢語句分發到其他的節點,然后合并各個節點返回的查詢結果,返回一個完整的數據集給用戶。

在這個過程中,節點 A 扮演的就是協調節點的角色。由此可見,協調節點會對 CPU、Memory 和 I/O 要求比較高。

集群的狀態有 Green、Yellow 和 Red 三種,如下所述:

  • Green:綠色,健康。所有的主分片和副本分片都可正常工作,集群 100% 健康。
  • Yellow:預警。所有的主分片都可以正常工作,但至少有一個副本分片是不能正常工作的。此時集群可以正常工作,但是集群的高可用性在某種程度上被弱化。
  • Red:紅色,集群不可正常使用。集群中至少有一個分片的主分片及它的全部副本分片都不可正常工作。

這時雖然集群的查詢操作還可以進行,但是也只能返回部分數據(其他正常分片的數據可以返回),而分配到這個分片上的寫入請求將會報錯,最終會導致數據的丟失。

3C 和腦裂

①共識性(Consensus)

共識性是分布式系統中最基礎也最主要的一個組件,在分布式系統中的所有節點必須對給定的數據或者節點的狀態達成共識。

雖然現在有很成熟的共識算法如 Raft、Paxos 等,也有比較成熟的開源軟件如 Zookeeper。

但是 Elasticsearch 并沒有使用它們,而是自己實現共識系統 zen discovery。

Elasticsearch 之父 Shay Banon 解釋了其中主要的原因:“zen discovery是 Elasticsearch 的一個核心的基礎組件,zen discovery 不僅能夠實現共識系統的選擇工作,還能夠很方便地監控集群的讀寫狀態是否健康。當然,我們也不保證其后期會使用 Zookeeper 代替現在的 zen discovery”。

zen discovery 模塊以“八卦傳播”(Gossip)的形式實現了單播(Unicat):單播不同于多播(Multicast)和廣播(Broadcast)。節點間的通信方式是一對一的。

②并發(Concurrency)

Elasticsearch 是一個分布式系統。寫請求在發送到主分片時,同時會以并行的形式發送到備份分片,但是這些請求的送達時間可能是無序的。

在這種情況下,Elasticsearch 用樂觀并發控制(Optimistic Concurrency Control)來保證新版本的數據不會被舊版本的數據覆蓋。

樂觀并發控制是一種樂觀鎖,另一種常用的樂觀鎖即多版本并發控制(Multi-Version Concurrency Control)。

它們的主要區別如下:

  • 樂觀并發控制(OCC):是一種用來解決寫-寫沖突的無鎖并發控制,認為事務間的競爭不激烈時,就先進行修改,在提交事務前檢查數據有沒有變化,如果沒有就提交,如果有就放棄并重試。樂觀并發控制類似于自選鎖,適用于低數據競爭且寫沖突比較少的環境。
  • 多版本并發控制(MVCC):是一種用來解決讀-寫沖突的無所并發控制,也就是為事務分配單向增長的時間戳,為每一個修改保存一個版本,版本與事務時間戳關聯,讀操作只讀該事務開始前的數據庫的快照。

這樣在讀操作不用阻塞操作且寫操作不用阻塞讀操作的同時,避免了臟讀和不可重復讀。

③一致性(Consistency)

Elasticsearch 集群保證寫一致性的方式是在寫入前先檢查有多少個分片可供寫入,如果達到寫入條件,則進行寫操作,否則,Elasticsearch 會等待更多的分片出現,默認為一分鐘。

有如下三種設置來判斷是否允許寫操作:

  • One:只要主分片可用,就可以進行寫操作。
  • All:只有當主分片和所有副本都可用時,才允許寫操作。
  • Quorum(k-wu-wo/reng,法定人數):是 Elasticsearch 的默認選項。當有大部分的分片可用時才允許寫操作。其中,對“大部分”的計算公式為 int((primary+number_of_replicas)/2)+1。

Elasticsearch 集群保證讀寫一致性的方式是,為了保證搜索請求的返回結果是當前版本的文檔,備份可以被設置為 Sync(默認值),寫操作在主分片和備份分片同時完成后才會返回寫請求的結果。

這樣,無論搜索請求至哪個分片都會返回文檔。但是如果我們的應用對寫要求很高,就可以通過設置 replication=async 來提升寫的效率,如果設置 replication=async,則只要主分片的寫完成,就會返回寫成功。

④腦裂

在 Elasticsearch 集群中主節點通過 Ping 命令來檢查集群中的其他節點是否處于可用狀態,同時非主節點也會通過 Ping 來檢查主節點是否處于可用狀態。

當集群網絡不穩定時,有可能會發生一個節點 Ping 不通 Master 節點,則會認為 Master 節點發生了故障,然后重新選出一個 Master 節點,這就會導致在一個集群內出現多個 Master 節點。

當在一個集群中有多個 Master 節點時,就有可能會導致數據丟失。我們稱這種現象為腦裂。

事務日志

我們在上面了解到,Lucene 為了加快寫索引的速度,采用了延遲寫入的策略。

雖然這種策略提高了寫入的效率,但其弊端是,如果數據在內存中還沒有持久化到磁盤上時發生了類似斷電等不可控情況,就可能丟失數據。

為了避免丟失數據,Elasticsearch 添加了事務日志(Translog),事務日志記錄了所有還沒有被持久化磁盤的數據。

Elasticsearch 寫索引的具體過程如下:首先,當有數據寫入時,為了提升寫入的速度,并沒有數據直接寫在磁盤上,而是先寫入到內存中,但是為了防止數據的丟失,會追加一份數據到事務日志里。

因為內存中的數據還會繼續寫入,所以內存中的數據并不是以段的形式存儲的,是檢索不到的。

總之,Elasticsearch 是一個準實時的搜索引擎,而不是一個實時的搜索引擎。

此時的狀態如圖 7 所示:

 

圖 7:Elasticsearch 寫數據的過程

然后,當達到默認的時間(1 秒鐘)或者內存的數據達到一定量時,會觸發一次刷新(Refresh)。

刷新的主要步驟如下:

  • 將內存中的數據刷新到一個新的段中,但是該段并沒有持久化到硬盤中,而是緩存在操作系統的文件緩存系統中。雖然數據還在內存中,但是內存里的數據和文件緩存系統里的數據有以下區別。

內存使用的是 JVM 的內存,而文件緩存系統使用的是操作系統的內存;內存的數據不是以段的形式存儲的,并且可以繼續向內存里寫數據。文件緩存系統中的數據是以段的形式存儲的,所以只能讀,不能寫;內存中的數據是搜索不到,文件緩存系統中的數據是可以搜索的。

  • 打開保存在文件緩存系統中的段,使其可被搜索。
  • 清空內存,準備接收新的數據。日志不做清空處理。

此時的狀態如圖 8 所示:

 

圖 8:Elasticsearch 寫數據的過程

刷新(Flush)。當日志數據的大小超過 512MB 或者時間超過 30 分鐘時,需要觸發一次刷新。

刷新的主要步驟如下:

  • 在文件緩存系統中創建一個新的段,并把內存中的數據寫入,使其可被搜索。
  • 清空內存,準備接收新的數據。
  • 將文件系統緩存中的數據通過 Fsync 函數刷新到硬盤中。
  • 生成提交點。
  • 刪除舊的日志,創建一個空的日志。

此時的狀態如圖 9 所示:

 

圖 9:Elasticsearch 寫數據的過程

由上面索引創建的過程可知,內存里面的數據并沒有直接被刷新(Flush)到硬盤中,而是被刷新(Refresh)到了文件緩存系統中,這主要是因為持久化數據十分耗費資源,頻繁地調用會使寫入的性能急劇下降。

所以 Elasticsearch,為了提高寫入的效率,利用了文件緩存系統和內存來加速寫入時的性能,并使用日志來防止數據的丟失。

在需要重啟時,Elasticsearch 不僅要根據提交點去加載已經持久化過的段,還需要根據 Translog 里的記錄,把未持久化的數據重新持久化到磁盤上。

根據上面對 Elasticsearch,寫操作流程的介紹,我們可以整理出一個索引數據所要經歷的幾個階段,以及每個階段的數據的存儲方式和作用,如圖 10 所示:

 

圖 10:Elasticsearch 寫操作流程

在集群中寫索引

假設我們有如圖 11 所示(圖片來自官網)的一個集群,該集群由三個節點組成(Node 1、Node 2 和 Node 3),包含一個由兩個主分片和每個主分片由兩個副本分片組成的索引。

 

圖 11:寫索引

其中,標星號的 Node 1 是 Master 節點,負責管理整個集群的狀態;p1 和 p2 是主分片;r0 和 r1 是副本分片。為了達到高可用,Master 節點避免將主分片和副本放在同一個節點。

將數據分片是為了提高可處理數據的容量和易于進行水平擴展,為分片做副本是為了提高集群的穩定性和提高并發量。

在主分片掛掉后,會從副本分片中選舉出一個升級為主分片,當副本升級為主分片后,由于少了一個副本分片,所以集群狀態會從 Green 改變為 Yellow,但是此時集群仍然可用。

在一個集群中有一個分片的主分片和副本分片都掛掉后,集群狀態會由 Yellow 改變為 Red,集群狀態為 Red 時集群不可正常使用。

由上面的步驟可知,副本分片越多,集群的可用性就越高,但是由于每個分片都相當于一個 Lucene 的索引文件,會占用一定的文件句柄、內存及 CPU,并且分片間的數據同步也會占用一定的網絡帶寬,所以,索引的分片數和副本數并不是越多越好。

寫索引時只能寫在主分片上,然后同步到副本上,那么,一個數據應該被寫在哪個分片上呢?

如圖 10 所示,如何知道一個數據應該被寫在 p0 還是 p1 上呢答案就是路由(routing),路由公式如下:

  1. shard = hash(routing)%number_of_primary_shards 

其中,Routing 是一個可選擇的值,默認是文檔的 _id(文檔的主鍵,文檔在創建時,如果文檔的 _id 已經存在,則進行更新,如果不存在則創建)。

后面會介紹如何通過自定義 Routing 參數使查詢落在一個分片中,而不用查詢所有的分片,從而提升查詢的性能。

Routing 通過 Hash 函數生成一個數字,將這個數字除以 number_of_primary_shards(分片的數量)后得到余數。

這個分布在 0 到 number_of_primary_shards - 1 之間的余數,就是我們所尋求的文檔所在分片的位置。

這也就說明了一旦分片數定下來就不能再改變的原因,因為分片數改變之后,所有之前的路由值都會變得無效,前期創建的文檔也就找不到了。

由于在 Elasticsearch 集群中每個節點都知道集群中的文檔的存放位置(通過路由公式定位),所以每個節點都有處理讀寫請求的能力。

在一個寫請求被發送到集群中的一個節點后,此時,該節點被稱為協調點(Coordinating Node),協調點會根據路由公式計算出需要寫到哪個分片上,再將請求轉發到該分片的主分片節點上。

 

圖 12:寫索引

寫操作的流程如下(參考圖 11,圖片來自官網):

  • 客戶端向 Node 1(協調節點)發送寫請求。
  • Node 1 通過文檔的 _id(默認是 _id,但不表示一定是 _id)確定文檔屬于哪個分片(在本例中是編號為 0 的分片)。請求會被轉發到主分片所在的節點 Node 3 上。
  • Node 3 在主分片上執行請求,如果成功,則將請求并行轉發到 Node 1 和 Node 2 的副本分片上。

一旦所有的副本分片都報告成功(默認),則 Node 3 將向協調節點報告成功,協調節點向客戶端報告成功。

集群中的查詢流程

根據 Routing 字段進行的單個文檔的查詢,在 Elasticsearch 集群中可以在主分片或者副本分片上進行。

 

圖 13

查詢字段剛好是 Routing 的分片字段如“_id”的查詢流程如下(見圖 12,圖片來自官網):

  • 客戶端向集群發送查詢請求,集群再隨機選擇一個節點作為協調點(Node 1),負責處理這次查詢。
  • Node 1 使用文檔的 routing id 來計算要查詢的文檔在哪個分片上(在本例中落在了 0 分片上)分片 0 的副本分片存在所有的三個節點上。

在這種情況下,協調節點可以把請求轉發到任意節點,本例將請求轉發到 Node 2 上。

  • Node 2 執行查找,并將查找結果返回給協調節點 Node 1,Node 1 再將文檔返回給客戶端。

當一個搜索請求被發送到某個節點時,這個節點就變成了協調節點(Node 1)。

協調節點的任務是廣播查詢請求到所有分片(主分片或者副本分片),并將它們的響應結果整合成全局排序后的結果集合。

由上面步驟 3 所示,默認返回給協調節點并不是所有的數據,而是只有文檔的 id 和得分 score,因為我們只返回給用戶 size 條數據,所以這樣做的好處是可以節省很多帶寬,特別是 from 很大時。

協調節點對收集回來的數據進行排序后,找到要返回的 size 條數據的 id,再根據 id 查詢要返回的數據,比如 title、content 等。

 

圖 14

取回數據等流程如下(見圖 13,圖片來自官網):

  • Node 3 進行二次排序來找出要返回的文檔 id,并向相關的分片提交多個獲得文檔詳情的請求。
  • 每個分片加載文檔,并將文檔返回給 Node 3。
  • 一旦所有的文檔都取回了,Node 3 就返回結果給客戶端。

協調節點收集各個分片查詢出來的數據,再進行二次排序,然后選擇需要被取回的文檔。

例如,如果我們的查詢指定了{"from": 20, "size": 10},那么我們需要在每個分片中查詢出來得分較高的 20+10 條數據,協調節點在收集到 30×n(n 為分片數)條數據后再進行排序。

排序位置在 0-20 的結果會被丟棄,只有從第 21 個開始的 10 個結果需要被取回。這些文檔可能來自多個甚至全部分片。

由上面的搜索策略可以知道,在查詢時深翻(Deep Pagination)并不是一種好方法。

因為深翻時,from 會很大,這時的排序過程可能會變得非常沉重,會占用大量的 CPU、內存和帶寬。因為這個原因,所以強烈建議慎重使用深翻。

分片可以減少每個片上的數據量,加快查詢的速度,但是在查詢時,協調節點要在收集數(from+size)×n 條數據后再做一次全局排序。

若這個數據量很大,則也會占用大量的 CPU、內存、帶寬等,并且分片查詢的速度取決于最慢的分片查詢的速度,所以分片數并不是越多越好。

作者:錢丁君

簡介:就職于永輝云創,擔任基礎架構開發,有多年基礎架構經驗,主要從事電商新零售、互聯網金融行業。技術發燒友,涉獵廣泛。熟悉 Java 微服務架構搭建、推進、衍化;多種中間件搭建、封裝和優化;自動化測試開發、代碼規約插件開發、代碼規范推進;容器化技術 Docker、容器化編排技術 Kubernetes,有較為豐富的運維經驗。

【51CTO原創稿件,合作站點轉載請注明原文作者和出處為51CTO.com】

 

責任編輯:武曉燕 來源: 51CTO技術棧
相關推薦

2019-05-21 09:40:47

Elasticsear高性能 API

2020-12-04 08:24:34

監控多維度立體化監控系統

2018-02-02 16:41:01

程序員編程Web

2025-10-20 08:53:00

2019-04-01 09:00:05

程序員編程語言HTML

2015-06-09 15:28:14

編程能力編程

2021-11-08 10:00:19

require前端模塊

2021-07-21 10:10:14

require前端代碼

2020-04-22 13:41:33

數據結構大數據數據

2017-05-10 08:39:34

裝機線纜機箱

2023-06-12 08:36:01

static?DOM文檔

2017-06-05 13:53:10

2017-08-23 09:05:09

交互設計UI界面

2022-12-09 08:19:43

HTTP協議MIME

2018-08-15 14:18:34

算法AI機器學習

2022-09-27 19:07:35

工具Hack延遲

2017-11-06 11:15:51

智能機器人工智能機器學習

2019-09-21 20:57:59

Android安卓開發

2025-08-21 01:15:00

CSS背景網頁

2017-11-12 21:12:34

HPC
點贊
收藏

51CTO技術棧公眾號

日韩免费视频| 91久久久久久白丝白浆欲热蜜臀| 国产999精品久久久久久绿帽| 欧美黑人巨大精品一区二区| 久久人妻少妇嫩草av无码专区| 性欧美18xxxhd| 中文字幕精品一区二区精品绿巨人| 国产日韩在线播放| 国产无码精品在线播放| 神马久久一区二区三区| 日韩一级免费一区| 欧美黄网站在线观看| 巨大荫蒂视频欧美另类大| 北条麻妃国产九九精品视频| 国产精品丝袜视频| 亚欧视频在线观看| 亚洲第一偷拍| 亚洲视频第一页| 911亚洲精选| jizz欧美| 日韩欧美精品网址| 四虎精品欧美一区二区免费| 国产在线视频网| 国产jizzjizz一区二区| 国产精品女主播| 久久久久久久极品| 国产真实久久| 久久精品夜夜夜夜夜久久| 老鸭窝一区二区| 91国内精品| 欧美顶级少妇做爰| 国产天堂在线播放| 超碰在线最新网址| 亚洲丝袜制服诱惑| 亚洲国产综合自拍| 久久免费看视频| www.日韩av| 91视频婷婷| 一级黄色a毛片| 噜噜噜在线观看免费视频日韩| 久久久久久综合网天天| 国产在线一卡二卡| 亚洲mv大片欧洲mv大片| 永久免费毛片在线播放不卡 | 成人涩涩免费视频| 91深夜福利视频| 中文字幕欧美色图| 久久激情网站| 日韩av免费在线| 日韩中文字幕在线观看视频| 日韩一级欧洲| 2023亚洲男人天堂| 亚洲男人的天堂在线视频| 亚洲激情综合| 亚洲91精品在线| 福利一区二区三区四区| 国产精品多人| 91av久久久| 99久久人妻无码精品系列| free性m.freesex欧美| 一个色在线综合| 欧洲金发美女大战黑人| av网站网址在线观看| 日韩久久一区二区| 在线观看污视频| 五月婷婷视频在线观看| 一区二区三区91| 日本阿v视频在线观看| 国产盗摄一区二区| 欧美日韩黄色大片| 免费av网址在线| 午夜精品久久久久久久久久蜜桃| 一本久道久久综合中文字幕 | 欧美日韩精品中文字幕一区二区| 欧美孕妇孕交| 欧美激情一区二区三区全黄| 亚洲视频小说| 性欧美1819sex性高清大胸| 一区二区国产视频| 国产乱子伦农村叉叉叉| 欧美日一区二区三区| 欧美顶级少妇做爰| youjizz.com国产| 亚洲人成伊人成综合图片| 中文字幕免费精品一区| www.av免费| 亚洲免费高清| 国产精品久在线观看| 国产喷水吹潮视频www| 粉嫩一区二区三区在线看| 久久另类ts人妖一区二区| 在线观看免费网站黄| 一区二区三区小说| 国产精品天天av精麻传媒| 国产va免费精品观看精品| 亚洲的天堂在线中文字幕| 蜜桃av免费看| 午夜精品毛片| 日产精品久久久一区二区福利| 在线观看免费视频一区| 国产麻豆成人精品| 色视频一区二区三区| av网站在线免费看推荐| 色综合色狠狠综合色| av噜噜在线观看| 欧美三级午夜理伦三级在线观看| 日韩在线视频一区| 日本熟伦人妇xxxx| 久久99久久99| 欧美日韩亚洲免费| 日本中文字幕中出在线| 在线观看成人免费视频| 娇妻高潮浓精白浆xxⅹ| 欧美电影免费观看高清| 欧美在线中文字幕| www精品国产| 国产精品高潮呻吟久久| 国产男女无遮挡| 一本色道69色精品综合久久| 中文字幕精品国产| 日本五十熟hd丰满| 国产精品99久久久久久有的能看| 欧美乱偷一区二区三区在线| 日本天码aⅴ片在线电影网站| 欧美性xxxxxxxx| 国产制服丝袜在线| 欧美激情综合色综合啪啪| 国产精品老牛影院在线观看| 性猛交xxxx| 亚洲综合精品久久| 不用播放器的免费av| 国产成人调教视频在线观看| 久久久久久国产| 国产乱码久久久| 国产精品网友自拍| 亚洲狼人综合干| 亚洲成人一品| 97国产精品视频| 精品人妻一区二区三区浪潮在线 | 久久久99久久精品欧美| 国产免费观看高清视频| av不卡一区| 欧美高清视频在线| 国产黄色高清视频| 亚洲精品日韩综合观看成人91| 日韩在线不卡一区| 日韩欧美视频在线播放| 国产精品国语对白| 黄色片免费在线| 欧美午夜精品电影| 成人欧美一区二区三区黑人一| 青草av.久久免费一区| 日本精品一区二区三区视频| 欧美大片1688| 一本一道久久a久久精品逆3p| 欧产日产国产69| 久久久久久久久久久久久久久99 | 精品一区免费观看| 福利一区福利二区| 成人黄色大片网站| 亚洲午夜久久| 国产成人一区二区三区| 国产日韩精品在线看| 精品视频123区在线观看| 中文字幕第24页| 久久99国产精品免费网站| 91制片厂免费观看| 一区二区三区亚洲变态调教大结局 | 牛牛电影国产一区二区| 亚洲成av人影院在线观看| 久久露脸国语精品国产91| www国产成人免费观看视频 深夜成人网| 怡红院av亚洲一区二区三区h| 亚洲福利天堂| 国产精品美女主播在线观看纯欲| 欧美被日视频| 精品国产凹凸成av人导航| 中文字幕在线观看免费视频| 国产欧美一区二区三区鸳鸯浴 | 丁香六月婷婷综合| 国产欧美日韩视频在线观看| 亚洲色图偷拍视频| 日韩亚洲国产精品| 亚洲精品tv久久久久久久久| 国产一区二区三区精品在线观看| 久久久久女教师免费一区| 深夜福利在线看| 欧美日韩一区二区三区四区五区 | 亚洲va欧美va国产综合剧情| hd国产人妖ts另类视频| 国产亚洲精品高潮| va婷婷在线免费观看| 欧美日韩在线视频一区| 黄色免费一级视频| youjizz久久| gai在线观看免费高清| 99国产精品自拍| 中文字幕在线中文字幕日亚韩一区| 加勒比中文字幕精品| 国产精品视频地址| av资源新版天堂在线| www.欧美三级电影.com| 午夜视频在线播放| 欧美男同性恋视频网站| 国产无遮无挡120秒| 国产欧美久久久精品影院| 动漫av在线免费观看| 日韩黄色免费电影| 亚洲一区二区三区av无码| 色橹橹欧美在线观看视频高清 | av7777777| 欧美黄色精品| 婷婷精品国产一区二区三区日韩| 成人中文字幕视频| 91久久在线观看| 人人视频精品| 91国产视频在线| 欧美成人性生活视频| 亚洲人成在线观| 人妻少妇精品无码专区久久| 欧美一区三区二区| 中文字幕第2页| 欧美日韩加勒比精品一区| 欧美国产日韩综合| 综合欧美亚洲日本| 国产不卡在线观看视频| 久久精品亚洲国产奇米99| 成年人小视频在线观看| 国产一区视频在线看| 五月婷婷狠狠操| 天堂在线亚洲视频| 美女av免费在线观看| 亚洲精品极品| 97超碰人人澡| 亚洲二区在线| av网站手机在线观看| 亚洲一区二区日韩| 在线国产99| 天天综合精品| 欧美h视频在线观看| 欧美好骚综合网| 亚洲精品一区二区三区av| 成人无号精品一区二区三区| 欧美精品人人做人人爱视频| 青青草久久爱| 久久久久久亚洲精品不卡4k岛国 | 成人一区二区三区| 精品人妻无码中文字幕18禁| 国产一区二区三区在线观看免费视频 | 老司机av福利| 欧美一区二区三区久久精品茉莉花| 一级一片免费播放| 婷婷精品进入| 中文字幕在线中文字幕日亚韩一区| 97久久视频| 国产一区一区三区| 欧美激情成人在线| 国产青草视频在线观看| 黄色免费成人| 国产午夜大地久久| 蘑菇福利视频一区播放| 国产精品第12页| 青青草伊人久久| 天堂av8在线| 国产河南妇女毛片精品久久久 | 国色天香2019中文字幕在线观看| 丰满的护士2在线观看高清| 久久久亚洲精品视频| 免费在线小视频| 国产精品r级在线| 午夜一级免费视频| 国产欧美午夜| 日韩中文字幕免费在线| 强制捆绑调教一区二区| www.五月天色| av在线播放成人| 欧美熟妇激情一区二区三区| 国产精品免费人成网站| 欧美成人免费观看视频| 亚洲五码中文字幕| 无码人妻丰满熟妇奶水区码| 精品视频在线免费| 精品国产999久久久免费| 亚洲成人国产精品| 番号集在线观看| 色综合男人天堂| 色成人免费网站| 91精品视频在线看| 欧美成人专区| 一区二区三区四区欧美| 黄色在线一区| 无限资源日本好片| 国产成人8x视频一区二区| 制服 丝袜 综合 日韩 欧美| 亚洲欧美国产毛片在线| 国产成人一级片| 日韩天堂在线观看| 久久久久国产精品嫩草影院| 久久在线免费视频| 少妇在线看www| 91青青草免费观看| 手机亚洲手机国产手机日韩| 免费超爽大片黄| 国产一区不卡在线| 中文字幕第20页| 亚洲va国产天堂va久久en| 中文字幕有码视频| 日韩精品亚洲精品| 污污视频在线| 国产精品福利在线| 婷婷精品在线| 成年在线观看视频| 蜜桃精品视频在线观看| 国产国语性生话播放| 亚洲一区二区五区| 国产精品欧美激情在线| 亚洲色图第三页| 日韩伦理在线一区| 国产不卡一区二区在线观看| 欧美激情偷拍自拍| 国产免费人做人爱午夜视频| 成人免费视频app| 91高清免费观看| 欧美色图片你懂的| 国产永久免费高清在线观看| 68精品国产免费久久久久久婷婷 | 91精品黄色片免费大全| 国产天堂在线| 欧美做爰性生交视频| 久久国产精品免费精品3p| 狠狠干视频网站| 国产乱码精品一品二品| 免费91在线观看| 欧美偷拍一区二区| 国产一级网站视频在线| 欧美在线视频网| 日韩欧美中文字幕电影| 免费看毛片的网址| 成人免费视频caoporn| 青娱乐91视频| 日韩一区二区免费电影| 国产激情小视频在线| 成人久久一区二区| 91精品国产成人观看| 午夜啪啪小视频| 综合激情成人伊人| 国产绳艺sm调教室论坛| 欧美超级免费视 在线| 无人区乱码一区二区三区| 强伦女教师2:伦理在线观看| 精品一区二区日韩| www日韩在线| 欧美一区二区成人| 污污视频在线看| 精品在线视频一区二区三区| 国产深夜精品| 免费污网站在线观看| 欧美亚洲高清一区二区三区不卡| av黄色在线观看| 91免费精品国偷自产在线| 欧美午夜不卡| 亚洲一区二区三区无码久久| 色香蕉久久蜜桃| 1024免费在线视频| 91免费在线视频| 韩国久久久久| 亚洲一区二区三区综合| 色婷婷久久久综合中文字幕| eeuss影院www在线观看| 成人黄色av网| 亚洲黄色大片| 一区二区三区伦理片| 欧美日韩国产欧美日美国产精品| 羞羞的视频在线观看| 激情视频一区二区| 日韩av一区二区在线影视| 国产精品视频一区二区三 | 欧美日本一区| 日本黄色片在线播放| 欧美无人高清视频在线观看| 免费黄色在线| 国产一区再线| 老司机精品视频导航| 免费在线观看黄视频| 亚洲热线99精品视频| 韩国一区二区三区视频| 九九爱精品视频| 国产网站一区二区| 丰满少妇高潮在线观看| 国产成人在线精品| 自拍视频亚洲| 亚洲成人黄色av| 欧美一二三四区在线| 亚洲精品一区| 大胆欧美熟妇xx| 国产欧美一区二区精品久导航 | 国产一区二区色噜噜| 国产 欧美 日韩 一区| 国产精品私人影院| 天堂av资源在线|