Elasticsearch介绍
简介
Elasticsearch是一个高可扩展的开源全文搜索和分析引擎,它允许存储、搜索和分析大量的数据,并且这个过程是近实时的。它通常被用作底层引擎和技术,为复杂的搜索功能和要求提供动力。
基础概念
Elasticsearch中有几个核心的概念,理解这些概念对于ES的学习还是很有帮助的。
近实时(Near Realtime NRT)
Elasticsearch是一个近实时的搜索平台,近实时的意思是指从你建立索引到你能够搜索这个文档这个过程,这个过程时间大概是1秒钟。
集群(Cluster)
一个集群包含一个或多个节点(node),用来保存全部数据,并且这些节点联合提供索引和搜索能力。集群使用唯一名称进行不同集群间的区分,默认名称是”elasticsearch”。集群的名称很重要,因为一个节点如果要加入到一个集群中,需要设置节点的集群名称。
在实际应用中,需要确保你的不同环境,所使用的名称是不同的,否则会导致节点加入到其它集群环境中。比如你可以使用:logging-dev、logging-stage、logging-prod分别用来搭建开发、过渡、生产环境。
集群可以只有一个节点,这个集群也能正常提供工作能力。此外,也可以搭建多个Elasticsearch集群,通过集群名称区分。
节点(Node)
在集群中一个节点是一个单独的服务,用来存储数据,为集群的索引和搜索提供能力。集群中的节点,也有唯一标识的,默认在节点启动的时候会随机指定一个UUID(Universally Unique IDentifiter)。如果不使用默认名称,可以为其指定一个名称。节点名称对于集群管理也是很重要的。
一个节点要加入到指定集群,需要为节点指定集群名称。默认情况下,每个节点都设置集群名称为”elasticsearch”,所以当在同一网络环境中,默认启动一些节点,这些节点会组装成一个名为elasticsearch集群。
索引(Index)
一个索引包含许多特征类似的文档。例如,有一个索引用来索引用户数据,另一个索引用来索引产品目录,其它的索引可以索引其它数据。一个索引需要指定一个名称(必须全部小写),执行索引、搜索、修改和删除操作,需要指定对应的索引名称。
在一个集群中,你可以创建多个索引。
类型(Type)
在一个索引中,可以定义多个类型。一个类型可以管理索引中符合特定逻辑的一部分数据。一般来说,类型定义具有公共字段的文档。例如你想创建一个博客平台,并且使用一个索引存储所有数据。在这个索引中,你可以定义一个类型用来存储用户数据,另一个类型用来存储博客数据,还可以创建一个类型用来存储评论。
文档(Documeng)
文档是能够被索引的基础单元。例如一个文档存储一个用户信息,另一个文档存储一个产品信息。Elasticsearch文档使用JSON(JavaScript Object Notation)来表现数据。
在一个索引/文档中可以存储许多文档。需要注意,尽管一个文档在物理存储上是存储在一个索引中的,但文档必须被索引或分配给索引的类型中。
分片(Shard)&副本(Replicas)
在实际应用中可能存在这样的场景,一个索引存储超过了一个节点的物理存储能力。例如一个索引有十亿的文档,超过了1TB的存储,在单个节点无法完全存储,并且对于单个节点的搜索请求是非常慢的。
为了解决这些问题,Elasticsearch提供了为索引切分成多个分片(Shard)的能力,当你创建一个索引的时候,你能够定义索引被分割成多少个shard。每一个shard都是功能完全,能够独立索引的,可以分配到集群中任何一个节点中。
使用分片有两个重要原因:
- 允许水平分割文档,分布存储。
- 允许你分配并且执行分片,在多个节点中能够提供查询性能和吞吐量。
shard如何分布,当一个请求发来后如何组织数据,这些对于用户来说都是透明的。
当在一个网络环境中,有可能某个节点或分片丢失。Elasticsearch提供了一种针对这种故障转移的功能,就是副本。Elasticsearch允许你为一个分片创建一个或多个副本。分片和副本又称为主/副分片。
使用副本有两个重要原因:
- 当一个节点或分片丢失后,能够继续使用。因此,需要注意的是主/副分片不会存储在一个节点中。
- 当进行搜索的时候,允许搜索所有的副本,所以提高了搜索性能。
所以一个索引能够分割成多个shard,一个索引能够可以有零个或多个副本。每个索引都有主分片(索引切割后的分片,又称原始分片)和副本分片(从原始分片复制过来的)。分片数量和副本数量在创建索引的时候可以被指定,当索引创建后,可以指定改变副本数量,但是不能改变分片数量。默认情况下每个索引在Elasticsearch有5个分片和1个副本,也就是说总共10个shard(副本是对分片来说的,每个shard又一个副本)。每一个shard内部都是一个Lucene索引实例,单个lucene索引最多能够存储2,147,483,519(Integer.MAX_value-128)个文档。
Elasticserach层级
理解Elasticsearch中索引、类型、文档、域(字段)可以和关系型数据库做个简单的对比:
- Relation DB: database->table->row->column
- Elasticsearch:index->type->document->field
Elasticsearch安装(5.2.0)
jdk安装
Elasticsearch安装前需要安装jdk,版本最低是1.8,因为ES源码中许多使用了lambda表达式等jdk8的语法。
下载Elasticsearch
https://www.elastic.co/downloads/elasticsearch
解压包
tar -zxvf elasticsearch-5.2.0.tar.gz
进入解压文件
cd elasticsearch-5.2.0/bin
启动ES
./elasticsearch
<pre>
2017-02-28T21:54:27,648][INFO ][o.e.n.Node ] [] initializing ...
[2017-02-28T21:54:27,736][INFO ][o.e.e.NodeEnvironment ] [fkHgUS8] using [1] data paths, mounts [[/ (/dev/disk1)]], net usable_space [117.6gb], net total_space [177.8gb], spins? [unknown], types [hfs]
[2017-02-28T21:54:27,736][INFO ][o.e.e.NodeEnvironment ] [fkHgUS8] heap size [1.9gb], compressed ordinary object pointers [true]
[2017-02-28T21:54:27,737][INFO ][o.e.n.Node ] node name [fkHgUS8] derived from node ID [fkHgUS8UTBOOYxQ4LaPYQg]; set [node.name] to override
[2017-02-28T21:54:27,739][INFO ][o.e.n.Node ] version[5.2.0], pid[4855], build[24e05b9/2017-01-24T19:52:35.800Z], OS[Mac OS X/10.11.6/x86_64], JVM[Oracle Corporation/Java HotSpot(TM) 64-Bit Server VM/1.8.0_91/25.91-b14]
[2017-02-28T21:54:28,453][INFO ][o.e.p.PluginsService ] [fkHgUS8] loaded module [aggs-matrix-stats]
[2017-02-28T21:54:28,453][INFO ][o.e.p.PluginsService ] [fkHgUS8] loaded module [ingest-common]
[2017-02-28T21:54:28,454][INFO ][o.e.p.PluginsService ] [fkHgUS8] loaded module [lang-expression]
[2017-02-28T21:54:28,454][INFO ][o.e.p.PluginsService ] [fkHgUS8] loaded module [lang-groovy]
[2017-02-28T21:54:28,454][INFO ][o.e.p.PluginsService ] [fkHgUS8] loaded module [lang-mustache]
[2017-02-28T21:54:28,454][INFO ][o.e.p.PluginsService ] [fkHgUS8] loaded module [lang-painless]
[2017-02-28T21:54:28,454][INFO ][o.e.p.PluginsService ] [fkHgUS8] loaded module [percolator]
[2017-02-28T21:54:28,454][INFO ][o.e.p.PluginsService ] [fkHgUS8] loaded module [reindex]
[2017-02-28T21:54:28,454][INFO ][o.e.p.PluginsService ] [fkHgUS8] loaded module [transport-netty3]
[2017-02-28T21:54:28,454][INFO ][o.e.p.PluginsService ] [fkHgUS8] loaded module [transport-netty4]
[2017-02-28T21:54:28,455][INFO ][o.e.p.PluginsService ] [fkHgUS8] no plugins loaded
[2017-02-28T21:54:30,225][INFO ][o.e.n.Node ] initialized
[2017-02-28T21:54:30,225][INFO ][o.e.n.Node ] [fkHgUS8] starting ...
[2017-02-28T21:54:30,420][INFO ][o.e.t.TransportService ] [fkHgUS8] publish_address {127.0.0.1:9300}, bound_addresses {[fe80::1]:9300}, {[::1]:9300}, {127.0.0.1:9300}
[2017-02-28T21:54:33,477][INFO ][o.e.c.s.ClusterService ] [fkHgUS8] new_master {fkHgUS8}{fkHgUS8UTBOOYxQ4LaPYQg}{XitBFBRHSYKtMEA-VwIBgw}{127.0.0.1}{127.0.0.1:9300}, reason: zen-disco-elected-as-master ([0] nodes joined)
[2017-02-28T21:54:33,495][INFO ][o.e.h.HttpServer ] [fkHgUS8] publish_address {127.0.0.1:9200}, bound_addresses {[fe80::1]:9200}, {[::1]:9200}, {127.0.0.1:9200}
[2017-02-28T21:54:33,496][INFO ][o.e.n.Node ] [fkHgUS8] started
[2017-02-28T21:54:33,500][INFO ][o.e.g.GatewayService ] [fkHgUS8] recovered [0] indices into cluster_state
</pre>
从启动信息种可以看到节点名称为fkHgUS8,由于是单节点,所以本身就是master,使用端口9200作为REST API访问端口。我们可以通过命令行为其指定集群名称和节点名称:
./elasticsearch -Ecluster.name=es_cluster -Enode.name=node01
探索集群
上面我们把elasticsearch集群启动起来了(虽然只有一个节点),接下来你应该试着与这个集群进行通信。Elasticsearch提供了非常全面和强大的REST API来与集群通信。通过这些API你可以:
- 检查集群、节点、索引健康、状态和统计
- 管理集群、节点、索引数据和元数据信息
- 执行CRUD操作
- 执行高级的搜索操作,比如分页(paging)、排序(sorting)、脚本(scripting)、聚合(aggregations)和其它操作
Elasticsearch交互
Elasticsearch提供了两种交互方式:java客户端(java api)和http协议(rest api)
- java api
节点客户端(node client):节点客户端加入集群,但是并不存储数据,但知道数据再集群的具体位置,并且能够转发请求到对应节点上。
传输客户端(Transport client):节点本身不加入集群,只简单发送请求给集群中的节点,这个更轻量。
两个客户端都是通过9300端口进行访问,并使用Elasticsearch传输协议进行通信(ETP:Elasticsearch Transport Protocol),集群节点间也是使用9300端口进行通信。 - 基于http协议
基于HTTP协议,以JSON为数据交互格式的RESTful API,非java语言可以使用RESTful API形式与ES进行交互,使用9200端口。
请求格式:
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING> ‘ -d <BODY>
VERB:HTTP方法,GET、POST、PUT、HEAD、DELETE
PROTOCOL:http协议或https协议
HOST:请求的节点主机
PORT:端口号,默认9200
PATH:请求路径
QUERY_STRING:可选请求参数
BODY:JSON形式请求的主题
example:curl -XGET http://localhost:9200/_count
得到响应主体,如果要请求头,在-XGET前加-i获取
集群健康
集群健康检查,这里会用到_cat语法:
curl http://127.0.0.1:9200/_cat/health?v
从结果可以看到,集群名称为elasticsearch,集群状态为绿色(green)。
集群状态分为绿色(green)、黄色(yellow)和红色(red)。
- 绿色代表集群当前状态是好的(集群功能齐全)。
- 黄色代表所有数据(主分片)都可用,但是一些副本(副分片)还没有分配(集群功能齐全)。
- 红色代表一些数据(主分片)不可用。
需要注意的是,即使集群是红色的,它仍然部分功能可用。例如:可用的shard能够继续为搜索提供服务。但是你应该尽快去解决你丢失的数据。
从上面的信息来看,我们就有一个节点(node),并且可以看到有0个shard,这是因为我们还没有导入数据。需要注意一点,我们启动Elasticsearch使用默认名称elasticsearch。由于Elasticsearch默认采用单播网络去发现其它节点,所以如果你启动了多个Elasticsearch实例,则会自动加入这个名称为”elasticsearch”的集群。
可以使用curl http://127.0.0.1:9200/_cat/nodes?v 查看node节点列表
node节点状态获取所有索引列表
curl http://127.0.0.1:9200/_cat/indices?v 索引列表
会发现没有任何索引纪录,因为我们集群还没有创建任何索引;
创建索引
curl -XPUT http://127.0.0.1:9200/customer 添加索引
然后在查看索引
curl http://127.0.0.1:9200/_cat?indices?v
可以看到现在健康状态为yello,之前我们说过yello说明当前副本并未分配,因为我们只有一个节点,而副本不会和主分片在一台机器上的,所以当添加节点(node)后,副本分片被分配后集群恢复green。还可以看到有一个索引名称为customer的索引,有5个主分片(pri)和1个副本分片(rep), 0个文档,删除过0个文档,存储大小,主分片存储大小。
索引操作
索引文档
索引文档,需要为其指定类型,也可以为其手动指定id(如果不指定,Elasticsearch随机提供一个id)
curL -XPUT http://127.0.0.1:9200/customer/external/1?pretty -d '{"name":"yjz”}’
pretty参数是指格式化返回结果
返回结果:
<pre>
{
"_index" : "customer",
"_type" : "external",
"_id" : "2",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"created" : true
}
</pre>
需要注意的是,elasticsearch不需要你在索引文档前提前创建索引或者类型。当你索引文档时候,如果发现索引不存在,则会自动创建。
获取索引
curL -XGET http://127.0.0.1:9200/customer/external/2?pretty
<pre>
{
"_index" : "customer",
"_type" : "external",
"_id" : "2",
"_version" : 1,
"found" : true,
"_source" : {
"name" : "Jok"
}
}
</pre>
能够发现已经找到该索引,_source字段是指索引时候的完整文档。
删除索引
curl -XDELETE http://127.0.0.1:9200/customer/external/2?pretty
<pre>
{
"found" : true,
"_index" : "customer",
"_type" : "external",
"_id" : "2",
"_version" : 2,
"result" : "deleted",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
}
}
</pre>
修改文档
Elasticsearch表面上支持文档的修改,其实底层是将旧的索引删除,然后重新索引一个新的文档,来实现修改功能。
curl -XPOST http://127.0.0.1:9200/customer/external/1?pretty -d '{"name":"Mark”}’
返回结果:
<pre>
{
"_index" : "customer",
"_type" : "external",
"_id" : "1",
"_version" : 2,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"created" : false
}
</pre>
批量操作
Elasticsearch提供了批量操作api(_bulk),来高效完成index、update、delete操作。批量操作可以将操作写在一个文件中,然后在执行命令的时候指定文件。
curl -XPOST 'http://10.4.237.77:9200/_bulk?pretty' --data-binary @esbatch.json
esbatch.json文件内容:
<pre>
{"index":{"_index":"blog","_type":"article","_id":1}}
{"title":"java is first","conent":"java is verry nice"}
{"create":{"_index":"blog","_type":"article","_id":2}}
{"title":"elasticsearch","content":"elasticsearch study"}
{"create":{"_index":"blog","_type":"article","_id":2}}
{"title":"elasticsearchV2","conent":"elasticsearchV2"}
{"delete":{"_index":"blog","_type":"article","_id":4}}
{"delete":{"_index":"blog","_type":"article","_id":1}}
</pre>
批量操作不会因为一个操作活动(action)失败而失败,如果批量操作中有一条纪录之行失败,则批量操作还会继续执行。批量操作会为每个执行活动返回一个执行状态,以便检查是否执行成功。
搜索
搜索可以使用两种方式:REST request URI和REST request BODY,一些简单搜索可以使用URI,而对于一些复杂的搜索可以使用request body。
curl -XGET http://127.0.0.1:9200/customer/_search?q=mark&sort=name:asc&pretty
返回结果:
<pre>
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 1.0,
"hits" : [
{
"_index" : "customer",
"_type" : "external",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "Mark"
}
}
]
}
}
</pre>
返回说明:
took:Elasticsearch执行搜索毫秒数
time_out:查询是否超时
_shards:告诉我们搜索了多少个shard,成功多少,失败多少。
hits:搜索结果
hits.total:总共多少个文档符合我们的搜索条件
hits.max_score:匹配最大的分
hits.hits:实际搜索结果数据(默认返回前十条)
Elasticsearch提供了JSON风格的domain-specific-language(DSL:领域特定语言)执行查询,这种查询语言是相当全面的。比如:
curl -XGET http://127.0.0.1:9200/customer/_search?pretty -d '{"query":{"match_all":{}}}’,由于使用curl所以json串看着不友好
如果使用kibana或head,使用还是很有好的:
<pre>
GET /customer/_search
{
“query”:{“match_all”:{}},
“from”:10,
“size”:10,
“sort”:{“balance”:{“order”:”desc"}}
}
</pre>
如果想返回部分字段可以使用_source字段:
<pre>
GET /customer/_search
{
“query”:{“match_all”:{}},
“_source”:[“xxxx”,”xxxx"]
}
</pre>
上面都是match_all全部匹配返回,可以指定匹配条件,下面这个匹配包含hello或word的文档。
<pre>
GET /customer/_search
{
“query”:{“match”:{“name”,”hello word"}}
}
</pre>
也可以匹配短语,就是包含hello word短语的文档,不进行拆词。
<pre>
GET /customer/_search
{
“query”:{“match_phrase”:{“name”,”hello word"}}
}
</pre>
布尔查询
布尔查询可以多条件匹配,可以完成and、or、not组合条件查询。
- 查询and操作,通过must控制,需要满足所有查询条件。
<pre>
GET /customer/_search
{
“query”:{
“must”:[
{“match”:{“name”:”hello”}},
{“match”:{“name”:”world"}}
]
}
}
</pre>
- 查询or操作,通过should控制,只需满足部分查询条件。
<pre>
GET /customer/_search
{
“query”:{
“should”:[
{“match”:{“name”:”hello”}},
{“match”:{“name”:”world"}}
]
}
}
</pre>
- 查询not操作,通过must_not控制,返回不包含条件列表的文档。
<pre>
GET /customer/_search
{
“query”:{
“must_not”:[
{“match”:{“name”:”hello”}},
{“match”:{“name”:”world"}}
]
}
}
</pre>
- 组合查询条件
<pre>
GET /customer/_search
{
“query”:{
“must”:[
{“match”:{“name”:”hello”}}
],
“must_not”:[
{“match”:{“name”:”world"}}
]
}
}
</pre>
过滤条件
比如range范围匹配
<pre>
GET /customer/_search
{
“query”:{
“must_not”:[
{“match”:{“name”:”hello”}},
{“match”:{“name”:”world"}}
],
“filter”:{
“range”:{
“balance”:{
“gte”:50,
“lte”:60
}
}
}
}
}
</pre>
聚合查询
聚合可以将数据分组和统计,Elasticsearch提供了聚合操作,聚合操作类似SQL中的group by语句。比如返回姓名相同的前十个(默认返回十个)
<pre>
GET /customer/_search
{
“size”:0,
“aggs”:{
“group_by_state”:{
“terms”:{
“field”:”name"
}
}
}
}
</pre>
Elasticsearch提供了近实时的数据操纵和搜索能力。默认情况下从索引、修改、删除到搜索出现在你结果中有1秒钟的延迟,这是和其它平台一个很大的区别,比如SQL数据操作完,立马可见。