天天看點

Index alias — Elastic Stack 實戰手冊

Index alias — Elastic Stack 實戰手冊
https://developer.aliyun.com/topic/download?id=1295 · 更多精彩内容,請下載下傳閱讀全本《Elastic Stack實戰手冊》 https://developer.aliyun.com/topic/download?id=1295 https://developer.aliyun.com/topic/es100 · 加入創作人行列,一起交流碰撞,參與技術圈年度盛事吧 https://developer.aliyun.com/topic/es100

創作人:楊松柏

别名的優勢

索引别名是一個非常好的”工具“,他可以幫助解決以下問題:

  • 如果對寫入 Elasticsearch 的資料進行極少的修改,索引别名+ Rollover 可以很好控制每個索引的大小,零停機切換索引;合适的索引大小可以提升資料的查詢性能,資料恢複性能。
  • 解耦 client 與索引的強耦合,Elasticsearch 維護人員可以對索引有更靈活的操作空間,且讓使用者側無感覺。
  • 結合 Reindex 可以很友善的完成索引重建。
  • 過濾别名和路由别名可以在一定程度上幫助提升查詢性能。
  • 一個綁定多個索引的别名,如果要查詢一次查詢這多個索引,别名可以使 uri 變的簡潔。

什麼是别名

别名,是為一個或多個索引而命名的第二名稱,第二名稱不得與叢集中任何索引同名;隻要把第二名稱和真實索引建立綁定關系,便可以使用别名對索引進行相關的操作。

别名管理

别名建立

索引别名的 REST 文法如下

#索引别名
PUT /<index>/_alias/<alias>?master_timeout=<time>&timeout=<time>
#過濾别名,路由别名
PUT /<index>/_alias/<alias>?master_timeout=<time>&timeout=<time>
{
  "routing" : "routing_value",
  "filter" : {
    "term" : {
      "filed" : value
    }
  }
}
#以下三種方式同上
POST /<index>/_alias/<alias>
PUT /<index>/_aliases/<alias>
POST /<index>/_aliases/<alias>           

URI參數釋意

必填參數,參數類型

string

;該參數可以由逗号分隔的索引,或者用通配符表達式。值也可為

_all

,表示作用于叢集中的所有索引。

string

,索引别名,建議名字使用有意義的單詞和數字組成。

master_timeout

可選參數,

value

值的機關可為

d

h

m

s

ms

micros

nanos

; 等待連接配接到主節點的時間。如果在逾時時間門檻值之前沒有收到響應,則請求失敗并傳回錯誤,預設值為

30s

timeout

value

d

h

m

s

ms

micros

nanos

;請求等待響應的時間。如果在逾時時間門檻值之前沒有收到響應,則請求失敗并傳回錯誤,預設值為

30s

請求體

建立一個索引别名的時候,通常路由别名和過濾别名需要指定請求體。

filter

必填參數,将過濾參數綁定到别名,使别名具有特定的查詢功能;包含此參數的别名,通常将其稱作為過濾别名。

routing

可選

string

類型參數,自定義路由值用于将操作路由到特定分片的;包含此參數的我們通常将其稱作為路由别名。

批量建立别名

批量建立别名 REST 文法如下

POST /_aliases?master_timeout=<time>&timeout=<time>
{
  "actions" : [
    { "<action>" : { "alias" : "index-alias", "<must_param>" : "value", "<option_param>" : "value",... } }
  ]
}           

請求體參數釋意

actions

必填參數,數組内包含一系列的動作

<action>

,支援的動作如下

add

為一個索引或多個索引添加一個别名

remove

将别名移除與索引的關聯關系

remove_index

删除索引,等效于 delete index API。該動作隻對索引别名生效,如果嘗試删除索引别名,将會失敗。

JSON 體内支援的參數包括必填參數和可選參數。

詳情如下:

必填參數:

index

參數類型

string

,支援通配符。如果

indices

沒有指定,則該參數必須指定。

注意:不能向索引别名添加資料流。

indices

string

數組,該數組内的索引将被執行相應的動作。如果

index

注意事項與index參數相同。

alias

string

,為以逗号分隔或者通配符表示的索引,

add

,

remove

delete

别名。如果

aliases

aliases

string array

,需要進行

add

remove

delete

的索引别名組。如果alias沒有指定,則該參數必須指定。

可選參數 :

query object

查詢對象體,綁定了過濾查詢的别名。如果指定,使用别名進行空查詢,将隻傳回滿足過濾條件的文檔。

is_hidden

bool

值,預設值為

false

;如果設定為

true

