之前已经写过一些ES的使用,也翻译了一篇官网上关于ES存储日志的建议日志。今天稍微总结一下近期以来实践出来的方案。
在测试期,可以单节点上设置成1 shard + 0 replica的方式,这种的indexing速度是最快的(存疑:我至今没搞清楚ES在index的时候集群”应该”是比单node快还是慢)。
我曾经按照”常理”(我想象中的)理解,设定成10 shards + 0 replica,期望能用上并行写双node加倍index,事实上压根没用,而且因为另一台node上有其他负载的原因导致更慢了。
更可怕的是:就在前几天,突然出现一个shard挂了,……毫无办法,整个index全作废了。
所以结论是:无论如何,一定要保证有 > 0 份的replica!! 至于shards,保持默认的5个,或者顶多到20个也就差不多了。在maillist里看到有哥们设了100个,然后苦着脸问性能问题…… 要知道shards的份数是一旦设定不能更改的。
刚开始的时候,每次实验都去改/etc/elasticsearch/elasticsearch.yml配置文件。事实上在template里修改settings更方便而且灵活!当然最主要的,还是调节里面的properties设定,合理的控制store和analyze了。
template设定也有多种方法。最简单的就是和存储数据一样POST上去。长期的办法,就是写成json文件放在配置路径里。其中,default配置放在/etc/elasticsearch/下,其他配置放在/etc/elasticsearch/templates/下。举例我现在的一个templates/template-logstash.json内容如下:
{
"template-logstash" : {
"template" : "logstash*",
"settings" : {
"index.number_of_shards" : 5,
"number_of_replicas" : 1,
"index" : {
"store" : {
"compress" : {
"stored" : true,
"tv": true
}
}
}
},
"mappings" : {
"_default_" : {
"properties" : {
"dynamic" : "true",
},
},
"loadbalancer" : {
"_source" : {
"compress" : true,
},
"_ttl" : {
"enabled" : true,
"default" : "10d"
},
"_all" : {
"enabled" : false
},
"properties" : {
"@fields" : {
"dynamic" : "true",
"properties" : {
"client" : {
"type" : "string",
"index" : "not_analyzed"
},
"domain" : {
"type" : "string",
"index" : "not_analyzed"
},
"oh" : {
"type" : "string",
"index" : "not_analyzed"
},
"responsetime" : {
"type" : "double",
},
"size" : {
"type" : "long",
"index" : "not_analyzed"
},
"status" : {
"type" : "string",
"index" : "not_analyzed"
},
"upstreamtime" : {
"type" : "double",
},
"url" : {
"type" : "string",
"index" : "not_analyzed"
}
}
},
"@source" : {
"type" : "string",
"index" : "not_analyzed"
},
"@timestamp" : {
"type" : "date",
"format" : "dateOptionalTime"
},
"@type" : {
"type" : "string",
"index" : "not_analyzed",
"store" : "no"
}
}
}
}
}
}
注意:POST 发送的 json 内容比存储的 json 文件内容要少最外层的名字,因为名字是在 url 里体现的。
上面template中除了index/shard/replica之外的部分,就是mapping了,大家注意到其中的dynamic,默认情况下,index会在第一条数据进入的时候自动分析这条数据的情况,给每个value找到最恰当的type,然后以此为该index的mapping。之后再PUT上来的数据,格式如果不符合mapping的,也能存储成功,但是就无法检索了。
mapping中关于store和compress的部分,之前翻译的《用ElasticSearch存储日志》已经说的比较详细了。这里我的建议是 disable 掉 _all
,但是 enable 住 _source
!! 经过我的惨痛测试,如果连 _source
也 disable 掉的话,一旦你重启进程,整个 index 里除了 _id
,_timestamp
和 _score
三个默认字段,啥都丢了……
ES的API,最基本的就是CRUD操作了,这部分是标准的REST,就不说了。
然后还有三个API比较重要且常用,分别是: bulk/count/search。
一旦使用search,必须至少提供query参数,然后在这个query的基础上进行接下来其他的检索。query参数又分三类:
"match_all" : { }
直接请求全部;"term"/"text"/"prefix"/"wildcard" : { "key" : "value" }
根据字符串搜索(严格相等/片断/前缀/匹配符);"range" : { "@timestamp" : { "from" : "now-1d", "to" : "now" } }
根据范围搜索,如果type是时间格式,可以使用内置的now表示当前,然后用-1d/h/m/s来往前推。上面提到的query的参数,在filter中也都存在。此外,还有比较重要的参数就是连接操作:
"or"/"and" : [{"range":{}}, {"prefix":""}]
两个filter的查询,交集或者合集;"bool" : ["must":{},"must_not":{},"should":{}]
上面的and虽然更快,但是只能支持两个,超过两个的,要用 bool 方法;"not"/"limit" : {}
取反和限定执行数。注意这个limit和mysql什么的有点不同:它限定的是在每个shards上执行多少条。如果你有5个shards,其实对整个index是limit了5倍大小的设定值。另一点比较关键的是:filter结果默认是不缓存的,如果常用,需要指定 "_cache" : true
。
facets接口可以根据query返回统计数据,最基础的是terms和statistical两种。不过在日志分析的情况下,最常用的是:
"histogram" : { "key_field" : "", "value_field" : "", "interval" : "" }
根据时间间隔返回柱状图式的统计数据;"terms_stats" : { "key_field" : "", "value_field" : "" }
根据key的情况返回value的统计数据,类似group by的意思。这里就涉及到前面mapping里为什么针对每个field都设定type的原因了。因为 histogram
里的 key_field
只能是 dateOptionalTime
格式的,value_field
只能是 string
格式的;而 terms_stats
里的 key_field
只能是 string
格式的,value_field
只能是 numberic
格式的。
而我们都知道,http code那些200/304/400/503神马的,看起来是数字,我们却需要的是他们的count数据,不是算他们的平均数。所以不能由ES动态的认定为long,得指定为string。
对于logstash分析日志,基本没有提到analyze的部分,包括Kibana也是。但是做web日志分析,其实也需要注意analyze。因为ES默认提供并开启了一些analyze。最简单的比如空格分隔表示单词,斜线分割表示url路径,@分割表示email地址等等。文档地址见http://www.elasticsearch.org/guide/reference/index-modules/analysis/。当然ES社区的中国人也有提供中文分词的plugin。通常情况下,analyze工作的很好。嗯,ES比其他全文索引工具在默认情况下都工作的好。
但是当你想算的是今天访问的url排名,或者来访者IP排名的时候,麻烦来了,你苦苦等待N久,最后一看排名是这样的:
jpg 2345678
html 123456
20121021 34567
bbs 9876
对,你的url被ES辛辛苦苦的用 / 和 . 分割了,然后每个单词排序来再返回给你。如果你是在一个数千万条的大型库上运行的话,基本吃个饭回来才能有结果。
事实上,url就是一个整体,所以在mapping中,要定义好在indexing的时候,不要启用analyzer。这样,返回一个你心目中想要的正确的url排名,时间从吃个午饭直接缩减到打个喷嚏了!而如果是访问者ip,时间则是眨下眼就够了!
注意:analyze不是只有indexing的时候能用,在query的时候,也可以单独指定某个analyze来分析记录。analyze其实是和search并列的API,不过目前场景下用不上,就不说了。
有以上API,基本上一个针对logstash的ES数据分析系统后台就足够构建出来了。剩下的就是前端页面的事情,这方面可以参考logstash的Kibana,更广义一些的ES数据可视化可以参考ES的blog: http://www.elasticsearch.cn/blog/2011/05/13/data-visualization-with-elasticsearch-and-protovis.html,笔者的译文见http://chenlinux.com/2012/11/18/data-visualization-with-elasticsearch-and-protovis。
ES周边的工具有很多。目前我主要用三种方式:
elasticsearch/bin/plugin -install mobz/elasticsearch-head
安装,然后浏览器里直接输入 http://$eshost:9200/_plugin/head/
就可以看到cluster/node/index/shards的状态了。elasticsearch/bin/plugin -install lukas-vlcek/bigdesk
直接安装了。然后浏览器里直接输入 http://$eshost:9200/_plugin/bigdesk/
就可以看到了。注意如果使用的 bulk_index
的话,如果选择的刷新间隔太长,indexing per second数据是不准的。#!/bin/sh
ES_HOST=$1
ES_URI="http://${ES_HOST}:9200/_cluster/health"
RES_JSON=`curl -s ${ES_URI}`
status=`echo ${RES_JSON}|awk -F\" '{print $8}'`
failed=`echo ${RES_JSON}|awk -F\" '{print $NF}'|sed 's/^:\([0-9]*\)}/\1/'`
if [[ "$status" -eq "green" ]];then
echo "ES Cluster OK | failed_node=${failed}"
exit 0
elif [[ "$status" -eq "yellow" ]];then
echo "Warning! ES Cluster shards relocating or initializing. | failed_node=${failed}"
exit 1
else
echo "Critical! ES Cluster shards unassigned. | failed_node=${failed}"
exit 2
fi
ES是一个很活跃的开源项目,所以如果有其他目前ES没有你有觉得有需要的功能,大可以上github搜索一下,或许别人早已经做完相关插件了。
比如我就在上面找到一个plugin叫elasticfacets。加强了ES的 date_histogram
功能,原先只能针对某个 value_field
做攻击,这个plugin可以在这个基础上,把 value_field
加强成又一层facets。项目地址: https://github.com/bleskes/elasticfacets。之前和作者反馈了在ES 0.19.8上的问题,不知道修复没。或许最好还是用0.19.9吧。
ES的邮件列表基本每天都有四五十封邮件,地址是:elasticsearch@googlegroups.com。