Elasticsearch詳解(二)


處理沖突:

樂觀並發控制:

Elasticsearch是分布式的。當文檔被創建、更新或刪除,文檔的新版本會被復制到集群的其它節點。Elasticsearch即是同步
的又是異步的,意思是這些復制請求都是平行發送的,並無序(out of sequence)的到達目的地。這就需要一種方法確保老版
本的文檔永遠不會覆蓋新的版本。
上文我們提到 index 、 get 、 delete 請求時,我們指出每個文檔都有一個 _version 號碼,這個號碼在文檔被改變時加一。
Elasticsearch使用這個 _version 保證所有修改都被正確排序。當一個舊版本出現在新版本之后,它會被簡單的忽略。

分布式文檔存儲

路由文檔到分片

Elasticsearch將文檔存儲的哪一個分片上,是根據算法:

shard = hash(routing) % number_of_primary_shards

routing 值是一個任意字符串,它默認是 _id 但也可以自定義。這個 routing

字符串通過哈希函數生成一個數字,然后除以主切片的數量得到一個余數

(remainder),余數的范圍永遠是 0 到 number_of_primary_shards - 1 ,這個

數字就是特定文檔所在的分片

新建、索引和刪除文檔都是寫操作,它們必須在主分片上成功完成才能復制到相
關的的復制分片上。

步驟:

1、客戶端給Node1發送新建、索引或刪除文檔請求。

2、主節點使用文檔的_id確定文檔屬於分片0,它轉發請求到Node3,分片0位於
這個節點上。

3、Node3在主分片上執行請求,如果成功,它轉發請求到相應的位於Node1和
Node2的復制節點上。當所有復制節點報告成功,Node3報告到請求的節點,請求
的節點在報告給客戶端。

請求參數:replication、consistency、timeout會更改上述過程

replication:默認值為sync,主分片得到復制分片的成功響應后才返回。

async,請求在主分片上被執行后就會返回給客戶端。它

依舊會轉發請求給復制節點,但你將不知道復制節點是否成功。

默認的sync復制允許Elasticsearch強制反饋傳輸。async復制可能導致在不等待
其他分片就緒的情況下發送過多的請求二使Elasticsearch過載

consistency:默認主分片在在嘗試寫入時需要規定數量(quorum)或過半的分片
可用,防止數據被寫入到錯的網絡分區。規定數量:

int ((primary + number_of_replicas) / 2) +1

值:one(只有一個主分片),all(所有主分片),或者默認的(quorum)或者
過半分片

timeout:當副本不足時會怎樣?Elasticsearch會等待更多的分片出現,默認為
一分鍾。timeout:1000*60;timeout:30s

新索引默認有 1 個復制分片,這意味着為了滿足 quorum 的要求需要兩個活動
的分片。當然,這個默認設置將阻止我們
在單一節點集群中進行操作。為了避開這個問題,規定數量只有在
number_of_replicas 大於一時才生效。

 

檢索文檔

下面我們羅列在主分片或復制分片上檢索一個文檔必要的順序步驟:

1. 客戶端給 Node 1 發送get請求。

2. 節點使用文檔的 _id 確定文檔屬於分片 0 。分片 0 對應的復制分片在三個
節點上都有。此時,它轉發請求到 Node 2 。

3. Node 2 返回endangered給 Node 1 然后返回給客戶端。

原因:對於讀請求,為了平衡負載,請求節點會為每個請求選擇不同的分片——

它會循環所有分片副本。

局部更新文檔:

下面我們羅列執行局部更新必要的順序步驟:

1. 客戶端給 Node 1 發送更新請求。

2. 它轉發請求到主分片所在節點 Node 3 。

3. Node 3 從主分片檢索出文檔,修改 _source 字段的JSON,然后在主分片上

重建索引。如果有其他進程修改了文檔,它以 retry_on_conflict 設置的次數

重復步驟3,都未成功則放棄。


4. 如果 Node 3 成功更新文檔,它同時轉發文檔的新版本到 Node 1 和 Node 2

上的復制節點以重建索引。當所有復制節點報告成功, Node 3 返回成功給請求

節點,然后返回給客戶端。

update API還接受routing``replication``consistency 和 timout 參數。

