ElasticSearch 2 (8) - 概覽與簡介


ElasticSearch 2 (8) - 概覽與簡介

摘要

  • 分布式集群架構,具有高擴充性,可隨時增加或移除節點,並保證數據正確。

  • 使用Apache Lucene儲存JSON文件,提供全文搜索功能

  • 所有操作均可透過RESTful API完成

  • 跨平台,Java寫成


版本

elasticsearch版本: elasticsearch-2.2.0

內容

為了搜索,你懂的

有誰在使用?

還有誰在用?

用來做什么?

  • 記錄

  • 搜尋

  • 分析

與關系數據庫有什么不一樣?

索引、類型?

關系數據庫 => 數據庫 => 表 => 行記錄 => 列

ElasticSearch => 索引 => 類型 => 文件 => 字段

如何使用

如何存入?
PUT /megacorp/employee/1
{
	“first_name": "John",
	"last_name" : "Smith",
	"age" : 25,
	"about" : "I am hero",
	"interests": [ "sports", "music" ]
}
  1. 創建megacorp的索引 Index
  2. 在里面創建一個employee的類型 Type
  3. 在里面建立一個_id是1的JSON文件 Document
如何讀取?
GET /megacorp/employee/1

GET /megacorp/employee/_search?q=music

GET /megacorp/_search?q=hero
如何回傳?
{
	……
	"took": 4,
	"hits": {
		"total": 1,
		"hits": [
			{
				"_index": "megacorp",
				"_type": "employee",
				"_id": "1",
				"_score": 0.095891505,
				"_source": {
					"first_name": "John",
					……
				}
			}
		]
	}
}	

集群里的日子

  • Cluster由一個或以上具有相同cluster.name的ElasticSearch節點所組成。當有節點加入或移除時,cluster會自動平均分配數據。

  • Cluster中會自動選出一個主節點負責cluster的變動,例如新增節點或創建新的Index。

  • 主節點可以不參與文件操作或搜索,因此只有一個主節點不會導致瓶頸。

  • 我們可以發送請求到任一節點,它會清楚在cluster中該如何處理,並回傳給我們最終結果。

Cluster的健康狀態

GET /_cluster/health
  • GREEN 所有的主要(master)與復制(replica)的shard都是啟動的。

  • YELLOW 所有的主要shard都是啟動的,但復制的沒有。

  • RED 所有的主要與復制的shard都沒有啟動。

創建索引

PUT /blogs
{
	"settings" : {
		"number_of_shards" : 3,
		"number_of_replicas" : 1
	}
}

創建一個名為blogs的Index,並設定它有3個主shard每個主shard總共要有一個副本shard在其他機器。

一個節點的時候

此時cluster健康狀態為黃色,因為沒有分配副本shard到其他機器。此時ElasticSearch可以正常運作,但是數據若遭到硬件問題時無法復原。

加入第二、第三個節點

此時cluster健康狀態為綠色,因所有shard都啟動了。此時其中一個節點遇到硬件問題都不會有影響。

增加復制的shard

PUT /blogs/_settings
{
	"number_of_replicas" : 2
}

如此一來,壞掉兩個節點也不影響。

數據如何分配到shard?

shard = hash(routing) % number_of_primary_shards

當有文件要儲存進入Index時,ElasticSearch經過上面的計算后決定要把該文件存儲到哪一個shard。

routing為任意字符串,預設為文件上的_id,可被改為其他的值。透過改變routing可以決定文件要儲存到哪個shard。

當新增或刪除文件的時候

  1. Node 1 收到新增或刪除請求。
  2. Node 1 算出請求的文件是屬於Shard 0,因此將請求轉給Node 3。
  3. Node 3 完成請求時,會再將請求轉給復制的shard所在的Node 1與Node 2,並確定他們也都完成,此請求才算是成功。

當取得指定文件的時候

  1. Node 1 收到獲取請求。
  2. Node 1 算出請求的文件是在Shard 0,而三台機器都有Shard 0,以上圖為例,它會將請求轉給Node 2。
  3. Node 2 將文件回傳給Node 1,再回傳給使用者。

問題:當多個Node上都具有相同shard時,主Node如何轉發請求?

當更新文件的時候

  1. Node 1 收到更新的請求。
  2. Node 1 算出請求的文件屬於Shard 0,因此將請求轉給Node 3。
  3. Node 3 將文件取出后更新_source並嘗試重新索引。此步驟可能重復retry_on_conflict次數。
  4. Node 3 完成請求時,會再將新的文件傳給復制的shard所在的Node 1與Node 2,並確定他們也都完成,此請求才算是成功。

問題:此種情況Node 1是否會優先將請求轉給主Shard所在Node?
問題:如果超過重復次數,系統行為如何?

