1. 程式人生 > >Elasticsearch & Logstash -- 一些經驗總結

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 -> Logstash-> 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過程中總結的一些經驗,可能給出的方案並非最佳,如有更好方案,歡迎大家進一步的交流。