很多人刚刚接触ELK都不知道如何使用它们来做分析,经常会碰到下面的问题:

  • 安装完ELK不知从哪下手
  • 拿到数据样本不知道怎么分解数据
  • 导入到elasticsearch中奇怪为什么搜不出来
  • 搜到结果后,不知道它还能干什么

本篇就以一个完整的流程介绍下,数据从 读取-->分析-->检索-->应用 的全流程处理。在阅读本篇之前,需要先安装ELK,可以参考之前整理安装文档:ELK5.0部署教程

在利用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。

如我的配置文件:

  1. input {
  2. file {
  3. path => "C:\Users\Documents\workspace\elk\page.csv"
  4. start_position => "beginning"
  5. }
  6. }
  7. filter {
  8. grok {
  9. match => {
  10. "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}"
  11. }
  12. }
  13. }
  14. output {
  15. stdout{codec => dots}
  16. elasticsearch {
  17. document_type => "test"
  18. index => "page"
  19. hosts => ["1.1.1.1:9200"]
  20. }
  21. }

上面的配置最不容易理解的就是Grok,其实它就是个正则表达式而已,你可以把它理解成是一段正则表达式的占位。至于grok都有哪些关键字,这些关键字对应的正则都是什么,可以直接参考logstash的源码,目录的位置为:

  1. logstash-5.2.2\vendor\bundle\jruby\1.9\gems\logstash-patterns-core-4.0.2\patterns

如果提供的话,可以直接在grokdebug上面进行测试:

另外一个技巧就是,如果开启stdout并且codec为rubydebug,会把数据输出到控制台,因此使用.代替,即可省略输出,又能检测到现在是否有数据正在处理。而且每个.是一个字符,如果把它输出到文件,也可以直接通过文件的大小,判断处理了多少条。

这样,数据的预处理做完了.....

基于字段创建Mapping

虽然说Es是一个文档数据库,但是它也是有模式的概念的。文档中的每个字段仍然需要定义字段的类型,使用者经常会遇到明明是数字,在kibana却做不了加法;或者明明是IP,kibana里面却不认识。这都是因为Mapping有问题导致的。

在Elasticsearch中其实是有动态映射这个概念的,在字段第一次出现时,ES会自动检测你的字段是否属于数字或者日期或者IP,如果满足它预定义的格式,就按照特殊格式存储。一旦格式设置过了,之后的数据都会按照这种格式存储。举个例子,第一条数据进入ES时,字段检测为数值型;第二条进来的时候,却是一个字符串,结果可能插不进去,也可能插进去读不出来(不同版本处理的方式不同)。

因此,我们需要事先就设定一下字段的Mapping,这样之后使用的时候才不会困惑。

另外,Mapping里面不仅仅有字段的类型,还有这个字段的分词方式,比如使用标准standard分词器,还是中文分词器,或者是自定义的分词器,这个也是很关键的一个概念,稍后再讲。

创建Mapping有两种方式:

第一种,直接创建索引并创建映射

创建索引时,可以直接指定它的配置和Mapping:

  1. PUT index_name
  2. {
  3. "settings" : {
  4. "number_of_shards" : 1
  5. },
  6. "mappings" : {
  7. "type_name" : {
  8. "properties" : {
  9. "field_name" : { "type" : "text" }
  10. }
  11. }
  12. }
  13. }

第二种,先创建索引,再创建映射

  1. # 先创建索引
  2. PUT index_name
  3. {}
  4. # 然后创建Mapping
  5. PUT /index_name/_mapping/type_name
  6. {
  7. "properties": {
  8. "ip": {
  9. "type": "ip"
  10. }
  11. }
  12. }
  13. # 最后查询创建的Mapping
  14. GET /index_name/_mapping/type_name

比如我们上面的URL场景,可以这么建立索引:

  1. PUT url/_mapping/test
  2. {
  3. "properties": {
  4. "url": {
  5. "type": "string",
  6. "fields": {
  7. "keyword": {
  8. "type": "keyword",
  9. "ignore_above": 256
  10. }
  11. }
  12. },
  13. "date": {
  14. "type": "date"
  15. },
  16. "pvs": {
  17. "type": "integer"
  18. },
  19. "uvs": {
  20. "type": "integer"
  21. }
  22. }
  23. }

