概要
本篇主要介紹一下Elasticsearch Document的數據格式,在Java應用程序、關係型數據庫建模的對比,介紹在Kibana平臺編寫Restful API完成基本的集羣狀態查詢,Document最基本CRUD操作示例以及bulk批處理示例。
Document數據格式
Java應用系統的數據模型都是面向對象的,有些對象比較複雜,傳統的業務系統,數據需要落地到關係型數據庫,在數據庫領域模型設計時,會把複雜的POJO對象設計成一對一或一對多的關係,進行扁平化處理,查詢的時候,需要多表查詢並還原回POJO對象的格式。
Elasticsearch是文檔數據庫,Document存儲的數據結構,可以和POJO保持一致,並且使用JSON格式,這樣查詢數據時比較方便。
Document文檔數據示例:
{
"fullname" : "Three Zhang",
"text" : "hello elasticsearch",
"org": {
"name": "development",
"desc": "all member are lovely"
}
}
Restful API讓請求更容易
前面文章有提及Elasticsearch與Kibana搭配使用,Kibana界面的Dev Tools菜單,可以發送Elasticsearch的Restful請求。後續的Restful API請求,如無例外,均是在Kibana平臺上執行的。
我們先拿幾個查詢集羣信息的請求來試試
- 檢查集羣的健康狀況
GET /_cat/health?v
epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1573626290 14:24:50 hy-application yellow 1 1 1 1 0 0 1 0 - 50.3%
從上面能看出node、shard的數量等,還有一個是集羣的狀態,上面顯示的是yellow,爲什麼是yellow?
集羣的狀態有green、yellow、red三種,定義如下:
- green:每個索引的primary shard和replica shard都是active狀態的
- yellow:每個索引的primary shard都是active狀態的,但是部分replica shard不是active狀態,處於不可用的狀態
- red:不是所有索引的primary shard都是active狀態的,部分索引有數據丟失了
我們的示例只啓動了一個elasticsearch實例,只有一個node,由於索引默認會使用5個primary shard和5個replica shard,並且同一個node下面的primary shard和replica shard不能分配在一臺機器上(容錯機制),所有隻有1個primary shard被分配和啓動了,replica shard沒有第二臺node去啓動,因而是yellow狀態。如果想變成green判斷,另外啓一臺node即可。
- 查看集羣中有哪些索引
GET /_cat/indices?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open location 48G_CgE7TiWomlYsyQW1NQ 5 1 3 0 11kb 11kb
yellow open company_mem s9DKUeyWTdCj2J8BaFYXRQ 5 1 3 0 15kb 15kb
yellow open %{[appname]} ysT9_OibR5eSRu19olrq_w 5 1 32 0 386.5kb 386.5kb
yellow open .kibana 4yS67TTOQGOD7l-uMtICOg 1 0 2 0 12kb 12kb
yellow open tvs EM-SvQdfSaGAXUADmDFHVg 5 1 8 0 16.3kb 16.3kb
yellow open company_org wIOqfx5hScavO13vvyucMg 5 1 3 0 14.6kb 14.6kb
yellow open blog n5xmcGSbSamYphzI_LVSYQ 5 1 1 0 4.9kb 4.9kb
yellow open website 5zZZB3cbRkywC-iTLCYUNg 5 1 12 0 18.2kb 18.2kb
yellow open files _6E1d7BLQmy9-7gJptVp7A 5 1 2 0 7.3kb 7.3kb
yellow open files-lock XD7LFToWSKe_6f1EvLNoFw 5 1 1 0 8kb 8kb
yellow open music i1RxpIdjRneNA7NfLjB32g 5 1 3 0 15.1kb 15.1kb
yellow open book_shop 1CrHN1WmSnuvzkfbVCuOZQ 5 1 4 0 18.1kb 18.1kb
yellow open avs BCS2qgfFT_GqO33gajcg_Q 5 1 0 0 1.2kb 1.2kb
- 查看node信息
GET /_cat/nodes?v
響應:
ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
192.168.17.137 38 68 0 0.08 0.03 0.05 mdi * node-1
我們可以看到node的名稱。
- 創建索引命令
創建名稱爲"location"的索引
PUT /location?pretty
響應:
{
"acknowledged": true,
"shards_acknowledged": true
}
查看索引,能看到剛剛創建的索引location
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open location 48G_CgE7TiWomlYsyQW1NQ 5 1 3 0 11kb 11kb
yellow open .kibana 4yS67TTOQGOD7l-uMtICOg 1 0 2 0 12kb 12kb
- 刪除索引命令
刪除名稱爲"location"的索引
DELETE /location?pretty
再查看索引,剛剛創建的索引location已經刪除掉了
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open .kibana 4yS67TTOQGOD7l-uMtICOg 1 0 2 0 12kb 12kb
是不是很簡單,交互界面挺友好吧?
入門CRUD操作
介紹document最基本的CRUD操作,以兒童英文歌曲爲背景
- 新增歌曲
我們設計的兒歌結構包含四個字段:歌名name,歌詞content,語言種類language,歌曲時間長length,單位是秒,放在JSON字符串裏。
PUT語法:
<REST Verb> /<Index>/<Type>/<ID>
REST Verbs可以是PUT、POST、DELETE,後斜槓後的內容分別是索引名、類型名、ID。
請求如下:
PUT /music/children/1
{
"name": "gymbo",
"content": "I hava a friend who loves smile, gymbo is his name",
"language": "english",
"length": "75"
}
響應內容包含索引名、類型名、ID值,version版本號(樂觀鎖控制),結果類型(created/updated/deleted三種),shard信息等,如果新增時該索引不存在,會自動創建索引,索引名即請求時指定的那個,document裏面的field類型,就根據elasticsearch定義的自動映射類型,並且每個field都會建立倒排索引,讓其可以被搜索到。
total和successful爲什麼數據不相等?
新增document時,會往primary shard和replica shard分別寫入document,但由於只有一個node,replica未啓動,所以總共寫入2次,primary shard成功,數量是1。failed只記錄primary shard寫入失敗的情況。
響應如下:
{
"_index": "music",
"_type": "children",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
- 修改歌曲
修改document有兩種方式,一種是增量修改,只修改指定的field,另一種是全量替換文檔,原有的信息全部被替換掉
- 增量修改length的值,注意是POST請求,並且尾部有_update,doc是固定寫法,請求如下:
POST /music/children/1/_update
{
"doc": {
"length": "76"
}
}
響應:
{
"_index": "music",
"_type": "children",
"_id": "1",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}
- 全量替換文檔,請求如下:
PUT /music/children/1
{
"name": "gymbo",
"content": "I hava a friend who loves smile, gymbo is his name",
"language": "english",
"length": "77"
}
響應:
{
"_index": "music",
"_type": "children",
"_id": "1",
"_version": 3,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}
細心的童鞋可以,全量替換文檔的語法和創建索引是一樣的,對!就是同一個語法 ,是創建還是更新取決於上面的ID存不存在,不存在做創建,存在做更新,更新成功version+1,但這種全量替換有個不好的地方,必須帶上完整的屬性,否則未聲明屬性就沒有了。
想想要使用這個語法的場景:先GET所有的屬性,然後把要更新的屬性更新上,再調用全量替換的更新語法。實際上這種做法不多,原因不外乎兩個:操作複雜,要先查詢後更新;報文過大(相對於增量更新)。所以企業研發一般使用增量方式做document更新。
- 查詢歌曲
查詢語句:GET /music/children/1
_source即爲JSON的內容,查詢結果:
{
"_index": "music",
"_type": "children",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"name": "gymbo",
"content": "I hava a friend who loves smile, gymbo is his name",
"language": "english",
"length": "75"
}
}
- 刪除歌曲
刪除語句:DELETE /music/children/1
響應結果:
{
"_index": "music",
"_type": "children",
"_id": "1",
"_version": 4,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 3,
"_primary_term": 1
}
bulk批處理
上一節提及的增刪改操作,是針對單個document的,Elasticsearch還有一個批處理命令,可以批量執行這些操作。
- bulk的基本語法示例
還是以上面的兒歌爲案例背景
POST /music/children/_bulk
{"index":{"_id":"1"}}
{"name": "gymbo", "content": "I hava a friend who loves smile, gymbo is his name", "language": "english", "length": "75"}
{"create":{"_id":"2"}}
{"name": "wake me, shark me", "content": "don't let me sleep too late", "language": "english", "length": "55"}
{ "update": {"_id": "2", "retry_on_conflict" : 3} }
{ "doc" : {"content" : "don't let me sleep too late, gonna get up brightly early in the morning"} }
{ "delete": {"_id": "3" }}
可以把多條命令放在一起執行,如果一個bulk請求內有不同的index和type,可以把index和type也可以寫在body json裏,每一個操作要兩個json串:
{"action": {"metadata"}}
{"data"}
delete例外,它只需要1個json串就可以了
action的類型有以下幾種:
- index:普通的PUT操作,ID不存在時創建document,ID存在時做全量替換
- create:強制創建,等同於PUT /index/type/id/_create命令
- update:執行的增量修改操作
- delete:刪除document操作
bulk注意事項
bulk api有嚴格的語法要求,每個json串內不能換行,同時每個json串之間必須要有一個換行,否則會報語法錯誤。
bulk既然是多條命令批量執行,遇到錯誤怎麼辦?會中斷嗎?
如果bulk請求內有命令執行錯誤,會直接跳過,繼續執行下一條,同時在響應報文裏會對每條命令的結果分別展示,正確的就展示正確的結果,錯誤的會相應提示錯誤日誌。bulk的性能問題
bulk既然是批處理,那bulk size與最佳性能肯定存在一定的聯繫,bulk請求的內存會先加載到內存裏,bulk的請求取決於命令的條數和每個命令內容的多少,與性能的關係示例圖(表達概念,數據不具備參考性)如下:
bulk性能優化的目標就是找到這個拐點,需要反覆嘗試一個最佳的size,跟具體的業務數據特性,併發量有關,常見的設置範圍一般是1000-5000之間,bulk請求的大小控制在5-15MB之間(僅供參考)。
小結
本篇簡單介紹了一下document的數據格式,並順帶講解了一下Elasticsearch集羣紅黃綠三種狀態的判定標準,重點是在kibana平臺演示的CRUD小案例和bulk批處理示例,最爲基礎,可以多花一些時間熟悉熟悉。
專注Java高併發、分佈式架構,更多技術乾貨分享與心得,請關注公衆號:Java架構社區