Elasticsearch 7.X data stream 深入詳解
直接從一個新概念的認知過程說下 elasticsearch data stream。
記得第一次聽到 data stream 的時候,還是去年下半年在公交大巴車上早 8 點聽魏彬老師的直播,后來就一直沒用。
等使用的時候,去翻看文檔和資料前,從認知的角度,不免會有如下的疑問:
沒有 data stream 的時候,如何管理時序型數(shù)據(jù)? 什么是 data stream? data stream 的特點有哪些? 為什么要有 data stream? data stream 能做什么? data stream 應用場景? data stream 和 索引 index 的關系? data stream 和 索引生命周期管理 ILM 的關系? data stream 實操有哪些注意事項?
帶著這些問題,我們開始下面的解讀。
PS:說明一下:后面所有博文的參考內容(含官方文檔)都會寫明詳盡的參考地址,以便大家參考學習。
0、認知前提
0.1 時序性數(shù)據(jù)
指那種每天新增數(shù)據(jù)量非常大、且包含時間戳特性、有時效性特點的數(shù)據(jù)。
時效性體現(xiàn)在:用戶往往更傾向關注最新、最熱、最實時的數(shù)據(jù)。
比如:日志數(shù)據(jù)、大數(shù)據(jù)輿情數(shù)據(jù)等。
0.2 rollover
滾動索引,可以基于:時間間隔、文檔數(shù)、分片大小進行滾動。
0.3 ILM
ILM = 索引生命周期管理的英文首字母組合。
1、沒有 data stream 的時候,如何管理時序型數(shù)據(jù)?
實戰(zhàn)一把,說的清楚。
1.1 基于 rollover 滾動索引機制管理時序數(shù)據(jù)
時序性索引數(shù)據(jù),5.X 版本推出的rollover 滾動索引機制操作步驟如下:
步驟1:創(chuàng)建日期序列索引。
PUT /%3Cmylogs-%7Bnow%2Fd%7D-1%3E
{
"aliases": {
"mylogs_write": {}
}
}
GET mylogs-2021.07.24-1
步驟2:插入一條數(shù)據(jù)
PUT mylogs_write/_doc/1
{
"message": "a dummy log"
}
步驟3:導入數(shù)據(jù)
POST mylogs_write/_bulk
{"index":{"_id":4}}
{"title":"test 04"}
{"index":{"_id":2}}
{"title":"test 02"}
{"index":{"_id":3}}
{"title":"test 03"}
步驟4:指定RollOver規(guī)則(必須手動指定)
POST mylogs_write/_rollover
{
"conditions": {
"max_docs": 3
}
}
步驟5:再次導入批量數(shù)據(jù)
POST mylogs_write/_doc/14
{"title":"test 14"}
POST mylogs_write/_bulk
{"index":{"_id":5}}
{"title":"test 05"}
{"index":{"_id":6}}
{"title":"test 06"}
{"index":{"_id":7}}
{"title":"test 07"}
{"index":{"_id":8}}
{"title":"test 08"}
{"index":{"_id":9}}
{"title":"test 09"}
{"index":{"_id":10}}
{"title":"test 10"}
早期生產環(huán)境使用 rollover,有個比較麻煩的地方就在于——需要自己結合滾動的三條件,在給定的時間點(比如凌晨0:00)定時腳本執(zhí)行一下 rollover,滾動才能生效。
看似腳本處理很簡單,實際會有這樣那樣的問題,用過你就知道有多苦。
rollover 優(yōu)點:實現(xiàn)了最原始的索引滾動。
rollover 缺點:需要手動或者腳本定時 rollover 非常麻煩。
這時候,讀者不禁要問,ILM 索引生命周期管理操作時序數(shù)據(jù)呢?
1.2 ILM 索引生命周期管理時序數(shù)據(jù)
篇幅原因,不再舉例。可以參考:干貨 | Elasticsearch 索引生命周期管理 ILM 實戰(zhàn)指南。
ILM 是模板、別名、生命周期 policy 的綜合體。
ILM 優(yōu)點:一次配置,索引生命周期全自動化。 ILM 適用場景:更適合和冷熱集群架構結合的業(yè)務場景。 ILM 缺點:ILM是普適的概念,強調大而全,不是專門針對時序數(shù)據(jù)特點的方案,且需要為 ilm 配置 index.lifecycle.rollover_alias 設置(對時序數(shù)據(jù)場景,這非常麻煩)。
上述 rollover、ILM 機制實現(xiàn):都涉及到多索引和別名的關系。
官方強調:別名在 Elasticsearch 中的實現(xiàn)方式存在一些不足(官方沒有細說哪些不足。我實戰(zhàn)環(huán)境發(fā)現(xiàn):一個別名對應多個索引,一個索引對應多個別名,索引滾動關聯(lián)別名也可能滾動,開發(fā)者可能很容易出錯和混淆),使用起來很混亂。
相比于別名具有廣泛的用途,而數(shù)據(jù)流將是針對時序數(shù)據(jù)的解決方案。
2、什么是 data stream?
data:數(shù)據(jù)。
stream:流。
data stream:數(shù)據(jù)流。
我X,這英語翻譯,小學生也會。沒勁!能不能通俗點說?
我把 data stream 比如:存儲時序數(shù)據(jù)的多個索引的抽象集合,簡稱為:數(shù)據(jù)流(data stream)。
數(shù)據(jù)流可以跨多個后備索引存儲僅追加(append-only,下文有詳細解釋)的時間序列數(shù)據(jù),同時對外提供一個同一訪問入口。
上面 data stream 的定義和別名的定義貌似一致,國外博主把它戲稱為“超能力別名”,我認為非常生動和貼切。
和別名不同的是:別名關聯(lián)多個索引,寫入的時候需要指定 “is_write_index",而 data stream 相對黑盒,這些細節(jié)用戶無需關注。
所以,它是索引、模板、rollover、ilm 基于時序性數(shù)據(jù)的綜合產物。
3、data stream 的特點有哪些?
3.1 關聯(lián)后備支撐索引(backing indices)
data stream 作為“帶頭大哥”在外拋頭露面,實際后面一堆“小弟”在全力支撐。