PS,在上面的例子中,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却收不到任何结果,是不是有点怀疑人生。

我们做个小例子,首先创建一个空的索引:

  1. PUT test1/test1/1
  2. {
  3. "text":"https://www.elastic.co/guide/en/elasticsearch/reference/5.2"
  4. }

然后查询这个字段被分解成了什么鬼?

  1. GET /test1/test1/1/_termvectors?fields=text

得到的内容如下:

  1. {
  2. "_index": "test1",
  3. "_type": "test1",
  4. "_id": "1",
  5. "_version": 1,
  6. "found": true,
  7. "took": 1,
  8. "term_vectors": {
  9. "text": {
  10. "field_statistics": {
  11. "sum_doc_freq": 7,
  12. "doc_count": 1,
  13. "sum_ttf": 7
  14. },
  15. "terms": {
  16. "5.2": {
  17. "term_freq": 1,
  18. "tokens": [
  19. {
  20. "position": 6,
  21. "start_offset": 56,
  22. "end_offset": 59
  23. }
  24. ]
  25. },
  26. "elasticsearch": {
  27. "term_freq": 1,
  28. "tokens": [
  29. {
  30. "position": 4,
  31. "start_offset": 32,
  32. "end_offset": 45
  33. }
  34. ]
  35. },
  36. "en": {
  37. "term_freq": 1,
  38. "tokens": [
  39. {
  40. "position": 3,
  41. "start_offset": 29,
  42. "end_offset": 31
  43. }
  44. ]
  45. },
  46. "guide": {
  47. "term_freq": 1,
  48. "tokens": [
  49. {
  50. "position": 2,
  51. "start_offset": 23,
  52. "end_offset": 28
  53. }
  54. ]
  55. },
  56. "https": {
  57. "term_freq": 1,
  58. "tokens": [
  59. {
  60. "position": 0,
  61. "start_offset": 0,
  62. "end_offset": 5
  63. }
  64. ]
  65. },
  66. "reference": {
  67. "term_freq": 1,
  68. "tokens": [
  69. {
  70. "position": 5,
  71. "start_offset": 46,
  72. "end_offset": 55
  73. }
  74. ]
  75. },
  76. "www.elastic.co": {
  77. "term_freq": 1,
  78. "tokens": [
  79. {
  80. "position": 1,
  81. "start_offset": 8,
  82. "end_offset": 22
  83. }
  84. ]
  85. }
  86. }
  87. }
  88. }
  89. }

看到了吧,没有elastic这个词,自然是搜不出来的。如果你不理解这句话,回头看看倒排索引的原理吧!或者看看我的这篇文章:分词器的作用

那么你可能很郁闷,我就是要搜elastic怎么办!没关系,换个分词器就行了~比如elasticsearch为我们提供的simple分词器,就可以简单的按照符号进行切分:

  1. POST _analyze
  2. {
  3. "analyzer": "simple",
  4. "text": "https://www.elastic.co/guide/en/elasticsearch/reference/5.2"
  5. }

得到的结果为:

  1. {
  2. "tokens": [
  3. {
  4. "token": "https",
  5. "start_offset": 0,
  6. "end_offset": 5,
  7. "type": "word",
  8. "position": 0
  9. },
  10. {
  11. "token": "www",
  12. "start_offset": 8,
  13. "end_offset": 11,
  14. "type": "word",
  15. "position": 1
  16. },
  17. {
  18. "token": "elastic",
  19. "start_offset": 12,
  20. "end_offset": 19,
  21. "type": "word",
  22. "position": 2
  23. },
  24. {
  25. "token": "co",
  26. "start_offset": 20,
  27. "end_offset": 22,
  28. "type": "word",
  29. "position": 3
  30. },
  31. {
  32. "token": "guide",
  33. "start_offset": 23,
  34. "end_offset": 28,
  35. "type": "word",
  36. "position": 4
  37. },
  38. {
  39. "token": "en",
  40. "start_offset": 29,
  41. "end_offset": 31,
  42. "type": "word",
  43. "position": 5
  44. },
  45. {
  46. "token": "elasticsearch",
  47. "start_offset": 32,
  48. "end_offset": 45,
  49. "type": "word",
  50. "position": 6
  51. },
  52. {
  53. "token": "reference",
  54. "start_offset": 46,
  55. "end_offset": 55,
  56. "type": "word",
  57. "position": 7
  58. }
  59. ]
  60. }

