Elasticsearch是開源搜索引擎,在深入使用Elasticsearch搜索引擎過程中,積累了一些經驗和技巧,建議用戶在使用云搜索服務時,作為參考。
提高索引效率
- 使用多進程或多線程發送數據到Elasticsearch
一個單線程發送bulk請求不能夠發揮一個集群的索引能力。為了更好地利用集群的資源,應該使用多線程或多進程來發送數據,提升數據處理效率。
對于相同大小的bulk請求,通過測試可以得到最優的線程數量。可以逐步增加線程數量直至到集群中的機器Load或CPU飽和。建議使用“nodes stats”接口查看節點中的cpu和load狀態,您可以通過“os.cpu.percent”、“os.cpu.load_average.1m”、“os.cpu.load_average.5m”和“os.cpu.load_average.15m”參數信息了解詳細信息。
例如,在執行bulk請求時,使用的線程數量為2個,觀察Load和CPU的情況,如果未飽和,可再增加線程數量。當線程數量增加到N個時,此時Load和CPU已飽和,建議就采用N個線程去執行bulk請求提高索引效率。通過測試獲得最優的線程數量。
- 增加refresh_interval刷新的間隔時間
默認情況下,每個分片每秒自動刷新一次。但并不是所有場景都需要每秒刷新。在使用Elasticsearch索引大量的日志文件,想優化索引速度而不是近實時搜索,可以通過設置,降低每個索引的刷新頻率。
PUT /my_logs
{
"settings": {
"refresh_interval": "30s"
}
}
- 在初始化索引時,可以禁用refresh和replicas數量
如果需要一次導入較大數據量的數據進index里面時,可以先禁用refresh,把“refresh_interval”設置成為“-1”,把“number_of_replicas”設置成“0”。當數據導入完成后,將refresh_interval和number_of_replicas設置回原來的值。
選擇合適的分片數和副本數
在創建索引數據時,建議指定相關的分片數和副本數,否則會使用服務器中的默認配置參數“shards=5, replicas=1”,即分片數為5,副本數為1。
分片數,與檢索速度非常相關的指標,如果分片數過少或過多都會導致檢索比較慢。分片數過多會導致檢索時打開比較多的文件,且會導致多臺服務器之間通訊慢。而分片數過少會導致單個分片索引過大,所以檢索速度慢。
根據機器數、磁盤數、索引大小等設置分片數,建議單個分片不要超過30GB。總數據量除以分片數,則為分片的大小。
PUT /my_index
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
}
}
將數據存放在不同的索引
Elasticsearch是基于Lucene進行索引和存儲數據的,主要的工作方式是密集的數據,即所有的document擁有相同的字段。
- 避免把無關聯的數據放在同一個index
不要把完全不同的數據結構document放在同一個index里。可以考慮創建一些較小的index,用較少的shard去存儲。
- 避免不同的type放在同一個index
多個type放在單個index看起來是個簡單的方法,但是Elasticsearch并不是基于type來存儲的,不同的type在單個index會影響效率。如果type沒有非常相似的mapping,建議放到一個單獨的index。
- 同一個index里面不同type之間字段不能沖突
如果有兩個不同的type,每個type都有同名的字段,但映射不同,這在Elasticsearch是不允許的。
按照時間范圍創建索引
在Elasticsearch用于存儲跟時間相關的數據時,如日志數據,建議按照時間范圍創建索引,而不是把所有數據都存放到一個超級大的索引里面。
基于時間范圍索引。可以開始于一個按年的索引(logs_2014)或按月的索引(logs_2014-10)。當數據量變得非常龐大的時候切換到一個按天的索引(logs_2014-10-24)。
按照時間范圍創建索引具有如下優勢:
- 擴容的時候根據當前數據量選擇合適的shard和Replica
針對時間范圍創建的每個索引都可以靈活的設置Shard數和Replica數,從而可以避免在一開始設置一個很大的shard來考慮擴容的情況。在集群擴容之后也可以方便的設置時間范圍周期來適配集群規模。
- 刪除舊數據只需要刪除舊的索引
DELETE /logs_2014-09
- 利用alias機制可以在索引間靈活切換
例如,將logs_current的alias機制中的logs_2014-09索引刪除,并在此alias機制中新增logs_2014-10索引。
POST
/_aliases{"actions": [{ "add": { "alias":
"logs_current",
"index": "logs_2014-10" }},{ "remove": { "alias":
"logs_current",
"index": "logs_2014-09" }}]}
- 針對不再更新的索引,如上周或者上月的索引,進行索引優化以提高查詢效率
將logs_2014-09-30索引下多個小segment合并成一個大的分片,以提高查詢效率。
7.x之前版本
PUT /logs_2014-09-30/_settings
{ "number_of_replicas": 0 }
POST /logs_2014-09-30/_forcemerge?max_num_segments=1
PUT /logs_2014-09-30/_settings
{ "number_of_replicas": 1 }
7.x之后版本
PUT /logs_2014-09-30/_settings
{ "number_of_replicas": 0 }
POST /logs_2014-09-30/_forcemerge
{
"max_num_segments":1
}
PUT /logs_2014-09-30/_settings
{ "number_of_replicas": 1 }
優化索引配置
- 區分text和keyword
在Elasticsearch中string字段被拆分成兩種新的數據類型:text用于全文搜索的,而keyword用于關鍵詞搜索。
對于不需要分詞的字符串精確值字段,如標簽或枚舉,建議配置為keyword類型。
7.x之前版本
PUT my_index1
{
"mappings": {
"my_type": {
"properties": {
"tags": {
"type": "keyword"
},
"full_name": {
"type": "text"
}
}
}
}
}
7.x之后版本
PUT my_index1
{
"mappings": {
"properties": {
"tags": {
"type": "keyword"
},
"full_name": {
"type": "text"
}
}
}
}
- 基于text字段的聚合統計
分詞字段的聚合統計不是一種常見的需求。在Elasticsearch對于分詞字段的聚合統計需要用到fielddata,默認是禁用的,開啟fielddata會帶來較大的內存負擔。
建議的做法是分詞字符串進行多字段映射,映射為一個text字段用于全文檢索,和一個keyword字段用于聚合統計。
7.x之前版本
PUT my_index2
{
"mappings": {
"my_type": {
"properties": {
"full_name": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
}
}
}
}
}
7.x之后版本
PUT my_index2
{
"mappings": {
"properties": {
"full_name": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
}
}
}
}
使用索引模板
Elasticsearch支持通過索引模板控制一些新建索引的設置(settings)和映射(mappings),如限制分片數為1,并且禁用_all域。索引模板可以用于控制何種設置(settings)應當被應用于新創建的索引:
- 索引模板可以通過template字段指定通配符。
- 多個索引模板可以通過order指定覆蓋順序。數值越大,優先級越高。
如下示例表示,logstash-*匹配的索引采用my_logs模板,且my_logs模板的優先級數值為1。
7.x之前版本
PUT /_template/my_logs
{
"template": "logstash-*",
"order": 1,
"settings": {
"number_of_shards": 1
},
"mappings": {
"_default_": {
"_all": {
"enabled": false
}
}
},
"aliases": {
"last_3_months": {}
}
}
7.x之后版本
7.x之后版本
PUT /_template/my_logsa
{
"index_patterns": ["logstasaah-*"],
"order": 1,
"settings": {
"number_of_shards": 1
},
"mappings": {
"properties": {
"_all": {
"enabled": false
}
}
},
"aliases": {
"last_3_months": {}
}
}
數據備份和恢復
Elasticsearch副本提供了高可靠性,讓您可以容忍零星的節點丟失而不會中斷服務。
但是,副本并不提供對災難性故障的保護。對這種情況,您需要的是對集群真正的備份,在某些東西確實出問題的時候有一個完整的拷貝。
備份集群,您可以使用創建快照的功能,將集群的數據保存到OBS桶中。其備份過程是智能的。第一個快照建議是數據的完整拷貝,后續的快照會保留的是已存快照和新數據之間的差異。隨著您不時的對數據進行快照,備份也在增量的添加和刪除。這意味著后續備份會相當快速,因為它們只傳輸很小的數據量。
用過濾提高查詢效率
過濾器的執行速度非常快,不會計算相關度(直接跳過了整個評分階段),而且很容易被緩存。
通常當查找一個精確值的時候,我們不希望對查詢進行評分計算。只希望對文檔進行包括或排除的計算,所以我們會使用constant_score查詢以非評分模式來執行term查詢并以一作為統一評分。
并以一作為統一評分。
GET /my_store/products/_search
{
"query" : {
"constant_score" : {
"filter" : {
"term" : {
"city" : "London"
}
}
}
}
}
采用scroll API返回大量數據
當返回大量數據時,先查后取的過程支持用from和size參數分頁,但有限制。結果集在返回之前需要在每個分片上先進行排序,然后合并之后再排序輸出。使用足夠大的from值,排序過程可能會變得非常沉重,使用大量的CPU、內存和帶寬。因此,強烈建議不要使用深分頁。
為了避免深度翻頁,推薦采用scroll查詢返回大量數據。
scroll查詢可以用來對Elasticsearch有效地執行大批量的文檔查詢,而又不用付出深度分頁那種代價。scroll查詢允許我們先做查詢初始化,然后再批量地拉取結果。
查詢(query)與過濾(filter)的區別
性能差異: 一般情況下,一次過濾會比一次評分的查詢性能更優異,并且表現更穩定。
當使用過濾情況時,查詢被設置為一個“不評分”或“過濾”查詢。即這個查詢只是去判斷是否匹配,結果是yes或no。
例如,以下情況是典型的過濾情況:
- created時間是否在2013與2014這個區間?
- status字段是否包含published這個單詞?
- lat_lon字段表示的位置是否在指定點的10km范圍內?
當使用查詢情況時,查詢會變成一個“評分”的查詢。和不評分的查詢類似,也要去判斷這個文檔是否匹配,同時還需要判斷這個文檔匹配的程度如何。此查詢的典型用法是用于查找以下文檔:
- 查找與full text search這個詞語匹配的文檔。
- 包含run這個詞,也能匹配runs、running、jog或者sprint。
- 包含quick、brown和fox這幾個詞,詞之間離的越近,文檔相關性越高。
- 標有lucene、search或java標簽,標簽越多,相關性越高。
驗證查詢是否合法
查詢在和不同的分析器與不同的字段映射相結合時,會比較難理解,可以用validate-query API來驗證查詢是否合法。
示例:在Kibana的Console界面中,執行如下命令。validate請求會告訴您這個查詢不合法。
7.x之前版本
GET /gb/tweet/_validate/query
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
7.x之后版本
GET /gb/tweet/_validate/query
{
"query": {
"productName" : {
"match" : "really powerful"
}
}
}
為了找出查詢不合法的原因,可以把explain參數加到查詢字符串中,執行如下命令。
7.x之前版本
GET /gb/tweet/_validate/query?explain
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
7.x之后版本
GET /gb/tweet/_validate/query?explain
{
"query": {
"productName" : {
"match" : "really powerful"
}
}
}
返回結果如下所示,可以從返回結果看出查詢類型(match)與字段名稱(tweet)搞混了。
{
"valid": false,
"error": "org.elasticsearch.common.ParsingException: no [query] registered for [tweet]"
}
因此,對于合法查詢,使用explain參數將返回可讀的描述,這對準確理解云搜索服務是如何解析query是非常有幫助的。