基於文檔的復制:

當主分片轉發更改給復制分片時,並不是轉發更新請求,而是轉發整個文檔的新版本。這些
修改轉發到復制節點是異步的,不保證到達的順序不發送的順序相同。如果Elasticsearch

轉發的僅僅是修改請求,修改的順序可能是錯誤的,哪得到的就是個損壞的文檔。

 

批量中每個引用的文檔屬於不同的主分片,每個分片可能被分布於集群中的某個節點上。這意味着批量中的每個操作
(action)需要被轉發到對應的分片和節點上。
如果每個單獨的請求被包裝到JSON數組中,那意味着我們需要:
解析JSON為數組(包括文檔數據,可能非常大)
檢查每個請求決定應該到哪個分片上
為每個分片創建一個請求的數組
序列化這些數組為內部傳輸格式
發送請求到每個分片
這可行,但需要大量的RAM來承載本質上相同的數據,還要創建更多的數據結構使得JVM花更多的時間執行垃圾回收。
取而代之的,Elasticsearch則是從網絡緩沖區中一行一行的直接讀取數據。它使用換行符識別和解析action/metadata行,以
決定哪些分片來處理這個請求。
這些行請求直接轉發到對應的分片上。這些沒有冗余復制,沒有多余的數據結構。整個請求過程使用最小的內存在進行。

數據是如何被索引的

映射(mapping)機制用於進行字段類型確認,將每個字段匹配為一種確定的數據類型( string , number , booleans ,
date 等)。
分析(analysis)機制用於進行全文文本(Full Text)的分詞,以建立供搜索用的反向索引。

在索引中有12個tweets,只有一個包含日期 2014-09-15 ,但是我們看看下面查詢中的 total hits。
GET /_search?q=2014 # 12 個結果
GET /_search?q=2014-09-15 # 還是 12 個結果 !
GET /_search?q=date:2014-09-15 # 1 一個結果
GET /_search?q=date:2014 # 0 個結果 !

 

date 類型的字段和 string 類型的字段的索引方式是不同的,因此導致查詢結果的不同,這並不會讓我們覺得驚訝。
你會期望每一種核心數據類型(strings, numbers, booleans及dates)以不同的方式進行索引,而這點也是現實:在
Elasticsearch中他們是被區別對待的。
但是更大的區別在於確切值(exact values)(比如 string 類型)及全文文本(full text)之間。(Elasticsearch中的數據可以大致分為兩種類型:

確切值 及 全文文本。)

這兩者的區別才真的很重要 - 這是區分搜索引擎和其他數據庫的根本差異。

確切值(Exact values) vs. 全文文本(Full text)

確切值是確定的,正如它的名字一樣。比如一個date或用戶ID,也可以包含更多的字符串比如username或email地址。
確切值 "Foo" 和 "foo" 就並不相同。確切值 2014 和 2014-09-15 也不相同。
全文文本,從另一個角度來說是文本化的數據(常常以人類的語言書寫),比如一片推文(Twitter的文章)或郵件正文。

確切值查詢較為簡單,和sql比較像

全文文本查詢首先對文本分析,然后使用結果建立一個倒排索引

倒排索引

例如,我們有兩個文檔,每個文檔 content 字段包含:
1. The quick brown fox jumped over the lazy dog
2. Quick brown foxes leap over lazy dogs in summer

將詞為統一為標准格式,

1. "Quick" 可以轉為小寫成為 "quick" 。
2. "foxes" 可以被轉為根形式 ""fox 。同理 "dogs" 可以被轉為 "dog" 。
3. "jumped" 和 "leap" 同義就可以只索引為單個詞 "jump"

搜索“+Quick +fox”(前綴 + 表示單詞必須匹配到)標准化處理為"+quick +fox"

分析和分析器

分析器完成上述標准化操作。包括三個功能:字符過濾器、分詞器、表征過濾

首先字符串經過字符過濾器(character filter),它們的工作是在表征化(譯者注:這個詞叫做斷詞更合適)前處理字符串。
字符過濾器能夠去除HTML標記,或者轉換 "&" 為 "and" 。
下一步,分詞器(tokenizer)被表征化(斷詞)為獨立的詞。一個簡單的分詞器(tokenizer)可以根據空格或逗號將單詞分開
(譯者注:這個在中文中不適用)。
最后,每個詞都通過所有表征過濾(token filters),它可以修改詞(例如將 "Quick" 轉為小寫),去掉詞(例如停用詞
像 "a" 、 "and"``"the" 等等),或者增加詞(例如同義詞像 "jump" 和 "leap" )