,使用通配符表達式别名進行搜尋排除,該别名關聯的資料将查詢不到;除非在請求中使用

expand_wildcards

參數重寫。對于共享同一個别名的所有索引,必須将此屬性設定為相同的值。

must_exist

bool

值,預設值為false,如果設定為

ture

,移除别名時,該别名必須存在。

is_write_index

bool

false

true

,則可以直接使用該别名對關聯的索引進行資料寫入或者配置修改等操作;若别名綁定多個索引,則隻能存在一個

is_write_index

值為

true

的綁定。注意:在同一個索引

is_hidden

is_write_index

不能同時設定為

true

;當别名隻綁定一個索引時無需現實設定該值為true,該别名具有寫權限,但是當别名再次綁定另外一個索引,則别名的寫權限取消,除非現實指定

is_write_index

的值為

true

string

,自定義值作為路由計算值,将操作路由到對應的分片上。

index_routing

string

,自定義值作為路由計算值,将寫入操作路由到對應的分片上。

search_routing

string

,自定義值作為路由計算值,将查詢操作路由到對應的分片上。

别名建立與修改示例

假設已經存在表

user1

user2

,為他們綁定别名,示例子如下:

#為 users1 表建立 index-alias-name1 别名
PUT /users1/_alias/index-alias-name1

#為 users1,users2 表建立 index-alias-name2 别名
PUT /users1,users2/_alias/index-alias-name2

#為 users 開頭的索引建立 index-alias-name3别名
PUT /users*/_alias/index-alias-name3

#為 users1 索引添加具有路由和過濾功能的别名 routing-filter-index-alias
PUT users1/_alias/routing-filter-index-alias
{
  "routing" : "12",
  "filter" : {
    "term" : {
      "user_id" : 12
    }
  }
}
#為 users1 索引添加路由别名routing-index-alias,路由計算值為12
PUT users1/_alias/routing-index-alias
{
  "routing" : "12"
}

#為 users1 索引添加過濾别名filter-index-alias,過濾 user_id 為12
PUT users1/_alias/filter-index-alias
{
  "filter" : {
    "term" : {
      "user_id" : 12
    }
  }
}           

别名建立成功之後,如果别名與索引的關系,為一個别名隻對應一個索引,或者有一個綁定關系的 is_write_index (後文會介紹)值為 true ;那麼我們可以通過别名往索引寫如資料。

#通過别名,寫入資料
PUT index-alias-name1/_bulk?refresh=true
{"index":{}}
{"user_id":"tom123456-user1"}

#通過路由别名,寫入資料
PUT routing-index-alias/_bulk?refresh=true
{"index":{}}
{"user_id":"kimchy123456-routing"}

#通過索引,寫入資料
PUT users2/_bulk?refresh=true
{"index":{}}
{"user_id":"kimchy123456-user2"}
{"index":{}}
{"user_id":"12"}           

插入資料後可以通過别名或者索引查詢

#以下三條查詢語句的結果是等價的,傳回2條資料
GET index-alias-name1/_search
GET users1/_search
GET routing-index-alias/_search

#以下三條查詢語句的結果是等價的(如果沒有其他 users 開頭的索引),傳回四條資料
GET users1,users2/_search
GET users*/_search
GET index-alias-name2/_search

#查詢 user2 的索引,傳回2條資料
GET users2/_search

#傳回值為空,因為索引 users1 插入的文檔沒有 user_id 值為2的
GET filter-index-alias/_search

# 傳回值為一條,因為索引 users2 有一條 user_id 值為2的文檔
GET index-alias-name3/_search           

批量建立索引别名示例

批量建立索引别名,即使用

POST /_aliases

中定義多個

action

。為

test1

test2

索引綁定一個名稱為

alias1

的别名。

POST /_aliases
{
  "actions" : [
    { "add" : { "index" : "test1", "alias" : "alias1" } }
    { "add" : { "index" : "test2", "alias" : "alias1" } }
  ]
}           

重命名别名

如果我們需要對一個索引進行别名替換,隻需要在同一個 API 中簡單的先

remove

掉舊别名,然後綁定新的别名即可;該操作為原子型操作,無需擔心别名在短時間内不指向索引

POST /_aliases
{
  "actions" : [
    { "remove" : { "index" : "test1", "alias" : "alias1" } },
    { "add" : { "index" : "test1", "alias" : "alias2" } }
  ]
}           

将一個别名關聯多個索引

test1

添加别名

alias1

test2

alias2

,代碼塊中的兩種方式等效。為索引綁定多個别名的文法與之類似,隻需将

alias

替換

aliases

數組。

