本文作为一些实践经验的总结,未必是最佳实践,欢迎大家交流。
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)" } } } }
在ES 1.3版本使用,默认支持的语言已经成为Groovy了,这点需要了解一下。还是要再次提醒一下,对于大量的数据,从我们的测试来看,这个数据类型转换的过程还是很慢的。
3. 使用terms过滤器,同时找到多个确切值
比如,我们想过滤出价格为20或者30的所有的文档,我们可以这样写:
GET /my_store/products/_search { "query" : { "filtered" : { "filter" : { "terms" : { #1 "price" : [20, 30] } } } } }
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" } } } } }'
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过程中总结的一些经验,可能给出的方案并非最佳,如有更好方案,欢迎大家进一步的交流。