圖片來自官方文檔
“帶頭大哥”指的是:數(shù)據(jù)流。它的特點:拋頭露面、光鮮亮麗。
“小弟”:指 backing indices,后備索引。它的特點:相對隱身、默默無聞。其實出力的都是它,除非大哥出事(一些特定約束搞不定),小弟們才會站出來。
3.2 @timestamp 字段不可缺
每個寫入到 dataSteam 的文檔必須包含 @timestamp 字段。 @timestamp 字段必須是:date 類型(若不指定,默認:date 類型)或者 date_nanos 類型。
3.3 data stream 后備索引規(guī)范
創(chuàng)建后備索引時,索引使用以下約定命名:
.ds-<data-stream>-<yyyy.MM.dd>-<generation>
舉例索引真實名稱:data-stream-2021.07.25-000001。
.ds:前綴開頭不可少。 data-stream: 自定義的數(shù)據(jù)流的名稱。 yyyy.MM.dd:日期格式 generation:rollover 累積值:—— 默認從:000001 開始。
3.4 Append-only 僅追加
僅追加:指只支持 op_type=create 的索引請求,我理解的是僅支持向后追加(區(qū)別于對歷史數(shù)據(jù)的刪除、更新操作)。
數(shù)據(jù)流只支持:update_by_query 和 delete_by_query 實現(xiàn)批量操作,單條文檔的更新和刪除操作只能通過指定后備索引的方式實現(xiàn)。
對于頻繁更新或者刪除文檔的業(yè)務場景,用 data stream 不合適,而相反的,使用:模板+別名+ILM更為合適。
4、為什么要有 data stream?
原有實現(xiàn)由于別名的缺陷實現(xiàn)不了時序數(shù)據(jù)的管理或實現(xiàn)起來會繁瑣、麻煩,data stream 是更為純粹的存儲僅追加時序數(shù)據(jù)的方式。
5、data stream 能做什么?
data stream 支持直接的寫入、查詢請求。 data stream 會自動將客戶端請求路由至關聯(lián)索引,以用來存儲流式數(shù)據(jù)。 可以使用索引生命周期管理 ILM 自動管理這些關聯(lián)索引。
6、data stream 的適用場景
日志(logs)、事件(events)、指標(metrics)和其他持續(xù)生成的數(shù)據(jù)。
兩大核心特點:
時序性數(shù)據(jù)。 數(shù)據(jù)極少更新(或者沒有更新)。
7、data stream 和索引有什么異同?
7.1 相同點
絕大多數(shù)命令一致。
7.2 不同點
數(shù)據(jù)流相對實體索引,有點“抽象層“的概念,其核心數(shù)據(jù)還是存儲在 .ds 前綴的后備索引中。
以下操作,只適用于數(shù)據(jù)流。
數(shù)據(jù)流對應映射必須包含日期類型的 @timestamp 字段。 數(shù)據(jù)流刪除和更新只支持 “_update_by_query” 和 “_delete_by_query”操作。 不能基于.ds 前綴的后備索引創(chuàng)建文檔,但可以基于:以.ds前綴的后備索引更新和刪除文檔。
我不聽,我非要指定后備索引寫入一條數(shù)據(jù),咋辦?試試吧:
POST .ds-my-data-stream-2021.07.25-000001/_doc/2
{
"@timestamp": "2099-05-06T16:21:15.000Z",
"message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
}
會有如下的報錯提示:不支持指定后備索引寫入,只能基于數(shù)據(jù)流寫入。
"reason" : "index request with op_type=index and no if_primary_term and if_seq_no set targeting backing indices is disallowed, target corresponding data stream [my-data-stream] instead"
也就是說:只能基于數(shù)據(jù)流寫入數(shù)據(jù)。
不能對后備索引執(zhí)行:clone, close, delete, freeze, shrink 和 split 操作。
8、data stream 和 模板的關系?
相同的索引模板可以用來支撐多個 data streams。可以類比為:1:N 關系。
不能通過 刪除 data Stream 的方式刪除索引模板。
9、data stream 和 ilm 的關系?
ILM 在 data stream 中起到索引生命周期管理的作用。
data stream 操作時序數(shù)據(jù)優(yōu)勢體現(xiàn)在:不再需要為 ilm 配置 index.lifecycle.rollover_alias。
10、data stream 實操指南
涉及 data stream 的創(chuàng)建(新增)、刪除、修改、查詢。
10.1 data stream 增
由于 data stream 涉及模板、索引、ILM、別名等,分步驟拆解更好理解。
步驟 1:創(chuàng)建索引生命周期 policy。
直接拿官方文檔的示例說明。
如下的 policy 包含了:熱、暖、冷、冷凍、刪除等階段。
PUT _ilm/policy/my-lifecycle-policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_primary_shard_size": "50gb"
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"shrink": {
"number_of_shards": 1
},
"forcemerge": {
"max_num_segments": 1
}
}
},
"cold": {
"min_age": "60d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"frozen": {
"min_age": "90d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"delete": {
"min_age": "735d",
"actions": {
"delete": {}
}
}
}
}
}
步驟 2:創(chuàng)建模板
模板組成包括:index_patterns、指定數(shù)據(jù)流 data stream、settings、mappings。
PUT _component_template/my-mappings
{
"template": {
"mappings": {
"properties": {
"@timestamp": {
"type": "date",
"format": "date_optional_time||epoch_millis"
},
"message": {
"type": "wildcard"
}
}
}
}
}
# Creates a component template for index settings
PUT _component_template/my-settings
{
"template": {
"settings": {
"index.lifecycle.name": "my-lifecycle-policy"
}
}
}
PUT _index_template/my-index-template
{
"index_patterns": ["my-data-stream*"],
"data_stream": { },
"composed_of": [ "my-mappings", "my-settings" ],
"priority": 500
}
步驟 3:創(chuàng)建 data stream 。
方式一:直接創(chuàng)建數(shù)據(jù)流 my-data-stream。
PUT _data_stream/my-data-stream
方式二:直接批量或者逐個導入數(shù)據(jù)(會間接生成 data stream 的創(chuàng)建)。