POST /_aliases
{
  "actions" : [
    { "add" : { "index" : "test1", "alias" : "alias1" } },
    { "add" : { "index" : "test2", "alias" : "alias1" } }
  ]
}
# 等效于上面的方式
POST /_aliases
{
  "actions" : [
    { "add" : { "indices" : ["test1", "test2"], "alias" : "alias1" } }
  ]
}           

除了以上兩種方式外,我們還可以使用

glob

模式,将别名與多個索引相關聯綁定,這種方式隻會對叢集中已存在的索引生效,不會對之後建立的索引生效。

POST /_aliases
{
  "actions" : [
    { "add" : { "index" : "test*", "alias" : "all_test_indices" } }
  ]
}           

如果錯誤的建立了一個索引,同樣可以通過别名的方式來解決。

例如:錯誤的建立了一個名稱為

test

的索引,而實際需要的索引名稱為

test_2

,但是已經有資料往索引裡面寫入資料了;為了解決這個問題,首先建立正确的索引名稱,然後用一個原子操作,将

test

别名綁定

test_2

,同時删除索引

test

叢集狀态中不會發生别名綁定不到索引的情況;但由于索引和搜尋涉及多個步驟,正在運作或排隊的請求,可能會由于臨時不存在索引而失敗。

# 建立索引test
PUT test   
#  建立索引test_2 
PUT test_2   
POST /_aliases
{
  "actions" : [
    { "add":  { "index": "test_2", "alias": "test" } },
    { "remove_index": { "index": "test" } }  
  ]
}           

建立索引時綁定别名

上述給索引綁定别名,均需要提前建立索引;若需要别名能給對新索引生效,可以在建立索引時進行指定。

PUT test1
{
  "aliases" : {
      "alias1" : { },
      "alias2" : { }
    },
    "mappings" : { },
    "settings" : {}
}           

除此之外,還可以在索引模闆裡面進行指定;

如下代碼塊建立了一個模版名稱為

test

order

權重為

索引模闆,之後隻要是以

test

開頭的索引都會綁定别名

alias1

alias2

PUT _template/test
{
   "order" : 0,
   "index_patterns" : [
      "test*"
    ],
     "aliases" : {
      "alias1" : { },
      "alias2" : { }
    },
     "mappings" : { },
    "settings" : {}
}           

别名檢視

Get index alias

别名的檢視方式有如下幾種方式

GET /_alias
GET /_alias/<alias>
GET /<index>/_alias/<alias>?allow_no_indices=true&expand_wildcards=all&local=false&ignore_unavailable=false
           

路徑參數

可選參數,參數類型為

string

;參數支援單個索引或者以逗号分隔的多個索引再或者通配符表達式形式

string

;參數支援單個别名或者以逗号分隔的多個别名再或者通配符表達式形式

查詢參數

allow_no_indices

Boolean

,預設值為

true

;如果設定為

false

,任何通配符表達式、索引别名或_all值隻針對丢失或關閉的索引,則請求将傳回一個錯誤。即使請求以其他開放索引為目标,此行為也适用。

expand_wildcards

string

,該參數主要用于控制哪些特性的索引的别名可以被檢視,參數可取如下幾種類型的值:

  • all

比對所有資料流或索引,包括隐藏的資料流或索引。預設值為 all。

  • open

比對索引狀态為

open

,非

hidden

的索引,以及非

hidden

的資料流。

  • closed

closed

hidden

hidden

的資料流(資料流不能夠被關閉)。

  • hidden

比對隐藏的資料流和索引,且索引必須是打開或者關閉狀态。

  • none

不接受通配符表達式,即參數

<index>

中不能包含通配符表達式

ignore_unavailable

可選參數,參數類型

Boolean

false

。如果請求路徑中

<index>

有索引不存在,則請求将會發生錯誤。

local

Boolean

false

。如果設定為

true

,則僅僅從本地節點擷取叢集元資訊(包括索引别名資訊);如果設定為

true

,則從

master

節點擷取,

master

的資訊最權威,可以避免因為網絡等問題,造成的元資訊下發不及時,造成的擷取元資訊有誤,但也會增加網絡開銷。

_cat API

除了通過

Get index alias API

進行檢視索引别名,還可以

_cat API

進行索引别名檢視。

GET _cat/aliases
GET _cat/aliases/<alias>           

别名删除

Delete index alias

RESTful API

文法如下

DELETE /<index>/_alias/<alias>?master_timeout=<time>&timeout=<time>
DELETE /<index>/_aliases/<alias>?master_timeout=<time>&timeout=<time>           

參數釋意

string