分布式文件存儲

如何制作索引?

假如有12條date是2014-xx-xx的文件。但只有一個文件的date是2014-09-15。那我們發送以下請求:

GET /_search?q=2014 

# 12 results

GET /_search?q=2014-09-15 

# 12 results !

GET /_search?q=date:2014-09-15 

# 1 result

GET /_search?q=date:2014 

# 0 results !

結果為什么這么奇怪?

映射與分析

跨字段搜索

當存儲文件時,ElasticSearch預設會另外存儲一個_all字段。該字段預設由所有字段串接而成,並使用inverted index制作索引提供全文搜索。例如:

{
	"tweet": "However did I manage before Elasticsearch?",
	"date": "2014-09-14",
	"name": "Mary Jones",
	"user_id": 1
}

該文件的_all如下:

"However did I manage before Elasticsearch? 2014-09-14 Mary Jones 1"

映射

當有文件存儲進來時,ElasticSearch預設會為該type自動生成mapping,用來決定如何制作索引以提供搜索。

{
	"gb": {
		"mappings": {
			"tweet": {
				"properties": {
					"date": {
						"type": "date",
						"format": "dateOptionalTime"
					},
					"name": {
						"type": "string"
					},
					"tweet": {
						"type": "string"
					},
					"user_id": {
						"type": "long"
					}
				}
			}
		}
	}
}

exact value 與full text

  • ElasticSearch把值分成兩類:exact value 與full text。

  • 當針對exact value的字段搜索時,使用布爾判斷,例如:Foo != foo。

  • 當針對full text的字段搜索時,則是計算相關程度,例如:UK與United Kingdom相關、jumping與leap也相關。

Inverted Index

ElasticSearch用inverted index建立索引,提供全文搜索。考慮以下兩份文件:

The quick brown fox jumped over the lazy dog

Quick brown foxes leap over lazy dogs in summer

  • 建立出來的inverted index看起來大概如下表。

      ---------------------------------------
      	Term 	|	Doc_1 	|	Doc_2
      ---------------------------------------
      	Quick 	| 			| 	X
      	The 	|	X 		|
      	brown 	| 	X 		| 	X
      	dog 	| 	X 		|
      	dogs 	| 			| 	X
      	fox 	| 	X 		|
      	foxes 	| 			| 	X
      	in 		| 			| 	X
      	jumped 	| 	X 		|
      	lazy 	| 	X 		| 	X
      	leap 	| 			| 	X
      	over 	| 	X 		| 	X
      	quick 	| 	X 		|
      	summer 	| 			|	X
      	the 	| 	X 		|
      ---------------------------------------
    
  • 搜索“quick brown”的結果如下表。

      ---------------------------------------
      	Term 	|	Doc_1 	|	Doc_2
      ---------------------------------------
      	brown 	| 	X 		| 	X
      ---------------------------------------
      	quick 	| 	X 		|
      ---------------------------------------
      	Total 	| 	2 		|	1
      ---------------------------------------
    
  • 此表還可以優化,例如

    • Quick可以變成quick
    • foxes,dogs可以變成fox與dog
    • jumped,leap可以變成jump

    這種分詞(tokenization)、正規化(normalization)過程叫做analysis

  • 優化結果如下

      ---------------------------------------
      	Term 	|	Doc_1 	|	Doc_2
      ---------------------------------------
      	brown 	| 	X 		| 	X
      	dog 	| 	X 		|	X
      	fox 	| 	X 		|	X
      	in 		| 			| 	X
      	jump 	| 	X 		|	X
      	lazy 	| 	X 		| 	X
      	over 	| 	X 		| 	X
      	quick 	| 	X 		|	X
      	summer 	| 			|	X
      	the 	| 	X 		|	X
      ---------------------------------------	
    

Analysis與Analyzers

Analysis程序由Analyzer完成,Analyzer由下面三個功能組成:

  1. Character filters

    首先,字符串先依次經過character filters處理過,再進行分詞。例如可能將html標簽移除,或將 & 轉換為 and。

  2. Tokenizer

    分詞器就是將字符串切為許多有意義的單詞。

  3. Token filters

    每個單詞再依序經過token filters做最后處理。例如可能將Quick變成quick、把leap換成jump。

中文分詞

回答之前的問題

假如有12條date是2014-xx-xx的文件。但只有一個文件的date是2014-09-15。那我們發送以下請求:

  1. 用2014去全文搜索_all字段

     GET /_search?q=2014 
    

    # 12 results

  2. 2014-09-05經過分析后變成使用2014,09,15去全文搜索_all字段,由於每份文件都有2014所以全部相關。

     GET /_search?q=2014-09-15 
    

    # 12 results !

  3. 針對date字段搜索exact value

     GET /_search?q=date:2014-09-15 
    

    # 1 result

  4. 針對date字段搜索exact value,沒有文件的date是2014
    GET /_search?q=date:2014

    # 0 results !

