Elasticsearch & Logstash -- 一些經驗總結
本文作為一些實踐經驗的總結,未必是最佳實踐,歡迎大家交流。
ES叢集環境:
節點配置: 8核CPU, 48GB記憶體, 4*2TB磁碟JBOD
節點數量:9
作業系統:CentOS 6.4 Final
JDK 1.7.0_45
ES版本:1.2.1
1. 通過管線化的思路增加索引速度
如果要保證準實時性,索引速度必須得到保證。為此進行了多種嘗試。包括增加ES_HEAPSIZE到16GB,禁用 _all欄位,設定所有欄位值為not_analyzed。在採取這些措施之後,進行的測試中其索引速度仍不過3000/s,與預期差距較大,部署模式是:
測試程式傳送資料 -> Redis訊息佇列 -> Logstash -> Elasticsearch叢集
在測試進行時做了一些觀察,發現在整個過程中Redis佇列中是有大量的資料積壓的。這裡有兩種可能,一是消費者處理能力不足;二是Redis佇列本身就是瓶頸。為此,在這個基礎之上又加入一個Logstash例項來對接到那一個Redis佇列上,發現索引速度甚至還不如之前,由此可斷定瓶頸應該就在Redis訊息佇列上。
Redis訊息佇列一個程序只能利用一個CPU核心,如果有大量的讀寫同時發生,那麼勢必會發生搶佔CPU資源的現象。這也在測試過程中得到了體現:如果測試傳送程式將資料全部發送到Redis佇列中後,每秒的索引量會有一個明顯的提升。
為了解決Redis佇列的瓶頸問題,使用多管線機制,來增加整個系統的吞吐量,為此,我們同時部署了多個Redis例項,和對應數量的Logstash例項:
測試程式傳送資料 -> Redis訊息佇列1 -> Logstash1 -> Elasticsearch叢集
測試程式傳送資料 -> Redis訊息佇列2 -> Logstash2 -> Elasticsearch叢集
測試程式傳送資料 -> Redis訊息佇列3 -> Logstash3 -> Elasticsearch叢集
...
通過多次嘗試,發現隨著管線數量的增加,索引速度也會有相應提高,最終我們使用了5條管線,將索引速度穩定在了1.2W~1.5W/s,這個已經可以滿足我們目前的需求了。
採用管線機制的好處是,擴充套件性是顯而易見的。
2. 聚合過程中通過script機制進行資料型別轉換
其實這更多是一個設計問題,而且這裡的資料型別轉換方案,在資料量較大的時候,並不具有可用性,對於幾千萬的資料的型別轉換大概需要幾十秒。這裡提一下,主要還是為了說明ES裡還是可以進行資料型別轉換的~
{
"size": 0,
"query": {
"filtered": {
"filter": {
"regexp": {
"who": "[0-9]+"
}
}
}
},
"aggs": {
"max_who": {
"max": {
"script": "Double.parseDouble(_source.who)"
}
}
}
}
以上的script部分使用的是預設的支援語言mvel指令碼,它還支援Python,Javascript等指令碼語言,不過都需要單獨安裝,並且需要在所有的節點上都安裝對應的語言外掛。
在ES 1.3版本使用,預設支援的語言已經成為Groovy了,這點需要了解一下。還是要再次提醒一下,對於大量的資料,從我們的測試來看,這個資料型別轉換的過程還是很慢的。
3. 使用terms過濾器,同時找到多個確切值
比如,我們想過濾出價格為20或者30的所有的文件,我們可以這樣寫:
GET /my_store/products/_search
{
"query" : {
"filtered" : {
"filter" : {
"terms" : { #1
"price" : [20, 30]
}
}
}
}
}
等價於SQL的:
SELECT *
FROM products
WHERE price IN (20, 30);
採用這種方式,要比使用多個bool過濾器的組合要簡單許多。
4. 日期直方圖(date_histogram)聚合的補零操作
有這樣一種場景,我們要看一年中的某一指標,在每個月的變化情況。這時,如果其中的幾個月沒有資料記錄,那麼這幾個月的結果就不會在聚合結果中顯示。為了顯示上的完整性,前端的應用程式可以對返回的資料做一下處理,但是,date_histogram已經內建了這樣的補零操作,非常方便使用:
curl -XGET 'localhost:9200/cars/transactions/_search?search_type=count&pretty=true' -d '
{
"aggs": {
"sales": {
"date_histogram": {
"field": "sold",
"interval": "month",
"format": "yyyy-MM-dd",
"min_doc_count": 0,
"extended_bounds": {
"min": "2014-01-01",
"max": "2014-12-31"
}
}
}
}
}'
以上的查詢會返回,2014年每個月的汽車銷售量的分佈,其中為了顯示完整性而設定的引數有兩個:
min_doc_count: 即使這個月中沒有任何的銷售記錄(也就是0),也將其包含在聚合結果中,值為0即可
extended_bounds: 顯示日期的範圍強制擴充套件到2014年一整年的
這樣,得出來的結果對於圖表顯示來說就非常友好了。
5. 索引時日期格式的自動識別
在索引資料時, ES會自動嘗試識別日期型別,預設情況下,ES會將yyyy-MM-ddTHH:mm:ssZ格式的資料視作日期型別,並以日期型別儲存,像是"2014-08-01T03:27:33.730Z"這樣的串會被識別成日期格式,而類似“2014-08-01 12:00:00”這樣的串,只會被識別成字串。這也是需要注意的一點。
以上就是到目前為止,使用ES過程中總結的一些經驗,可能給出的方案並非最佳,如有更好方案,歡迎大家進一步的交流。