;參數支援單個索引或者以逗号分隔的多個索引再或者通配符表達式形式。

_all

*

表示對叢集中所有索引。

string

;參數支援單個别名或者以逗号分隔的多個别名再或者通配符表達式形式。

_all

*

表示删除

<index>

的所有别名。

master_timeout

timeout

的預設值

30s

bulk 删除

使用

bulk

的方式,同時為一個索引或者一組索引,移除關聯的索引别名;假如已經為

test1

添加了别名

alias1

test2

alias2

,現在需要進行别名移除,可以執行如下四種操作。

# 移除指定索引的指定别名
POST /_aliases
{
  "actions" : [
    { "remove": { "index" : "test1", "alias" : "alias1" } },
    { "remove": { "index" : "test2", "alias" : "alias2" } }
  ]
} 
POST /_aliases
{
  "actions" : [
    {  "remove": { "indices" : ["test1","test2"], "aliases" : ["alias1","alias2"]}}
  ]
} 
# 移除以test開頭的索引的指定别名;使用通配符方式,需要注意影響範圍
POST /_aliases
{
  "actions" : [
    { "remove": { "index" : "test*", "alias" : "alias1" } },
    { "remove": { "index" : "test*", "alias" : "alias2" } }
  ]
} 

POST /_aliases
{
  "actions" : [
    { "remove": { "index" : "test*", "aliases" : ["alias1","alias2"] } }
  ]
}            

别名的分類與應用

依據不同的使用場景,我們可以簡單把别名分為三類:(1)索引别名(2)過濾别名(3)路由别名

過濾别名

一種建立同一索引的不同”視圖“的簡便方法。

通過将過濾條件綁定到對應别名,使用不同别名即擷取滿足不通條件的資料;使用

Query DSL

定義過濾器。

使用過濾别名,必須得保證過濾字段存在,是以提前建立好索引,并設定該字段的

schema

如下示例,首先建立一個名為

my-index-000001

的索引

PUT /my-index-000001
{
  "mappings": {
    "properties": {
      "user": {
        "properties": {
          "id": {
            "type": "keyword"
          }
        }
      }
    }
  }
}           

再為索引添加上過濾别名,并批量插入三個文檔

#添加過濾别名
POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "my-index-000001",
        "alias": "alias2",
        "filter": { "term": { "user.id": "kimchy" } }
      }
    }
  ]
}
#批量插入文檔
PUT my-index-000001/_bulk
{"index":{}}
{"user.id":"kimchy"}
{"index":{}}
{"user.id":"tom"}
{"index":{}}
{"user.id":"jerry"}           

使用别名執行一個空搜尋

GET alias2/_search

,和預期的一緻隻傳回

user.id

kimchy

的文檔

# 注意:傳回内容中省略了與本節無關的内容
{
  "hits" : {
    "hits" : [
      {
        "_index" : "my-index-000001",
        "_type" : "_doc",
        "_id" : "b_VkIXkB9LctWlE3HOtS",
        "_score" : 1.0,
        "_source" : {
          "user.id" : "kimchy"
        }
      }
    ]
  }
}           

路由别名

将路由字段綁定到對應的别名,通過别名操作時,會執行預設的路由規則,也可以了解為索引的另外一種“視圖”。使用路由别名,在一定程度提升寫入和查詢的性能,結合過濾别名,可以讓操作發送到準确的分片上。

如下示例首先建立一個索引,因為本文示範的 Elasticsearch 叢集隻有兩個資料節點,為了友善觀察,設定副本數目為 0,主分片數目設定為 2。

PUT /routing-index-000001
{
  "settings" :{
    "index":{
      "number_of_shards":2,
      "number_of_replicas": 0
    }
  },
  "mappings": {
    "properties": {
      "user": {
        "properties": {
          "id": {
            "type": "keyword"
          }
        }
      }
    }
  }
}           

建立一個路由别名

routing-index-alias1

綁定索引

routing-index-000001

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "routing-index-000001",
        "alias": "routing-index-alias1",
        "routing": "2"
      }
    }
  ]
}           

建立成功之後,使用别名進行的所有操作,都将以 2 計算路由位址,使用别名寫入一個文檔。

PUT routing-index-alias1/_bulk
{"index":{}}
{"user.id":"kimchy"}           

除了建立一個路由别名,也可以用索引在寫入文檔時,後面加上路由參數;往

routing-index-000001

索引寫入兩個文檔,并指定路由參數為 12。

PUT routing-index-000001/_bulk?routing=12
{"index":{}}
{"user.id":"tom"}
{"index":{}}
{"user.id":"jerry"}           