圖片來自官方文檔
PUT my-data-stream/_bulk
{ "create":{ } }
{ "@timestamp": "2099-05-06T16:21:15.000Z", "message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736" }
{ "create":{ } }
{ "@timestamp": "2099-05-06T16:25:42.000Z", "message": "192.0.2.255 - - [06/May/2099:16:25:42 +0000] \"GET /favicon.ico HTTP/1.0\" 200 3638" }
POST my-data-stream/_doc
{
"@timestamp": "2099-05-06T16:21:15.000Z",
"message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
}
兩個注意的地方:
第一:批量 bulk 操作,必須使用:create 指令,而非 index(使用 index 不會報錯, 會把流當做索引處理了)。
第二:文檔必須包含:@timestamp 時間戳字段。
如果不包含 @timestamp 會報錯如下:
"reason" : "data stream timestamp field [@timestamp] is missing"
10.2 data stream 刪
刪除數(shù)據(jù)流
刪除 data stream 和 刪除索引、刪除模板語法基本一致。
DELETE _data_stream/my-data-stream
執(zhí)行刪除操作之后,該 data stream 以及 關聯(lián)索引都會被一并刪除。
單條刪除文檔
DELETE data-stream-2021.07.25-000001/_doc/1
批量刪除文檔
批量刪除數(shù)據(jù)的方式如下:
POST /my-data-stream/_delete_by_query
{
"query": {
"match": {
"user.id": "vlb44hny"
}
}
}
10.3 data stream 改
單條數(shù)據(jù)修改/更新
# 插入一條數(shù)據(jù)
POST my-data-stream/_bulk
{"create":{"_id":1}}
{"@timestamp":"2099-05-06T16:21:15.000Z","message":"192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"}
# 獲取數(shù)據(jù)流關聯(lián)索引
GET /_data_stream/my-data-stream
# 執(zhí)行更新
PUT .ds-my-data-stream-2021.07.25-000001/_doc/1?if_seq_no=1&if_primary_term=1
{
"@timestamp": "2099-03-08T11:06:07.000Z",
"user": {
"id": "8a4f500d"
},
"message": "Login successful"
}
# 查看驗證是否已經更新(已經驗證,可以更新)
GET .ds-my-data-stream-2021.07.25-000001/_doc/1
這里要強調:原來更新單條索引數(shù)據(jù)的 _update/id 的方法不適用于數(shù)據(jù)流。
批量更新
支持通過:update_by_query 批量更新數(shù)據(jù)。
POST /my-data-stream/_update_by_query
{
"query": {
"match": {
"user.id": "l7gk7f82"
}
},
"script": {
"source": "ctx._source.user.id = params.new_id",
"params": {
"new_id": "XgdX0NoX"
}
}
}
10.4 data stream 查
GET _data_stream/my-data-stream

