做者簡介git
巧爺,餓了麼物流研發部數據中心技術負責人,有豐富的ES、Influxdb使用經驗和數據應用開發經驗,but...文風驚奇,校稿人累覺不愛github
Elasticsearch ,簡稱es,主要運用於全文搜索、數據分析, 底層使用開源庫Lucene,擁有豐富的REST API,開箱即用。分佈式的數據存儲、倒排索引等設計,使其能夠快速存儲、搜索、分析海量數據。典型的使用方和應用場景,如github,StackOverflow,elasticsearch+logstash+kibana 一體化的日誌分析。數據庫
下面主要從咱們如何存儲數據、用這些數據幹什麼兩個方面來展開咱們對於es的應用。有啥子不合理、不足之處歡迎你們指正。apache
隨着餓了麼運單數據的增加,傳統的數據庫很難支撐現有的業務:api
一、各類場景的數據查詢、統計,致使數據庫必須加入各類字段的索引,大大增長了在生成一條運單,插入數據所須要的成本,嚴重影響了生成運單的併發度;大量的索引同時佔用了很大的磁盤空間,同時給數據庫變動帶來的更大的風險;數組
二、不少場景沒有辦法或者很難實現,如:運單分頁查詢。大致量的數據數據庫必然是sharding的,此時數據在分頁上面必須須要經過別的工具。此時咱們引入了ES,來處理容許必定延遲的數據查詢、統計的業務。bash
如你們所知的那樣,ES不支持事務、複雜的數據關係(後期版本稍有改善,可是仍然支持的不是很好),利用_version (版本號)的方式來確保應用中相互衝突的變動不會致使數據丟失,那麼咱們是如何存儲咱們的數據,數據結構是什麼樣子,如何保證數據的完整性和一致性的呢?數據結構
首先說下咱們的運單數據索引的數據結構。併發
一、合適的分片數和副本數。網上有不少關於如何規劃分片數的文章,本人感受能夠做爲參考, 在機器性能、數據量的大小、使用場景等等的不一樣,分片、副本的數量最好能夠經過壓測或者是線上實際流量來作調整。app
二、咱們會盡可能減小咱們所須要的字段,作到夠用就好,mapping設置方面:設置"_all"爲false,String類型"index"儘可能設置爲不分詞("not_analyzed",根據須要設置analyzed),商家名稱這類String類型字段只存儲索引結構,不存儲原始文檔(後面會聊到如何拿到原始文檔)。
起初咱們在建運單索引的時候,咱們是儘可能冗餘運單上面的全部信息,致使一個星期的運單數據達到一個T的大小,而上面大部分的字段都是不須要的,磁盤利用率很低,而用於該集羣的都是ssd盤,經常因爲磁盤存不下,而須要添加機器,致使大量的資源浪費。這也須要咱們支持一個額外的能力,萬一須要添加某個運單字段,咱們須要在需求上線以前迅速將歷史數據補齊這個字段,同時不影響線上。(咱們如今能夠一個晚上重刷咱們須要週期內的歷史數據)。
"mappings": {
"index_type_name": {
"_all": {
"enabled": false
},
"_source": {
"excludes": ["merchant_name"]
},
"properties": {
"order_id": {
"type": "long"
},
"merchant_name": {
"type": "string",
"index": "not_analyzed"
}
...
}
}
}
複製代碼
三、以一天爲一個索引(根據業務場景,由於咱們的業務場景大部分要的都是某天的數據),這也爲咱們根據實際線上流量調整咱們分片、副本數量提供了方便,修改完索引的模板("_template")以後,次日會自動生效,而查詢多天不一樣分片數量的運單索引的聯合查詢不會影響查詢結果。
四、儘可能避免Nested Objects數據類型(nested數據結構)。每個nested object 將會做爲一個隱藏的單獨文本創建索引,雖然官網上說在查詢的時候將根文本和nested文檔拼接是很快的,就跟把他們當成一個單獨的文本同樣的快。可是其實仍是有一部分的額外的消耗,尤爲是在aggs聚合的時候,它會使一層聚合其實變成了兩層聚合:須要先聚合隱藏文件,再對實際需求進行聚合。若是真的須要放入數組類型的數據,能夠根據實際需求,轉化爲一個字段,直接建在主數據上面(有必要的話,能夠對nested object直接建一個新的索引)。
好比:咱們如今有一個索引,裏面有某個學校天天每一個學生的學習、生活狀況,每一個學生天天會產生一條數據。如今咱們想統計每一個班級某天 中午在校吃飯的人數、以及一天在校的用餐次數,咱們能夠設計一個nested Objects數據結構來存儲一天三餐的狀況,也能夠在主數據上添加四個字段:早上是否在校吃飯,中午是否在校吃飯、晚上是否在校吃飯,三餐在校用餐次數,這樣就能夠直接對着這四個字段進行數據統計。
五、儘可能減小script line的使用。一樣的道理,咱們能夠預先將須要用script line 的中間值先存到主數據上面。避免查詢、統計時候的額外消耗。
一、考慮在不影響已有的業務狀況下,咱們採起解析運單數據落庫產生的binlog日誌來建索引(binlog日誌公司有一套解決方案,不必定非要使用binlog日誌,運單狀態變化的mq消息也是能夠的),使其與運單正常業務解耦。
二、此時咱們不會直接拿這條數據插入ES,由於運單狀態變化在同一個時刻可能會發生屢次,每次的數據插入不必定是數據庫當前的狀態,並且不論binlog日誌、仍是運單狀態變化消息都只是涵蓋了部分數據,若是要運單在發消息的時候,把全部須要的數據補齊,對於運單的業務來講,會面臨常常修改消息結構的問題,這已經違背了咱們要使其與運單正常業務解耦的初衷, 因此咱們在收到這條數據變動的時候,會經過運單id反查運單數據,運單確定會時時刻刻保持有最新的經過運單id查詢運單所有信息的接口,這樣咱們就能夠拿到咱們想要的任何最新數據。
三、經過es建索引的 bulk api 減小與es集羣的交互次數,提升數據寫入的吞吐量。
四、同一條運單數據,在同一個時刻可能會在機器A和機器B中同時發生更新操做,機器A查詢到的是舊數據,機器B查詢到了新數據,可是寫入索引的時候機器B先寫入ES集羣,機器A後寫入集羣,致使數據錯誤。解決方案:每條數據寫入的時候,添加一個分佈式鎖,相同運單號的數據在同一個時刻只能有一條發生寫索引的動做,沒有得到分佈式鎖消息,丟入延遲隊列,下次再消費。
五、數據的補償(此處就不展開了)。
前面咱們講述了咱們ES中的索引結構遵循的一些原則,其中有一條是,咱們不會在ES中存儲原始文檔,那麼咱們是如何支持查詢運單的具體數據的呢?其實這就是一個ES集羣的定位問題,咱們的ES集羣僅僅是用來豐富運單查詢、支持數據統計的功能,咱們並不支持數據的實際存儲,咱們存儲的僅僅只是每一個字段的索引而已,經過每一個字段的索引支持各類各樣的運單查詢、數據統計,若是須要查詢運單的詳細信息,咱們經過ES查詢獲得運單id後,會去運單的查詢服務查詢到該信息,再吐給需求方,咱們會將這個步驟包掉,需求方無感知,且返回的數據只是將運單的查詢服務的數據包了一層,儘量減小其餘方的接入成本。
ES在作數據統計的時候每每會很消耗ES集羣的資源,因此咱們一般不容許需求方直接經過接口訪問ES,咱們會將各個維度的數據提早算好放入其餘類型的數據庫中,供業務方使用,此處也不進行展開了。
(此處想到什麼講什麼了。)
一、ES數據統計查詢的時候,一樣的查詢條件,兩次查詢出來的數據結果可能會不同,這是由於副本分片和主分片數據不一致(ES只保證最終一致),ES在寫操做的時候有個consistency的參數來控制寫入的一致性,具體值爲one(primary shard),all(all shard),quorum(default)。
one:要求咱們這個寫操做,只要有一個primary shard是active活躍可用的,就能夠執行
all:要求咱們這個寫操做,必須全部的primary shard和replica shard都是活躍的,才能夠執行這個寫操做
quorum:默認的值,要求全部的shard中,必須是大部分的shard都是活躍的,可用的,才能夠執行這個寫操做
可是就算設置成了all以後,查詢仍是有不一致的狀況,這是使用lucene索引機制帶來的refresh問題,完全解決該問題就勢必會增長寫入的成本,咱們選取了另外一種方式:對於會短期內出現先後兩次查詢的需求指定從primary shard讀。
二、ES查詢成功,部分shard失敗;這個問題很尷尬,由於我在前期很長時間都沒注意到這個問題,發現查詢成功後,就直接把結果丟出去了,後來一次ES集羣異常,發現查詢出來的數據要比正常小不少,不多是ES主、副本分片數據不一致的問題,才發現時該問題。
三、新增字段的時候,必定要先更新全部已存在的索引的Mapping,再更新template,最後才能發更新後的程序。因爲ES集羣寫操做在默認狀況下,Mapping中沒有的字段,會被自動識別,而自動識別的字段可能不是咱們想要的字段類型,而這個時候想要不斷服務的修改,會很複雜。因此必定要在發新的程序以前修改好Mapping、template。
四、有時候爲了提升ES集羣的性能,咱們會按期的手工作一些段合併,此時要注意設置段合併的線程數,防止影響到正常業務。
五、監控ES的慢查詢,雖然ES集羣是分佈式的,可是同樣會因爲過分的慢查詢而打爆集羣的狀況。
六、作好ES集羣的監控很重要,網上有不少教程,此處也再也不重述了。
在ElaticSearch裏面,路由功能算是一個高級用法,大多數時候咱們用的都是系統默認的路由功能,ES的_routing字段的取值默認是_id字段,而現實在咱們的業務中,有太多的字段能夠且須要做爲路由字段。若是有機會,後續篇中,我將會介紹餓了麼物流數據中心是如何經過公司已有的多活系統來支持咱們的ES路由功能的相關話題。
閱讀博客還不過癮?
歡迎你們掃二維碼經過添加羣助手,加入交流羣,討論和博客有關的技術問題,還能夠和博主有更多互動
博客轉載、線下活動及合做等問題請郵件至 shadowfly_zyl@hotmail.com 進行溝通