內建的分析器:標准分析器、簡單分析器、空格分析器、語言分析器

"Set the shape to semi-transparent by calling set_trans(5)"

標准分析器是Elasticsearch默認使用的分析器。對於文本分析,它對於任何語言都是最佳選擇(譯者注:就是沒啥特殊需
求,對於任何一個國家的語言,這個分析器就夠用了)。它根據Unicode Consortium的定義的單詞邊界(word boundaries)
來切分文本,然后去掉大部分標點符號。最后,把所有詞轉為小寫。產生的結果為:
set, the, shape, to, semi, transparent, by, calling, set_trans, 5


簡單分析器將非單個字母的文本切分,然后把每個詞轉為小寫。產生的結果為:
set, the, shape, to, semi, transparent, by, calling, set, trans


空格分析器依據空格切分文本。它不轉換小寫。產生結果為:
Set, the, shape, to, semi-transparent, by, calling, set_trans(5)


特定語言分析器適用於很多語言。它們能夠考慮到特定語言的特性。例如, english 分析器自帶一套英語停用詞庫——
像 and 或 the 這些與語義無關的通用詞。這些詞被移除后,因為語法規則的存在,英語單詞的主體含義依舊能被理解(譯者
注: stem English words 這句不知道該如何翻譯,查了字典,我理解的大概意思應該是將英語語句比作一株植物,去掉無用
的枝葉,主干依舊存在,停用詞好比枝葉,存在與否並不影響對這句話的理解。)。
english 分析器將會產生以下結果:
set, shape, semi, transpar, call, set_tran, 5
注意 "transparent" 、 "calling" 和 "set_trans" 是如何轉為詞干的。

測試分析器

 

指定分析器:通過映射(mapping)人工設置這些字段。

映射

索引中每個文檔都有一個類型(type)。 每個類型擁有自己的映射(mapping)或者模式定義
(schema definition)一個映射定義了字段類型,每個字段的數據類型,以及字段被Elasticsearch處理的方式。映射還用於

設置關聯到類型上的元數據。

如果你索引一個帶引號的數字—— "123" ,它將被映射為 "string" 類型,而不是 "long" 類型。然而,如果
字段已經被映射為 "long" 類型,Elasticsearch將嘗試轉換字符串為long,並在轉換失敗時會拋出異常。

我們可以使用 _mapping 后綴來查看Elasticsearch中的映射

GET /gb/_mapping/tweet

注意:

錯誤的映射,例如把 age 字段映射為 string 類型而不是 integer 類型,會造成查詢結果混亂。
要檢查映射類型,而不是假設它是正確的!

自定義字段映射

映射中最重要的字段參數是 type 。除了 string 類型的字段,你可能很少需要映射其他的 type

string 類型的字段,默認的,考慮到包含全文本,它們的值在索引前要經過分析器分析,並且在全文搜索此字段前要把查詢
語句做分析處理。
對於 string 字段,兩個最重要的映射參數是 index 和 analyer 。

string 類型字段默認值是 analyzed 。如果我們想映射字段為確切值,我們需要設置它為 not_analyzed :
{
"tag": {
"type": "string",
"index": "not_analyzed"
}
}
其他簡單類型—— long 、 double 、 date 等等——也接受 index 參數,但相應的值只能是 no 和 not_analyzed ,它們
的值不能被分析。

analyer

你可以在第一次創建索引的時候指定映射的類型。此外,你也可以晚些時候為新類型添加映射(或者為已有的類型更新映
射)。
你可以向已有映射中增加字段,但你不能修改它。如果一個字段在映射中已經存在,這可能意味着那個字段的數據已
經被索引。如果你改變了字段映射,那已經被索引的數據將錯誤並且不能被正確的搜索到。
我們可以更新一個映射來增加一個新字段,但是不能把已有字段的類型那個從 analyzed 改到 not_analyzed 。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM