1. 程式人生 > >Elasticsearch 索引管理和內核探秘

Elasticsearch 索引管理和內核探秘

區分 執行 每隔一秒 流程 禁用 tex buffer scroll 似的

1. 創建索引,修改索引,刪除索引

//創建索引
PUT /my_index
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0
  },
  "mappings": {
    "my_type": {
      "properties": {
        "my_field": {
          "type": "text"
        }
      }
    }
  }
}

//修改索引
PUT /my_index/_settings
{
    "number_of_replicas"
: 1 } //刪除索引 DELETE /my_index DELETE /index_one,index_two DELETE /index_* DELETE /_all

2. 默認分詞器standard

standard tokenizer:以單詞邊界進行切分
standard token filter:什麽都不做
lowercase token filter:將所有字母轉換為小寫
stop token filer(默認被禁用):移除停用詞,比如a the it等等

修改分詞器設置:

啟用english停用詞token filter

PUT /my_index
{
  "settings": {
    
"analysis": { "analyzer": { "es_std": { "type": "standard", "stopwords": "_english_" } } } } }

3.內核--type底層數據結構

type,是一個index中用來區分類似的數據的,類似的數據但可能有不同的fields;
field的value,在底層的lucene中建立索引的時候,全部是opaque bytes類型,不區分類型的,因為lucene是沒有type的概念的,在document中,實際上將type作為一個document的field來存儲,即_type,es通過_type來進行type的過濾和篩選;


一個index中的多個type,實際上是放在一起存儲的,因此一個index下,不能有多個type重名,而類型或者其他設置不同的,因為那樣是無法處理的;

最佳實踐,將類似結構的type放在一個index下,這些type應該有多個field是相同的;
假如說,你將兩個type的field完全不同,放在一個index下,那麽就每條數據都至少有一半的field在底層的lucene中是空值,會有嚴重的性能問題;

//底層存儲,一個index下所有的field都會存儲,對於不同type下的字段若不存在,則置為空;
{
  "_type": "elactronic_goods",
  "name": "geli kongtiao",
  "price": 1999.0,
  "service_period": "one year",
  "eat_period": ""
}

{
  "_type": "fresh_goods",
  "name": "aozhou dalongxia",
  "price": 199.0,
  "service_period": "",
  "eat_period": "one week"
}

4. 定制dynamic策略和dynamic mapping策略

定制dynamic策略:

true:遇到陌生字段,就進行dynamic mapping;
false:遇到陌生字段,就忽略;
strict:遇到陌生字段,就報錯;

dynamic mapping策略:

1)date_detection

默認會按照一定格式識別date,比如yyyy-MM-dd。但是如果某個field先過來一個2017-01-01的值,就會被自動dynamic mapping成date,後面如果再來一個"hello world"之類的值,就會報錯。可以手動關閉某個type的date_detection,如果有需要,自己手動指定某個field為date類型。

PUT /my_index/_mapping/my_type
{
    "date_detection": false
}

2)定制自己的dynamic mapping template(type level)

PUT /my_index
{
    "mappings": {
        "my_type": {
            "dynamic_templates": [
                { "en": {
                      "match":              "*_en", 
                      "match_mapping_type": "string",
                      "mapping": {
                          "type":           "string",
                          "analyzer":       "english"
                      }
                }}
            ]
}}}
PUT /my_index/my_type/1
{
  "title": "this is my first article"
}
PUT /my_index/my_type/2
{
  "title_en": "this is my first article"
}

title沒有匹配到任何的dynamic模板,默認就是standard分詞器,不會過濾停用詞,is會進入倒排索引,用is來搜索是可以搜索到的
title_en匹配到了dynamic模板,就是english分詞器,會過濾停用詞,is這種停用詞就會被過濾掉,用is來搜索就搜索不到了

3)定制自己的default mapping template(index level)

PUT /my_index
{
    "mappings": {
        "_default_": {
            "_all": { "enabled":  false }
        },
        "blog": {
            "_all": { "enabled":  true  }
        }
    }
}

5. 重建索引

一個field的設置是不能被修改的,如果要修改一個Field,那麽應該重新按照新的mapping,建立一個index,然後將數據批量查詢出來,重新用bulk api寫入index中;批量查詢的時候,建議采用scroll api,並且采用多線程並發的方式來reindex數據,每次scoll就查詢指定日期的一段數據,交給一個線程即可;

