ElasticSearch学习笔记
本文是对ElasticSearch组件初步学习的一个知识总结,包括如下章节的内容:
- 概述
- 快速上手
- 逻辑概念
- 用户接口
- 关于ELKB
预备知识:
1、本文对ElasticSerach的介绍会涉及与关系数据库的对比,为了更好地学习,可提前对关系数据库的基本概念有所了解。
2、ElasticSerach的数据存储采用JSON的数据格式,为了更好地学习,可提前对JSON的概念有所了解。
3、ElasticSerach对外提供的是RESTful Api访问接口,为了更好地学习,可提前对RESTful的概念有所了解。
二、快速上手
ES虽然是一个强大的分布式系统,但其易用性设计的非常好,安装和使用都很简单,下面我们来快速体验下。
(一)安装
ES需要java环境,最好是java8,需要设置JAVA_HOME环境变量。ES的安装非常简单,开箱即用,首先我们可以从官网www.elastic.co下载相应的二进制版本,对于Windows操作系统,可下载zip文件,对于linux操作系统,可下载tar文件。
下载二进制包后,解压到任何一个目录下。如果不做任何配置,则采用默认的配置启动ES。运行ES安装目录下的bin目录的elasticsearch脚本(windows下为elasticsearch.bat)则可以以单实例的方式启动ES。在linux系统中启动ES有可能会报错误,这在下面会详细介绍。
我们这里是启动的ES的单个实例,ES可以以集群的方式运行(即在多台机器上启动多个ES实例)。因为单节点模式和集群模式从使用角度看并没有太多的区别,所以本文介绍的都是基于ES在单节点方式运行的。集群的方式在后续的文章中介绍。
启动ES后,下面就可以利用ES提供的RESTful API去使用它。我们可以在浏览器或任何web工具中输入 http://localhost:9200 地址,得到如下的信息(ES系统的基本信息):
{ "name" : "H12K5tk", "cluster_name" : "elasticsearch", "cluster_uuid" : "2RAwSskjQh6xDxLrDHBTtQ", "version" : { "number" : "5.6.9", "build_hash" : "877a590", "build_date" : "2018-04-12T16:25:14.838Z", "build_snapshot" : false, "lucene_version" : "6.6.1" }, "tagline" : "You Know, for Search" }
返回的是本节点(ES实例的)name,以及ES集群的一些基本信息,如集群名,版本信息等
(二)启动错误
在Linux系统中启动ES,可能会报一些错误,导致启动失败。下面是一些常见的错误与解决方案(这也是本文在Centos7系统中启动ES碰到的问题)。
错误很好理解,就是指es进程可允许打开的最大文件数只有4096,太小了,需要提升到65536。因为ES需要打开很多文件,最大4096个不够,需要调整操作系统中的配置。
在Linux的系统中对于进程(Process)会有一些限制(limit),限制有很多种,常见的如对打开文件(Open Files)最大数量的限制。在linux中这些限制是分为软限制(soft limit)和硬限制(hard limit)两类。它们的区别就是软限制可以在程序的进程中自行改变(突破限制),而硬限制则不行(除非程序进程有root权限)。
使用ulimit 命令可以分别查看软限制和硬限制,区别是在查看的参数前加 S 或 H。例如,查看打开文件数限制(参数是n):
#查看软限制 ulimit -Sn #查看硬限制 ulimit -Hn
改变这个限制的办法是修改/etc/security/limits.conf文件,增加配置,如在配置文件中增加如下信息:
* soft nofile 65536 * hard nofile 65536
注意,最前面的 * 代表这个设置对所有进程都有效,如果只对指定进程有效,将*改为具体的进程名。
2、错误2:max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
这个错误是指Linux进程的vma大小不足,需要提高。这可以修改/etc/sysctl.conf文件,增加配置vm.max_map_count=262144,如:
vm.max_map_count=262144
修改配置文件后,执行 命令sysctl -p 让生效(注意,一定要执行),如:
[hadoop@master bin]$ sudo sysctl -p [sudo] password for hadoop: vm.max_map_count = 262144
另外,在操作系统配置文件被修改后,最好重新登录,确保新的终端能获取到修改后的信息。
(三)访问ES
因为ES对外提供了RESTful的访问接口,所以我们可以用任意一种web client工具来访问ES,最简单的如linux下的curl程序,还可以用如Postman这样的工具(Postman是一个非常有用的 Http Client 工具,可用来测试 Web 服务),也可以在浏览器上安装插件,如chrome中的Sense插件(该插件可以很方便的操作ES)。
当然我们可以利用第三方http client库自己编写代码来访问ES。不过ES自身也提供了JAVA API来操作ES,这个在后面介绍
下面我们使用RESTful API来举例使用ES。一个完整的REST请求包括如下三部分:
1、操作类型,如GET,PUT,POST,DELETE等
2、URL部分,如http://localhost:9200/blogs/blog/1
3、请求正文内容。对于GET请求,没有单独的请求正文内容,请求信息都包含在URL中。对于PUT,POST等操作,可以带正文内容,正文内容采用josn的格式,具体下面例子会看到。
ES的REST请求的返回内容的格式绝大部分都是JOSN格式的。也就是说请求和响应的内容都是JSON格式,这样有较好的一致性。
下面来看具体的例子:
1、插入数据
往ES中插入数据,可以执行如下的操作:
PUT http://localhost:9200/blogs/blog/1 { "title": "es info", "content": "about elasticsearch", "author": "jack", "year":2018 }
该请求成功后服务器返回的信息如下:
{ "_index": "blogs", "_type": "blog", "_id": "1", "_version": 1, "result": "created", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "created": true }
上面响应信息的格式也是josn格式,其中字段的含义我们后面再介绍。
2、查询数据
上面往ES中插入了一条数据,下面我们可以利用http的GET操作来获取刚才插入的数据。http请求如下:
GET http://localhost:9200/blogs/blog/1
该请求成功后服务器返回的信息如下:
{ "_index": "blogs", "_type": "blog", "_id": "1", "_version": 1, "found": true, "_source": { "title": "es info", "content": "about elasticsearch", "author": "jack", "year": 2018 } }
可以看出,返回的josn内容中,相比插入操作,有这几个字段的内容发生了变化:
"_version" : 2, "result" : "updated", "created" : false
可以看到,记录的 Id 没变,但是版本(version)从1变成2,操作类型(result)从created变成updated,created字段变成false,因为这次不是新建记录,而是修改操作。
这时如果我们执行
GET http://localhost:9200/blogs/blog/1
查询操作,会看到返回的Document数据是修改后的数据。
ES文档的版本号是有用的,我们在修改数据时,可以传入一个版本号。这样当在一个客户端读取和更新文档的间隔中,有另外客户端更新了数据。如果这个客户端的更新操作传入了版本号(前面读取的版本号),这时因为传入的版本号和当期实际的版本号不一致,就会操作失败。这样就防止可能的数据冲突。这也是ES采用乐观并发控制(OCC)机制的体现。
加入版本号进行更新,只需在url后面加上version参数,如:
PUT http://localhost:9200/blogs/blog/1?version=2
4、删除操作
通过DELETE操作,可以删除ES中的数据。HTTP请求如下:
DELETE http://localhost:9200/blogs/blog/1
该请求成功后服务器返回的信息如下:
{ "found": true, "_index": "blogs", "_type": "blog", "_id": "1", "_version": 2, "result": "deleted", "_shards": { "total": 2, "successful": 1, "failed": 0 } }
如果我们这时去执行GET操作,如
GET http://localhost:9200/blogs/blog/1
该请求成功后服务器返回的信息如下:
{ "_index": "blogs", "_type": "blog", "_id": "1", "found": false }
从响应的结果可以看出,没有查到数据。
5、搜索操作
下面我们来看下ES最强大的搜索操作,首先我们来先插入2条数据,包括前面插入和被删除的数据。
PUT http://localhost:9200/blogs/blog/1 { "title": "es info", "content": "about elasticsearch", "author": "jack", "year":2018 } PUT http://localhost:9200/blogs/blog/2 { "title": "zk info", "content": "about zookeeper", "author": "jack", "year":2018 }
下面我们来执行根据给定的字符串进行查找,只要数据中有包含给定字符串的内容就认为匹配上,查询命令如下:
POST http://localhost:9200/_search { "query": { "query_string": { "query": "elasticsearch" } } }
返回的结果如下:
{ "took": 3, "timed_out": false, "_shards": { "total": 10, "successful": 10, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 0.26742277, "hits": [ { "_index": "blogs", "_type": "blog", "_id": "1", "_score": 0.26742277, "_source": { "title": "es info", "content": "about elasticsearch", "author": "jack", "year": 2018 } } ] } }
可以看出,搜索到了数据。我们可以试着改变上面查询条件中"query"参数的值,来观察结果的变化。
上面的搜索是搜索整个json数据中的信息,我们也可以只搜索指定json字段中的内容。如:
POST http://localhost:9200/_search { "query": { "query_string": { "query": "jack", "fields": ["author"] } } }
上面的REST语句表示只在字段author中查找存在jack字符串的数据。字段通过查询条件中的filelds参数指定,可以看出,同时可指定多个字段。 对比关系数据库,ES的搜索操作有点类似关系数据库中带like子句的select语句。
可以看出,利用ES的RESTful Api进行操作还是比较简单的,当然我们只是列举了几个最基本的操作。上面通过举例让我们站在用户的角度对ES有了个初步的认识,下面章节继续展开更为详细的介绍。
(四)远程访问
上面例子中我们都是在本地利用REST接口访问ES的,即web客户端运行在ES实例所在的机器上,所以url中用的是localhost。 如果需要支持对ES的远程访问,可以修改ES安装目录的config/elasticsearch.yml文件,去掉network.host的注释,将它的值改成0.0.0.0,然后重新启动 ES。配置文件中信息如:
network.host: 0.0.0.0
上面配置,设成0.0.0.0让任何一台机器都可以访问。线上服务不要这样设置,要设成具体的 IP,如:
network.host: 192.168.0.1
三、逻辑概念
(一)索引(Index)
ES数据管理的顶层单位就叫做 Index(索引)。它不是关系数据库中索引的概念,是单个数据库的同义词,即可以把它等价于关系数据库中的单个数据库。Index的名称必须小写。
既然Index是ES管理数据的顶层单位,肯定先需要有索引,才能进行后续的数据存储等操作,类似关系数据库中得首先有数据库一样。可前面的操作我们并没有看到创建Index的操作。是不是ES启动后有一个默认的Index呢?
其实情况是这样的,上面的REST请求 http://localhost:9200/blogs/blog/1 中的 blogs就是一个索引名,当我们执行数据插入操作时,如果url中指定的索引不存在,则ES会自动创建该索引。就如我们前面的例子,我们插入数据时,索引blogs并不存在,所以ES按照默认的设置自动创建了blogs索引。
我们可以单独创建一个索引,操作如下:
PUT http://localhost:9200/topics
上面操作通过http的PUT操作,创建了一个名称为topics的索引。
操作成功后的响应信息如下:
{ "acknowledged": true, "shards_acknowledged": true, "index": "topics" }
我们可以查看到ES中的所有索引,操作如下:
GET http://localhost:9200/_cat/indices?v
响应结果如下:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open blogs hM... 5 1 2 0 10.2kb 10.2kb yellow open topics hV... 5 1 0 0 810b 810b
结果以表格的形式显示,第一行为表头。因为前面的操作我们已经创建了blogs索引,加上刚创建的topics索引,所以我们看到结果有2条记录。
通过DELETE操作,我们可以删除指定的索引,如下面操作:
DELETE http://localhost:9200/topics
操作成功的响应结果如下:
{ "acknowledged": true }
这表示删除索引成功,如果我们这时再去查找所有索引,会发现topics索引没有了。
(二)类型(Type)
在索引中,可以定义一个或多个类型(type),类型是索引的逻辑分区,是同一索引中有公共字段的文档的集合。比如在一个博客系统中,我们可以将用户数据放到一种类型中,将文章数据放到一种类型中,将对文章的评论信息放到一种类型中。
类型不需要单独创建,在插入数据(如上面的例子)会自动创建。对于 http://localhost:9200/blogs/blog/1 这个url,其中的blog就是类型(type)名。
相对关系数据库来说,类型可以类比成关系数据库中的表。
需要注意的是,根据规划,Elastic 6.x 版只允许每个 Index 包含一个 Type,7.x 版将会彻底移除 Type。
所以我们在实际使用中,不用过多考虑Type的作用。
(三)文档(Document)
Document是Index中的单条记录,它就像关系数据库中表中的记录(行)。Document 使用 JSON 格式表示,如上面插入数据时的url后面跟的请求数据:
PUT http://localhost:9200/blogs/blog/1 { "title": "es info", "content": "about elasticsearch", "author": "jack", "year":2018 }
ES的每个存储在Index中的Document都有一个类型(type)和一个唯一的ID。这个ID可以用户在创建文档时显示指定,如上面url最后的1,也可以由ES自动生成(后面会介绍)。
JSON数据是一种半结构化的数据格式,它由一个个的字段组成,字段有字段名、字段的值,和字段类型,比如上面的title字段就是一个字符串类型,year字段就是一个整数类型。JSON数据的字段类型不需要显示定义,由字段的内容推断出来,比如字符串是要放到引号中,整数则不能。
同一个 Index 里面的 Document(即使是位于同一个Type下),不要求有相同的结构(即相同的字段和字段类型),但是最好保持相同,这样有利于提高搜索效率。需要注意的是,同一个字段名的字段,不能在这个文档中是字符串类型,而在另外一个字符串中是数组类型。举个例子,上面举例中我们插入了如下的数据:
PUT http://localhost:9200/blogs/blog/1 { "title": "es info", "content": "about elasticsearch", "author": "jack", "year":2018 }
这时如果插入如下数据:
PUT http://localhost:9200/blogs/blog/2 { "title": "es info", "content": "about elasticsearch", "author": "jack", "year": "2018年" }
也就是这里插入的year字段值是个字符串,则时ES的响应就会报错,报类型不一致的错误,因为前面插入的数据已经让ES把year字段认作为整型数据。但如果我们写成 "year": "2018" 这样的格式,虽然看上去year字段是字符串类型,但因为实际数据是整数,则不会报错。
因为即使是同一个Type下的Docuemnt也不要求有相同的数据模型,所以逻辑上Type与关系数据库的表对应,实际意义差别还是挺大的。在关系数据库下,表中的每条记录数据模型都是严格相同的。
(四)字段(Field)
(五)映射(mapping)
mapping是类似于数据库中的表结构定义,主要作用如下:
- 定义index下的字段名
- 定义字段类型,比如数值型、浮点型、布尔型等
- 定义倒排索引相关的设置,比如是否索引、记录position等。
- 设置分词器等
与表结构不同的是,关系数据库的表的结构必须事先通过create table语句来明确。而映射既可以显示的通过命令来事先定义,也可以在存储文档(插入数据)时由ES来自动识别。如我们上面的例子,并没有显示的去定义Index的mapping信息。
可以通过 GET http://localhost:9200/_mapping 来查看Index的mapping信息。
(六)主键(ID)
ES中的每个Document都有一个唯一的ID(在type下唯一),也就是说 index/type/id必须是唯一的。
我们可以插入数据时显示的指定ID,如前面的操作:
PUT http://localhost:9200/blogs/blog/1
也可以也ES系统自动生成,注意这时就不能用PUT操作,要用POST操作,如下面操作:
POST http://localhost:9200/blogs/blog { "title": "es info", "content": "about elasticsearch", "author": "jack", "year":"2018" }
上面请求中url的最后没有指定要插入的Document的ID,操作成功后的响应信息如下:
{ "_index": "blogs", "_type": "blog", "_id": "AWeXZiyTS_34KbUEtT3A", "_version": 1, "result": "created", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "created": true }
可以看出,响应信息中带回来了ES自动生成的ID值。
关于ELKB
先说下ELK,ELK是一个流行的日志系统解决方案,注意,ELK不是一个软件名,而是一个解决方案的缩写,即Elasticsearch+Logstash+Kibana(ELK Stack)这三个软件的集合。
这几个都是开源的java产品,但是众所周知,java的东西很吃内存和CPU,Logstash在作为收集日志的Agent时,就显得太过臃肿了。后来官方在logstash-forwarder的基础上推出了beat系列,里面包括四个系统,分别是:
1、Packetbeat(搜集网络流量数据);
2、Topbeat(搜集系统、进程和文件系统级别的 CPU 和内存使用情况等数据);
3、Filebeat(搜集文件数据),Filebeat占用资源少,适合于在各个服务器上搜集日志后传输给Logstash;
4、Winlogbeat(搜集 Windows 事件日志数据)。
所以Filebeat也就这样加入了“日志收集分析”的团队里,所以虽然大家还是习惯性的叫ELK,其实准确的说法已经是ELKB了。
ELKB中的几个软件的分工如下:
1、Elasticsearch:分布式搜索和分析引擎,具有高可伸缩、高可靠和易管理等特点。基于 Apache Lucene 构建,能对大容量的数据进行接近实时的存储、搜索和分析操作。通常被用作某些应用的基础搜索引擎,使其具有复杂的搜索功能。
2、Logstash:数据收集额外处理和数据引擎。它支持动态的从各种数据源搜集数据,并对数据进行过滤、分析、丰富、统一格式等操作,然后存储到用户指定的位置。
3、Kibana:数据分析和可视化平台。通常与 Elasticsearch 配合使用,对其中数据进行搜索、分析和以统计图表的方式展示。
4、Filebeat:ELK 协议栈的新成员,在需要采集日志数据的服务器上安装 Filebeat,并指定日志目录或日志文件后,Filebeat 就能读取数据,迅速发送到 Logstash 进行解析,亦或直接发送到 Elasticsearch 进行集中式存储和分析。