这样你就可以搜索elastic了,但是前提是需要在Mapping里面为该字段指定使用simple分词器,方法为:

  1. PUT url/_mapping/test
  2. {
  3. "properties": {
  4. "url": {
  5. "type": "string",
  6. "analyzer": "simple",
  7. "fields": {
  8. "keyword": {
  9. "type": "keyword",
  10. "ignore_above": 256
  11. }
  12. }
  13. },
  14. "date": {
  15. "type": "date"
  16. },
  17. "pvs": {
  18. "type": "integer"
  19. },
  20. "uvs": {
  21. "type": "integer"
  22. }
  23. }

修改Mapping前,需要先删除索引,然后重建索引。删除索引的命令为:

  1. DELETE url

不想删除索引,只想改变Mapping?想得美....你当ES是孙悟空会72变?不过,你可以创建一个新的索引,然后把旧索引的数据导入到新索引就行了,这也不失为一种办法。如果想这么搞,可以参考reindex api,如果版本是5.0之前,那么你倒霉了!自己搞定吧!

检索

ES里面检索是一个最基础的功能了,很多人其实这个都是一知半解。由于内容太多,我就结合Kibana讲讲其中的一小部分吧。

很多人安装完kibana之后,登陆后不知道该干啥。如果你的elasticsearch里面已经有数据了,那么此时你需要在Kiban新建对应的索引。

如果你的es的索引是name-2017-03-19,name-2017-03-20这种名字+时间后缀的,那么可以勾选1位置的选项,它会自动聚合这些索引。这样在这一个索引中就可以查询多个索引的数据了,其实他是利用了索引的模式匹配的特性。如果你的索引仅仅是一个简单的名字,那么可以不勾选1位置的选项,直接输入名字,即可。

然后进入Kibana的首页,在输入框里面就可以任意输入关键字进行查询了。

查询的词,需要是上面_termvectors分析出来的词,差一个字母都不行!!!!!

这个搜索框其实就是elasticsearch中的query string,因此所有的lucene查询语法都是支持的!

如果想要了解更多的查询语法,也可以参考我之前整理的文章,Lucene查询语法

另外,这个输入框,其实也可以输入ES的DSL查询语法,只不过写法过于蛋疼,就不推荐了。

自定义查询语法

如果不使用kibana,想在自己的程序里面访问es操作,也可以直接以rest api的方式查询。

比如查询某个索引的全部内容,默认返回10个:

  1. GET /page/test/_search?pretty

再比如,增加一个特殊点的查询:

  1. GET /page/test/_search?pretty
  2. {
  3. "query": {
  4. "query_string" : {
  5. "default_field" : "url",
  6. "query" : "颜色"
  7. }
  8. },
  9. "size": 10,
  10. }

聚合

在es中一个很重要的亮点,就是支持很多的聚合语法,如果没有它,我想很多人会直接使用lucene吧。在ES中的聚合,大体上可以为两类聚合方法,metric和bucket。metic可以理解成avg、sum、count、max、min,bucket可以理解为group by 。有了这两种聚合方法,就可以对ES中的数据做很多处理了。

比如在kibana中,做一个最简单的饼图:

其实它在后台发送的请求,就是这个样子的:

  1. {
  2. "size": 0,
  3. "query": {
  4. "query_string": {
  5. "query": "颜色",
  6. "analyze_wildcard": true
  7. }
  8. },
  9. "_source": {
  10. "excludes": []
  11. },
  12. "aggs": {
  13. "2": {
  14. "terms": {
  15. "field": "url.keyword",
  16. "size": 5,
  17. "order": {
  18. "_count": "desc"
  19. }
  20. }
  21. }
  22. }
  23. }

如果不适用kibana,自己定义聚合请求,那么可以这样写:

  1. GET /page/test/_search?pretty
  2. {
  3. "query": {
  4. "query_string" : {
  5. "default_field" : "url",
  6. "query" : "颜色"
  7. }
  8. },
  9. "size": 0,
  10. "aggs" : {
  11. "agg1" : {
  12. "terms" : {
  13. "field" : "url.keyword",
  14. "size" : 10
  15. },
  16. "aggs" : {
  17. "pvs" : { "sum" : { "field" : "pvs" } },
  18. "uvs" : { "sum" : { "field" : "uvs" } }
  19. }
  20. }
  21. }
  22. }

另外,聚合也支持嵌套聚合,就是跟terms或者sum等agg并列写一个新的aggs对象就行。

高亮

如果是自己使用elasticsearch,高亮也是一个非常重要的内容,它可以帮助最后的使用者快速了解搜索的结果。

后台的原理,是利用ES提供的highlight API,针对搜索的关键字,返回对应的字段。该字段中包含了一个自定义的标签,前端可以基于这个标签高亮着色。

举个简单的例子:

  1. GET /_search
  2. {
  3. "query" : {
  4. "match": { "content": "kimchy" }
  5. },
  6. "highlight" : {
  7. "fields" : {
  8. "content" : {}
  9. }
  10. }
  11. }

上面的请求会针对content字段搜索kimchy。并且返回对应的字段,比如原来的字段内容时hello kimchy,经过高亮后,会再搜索结果的hits中返回:

  1. {
  2. "took": 3,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 5,
  6. "successful": 5,
  7. "failed": 0
  8. },
  9. "hits": {
  10. "total": 30,
  11. "max_score": 13.945707,
  12. "hits": [
  13. {
  14. "_index": "page",
  15. "_type": "test",
  16. "_id": "AVrvHh_kvobeDQC6Q5Sg",
  17. "_score": 13.945707,
  18. "_source": {
  19. "date": "2016-03-14",
  20. "pvs": "3",
  21. "url": "hello kimchy",
  22. "@timestamp": "2017-03-21T04:29:07.187Z",
  23. "uvs": "1",
  24. "@version": "1"
  25. },
  26. "highlight": {
  27. "url": [
  28. "hello <em>kimchy</em>"
  29. ]
  30. }
  31. }
  32. ]
  33. }
  34. }