1)一開始,依靠dynamic mapping,插入數據,但是些數據是2017-01-01這種日期格式的,所以title這種field被自動映射為了date類型,實際上它應該是string類型的;

2)當後期向索引中加入string類型的title值的時候,就會報錯;如果此時想修改title的類型,是不可能的;

3)唯一的辦法是進行reindex,也就是說,重新建立一個索引,將舊索引的數據查詢出來,再導入新索引;

4)如果說舊索引的名字,是old_index,新索引的名字是new_index,終端java應用,已經在使用old_index在操作了,難道還要去停止java應用,修改使用的index為new_index,才重新啟動java應用嗎?這個過程中,就會導致java應用停機,可用性降低;所以說,給java應用一個別名,這個別名是指向舊索引的,java應用先用著,java應用先用goods_index alias來操作,此時實際指向的是舊的my_index; PUT /my_index/_alias/goods_index

5)新建一個index,調整其title的類型為string;

6)使用scroll api將數據批量查詢出來;采用bulk api將scoll查出來的一批數據,批量寫入新索引;

POST /_bulk
{ "index":  { "_index": "my_index_new", "_type": "my_type", "_id": "2" }}
{ "title":    "2017-01-02" }

7)反復循環6,查詢一批又一批的數據出來,采取bulk api將每一批數據批量寫入新索引

8)將goods_index alias切換到my_index_new上去,java應用會直接通過index別名使用新的索引中的數據,java應用程序不需要停機,零提交,高可用;

POST /_aliases
{
    "actions": [
        { "remove": { "index": "my_index", "alias": "goods_index" }},
        { "add":    { "index": "my_index_new", "alias": "goods_index" }}
    ]
}

9)直接通過goods_index別名來查詢,GET /goods_index/my_type/_search

5.2 基於alias對client透明切換index
PUT /my_index_v1/_alias/my_index

client對my_index進行操作
reindex操作,完成之後,切換v1到v2
POST /_aliases
{
    "actions": [
        { "remove": { "index": "my_index_v1", "alias": "my_index" }},
        { "add":    { "index": "my_index_v2", "alias": "my_index" }}
    ]
}

6. 倒排索引不可變的好處

(1)不需要鎖,提升並發能力,避免鎖的問題;
(2)數據不變,一直保存在os cache中,只要cache內存足夠;
(3)filter cache一直駐留在內存,因為數據不變;
(4)可以壓縮,節省cpu和io開銷;

倒排索引不可變的壞處:每次都要重新構建整個索引;

7. document寫入的內核級原理

技術分享圖片

(1)數據寫入buffer緩沖和translog日誌文件

(2)每隔一秒鐘,buffer中的數據被寫入新的segment file,並進入os cache,此時segment被打開並供search使用,不立即執行commit;--實現近實時;

數據寫入os cache,並被打開供搜索的過程,叫做refresh,默認是每隔1秒refresh一次。也就是說,每隔一秒就會將buffer中的數據寫入一個新的index segment file,先寫入os cache中。所以,es是近實時的,數據寫入到可以被搜索,默認是1秒。

(3)buffer被清空

(4)重復1~3,新的segment不斷添加,buffer不斷被清空,而translog中的數據不斷累加

(5)當translog長度達到一定程度的時候,commit操作發生

(5-1)buffer中的所有數據寫入一個新的segment,並寫入os cache,打開供使用

(5-2)buffer被清空

(5-3)一個commit ponit被寫入磁盤,標明了所有的index segment;會有一個.del文件,標記了哪些segment中的哪些document被標記為deleted了;

(5-4)filesystem cache中的所有index segment file緩存數據,被fsync強行刷到磁盤上

(5-5)現有的translog被清空,創建一個新的translog

//調整刷新的頻率
PUT /my_index
{
  "settings": {
    "refresh_interval": "30s" 
  }
}

默認會在後臺執行segment merge操作,在merge的時候,被標記為deleted的document也會被徹底物理刪除;

每次merge操作的執行流程:

(1)選擇一些有相似大小的segment,merge成一個大的segment;
(2)將新的segment flush到磁盤上去;
(3)寫一個新的commit point,包括了新的segment,並且排除舊的那些segment;
(4)將新的segment打開供搜索;
(5)將舊的segment刪除;

POST /my_index/_optimize?max_num_segments=1,盡量不要手動執行,讓它自動默認執行;

Elasticsearch 索引管理和內核探秘