Elasticsearch
1、开始
Elasticsearch 是一个高度可扩展的开源的全文搜索分析引擎。它允许你近实时的快速存储、搜索和分析海量数据。它通常的应用场景就是为一些复杂的搜索应用提供一个底层的引擎或技术,在其上搭建应用,解决海量数据的搜索问题。
下面是Elasticsearch几个常见的应用场景:
- 网上商城。你可以用Elasticsearch来存储你全部的商品类目和清单并提供商品搜索与推荐的功能。
- 日志分析,通过分析应用log来发现一些趋势、统计数据、概要亦或是异常信息。这种场景下你可以使用Elasticsearch的Logstash来收集、汇聚、分析数据,然后通过Logstash消费这些数据并写入Elasticsearch。一旦这些数据进入Elasticsearch你就可以搜索、汇集这些数据来挖掘你感兴趣的信息。
- 比价推荐系统,根据各个供应商提供同一商品的价格来为感兴趣的买家推荐便宜的商品。你可以搜集各个商家的商品价格信息放入Elasticsearch,然后使用reverse-search来与买家期望的价格来比较,一旦发现匹配数据就推送给买家。
- 智能数据分析,海量数据的调查、分析可视化,可以使用Elasticsearch来存储数据然后使用Kibana来做数据可视化,你还可以使用Elasticsearch的聚合功能来执行商业智能的很多查询。
1.1、基本概念
Elasticsearch中有几个核心的概念,在学习的开始能够理解这些概念对于整个学习过程有非常大的帮助。
1.近实时
Elasticsearch是一个近实时的搜索平台,意思是从你开始索引一个文档到文档可检索之间只有很少的一点时间(一般是1秒)。
2.集群
集群是一个或多个节点(服务器)的集合,它们一起保存你的整个暑假并在所有节点上提供联合索引与搜索功能。每个集群使用唯一的名字来区分,默认为“elasticsearch”,你可以在配置文件中修改,这个名字非常重要,因为子节点通过设置名字来加入到集群中。
当然也可以建立一个只有一个节点的集群,这是合理合法的。
注意:集群名字必须唯一,否则节点可能加入到其它集群。
3.节点
节点属于集群中的一部分,它属于一个单个的服务,用来存储数据并参与集群的索引与搜索。节点也使用唯一的名字来区分,默认在服务开启的时候分配一个唯一的UUID。这个名字对于指定集群中那些节点提供指定服务来说十分重要。
每个节点都可以配置加入某个特定的集群通过集群名称,默认如果各个节点可以发现其它节点他们会加入同一个名为elasticsearch的集群。
4.索引
一个索引是一个有着公共属性的文档的集合。例如:客户数据索引;商品目录索引;订单索引等。
索引通过唯一的索引名字来区分(必须都是小写字母),这个索引名可以在后期的索引(索引、搜索、更新、删除)过程中使用。
一个集群中你可以定义任意多的索引。
5.类型
在一个索引中你可以定义各个或者多个类型。类型是你索引的逻辑分类,你可以自己定义类型,只要你自己可以区分这种类型是什么东西即可。通常,一个类型定义了一个有着公共字段的文档集合。例如:用户数据;博客数据;评论数据等。
6.文档
文档是elasticsearch可以索引的基本单位。使用json来标示。
在一个索引–类型里面你可以定义任意多个文档。
Relational DB | Databases | Tables | Rows | Columns |
---|---|---|---|---|
Elasticsearch | Indices | Types | Documents | Fields |
7.分片与副本
一个索引可以存储超过单个节点硬件限制的存储数据量。Elasticsearch通过将一个索引分割成多个分片来实现在集群中存储超出单个节点的存储能力。以下连个原因说明分片很重要:
1、它允许你水平分割你的数据卷。
2、它允许你跨分片分布并行操作,从而提高吞吐量。
为了防止数据丢失,Elasticsearch提供了副本的概念,副本是对索引分片的拷贝。创建副本的主要原因有:
1、高可用,副本不会被分配在于源同一个节点上。
2、扩展搜索吞吐量,搜索可以并行的在所有副本上执行。
分片与副本的数量在新建索引的时候被定义,副本的数量可以在运行过程中动态修改,但是分片的数量一旦定义完成不可以修改,默认一个索引有5个主分片与一个副本(五个分片)。
一个索引中最大可以存储大于21亿个文档。
1.2、探查你的集群
现在我们的集群已经运行起来了,下一步我们就要去与集群进行数据交互,Elasticsearch提供了一个非常强大的REST API,我们可以通过它来进行一下操作:
1.检查你的集群、节点以及索引的健康情况、运行状态以及数据统计情况。
2.管理你的集群、节点、索引数据以及元数据。
3.通过索引的名字进行CRUD(Create、Read、Update、Delete)以及搜索操作。
4.执行高级搜索功能,如分页、排序、过滤、脚本、聚合以及其它高级功能。
1.2.1、集群健康状态
Elasticsearch健康检查,可以帮我们了解我们的集群在干什么,使用_cat API提供的REST API可以查看
GET http://192.168.0.103:9200/_cat/health?v
显示结果:
epoch | timestamp | cluster | status | node.total | node.data | shards | pri | relo | init | unassign | pending_tasks | max_task_wait_time | active_shards_percent |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1488117930 | 22:05:30 | somnus-app | green | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | - | 100.0% |
集群的状态有绿色、黄色和红色。
绿色
说明集群一切都ok,集群能够提供完全的功能;
黄色
说明所有的数据都可以访问,集群提供完全的功能,但是一些副本没有被分配,存在单点故障问题;
红色
说明一些数据已经不可以访问了,但是集群仍然可以提供部分功能(你可以从可达的分片中搜索数据),这时候要尽快修复要不然会丢数据。
1.2.2、查看集群中的节点列表
可以通过集群中任意一个节点执行下面的命令,返回集群中所有的节点列表以及各个节点的信息。
GET http://192.168.0.103:9200/_cat/nodes?v
显示结果:
ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
192.168.0.103 10 79 0 0.01 0.07 0.08 mdi * node-1
1.2.3、查看集群的所有指数信息
通过REST API查看集群中所有的指数信息,包括健康状况、索引情况、文档数等等一些信息。
GET http://192.168.0.103:9200/_cat/indices?v
显示结果:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
green open customer EfbKVtP-Q4m2UhfGB-rGXg 5 1 0 0 1.2kb 650b
1.2.4、创建索引
创建索引要使用PUT命令,后面加上pretty可以让ES输出人性化的JSON反馈信息。
PUT http://192.168.0.103:9200/customer?pretty
1.2.5、索引并查询一个文档
为了索引一个文档,我们必须告诉ES要知道索引的Type。
PUT http://192.168.0.103:9200/customer/external/1?pretty
{
"name":"somnus"
}
返回结果:
{
"_index":"customer",
"_type":"external",
"_id":"1",
"_version":1,
"result":"created",
"_shards":{
"totle":2,
"successful":2,
"failed":0
},
"created":true
}
ES并不需要我们事先创建好索引,当我们索引文档时,如果索引不存在,ES会自动创建。
取回文档:
GET http://192.168.0.103:9200/customer/external/1?pretty
返回结果:
{
"_index":"customer",
"_type":"external",
"_id":"1",
"_version":1,
"found":true,
"_source":{
"name":"somnus"
}
}
“_source”下面就是我们索引的文档信息的json格式数据
1.2.6、删除一个索引
DELETE http://192.168.0.103:9200/customer?pretty
删除一个type以及一个特定的文档原理相同,只要跟上相应的type以及id即可。
1.2.7、总结
通过如上几个命令的学习,我们总结下ES的REST API的一般模式如下:
<REST Verb> http://ip:9200/<Index>/<Type>/<ID>
1.3、修改数据
ES提供近实时的数据操作与搜索。默认情况下当你提交了一个index/update/delete命令时会有一秒左右的延时才会反映到你的ES中去,这个平常使用的其它平台可能不太一样,比如sql,当你提交命令后数据会立即显示/可见。
值得注意的是,如果你对ES索引相同的数据(index/type/id都相同),那么ES会覆盖老数据。
如果你在索引的时候不提供id字段,那么ES会生成一个随机的id给它,但是这时只能用POST而不能用PUT。
POST http://192.168.0.103:9200/customer/external?pretty
{
"name":"zomnus"
}
1.3.1、更新单个文档
除了能够索引以及替换一个文档外,我们也可以更新一个文档。但是ES内部并不会直接更新一个文档,它是先删除老的文档然后索引一个新的文档。
更新文档使用带_update后缀url的REST API。
有两种方式可以更新一个文档,doc以及scripts
1.3.1.1、更新文档之-doc
POST http://192.168.0.103:9200/customer/external/1/_update?pretty
{
"doc":{
"name":"lilei", --修改了名字
"age":21 --添加了一个年龄字段
}
}
1.3.1.2、更新文档之-scripts
POST http://192.168.0.103:9200/customer/external/1/_update?pretty
{
"script":"ctx._source.age+=5" --年龄加5岁
}
使用scripts更新文档中,ctx._source代表要更新的源文档。
1.3.2、删除单条文档
在上面删除索引的时候已经提过了,删除文档只要将相应的type与id指定好即可删除单个文档。
DELETE http://192.168.0.103:9200/customer/external/1?pretty
1.3.3、批量处理
除了能够索引、更新、删除单个文档,ES提供了_bulk API来批量执行以上操作,这样能够尽量节省网络流量的情况下提高操作效率。
POST http://192.168.0.103:9200/customer/external/_bulk?pretty
{
{"index":{"_id":"1"}}
{"name":"xiaowang"} --索引一个文档,注意都没有逗号
{"update":{"_id":"1"}}
{"doc":{"age":21}} --更新一个文档
{"delete":{"_id":"1"}} --删除一个文档
}
注意:最后一组操作不要忘记换行
可以看出,除了删除操作,索引跟更新都需要两行,第二行是对应的文档数据,因为删除操作只需要根据id找到并删除即可,不需要文档其它信息。
另外,批量操作中如果有一个操作失败了并不会导致整个批量操作失败,ES会按照你批量操作的顺序返回执行结果,所以你可以查看返回结果的状态来判断哪个错做失败
1.4、探索Elasticsearch中的数据
首先我们先随机填充一些数据进去,使用上节刚看到的_bulk命令即可完成,但是一般我们准备的数据都是保存成json格式的文件中,这时就需要使用到了 –data-binary参数了。
POST http://192.168.0.103:9200/bank/account/_bulk?pretty&refresh --data-binary "account.json"
1.4.1、搜索(Search)API
有两种基本的方法可以执行搜索操作,一种是使用REST request URL将数据绑定在URL上一同发送给ES;另一种是使用REST request body,参数以数据体的格式跟随URL传过去,很明显request body的方式更好一些.ES 搜索RESR API以_search结尾。
1.4.1.1、REST request URL
request URL格式,url后面带搜索参数,具体参数见后期应用。
POST http://192.168.0.103:9200/bank/account/_search?q=*&sort=account_number:asc&pretty
1.4.1.2、REST request body
request body格式将一个json格式的查询数据体发送给ES。
POST http://192.168.0.103:9200/bank/_search
{
"query":{"match_all":{}},
"sort":[
{"account_number":"asc"}
]
}
需要注意的是:当ES返回结果之后,ES的工作就全部做完了,不会再跟其他产品一样保留一个服务端的资源或者游标类似的东西,它是一次返回所有数据并关闭所有相关的资源。(像db,可以像分页一样先取回一部分数据,然后再回去取另一页的数据,ES结束就结束了)
1.4.2、引入查询语言DSL
Elasticsearch提供了一种JSON风格的特定于域的语言,可用于执行查询。这被称为查询DSL。它的功能相当强大。
下面我们解释几个例子:
- 例1:查询条件(search)
POST http://192.168.0.103:9200/bank/_search
{
"query":{
"match_all":{}
}
}
在这个例子中,search表明我们查询的定义;match_all部分是我们要运行的查询类型。
match_all表示查询指定索引(bank)下的所有文档。
- 例2:影响条件(size)
POST http://192.168.0.103:9200/bank/_search
{
"query":{
"match_all":{}
},
"size":1
}
除了query这个参数外,我们还可以添加其他参数来影响查询结果,例如sort、size(如果未设置size,默认显示10条,即size=10)
- 例3:分页(from,size)
POST http://192.168.0.103:9200/bank/_search
{
"query":{
"match_all":{}
},
"from":10,--从0开始,默认为0
"size":10
}
- 例4:排序,主要是很灵活(order)
POST http://192.168.0.103:9200/bank/_search
参数一:完整的排序写法,一个排序条件
{
"query":{
"match_all":{}
},
"sort":{
"balance":{"order":"desc"}
}
}
参数二:简写排序,多个条件
{
"query":{
"match_all":{}
},
"sort":[
{"age":"desc"},
{"balance":{"order":"asc"}}
],
"size":12
}
1.4.3、执行搜索(高级查询)
- 例5:返回部分数据(_source)
POST http://192.168.0.103:9200/bank/_search
{
"query":{
"match_all":{}
},
"_source":["name","age"]
}
默认会返回文档的所有字段信息,可以使用_source来过滤要显示的字段。
- 例6:匹配(match,模糊匹配,分词处理)
POST http://192.168.0.103:9200/bank/_search
{
"query":{
"match":{
"firstname":"zhang san"
}
}
}
match_all匹配的是所有的文档,我们可以使用match来对特定字段来进行搜索,匹配zhang 、san、zhang san。
- 例7:匹配(match_phrase,精确匹配)
POST http://192.168.0.103:9200/bank/_search
{
"query":{
"match_phrase":{
"firstname":"zhang san"
}
}
}
只能匹配zhang san
1.4.4、boolean查询(sql中的and、or)
boolean查询可以将多个小的查询通过boolean逻辑组合成一个大的查询条件。
- 例8:must,与逻辑,相当于and
POST http://192.168.0.103:9200/bank/_search
{
"query":{
"bool":{
"must":[
{"match":{"address":"shandong"}},
{"match":{"address":"nanjing"}}
]
}
}
}
返回所有既包含shandong又包含nanjing的文档。
- 例9:should,或逻辑,相当于or
POST http://192.168.0.103:9200/bank/_search
{
"query":{
"bool":{
"should":[
{"match":{"address":"shandong"}},
{"match":{"address":"nanjing"}}
]
}
}
}
匹配所有包含shandong或者包含nanjing的文档。
- 例10:must_not,非逻辑,相当于not in
{
"query":{
"bool":{
"must_not":[
{"match":{"address":"shandong"}},
{"match":{"address":"nanjing"}}
]
}
}
}
返回既不包含shandong又不包含nanjing的所有文档。
- 例11:混合搭配组成复杂查询
{
"query":{
"bool":{
"must_not":[
{"match":{"address":"shandong"}}
],
"must":[
{"match":{"age":15}}
]
}
}
}
返回15岁非shandong籍的所有文档。
1.4.5、执行过滤
上面讲的查询都会返回一个文档的score分数,这个分数代表文档与查询条件的相符程度,分数越高说明相关度越好。但是我们并不是每次都需要这个分数的,而且计算分数会降低ES性能,这时我们可以使用filter过滤。
- 例12:filter,过滤
{
"query":{
"bool":{
"must":{"match_all":{}},
"filter":[
{
"match":{"address":"171"}
},
{
"range":{
"age":{"gt":10,"lt":90}
}
}
]
}
}
}
1.4.6、执行聚合,类似group by
- 例13:aggs,聚合,sql-group by
{
"size":0,
"aggs":{
"group_by_gender":{
"terms":{
"field":"gender.keyword"
}
}
}
}
类似于SQL:
select gender,count(*) from bank group by bank
设置size=0,不显示hits即文档信息,只显示聚合的结果
最后附上思维导图一副