本文檔適用人員:研發和運維
- 曾經的基於MongoDB的篩選+排序解決方案
- MongoDB方案的缺陷
- 看中了搜索引擎的facet特性
- 看中了ES的簡潔
- 看中了ES的天生分布式設計
- 窩窩的ES方案
- ES的幾次事故和教訓
- ES自身存在的問題
|
由於頻道頁流量小於首頁,尤其是用戶很少點擊到的深度篩選條件組合查詢,所以下圖中的所有枚舉項商品數量都容易緩存失效或緩存擠出:
![]()
圖2 篩選越來越復雜,標題數字卻要保持准確性
一旦緩存失效后,但凡我從上圖的“20元以下”點擊切換到“51-80元”或做更深層次篩選,那么程序就要針對上面所有組合條件對 MongoDB 商品記錄逐一做 count 計算。
雖然每一個 count 計算都很快不屬於慢查詢,但也架不住多啊,尤其是配上區縣和商圈等動輒6、7層深的篩選組合,點擊一次輕易就涉及成百次的 count 計算,代價還是很大的。
由於在商城模式下,不同頻道很可能不斷增加新篩選條件,導致篩選組合越來越復雜,最終可能要求我們從基於 NoSQL 的排序和篩選方案,盡快轉變為基於搜索引擎的排序和篩選方案。
|
介紹分面
分面是指事物的多維度屬性。例如一本書包含主題、作者、年代等分面。而分面搜索是指通過事物的這些屬性不斷篩選、過濾搜索結果的方法。可以將分面搜索看成搜索和瀏覽的結合。
靈活使用分面 有時用戶並不明確自己的目的,因此提供寬松的篩選方式更符合這部分用戶的預期。Bing 的旅行搜索中選擇航班時,用戶可以通過滑塊來選擇某個時間段起飛的航班。 |
- Field Facet:如果需要對多個字段進行Facet查詢,那么將 facet.field 參數聲明多次,Facet字段必須被索引;
- Date Facet:時間字段的取值有無限性,用戶往往關心的不是某個時間點而是某個時間段內的查詢統計結果,譬如按月份查;
- Facet Query:利用類似於filter query的語法提供了更為靈活的Facet,譬如根據價格字段查詢時,可設定不同價格區間;

商品維度是我們主要的查詢維度,其業務復雜度也比較高。針對網站查詢特性,我們的商品主索引方案為:每個城市建立一個 index,所以一共有400多個 index,每個城市僅有1個主 shard(不分片)。這樣做的好處是以后我們根據熱點城市和非熱點城市,可以將各個 index 手工分配到不同的 node 上,可以做很多優化。
其結構為:
圖7 goodsinfo
為了減少索引量和功能拆分,減少商品索引的內存占用,所以我們把全文檢索單獨建為一個索引。
每個城市索引或者商品索引按頻道分為幾個type,如下圖所示。
圖9 type
商品頻道映射到es的type是很容易理解的,因為每個頻道的模型不同:有的頻道特有“用餐人數”屬性,有的頻道特有“出發城市”和“目的地城市”屬性 所以每個頻道對應一個es的type,每個type綁定一種特定的mapping(這個mapping里面可以指定該頻道各自的特殊屬性如何儲存到ES)。ES 會保證所有 shard 的主副本不在同一個 node 上面,但我們是 ES 服務器集群,每台服務器上有多個 node,一個 shard 的主副本不在同一個節點還是不夠的,我們還需要一個 shard 的主副本不在同一台服務器,甚至在多台物理機的情況下保證要保證不在同一個機架上,才可以保證系統的高可用性。
所以ES提供了一個配置:cluster.routing.allocation.awareness.attributes: rack_id。
這個屬性保證了主副 shard 會分配到名稱不同的 rack_id 上面。
當我們停止一個節點時,如停止 174_node_2,則 ES 會自動重新平衡數據,如下圖所示:
圖13 重新分布
即使一台物理機完全 down 掉,我們可以看到其他物理機上的數據是完整的,ES 依然可以保證服務正常。