圖片來自官方文檔
返回結果如下:
{
"data_streams" : [
{
"name" : "my-data-stream",
"timestamp_field" : {
"name" : "@timestamp"
},
"indices" : [
{
"index_name" : ".ds-my-data-stream-2021.07.25-000001",
"index_uuid" : "Akg3-bWgStiKG_39Tk5PRw"
}
],
"generation" : 1,
"status" : "GREEN",
"template" : "my-index-template",
"ilm_policy" : "my-lifecycle-policy",
"hidden" : false
}
]
}
10.5 其他操作
reindex 操作
POST /_reindex
{
"source": {
"index": "archive"
},
"dest": {
"index": "my-data-stream",
"op_type": "create"
}
}
滾動操作
POST my-data-stream/_rollover
查看 data stream 基礎信息
GET /_data_stream/my-data-stream
11、小結
在規(guī)劃產品的時候,往往會細數(shù)一下產品的新功能,這些新功能可能是:集成競品、原創(chuàng)更新、其他產品線功能更新等。
data stream 這個 7.9 版本才推出的新功能點,其實是基于原有實現(xiàn)不能滿足時序數(shù)據(jù)特點,更為確切說原有實現(xiàn)機制相對復雜而推出的新功能。
提煉一下本文內容,劃重點如下:
data stream 是時序索引的上層抽象。 data stream 是模板、索引、settings、mappings、ilm policy等綜合體的概念。 data stream 相當于拋頭露面的帶頭大哥,離不開看似隱身、默默無聞、實際埋頭苦干的后備索引的支撐。 data stream 不適合頻繁刪除、更新的業(yè)務場景,更適合僅追加時序數(shù)據(jù)場景。 只能基于 data stream 寫入數(shù)據(jù),不能基于后備索引寫入數(shù)據(jù)。 data stream 不支持單條刪除或單條更新數(shù)據(jù),只支持:update_by_query 以及 delete_by_query。
參考
https://www.elastic.co/guide/en/elasticsearch/reference/7.x/data-streams.html
https://opster.com/elasticsearch-glossary/elasticsearch-data-streams/
https://aravind.dev/elastic-data-stream/
https://github.com/elastic/elasticsearch/issues/53100
https://www.elastic.co/guide/en/elasticsearch/reference/7.13/release-notes-7.9.0.html
推薦