这样就可以直接利用highlight中的字段做前端的显示了。

另外,上面的<em>标签可以自定义,比如:

  1. GET /_search
  2. {
  3. "query" : {
  4. "match": { "user": "kimchy" }
  5. },
  6. "highlight" : {
  7. "pre_tags" : ["<tag1>"],
  8. "post_tags" : ["</tag1>"],
  9. "fields" : {
  10. "_all" : {}
  11. }
  12. }
  13. }

经过上面的一步一步的探索,你应该了解ELK的数据分析的流程与技巧了吧!如果有任何问题,也可以直接留言,可以再交流!

参考

基于ELK的数据分析实践——满满的干货送给你的更多相关文章

  1. 基于ELK的简单数据分析

    原文链接: http://www.open-open.com/lib/view/open1455673846058.html 环境 CentOS 6.5 64位 JDK 1.8.0_20 Elasti ...

  2. 以数字资产模型为核心驱动的一站式IoT数据分析实践

    [摘要] 一个不会直播的云服务架构师,不是一个好的攻城狮! 在这个全民直播的时代 一个不会直播的云服务架构师 不是一个好的攻城狮 3月23日15:00-15:50,华为云IoT物联网数据分析服务架构师 ...

  3. 向大家介绍我的新书:《基于股票大数据分析的Python入门实战》

    我在公司里做了一段时间Python数据分析和机器学习的工作后,就尝试着写一本Python数据分析方面的书.正好去年有段时间股票题材比较火,就在清华出版社夏老师指导下构思了这本书.在这段特殊时期内,夏老 ...

  4. 基于 GraphQL 的 BFF 实践

    随着软件工程的发展,系统架构越来越复杂,分层越来越多,分工也越来越细化.我们知道,互联网是离用户最近的行业,前端页面可以说无时无刻不在变化.前端本质上还是用户交互和数据展示,页面的高频变化意味着对数据 ...

  5. 基于Python的数据分析(2):字符串编码

    在上一篇文章<基于Python的数据分析(1):配置安装环境>中的第四个步骤中我们在python的启动步骤中强制要求加载sitecustomize.py文件并设置其默认编码为"u ...

  6. 基于Python的数据分析(1):配置安装环境

    数据分析是一个历史久远的东西,但是直到近代微型计算机的普及,数据分析的价值才得到大家的重视.到了今天,数据分析已经成为企业生产运维的一个核心组成部分. 据我自己做数据分析的经验来看,目前数据分析按照使 ...

  7. 基于ELK进行邮箱访问日志的分析

    公司希望能够搭建自己的日志分析系统.现在基于ELK的技术分析日志的公司越来越多,在此也记录一下我利用ELK搭建的日志分析系统. 系统搭建 系统主要是基于elasticsearch+logstash+f ...

  8. 数据可视化之PowerQuery篇(十九)PowerBI数据分析实践第三弹 | 趋势分析法

    https://zhuanlan.zhihu.com/p/133484654 ​本文为星球嘉宾"海艳"的PowerBI数据分析工作实践系列分享之三,她深入浅出的介绍了PowerBI ...

  9. 从0搭建一个基于 ELK 的日志、指标收集与监控系统

    为了使得私有化部署的系统能更健壮,同时不增加额外的部署运维工作量,本文提出了一种基于 ELK 的开箱即用的日志和指标收集方案. 在当前的项目中,我们已经使用了 Elasticsearch 作为业务的数 ...