數據是分布式存儲的

分布式搜索

  • ElasticSearch將搜索分成兩個階段,來完成在分布式系統中的搜索與排序:query與fetch。

  • 排序的其中一個目的是為了分頁,考慮一下請求:

      GET /_search
      {
      	"from": 90,
      	"size": 10
      }	
    

query階段

  1. Node 3 收到搜索請求后制作一個大小為from + size = 100的priority queue來排序。
  2. Node 3 將搜索請求轉給其他每個shard,此例為0號與1號。每個shard將自己搜索,並用priority queue排序出前from + size = 100個結果。
  3. 每個shard將各自結果的IDs與排序值回傳給Node 3,Node 3再將這些結果加入priority queue中。

問題:為什么每個Node都是from + size = 100個結果?

fetch階段

  1. Node 3 將排序完的IDs取出需要的部分,即最后10筆,再發送Multi-GET請求跟文件所在的shard取得完整的文件。
  2. shard 各自收到請求后,取出文件,若需要的話再經過處理,例如加上metadata與片段高亮,再回傳給Node 3。
  3. Node 3 取得所有結果后再回傳給客戶端。

在分布式系統中的排序與分頁

  • ElasticSearch搜索結果預設只會回傳10個經過_score排序的文件。

  • 我們可以透過size與from參數,取得其他分頁的結果。例如

      GET /_search?size=10&from=10000
    
  • 但是在分布式系統中分頁的成本非常高。預設一個Index有5個shard,若要取出第1000頁的內容,必須從每個shard取出前10010個文件。再將總共50050個結果重新排序取出10個(10001 ~ 100010)。因此若要取出大量數據,不建議使用排序與分頁的功能。

scan與scroll

GET /old_index/_search?search_type=scan&scroll=1m
{
	"query": { "match_all": {}},
	"size": 1000
}
  • 設定 search_type = scan,這樣一來ElasticSearch不會對結果進行排序。

  • 設定scroll = 1m,將會對這個搜索建立快照,並維持一分鍾。根據回傳的_scroll_id可以取得下一批的結果。經由size參數可以設定一個shard一批最多取多少文件,因此每一批取得的數量最多為

      size * number_of_primary_shards
    
  • 透過scan與scroll,我們可以批次取得大量的文件,而且不會有分頁成本。若有需要重新索引整個index時,可以使用此方法完成。

最佳化Index

Index 設定

雖然ElasticSearch在存入文件的時候就會自動創建Index,但使用預設設定可能不是個好主意。例如:

  • 主要的shard數量不能夠被修改

  • 自動mapping可能會猜錯

  • 使用不符需求的Analyzer

這些設定都需要在存儲數據之前設定完成,否則將會需要重新索引整個index。

除了手動設定index之外,也可以使用index template自動套用設定。

mapping的部分則可以設定dynamic_templates自動套用。

別名零宕機Zero downtime

若想要改變已經既有字段的索引方式,例如改變Analyzer。將需要重新索引整個index,否則既有的數據與新索引的不一致。

利用Index別名,可以做到Zero downtime的重新索引。

  • Application請求的index叫做my_index。實際上my_index是個別名,指到的是my_index_v1。

  • 若要重新索引,則創建新的my_index_v2,並套用新的設定。再透過scan與scroll將文件從my_index_v1放入my_index_v2。

  • 最后將my_index別名導向my_index_v2即可。

Shard內部

  • shard是一個低階的工作單元,事實上是一個Lucene實例,負責文件的存儲與搜索。
  • inverted index由shard創建,並寫入磁盤,而且建立好的inverted index不會被更改。

不會被修改的inverted index

inverted index的不變性帶來許多好處,如:

  • 當多個processes來讀取時,不需要鎖定。

  • 一旦被讀入系統的cache,將會一直保留在cache中,如此一來便不用再訪問磁盤。

更新inverted index

Lucene 帶來了per-segment search的概念,segment就是一個inverted index。既有的segment不被改變,因此當有新文件存入的時候,就建立新的segment;若更新或刪除文件時,則另外將舊文件標記.del檔案。而搜索時,就依序對各個segment搜索。

合並segments

然而隨着時間經過,segment數量越來越多,搜索成本也越來越大。ElasticSearch會定期將segment合並,同時一除掉那些被標記為刪除的文件。

問題:如何定期?

參考

參考來源:

《ElasticSearch: The Definitive Guide》

Slideshare: Elasticsearch 簡介

SlideShare: What is in a Lucene index?

結束


免責聲明!

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



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