一、前言
不少人刚刚接触ELK都不知道如何使用它们来作分析,常常会碰到下面的问题:
-
安装完ELK不知从哪下手
-
拿到数据样本不知道怎么分解数据
-
导入到elasticsearch中奇怪为何搜不出来
-
搜到结果后,不知道它还能干什么
本篇就以一个完整的流程介绍下,数据从 读取-->分析-->检索-->应用 的全流程处理。
在利用ELK作数据分析时,大体为下面的流程:正则表达式
1、基于logstash分解字段
2、基于字段建立Mapping
3、查看分词结果
4、检索
5、聚合
6、高亮
可能会根据第4步重复第2步的工做,调整分词等规则。
二、业务背景
为了便于理解,先说一下本文的业务背景:我须要统计一个url对应的pv和uv,这个url须要支持全文检索。天天同一个url都会产生一条数据。最后会按照特定的日期范围对数据进行聚合。
三、基于logstash分解字段
在使用logstash前,须要对它有必定的了解。logstash的组件其实很简单,主要包括input、filter、output、codec四个部分。
-
input 用于读取内容,经常使用的有stdin(直接从控制台输入)、file(读取文件)等,另外还提供了对接redis、kafka等的插件。
-
filter 用于对输入的文本进行处理,经常使用的有grok(基于正则表达式提取字段)、kv(解析键值对形式的数据)、csv、xml等,另外还提供了了一个ruby插件,这个插件若是会用的话,几乎是万能的。
-
output 用于把fitler获得的内容输出到指定的接收端,经常使用的天然是elasticsearch(对接ES)、file(输出到文件)、stdout(直接输出到控制台)
-
codec 它用于格式化对应的内容,能够在Input和output插件中使用,好比在output的stdout中使用rubydebug以json的形式输出到控制台
理解上面的内容后,再看看logstash的使用方法。首先须要定义一个配置文件,配置文件中配置了对应的input,filter,output等,至少是一个input,output。如个人配置文件:
input {
file {
path => "C:\Users\Documents\workspace\elk\page.csv"
start_position => "beginning"
}
}
filter {
grok {
match => {
"message" => "%{NOTSPACE:url}\s*%{NOTSPACE:date}\s*%{NOTSPACE:pvs}\s*%{NOTSPACE:uvs}\s*%{NOTSPACE:ips}\s*%{NOTSPACE:mems}\s*%{NOTSPACE:new_guests}\s*%{NOTSPACE:quits}\s*%{NOTSPACE:outs}\s*%{NOTSPACE:stay_time}"
}
}
}
output {
stdout{codec => dots}
elasticsearch {
document_type => "test"
index => "page"
hosts => ["1.1.1.1:9200"]
}
}
上面的配置最不容易理解的就是Grok,其实它就是个正则表达式而已,你能够把它理解成是一段正则表达式的占位。至于grok都有哪些关键字,这些关键字对应的正则都是什么,能够直接参考logstash的源码,目录的位置为:
logstash-5.2.2\vendor\bundle\jruby\1.9\gems\logstash-patterns-core-4.0.2\patterns
若是提供的话,能够直接在grokdebug上面进行测试:
1另一个技巧就是,若是开启stdout而且codec为rubydebug,会把数据输出到控制台,所以使用.
代替,便可省略输出,又能检测到如今是否有数据正在处理。并且每一个.
是一个字符,若是把它输出到文件,也能够直接经过文件的大小,判断处理了多少条。这样,数据的预处理作完了。
四、基于字段建立Mapping
虽说ES是一个文档数据库,可是它也是有模式的概念的。文档中的每一个字段仍然须要定义字段的类型,使用者常常会遇到明明是数字,在kibana却作不了加法;或者明明是IP,kibana里面却不认识。这都是由于Mapping有问题致使的。
在Elasticsearch中实际上是有动态映射这个概念的,在字段第一次出现时,ES会自动检测你的字段是否属于数字或者日期或者IP,若是知足它预约义的格式,就按照特殊格式存储。一旦格式设置过了,以后的数据都会按照这种格式存储。举个例子,第一条数据进入ES时,字段检测为数值型;第二条进来的时候,倒是一个字符串,结果可能插不进去,也可能插进去读不出来(不一样版本处理的方式不一样)。
所以,咱们须要事先就设定一下字段的Mapping,这样以后使用的时候才不会困惑。另外,Mapping里面不只仅有字段的类型,还有这个字段的分词方式,好比使用标准standard分词器,仍是中文分词器,或者是自定义的分词器,这个也是很关键的一个概念,稍后再讲。
建立Mapping有两种方式:
4.1 第一种,直接建立索引并建立映射
建立索引时,能够直接指定它的配置和Mapping
PUT index_name
{
"settings" : {
"number_of_shards" : 1
},
"mappings" : {
"type_name" : {
"properties" : {
"field_name" : { "type" : "text" }
}
}
}
}
4.2 第二种,先建立索引,再建立映射
# 先建立索引
PUT index_name
{}
# 而后建立Mapping
PUT /index_name/_mapping/type_name
{
"properties": {
"ip": {
"type": "ip"
}
}
}
# 最后查询建立的Mapping
GET /index_name/_mapping/type_name
好比咱们上面的URL场景,能够这么创建索引
PUT url/_mapping/test
{
"properties": {
"url": {
"type": "string",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"date": {
"type": "date"
},
"pvs": {
"type": "integer"
},
"uvs": {
"type": "integer"
}
}
}
补充:
在上面的例子中,url须要有两个用途,一个是做为聚合的字段;另外一个是须要作全文检索。在ES中全文检索的字段是不能用来作聚合的,所以使用嵌套字段的方式,新增一个url.keyword字段,这个字段设置成keyword类型,不采用任何分词(这是5.0的新特性,若是使用之前版本,能够直接设置string对应的index属性便可);而后自己的url字段则采用默认的标准分词器进行分词。
这样,之后在搜索的时候能够直接以query string的方式检索url,聚合的时候则能够直接使用url.keyword
五、查看分词结果
若是字段为https://www.elastic.co/guide/en/elasticsearch/reference/5.2
,使用standard标准分词器,输入elastic却搜不到任何结果,是否是有点怀疑人生。咱们作个小例子,首先建立一个空的索引:
PUT test1/test1/1
{
"text":"https://www.elastic.co/guide/en/elasticsearch/reference/5.2"
}
而后查询这个字段被分解成了什么鬼?
GET /test1/test1/1/_termvectors?fields=text
获得的内容以下
{
"_index": "test1",
"_type": "test1",
"_id": "1",
"_version": 1,
"found": true,
"took": 1,
"term_vectors": {
"text": {
"field_statistics": {
"sum_doc_freq": 7,
"doc_count": 1,
"sum_ttf": 7
},
"terms": {
"5.2": {
"term_freq": 1,
"tokens": [
{
"position": 6,
"start_offset": 56,
"end_offset": 59
}
]
},
"elasticsearch": {
"term_freq": 1,
"tokens": [
{
"position": 4,
"start_offset": 32,
"end_offset": 45
}
]
},
"en": {
"term_freq": 1,
"tokens": [
{
"position": 3,
"start_offset": 29,
"end_offset": 31
}
]
},
"guide": {
"term_freq": 1,
"tokens": [
{
"position": 2,
"start_offset": 23,
"end_offset": 28
}
]
},
"https": {
"term_freq": 1,
"tokens": [
{
"position": 0,
"start_offset": 0,
"end_offset": 5
}
]
},
"reference": {
"term_freq": 1,
"tokens": [
{
"position": 5,
"start_offset": 46,
"end_offset": 55
}
]
},
"www.elastic.co": {
"term_freq": 1,
"tokens": [
{
"position": 1,
"start_offset": 8,
"end_offset": 22
}
]
}
}
}
}
}
看到了吧,没有elastic这个词,天然是搜不出来的。若是你不理解这句话,回头看看倒排索引的原理吧。或者看看这篇文章:分词器的作用
那么你可能很郁闷,我就是要搜elastic怎么办!不要紧,换个分词器就好了~好比elasticsearch为咱们提供的simple分词器,就能够简单的按照符号进行切分:
POST _analyze
{
"analyzer": "simple",
"text": "https://www.elastic.co/guide/en/elasticsearch/reference/5.2"
}
获得的结果为
{
"tokens": [
{
"token": "https",
"start_offset": 0,
"end_offset": 5,
"type": "word",
"position": 0
},
{
"token": "www",
"start_offset": 8,
"end_offset": 11,
"type": "word",
"position": 1
},
{
"token": "elastic",
"start_offset": 12,
"end_offset": 19,
"type": "word",
"position": 2
},
{
"token": "co",
"start_offset": 20,
"end_offset": 22,
"type": "word",
"position": 3
},
{
"token": "guide",
"start_offset": 23,
"end_offset": 28,
"type": "word",
"position": 4
},
{
"token": "en",
"start_offset": 29,
"end_offset": 31,
"type": "word",
"position": 5
},
{
"token": "elasticsearch",
"start_offset": 32,
"end_offset": 45,
"type": "word",
"position": 6
},
{
"token": "reference",
"start_offset": 46,
"end_offset": 55,
"type": "word",
"position": 7
}
]
}
这样你就能够搜索elastic了,可是前提是须要在Mapping里面为该字段指定使用simple分词器,方法为:
PUT url/_mapping/test
{
"properties": {
"url": {
"type": "string",
"analyzer": "simple",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"date": {
"type": "date"
},
"pvs": {
"type": "integer"
},
"uvs": {
"type": "integer"
}
}
修改Mapping前,须要先删除索引,而后重建索引。删除索引的命令为:
DELETE url
不想删除索引,只想改变Mapping?想得美....你当ES是孙悟空会72变?不过,你能够建立一个新的索引,而后把旧索引的数据导入到新索引就好了,这也不失为一种办法。若是想这么搞,能够参考reindex api,若是版本是5.0以前,那么你倒霉了,本身搞定吧。
六、检索
ES里面检索是一个最基础的功能了,不少人其实这个都是只知其一;不知其二。因为内容太多,我就结合Kibana讲讲其中的一小部分吧。不少人安装完kibana以后,登录后不知道该干啥。若是你的elasticsearch里面已经有数据了,那么此时你须要在Kiban新建对应的索引。
2若是你的es的索引是name-2017-03-19,name-2017-03-20这种名字+时间后缀的,那么能够勾选1位置的选项,它会自动聚合这些索引。这样在这一个索引中就能够查询多个索引的数据了,其实他是利用了索引的模式匹配的特性。若是你的索引仅仅是一个简单的名字,那么能够不勾选1位置的选项,直接输入名字,便可。
3而后进入Kibana的首页,在输入框里面就能够任意输入关键字进行查询了。查询的词,须要是上面_termvectors分析出来的词,差一个字母都不行。
这个搜索框其实就是elasticsearch中的query string,所以全部的lucene查询语法都是支持的。若是想要了解更多的查询语法,也能够参考我以前整理的文章,Lucene查询语法。另外,这个输入框,其实也能够输入ES的DSL查询语法,只不过写法过于蛋疼,就不推荐了。
6.1 自定义查询语法
若是不使用kibana,想在本身的程序里面访问es操做,也能够直接以rest api的方式查询。好比查询某个索引的所有内容,默认返回10个:
GET /page/test/_search?pretty
再好比,增长一个特殊点的查询:
GET /page/test/_search?pretty
{
"query": {
"query_string" : {
"default_field" : "url",
"query" : "颜色"
}
},
"size": 10,
}
6.2 聚合
在ES中一个很重要的亮点,就是支持不少的聚合语法,若是没有它,我想不少人会直接使用lucene吧。在ES中的聚合,大致上能够为两类聚合方法,metric和bucket。metic能够理解成avg、sum、count、max、min,bucket能够理解为group by 。有了这两种聚合方法,就能够对ES中的数据作不少处理了。好比在kibana中,作一个最简单的饼图:
4其实它在后台发送的请求,就是这个样子的:
{
"size": 0,
"query": {
"query_string": {
"query": "颜色",
"analyze_wildcard": true
}
},
"_source": {
"excludes": []
},
"aggs": {
"2": {
"terms": {
"field": "url.keyword",
"size": 5,
"order": {
"_count": "desc"
}
}
}
}
}
若是不适用kibana,本身定义聚合请求,那么能够这样写:
GET /page/test/_search?pretty
{
"query": {
"query_string" : {
"default_field" : "url",
"query" : "颜色"
}
},
"size": 0,
"aggs" : {
"agg1" : {
"terms" : {
"field" : "url.keyword",
"size" : 10
},
"aggs" : {
"pvs" : { "sum" : { "field" : "pvs" } },
"uvs" : { "sum" : { "field" : "uvs" } }
}
}
}
}
另外,聚合也支持嵌套聚合,就是跟terms或者sum等agg并列写一个新的aggs对象就行。
6.3 高亮
若是是本身使用elasticsearch,高亮也是一个很是重要的内容,它能够帮助最后的使用者快速了解搜索的结果。后台的原理,是利用ES提供的highlight API,针对搜索的关键字,返回对应的字段。该字段中包含了一个自定义的标签,前端能够基于这个标签高亮着色。
举个简单的例子:
GET /_search
{
"query" : {
"match": { "content": "kimchy" }
},
"highlight" : {
"fields" : {
"content" : {}
}
}
}
上面的请求会针对content字段搜索kimchy。而且返回对应的字段,好比原来的字段内容时hello kimchy,通过高亮后,会再搜索结果的hits中返回:
{
"took": 3,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 30,
"max_score": 13.945707,
"hits": [
{
"_index": "page",
"_type": "test",
"_id": "AVrvHh_kvobeDQC6Q5Sg",
"_score": 13.945707,
"_source": {
"date": "2016-03-14",
"pvs": "3",
"url": "hello kimchy",
"@timestamp": "2017-03-21T04:29:07.187Z",
"uvs": "1",
"@version": "1"
},
"highlight": {
"url": [
"hello <em>kimchy</em>"
]
}
}
]
}
}
这样就能够直接利用highlight中的字段作前端的显示了。另外,上面的<em>标签能够自定义,好比:
GET /_search
{
"query" : {
"match": { "user": "kimchy" }
},
"highlight" : {
"pre_tags" : ["<tag1>"],
"post_tags" : ["</tag1>"],
"fields" : {
"_all" : {}
}
}
}
网友评论