随机推荐

  1. 微信小程序----关于变量对象data 和 前端wxml取后台js变量值

    (一)页面变量对象data 对象data 有两个方面用途 第一,前端wxml的数据渲染是通过设置此对象中定义的变量进行关联展现的 第二,定义JS页面中的页面局部变量,使其整个页面中可使用或调用 对象d ...

  2. Unity3d获取游戏对象的几种方法

    1.GameObject.Find() 通过场景里面的名子或者一个路径直接获取游戏对象. GameObject root = GameObject.Find("GameObject" ...

  3. 《响应式Web设计—HTML5和CSS3实战》 学习记录

    作者:Ben Frain 学习时间   2016/5/12 第一章   设计入门 *视口调试工具 IE:Microsoft Internet Explorer Develop Toolbar Safa ...

  4. 【LeetCode题解】数组Array

    1. 数组 直观地看,数组(Array)为一个二元组<index, value>的集合--对于每一个index,都有一个value与之对应.C语言中,以"连续的存储单元" ...

  5. Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSInvocation setArgument:atIndex:]: index (3) out of bounds [-1, 2]'

    这是相机调用方法的时候参数错误

  6. SpringMVC中404错误解决方法总结

    在新手配置Spring MVC的时候,感觉都弄好了之后,运行起来却显示404错误. 网上对出现404的问题不同情况,都有了解决方法,前几天我也遇到了这个问题,顺便把这些问题总结一下. 解决问题最重要的 ...

  7. 解读Java内部类

    一.基本概念: 顾名思义,内部类存在于外部类当中,依附于外部类.就像眼睛和脑袋的关系一样. 二.几点说明: 1.内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以 ...

  8. Hadoop学习笔记:使用Mrjob框架编写MapReduce

    1.mrjob介绍 一个通过mapreduce编程接口(streamming)扩展出来的Python编程框架. 2.安装方法 pip install mrjob,略.初学,叙述的可能不是很细致,可以加 ...

  9. 虚拟机Centos开机以后,有eth0网卡,但是没有IP,Determine IP information for eth0.. no link present check cable

    Determine IP information for eth0.. no link present check cable 如果你的VMware虚拟机centos6.5使用NAT模式,开机以后,使 ...

  10. 从源码解析LinkedList集合

         上篇文章我们介绍了ArrayList类的基本的使用及其内部的一些方法的实现原理,但是这种集合类型虽然可以随机访问数据,但是如果需要删除中间的元素就需要移动一半的元素的位置,效率低下.并且它内 ...