查看其內存使用狀況發現,ES 各個節點的 JVM perm 區均處於滿或者將要滿的狀態,如下圖所示:
圖15 當時perm的容量
注1:jstat -gc <pid>命令返回結果集中,上圖紅色方框中字段的含義為:
PC Current permanent space capacity (KB). 當前perm的容量 ;
PU Permanent space utilization (KB). perm的使用。
大家可以看到圖1中的PU值基本等於PC值了。
由於 ES 各個節點的 perm 區接近飽和狀態,所以造成了服務器負載升高,GC 頻繁,並進一步造成 ES 集群出現了類似於“腦裂”的狀態。
- 引入新技術,還是要謹慎,畢竟如果真是 mevl 腳本引起的問題,其實線下做壓力測試就能提前發現。
- 加強ES的監控。
- 雖然現在回過頭來看,如果在第一時間重啟所有 nodes,損失應該是最小的——但是王超認為當時采用的保守策略依然是有意義的,因為在弄清楚問題原因之前,直接重啟 nodes 有可能反而造成更大的數據破壞。
Elastic Search 在窩窩運行幾年來基本穩定,可靠性和可用性也比較高,但是也暴露了一些問題:
- ES 的更新效率,作為基於 lucene 的分布式中間件,受限於底層數據結構,所以其更新索引的效率較低,lucene 一直在優化;
- ES 的可靠性的前提是保證其集群的整體穩定性,但我們遇到的情況,往往是當某個節點性能不佳的情況下,可能會拖累與其同服務器上的所有節點,從而造成整個集群的不穩定。
- 其實解決這個問題不難,兩種方法:
- 增加服務器,讓節點盡可能地散開;
- 當某個節點出現問題的時候,需要我們及早發現處理,不至於拖累整個集群。其實監控一個節點是否正常的方法不難,ES 是基於 JVM 的服務,它出現問題,往往和 GC、和內存有關,所以只要監控其內存達到某個上限就報警即可;
- 沒有一個好的客戶端可視化集群管理工具,官方或者主流的可視化管理工具,基本都是基於 ES 插件的,不符合我們的要求,所以需要一款可用的客戶端可視化集群管理工具;
- ES 的升級問題,由於 ES 是一個快速發展的中間件系統,每一次新版本的更新,更改較大,甚至導致我們無法兼容老版本,所以 ES 升級問題是個不小的問題,再加上我們數據量較大,遷移也比較困難。
——END——
窩窩的解決方案介紹列表:
#研發解決方案#基於StatsD+Graphite的智能監控解決方案
es術語介紹:
cluster:
代表一個集群,集群中有多個節點,其中有一個為主節點。這個主節點是可以通過選舉產生的。注意,主從節點是對於集群內部來說的。es的一個概念就是去中心化,字面上理解就是無中心節點,這是對於集群外部來說的,因為從外部來看es集群,在邏輯上是個整體,你與任何一個節點的通信和與整個es集群通信是等價的。
shards
代表索引分片。es可以把一個完整的索引分成多個分片,這樣的好處是可以把一個大的索引拆分成多個,分布到不同的節點上。構成分布式搜索。分片的數量只能在索引創建前指定,並且索引創建后不能更改。
replicas
代表索引副本,es可以設置多個索引的副本。副本的作用,一是提高系統的容錯性,當某個節點的某個分片損壞或丟失時可以從副本中恢復,二是提高es的查詢效率,es會自動對搜索請求進行負載均衡。
recovery
代表數據恢復或叫數據重新分布,es在有節點加入或退出時會根據機器的負載對索引分片進行重新分配,掛掉的節點重新啟動時也會進行數據恢復。
river
代表es的一個數據源,也是其他存儲方式(如:數據庫)同步數據到es的一個方法。它是以插件方式存在的一個es服務,通過讀取river中的數據並把它索引到es中,官方的river有couchDB的,RabbitMQ的,Twitter的,Wikipedia的。
gateway
代表es索引快照的存儲方式。es默認是先把索引存放到內存中,當內存滿了時再持久化到本地硬盤。gateway對索引快照進行存儲,當這個es集群關閉再重新啟動時,就會從gateway中讀取索引備份數據。es支持多種類型的gateway,有本地文件系統(默認),分布式文件系統,Hadoop的HDFS和amazon的s3雲存儲服務。
discovery.zen
代表es的自動發現節點機制。es是一個基於p2p的系統,它先通過廣播尋找存在的節點,再通過多播協議來進行節點之間的通信,同時也支持點對點的交互。
Transport
代表es內部節點或集群與客戶端的交互方式。默認內部是使用tcp協議進行交互,同時它支持http協議(json格式)、thrift、servlet、memcached、zeroMQ等的傳輸協議(通過插件方式集成)。