通過檢視

GET _cat/shards/routing-index-000001?v&h=index,shard,prirep,docs,node

,可以發現

routing=12

的文檔被寫到了 1 号分片,

routing=2

的文檔被寫到了 0 号分片。

index                shard prirep docs node
routing-index-000001 1     p         2 es-cn-n6w24fib900797tgz-29e2dafd-0003
routing-index-000001 0     p         1 es-cn-n6w24fib900797tgz-29e2dafd-0001           

為了驗證結果是不是這樣的,我可以執行以下 3 種查詢進行驗證。前兩個查詢的是等效的,都是以 2 為計算路由值,将傳回

user.id

kimchy

一個文檔;第三個查詢語句将傳回以

routing=12

寫入的文檔。

GET /routing-index-alias1/_search
GET /routing-index-000001/_search?routing=2
GET /routing-index-000001/_search?routing=12           

查詢的時候,使用别名查詢或者查詢參數指定路由值,查詢效果是等價的。

routing

的值并不一定要等于2,隻要滿足

hash

函數計算出來的結果一樣,定位到分片結果一緻。為了保證能夠正确查詢到文檔,建議

routing

的值和寫入的值保持一緻。

除了統一設定一個

routing

,還可以分别設定對不同動作生效的路由。

如下面分别設定查詢路由(

search_routing

)和寫入路由(

index_routing

),使用别名進行操作,查詢請求會發送到路由值 12 和 2 對應的分片,寫入操作隻會寫入路由

12

對應的分片。

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "routing-index-000001",
        "alias": "routing-index-alias2",
        "search_routing": "12,2",
        "index_routing": "12"
      }
    }
  ]
}           

如上代碼塊所示

search_routing

可以由多個以逗号分隔的路由值,

index_routing

隻能有一個值。

其實這也比較好了解,寫入的時候,通過一個路由值計算資料,應該寫到具體哪個分片,如果寫入有多個路由值,将無法确定寫入到哪一個分片;一個給定的分片上,可以有很多擁有不同路由值的文檔,是以在查詢的時候可以寫多個路由值。

使用路由别名查詢并且參數重新指定

routing

值;若

search_routing

的和路徑參數

routing

的個數大于 2,則取兩者的交集作為路由參數;若小于等于2且沒有交集,則取

search_routing

的值(寫入路由同理)。如下代碼是查詢不到任何文檔的,因為其取交集後的路由值為 2,而包含

tom

這個文檔寫入的路由值為 12,是以将查詢到對應的内容。

GET /routing-index-alias2/_search?q=user.id:tom&routing=2           

正如所預期的一樣傳回内容如下

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}           

索引别名(寫權限)

當一個索引别名綁定較多的索引,這時若需要使用别名進行寫操作,則需要對其中一對綁定關系進行标注,指定别名對特定索引具有寫操作權限,沒有标注具有寫權限的索引别名即為普通索引别名。具有寫權限的索引别名,操作索引别名時,會轉化為對真實索引的操作。

索引别名(索引為動詞)的應用場景主要包括,

reindex

索引和

Rollover

索引。如下所示,将别名

write-index-alias1

同時綁定索引

test

test2

,别名與

test

索引的綁定關系标注了寫權限。

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "test",
        "alias": "write-index-alias1",
        "is_write_index": true
      }
    },
    {
      "add": {
        "index": "test2",
        "alias": "write-index-alias1"
      }
    }
  ]
}           

使用索引别名進行資料寫入

PUT /write-index-alias1/_doc/1
{
  "foo": "bar"
}           

通過以下方式可以驗證,資料寫入到了

test

索引

#可以擷取得到對應文檔
GET test/_doc/1

#可以檢視到兩個索引的文檔數量,發現 test 表增加了一個文檔
GET _cat/indices/write-index-alias1?v           

在進行索引

Rollover

或者

Reindex

時,為了做到零停機切換索引;還可以通過

Bulk API

切換别名與索引綁定的寫權限标注,該 API 為原子操作,

actions

中的動作編寫順序不影響交換執行。

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "test",
        "alias": "write-index-alias1",
        "is_write_index": false
      }
    },
    {
      "add": {
        "index": "test2",
        "alias": "write-index-alias1",
        "is_write_index": true
      }
    }
  ]
}           

創作人簡介:

楊松柏,目前就職于好未來教育科技集團,任資料平台資深研發工程師。 長期關注

ELK、TiDB、clickhouse 等分布式存儲技術,對于 Elasticsearch 和 TiDB 都有深入的

了解。

部落格:

https://blog.csdn.net/yang52017