一.介绍
ElasticSearch是一个分布式搜索引擎和数据分析引擎,能够实现对海量的数据进行近实时的处理。支持全文检索,结构化检索,数据分析。
分布式:ES自动将海量数据分散到多台服务器上存储和检索
海量数据的处理:分布式以后,就可以采用大量的服务器去存储和检索数据,提高吞吐量;
近实时:秒级别的数据检索响应速度;
lucene: 一个全文搜索引擎,单机应用,在单机服务器上使用;里面封装了各种建立倒排索引、以及进行搜索的代码,包括各种算法;在做java开发的时候可以直接引入lucene的jar包,然后基于lucene进行开发就可以了;
ES是基于lucene开发的,实现了分布式存储和检索的功能;隐藏了复杂性,技工简单可用的restful api接口、java api接口等;
ES的特点:
(1).可以作为一个大型分布式集群,处理PB级别的数据,也可以运行在单机上;
(2).是将全文检索,数据分析以及分布式技术,合并在一起,基于lucene封装的应用;
(3).对于用户而言,开箱即用,简单部署,数据量不大的话就可以用进行开发了;
(4).数据库的功能对很多领域是不够用的,一些特殊的功能,比如全文检索,同义词处理,相关度排名,复杂数据分析,近实时查询;
倒排索引:
二.ES的核心概念
1.Near Realtime(NRT) :近实时,从写入数据到数据被搜索到有一个很小的延迟,可以达到秒级响应;
2.Cluster:集群,包含多个节点,每个节点属于哪个集群是通过配置(集群名称,默认是elasticsearch)来决定的,对于中小型应用,刚开始一个集群就一个节点很正常;
3.Node:节点,集群中的一个节点,节点也有一个名称(默认是随机分配的),节点名称很重要,默认节点会去加入‘elasticsearch’的集群,如果直接启动一堆节点,它们会自动组成一个集群;
4.Document:文档,es中的最小数据单元,每个index下的type都可以去存储多个document;
5.Index:索引,包含一堆有相似的文档数据
6.Type:类型,每个索引里都可以有一个或多个type;
7.shard:数据片,单台机器无法存储大量数据,es可以将一个index中的数据切分成
多个shard,分布存在多台服务器上。有了shar的就可以横向扩展,存储更多的数据,让搜索操作分布到多台服务器上去执行,提升吞吐量和性能。
8.replica:shard副本,任何一个服务器可能会宕机,上面存储的shard可能会丢失,因此可以为多个shard创建多个replica副本,在服务故障的时候提供备用服务,保证数据不丢失,多个replica还可以提升都多的性能。primary shard(建立索引的一次设置,不能修改,默认是5个),replica shard(随时修改配置,默认1个),默认每个index10个shard,5个primary shar的,5个replica shard;最小的高可用配置是2台服务器.
简单的理解为:
index 对应 schema
type 对应 table
document 对应 row
image.png
三.安装ES
1.首先要安装jdk,要求至少1.8以上的版本
2.下载Elasticsearch安装包,解压
https://www.elastic.co/downloads/elasticsearch
3.启动ES,就在bin目录下的elasticsearch.bat
4.检查ES是否启动成功,localhost:9200?pretty
如图,启动成功
5.修改集群名称,在elasticsearch.yml文件中
cluster.name: my-application
6.下载Kibana
https://artifacts.elastic.co/downloads/kibana/kibana-6.0.0-windows-x86_64.zip
使用里面的可视化界面,和dev tool,作为学习ES restful请求的一个工具,
启动bin/Kibana.bat,
输入localhost:5601,进入dev tool界面
image.png
ES提供一套api叫做cat api,可以查看ES中的各种数据
(1).GET /_cat/health?v
查看当前ES集群的状态
健康状态:green,yellow,red
green:每个索引的primary shard和replica shard都是active状态的
yellow:每个primary shard都是active状态的,按时部分replica shard不是active状态的,处于不可用状态;
red:不是所有的索引的primary shard都是active状态的,有部分索引数据丢失;
(2).GET /_cat/indices?v
查看所有的索引
(3).简单的索引操作
创建索引:PUT /test_index?pretty
删除索引: DELETE /test_index?pretty
四.基本的一些CRUD操作
(1).新增商品:
PUT /index/type/1
PUT /shop/product/1
{
"name":"gaolujie yagao",
"desc":"gaoxiao meibai",
"price":30,
"pruducer":"gaolujie producer",
"tags":["meibai","fangzhu"]
}
PUT /shop/product/2
{
"name":"zhonghua yagao",
"desc":"fangzhu meibai",
"price":40,
"pruducer":"zhonghua producer",
"tags":["fangzhu ","meibai"]
}
PUT /shop/product/3
{
"name":"heiren yagao",
"desc":"fangzhu meibai",
"price":20,
"pruducer":"heiren yagao producer",
"tags":["fangzhu ","meibai"]
}
(2).查询商品
GET /index/type/id
GET /shop/product/1
{
"_index" : "shop",
"_type" : "product",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "gaolujie yagao",
"desc" : "gaoxiao meibai",
"price" : 30,
"pruducer" : "gaolujie producer",
"tags" : [
"meibai",
"fangzhu"
]
}
}
(3).修改商品,替换文档
修改name
PUT /shop/product/1
{
"name":"xin gaolujie yagao",
"desc":"gaoxiao meibai",
"price":30,
"pruducer":"gaolujie producer",
"tags":["meibai","fangzhu"]
}
这种方式会替换之前的document
另一种是更新POST操作
POST /shpo/product/1/_update
{
"doc":{
"name":"xin gaolujie yagao"
}
}
(4).删除文档
DELETE /shop/product/1
五.多种搜索方式
1.query string search
(1).搜索全部商品
GET /shop/product/_search
结果
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "shop",
"_type" : "product",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"name" : "zhonghua yagao",
"desc" : "fangzhu meibai",
"price" : 40,
"pruducer" : "zhonghua producer",
"tags" : [
"fangzhu ",
"meibai"
]
}
},
{
"_index" : "shop",
"_type" : "product",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "gaolujie yagao",
"desc" : "gaoxiao meibai",
"price" : 30,
"pruducer" : "gaolujie producer",
"tags" : [
"meibai",
"fangzhu"
]
}
},
{
"_index" : "shop",
"_type" : "product",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"name" : "heiren yagao",
"desc" : "fangzhu meibai",
"price" : 50,
"pruducer" : "heiren yagao producer",
"tags" : [
"fangzhu ",
"meibai"
]
}
}
]
}
}
took:耗费时间,ms
time_out:是否超时
_shards:数据拆分分片,所以对于搜索请求会分到不同的primary shard上面去(或者replica上)
hits.total:查询的数量
max_score:相关度匹配分数,分数越高越匹配
hits.hits:包括查询出来的相关document数据
(2)搜索商品中name包含 "yagao"的商品,且价格按照降序
GET /shop/product/_search?q=name:yagao&sort=price:desc
一般不用query string的方式
2.query DSL
(1).查询所有商品
GET /shop/product/_search
{
"query":{
"match_all": {}
}
}
(2).查询商品名包含yagao的,价格降序
GET /shop/product/_search
{
"query":{
"match":{
"name":"yagao"
}
},
"sort":[{
"price":"desc"
}]
}
match默认按照most_fields(尽可能的匹配多个field)的策略匹配相关度分数排序,
还有一种方式叫best_fields(filed尽可能匹配多个查询条件)的策略匹配相关度分数排序
使用 multimatch
GET /shop/product/_search
{
"query":{
"multi_match": {
"query": "yagao",
"type": "best_fields",
"fields": ["name","desc"]
}
}
}
(3).分页查询商品,每页显示1条数据,总共2条数据,显示第二页
GET /shop/product/_search
{
"query":{
"match_all":{
}
},
"from":1,
"size":1
}
(4).指定查询出来name和price就行
GET /shop/product/_search
{
"query":{
"match_all":{
}
},
"_source":["name","price"]
}
3.query filter 对数据进行过滤
查询name包含"yagao",price大于等于35的
GET /shop/product/_search
{
"query":{
"bool": {
"must": {
"match":{
"name":"yagao"
}
},
"filter": {
"range": {
"price": {
"gte": 45
}
}
}
}
}
}
filter只是简单过滤出符合条件的数据结果,不会生成相关度score,也不排序,所以filter的性能比query要高,执行先后度也在query之前
bigset机制:filter搜索会将doc结果构建成一个bigset([0,1,0,1]),1代表满足条件的doc,0代表不知满足的,然后进行遍历先从分布稀疏的bigset开始(可以尽可能过滤掉多的数据),遍历完所有的bigset后返回结果
caching bigset:其次会将query的结果,缓存到其bigset,如果下次查询会直接从缓存中拿到结果,不用继续扫描倒排索引构建bigset;如果查询对应的倒排索引segment很小,则不回去缓存,因为小的话即使去查也会很快,再缓存就没什么意义了;如果document有新增或者修改,name会自动更新cach
组合多个搜索条件
GET /shop/product/_search
{
"query":{
"bool": {
"must": {"match": {"name": "yagao"}},
"should": {"match":{"desc":"meibai"}},
"filter": [
{"range": {
"price": {
"gte": 40,
"lte": 50
}
}}
]
}
}
}
match对应的还有一种term查询,区别是term是代表完全匹配,也就是精确查询,搜索前不会再对搜索词进行分词拆解。
{
"query":{
"term":{
"name":"yagao"
}
}
}
还有一种terms查询,terms类型sql中的in
GET /shop/product/_search
{
"query":{
"terms": {
"price": [
35,
50
]
}
}
}
(4).full-text search 全文检索
GET /shop/product/_search
{
"query":{
"match": {
"pruducer": "yagao producer"
}
}
}
结果
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 0.99774146,
"hits" : [
{
"_index" : "shop",
"_type" : "product",
"_id" : "3",
"_score" : 0.99774146,
"_source" : {
"name" : "heiren yagao",
"desc" : "fangzhu meibai",
"price" : 50,
"pruducer" : "heiren yagao producer",
"tags" : [
"fangzhu ",
"meibai"
]
}
},
{
"_index" : "shop",
"_type" : "product",
"_id" : "2",
"_score" : 0.14181954,
"_source" : {
"name" : "zhonghua yagao",
"desc" : "fangzhu meibai",
"price" : 40,
"pruducer" : "zhonghua producer",
"tags" : [
"fangzhu ",
"meibai"
]
}
},
{
"_index" : "shop",
"_type" : "product",
"_id" : "1",
"_score" : 0.14181954,
"_source" : {
"name" : "gaolujie yagao",
"desc" : "gaoxiao meibai",
"price" : 30,
"pruducer" : "gaolujie producer",
"tags" : [
"meibai",
"fangzhu"
]
}
}
]
}
}
搜索条件是两个单词,观察发现不包含yagao的也搜索出来了但是score分数不一样;
producer这个词会被拆出来,建立倒排索引:
producer 1,2,3
heiren 3
yagao 3
gaolujie 1
zhonghua 2
所以yagao producer两个词,匹配到了3个,所以分数是最高的
(5).phrase search短语搜索
跟全文索引相反,全文检索会将输入的搜索串拆分开来,去倒排索引里面一一匹配,只要能匹配到拆分开的词,就可以作为返回结果;
phrase search,要求输入的搜索串,必须在指定的字段文本中,完全包含一模一样的,才可以算算匹配结果;
GET /shop/product/_search
{
"query":{
"match_phrase": {
"pruducer": "yagao producer"
}
}
}
结果
{
"took" : 19,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.99774146,
"hits" : [
{
"_index" : "shop",
"_type" : "product",
"_id" : "3",
"_score" : 0.99774146,
"_source" : {
"name" : "heiren yagao",
"desc" : "fangzhu meibai",
"price" : 50,
"pruducer" : "heiren yagao producer",
"tags" : [
"fangzhu ",
"meibai"
]
}
}
]
}
}
结果只有一条;
(6). highlight 高亮搜索结果
GET /shop/product/_search
{
"query":{
"match": {
"pruducer": "heiren"
}
},
"highlight":{
"fields": {
"pruducer":{}
}
}
}
结果
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.8781843,
"hits" : [
{
"_index" : "shop",
"_type" : "product",
"_id" : "3",
"_score" : 0.8781843,
"_source" : {
"name" : "heiren yagao",
"desc" : "fangzhu meibai",
"price" : 50,
"pruducer" : "heiren yagao producer",
"tags" : [
"fangzhu ",
"meibai"
]
},
"highlight" : {
"pruducer" : [
"<em>heiren</em> yagao producer"
]
}
}
]
}
}
可以看到highlight里面是高亮的数据,就好比在百度上搜索牙膏,网页中搜索结果带牙膏的都会标红色。
(7).批量查询
GET /_mget
{
"docs":[{
"_index":"shop",
"_type":"product",
"_id":1
},
{
"_index":"shop",
"_type":"product",
"_id":2
}]
}
GET /shop/product/_mget
{
"ids":[1,2]
}
批量增删改,
POST /shop/_bulk
{"delete":{"_type":"product","_id":"1"}}
POST /shop/_bulk
{"create":{"_type":"product","_id":"1"}}
{"field":"value"}
POST /shop/_bulk
{"update":{"_type":"product","_id":"1","retry_on_conflict":5}}
{"field":"value"}
POST /shop/_bulk
{"index":{"_type":"product","_id":"1","retry_on_conflict":5}}
{"field":"value"}
六.聚合,分析查询
1.聚合
(1)根据tags字段进行聚合
GET /shop/product/_search
{
"size":0,
"aggs":{
"group_by_tabs":{
"terms": {
"field": "tags"
}
}
}
}
结果
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"group_by_tabs" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "fangzhu",
"doc_count" : 3
},
{
"key" : "meibai",
"doc_count" : 3
}
]
}
}
}
同样可以加query查询条件
GET /shop/product/_search
{
"size":0,
"query":{
"match": {
"name": "heiren"
}
},
"aggs":{
"group_by_tabs":{
"terms": {
"field": "tags"
}
}
}
}
(2).先分组,再算每组的平均值
GET /shop/product/_search
{
"size":0,
"query":{
"match": {
"name": "heiren"
}
},
"aggs":{
"group_by_tabs":{
"terms": {
"field": "tags"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
也可以加排序
GET /shop/product/_search
{
"size":0,
"query":{
"match": {
"name": "heiren"
}
},
"aggs":{
"group_by_tabs":{
"terms": {
"field": "tags",
"order": {
"avg_price": "desc"
}
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
(4).根据价格区间分组,在在分组内按照tabs字段聚合分组计算平均值
GET /shop/product/_search
{
"size":0,
"aggs":{
"group_by_price":{
"range": {
"field": "price",
"ranges": [
{
"from": 30,
"to": 40
},
{
"from": 40,
"to": 50
}
]
},
"aggs":{
"group_by_tabs":{
"terms": {
"field": "tags",
"order": {
"avg_price": "desc"
}
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
}
}
七.ES分布式架构
1.Elasticsearch对复杂分布式机制的透明隐藏性
Elasticsearch是一套分布式体统,分布式是为了应对大数据量隐藏了复杂的分布式机制,比如,分片机制(我们随随便便就将一些document插入到es集群中去了,我们并没有关注过对数据怎么进行分片,分到哪个shard中),
cluster discovery(集群发现机制,我们在启动另一个ES服务的时候,这个服务直接就加入到我们的ES集群中去了,并且还分到了一些数据,replica shard),
shard负载均衡(假设有3个ES节点,总共有25个shard要分配到3个节点上去,ES会自动进行均匀分配,以保持每个节点的均衡的读写负载),
shard的副本怎么创建,
请求路由,
集群扩容,
shard重分配。
我们都并没有去关心,对用户都是隐藏的;
2.Elasticsearch的垂直扩容和水平扩容;
(1).垂直扩容
3台1T的服务器,现在数据量需要到5T,这时候新购置两台2T的服务器,替换掉其中2台服务器;
(2)水平扩容
新增两台1T的服务器加入集群
正常会选择水平扩容的方式,垂直扩容成本昂贵,而且会有瓶颈;
3.rebanlance
某个服务器的负载重些,在新加入节点的时候,数据和shard会重新rebanlance
4.master节点
master节点管理ES集群的元数据:比如索引的创建和删除,维护索引元数据,节点的增加和移除,维护集群;
默认会自动选择一个节点作为master节点;matser并不承载所有的请求,所以不会是一个单点瓶颈
5.节点平等的分布式架构
(1).节点对等,每个节点都能接受所有的请求
(2).自动路由请求,每个节点接收到请求都可以自动把这些请求路由到其他有数据的节点上
(3).响应收集,最原始接受到请求的节点,会负责从其他节点上收集数据,返回给客户端。
八.ES的数据一致性
(1).在分布式环境下,一致性指的是多个数据副本是否能保持一致的特性。
在一致性的条件下,系统在执行数据更新操作之后能够从一致性状态转移到另一个一致性状态。
对系统的一个数据更新成功之后,如果所有用户都能够读取到最新的值,该系统就被认为具有强一致性。
image(2).ES 数据并发冲突控制是基于的乐观锁和版本号的机制
分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容忍性(P:Partition Tolerance),最多只能同时满足其中两项。
ES 数据并发冲突控制是基于的乐观锁和版本号的机制.
一个document第一次创建的时候,它的_version内部版本号就是1;以后,每次对这个document执行修改或者删除操作,都会对这个_version版本号自动加1;哪怕是删除,也会对这条数据的版本号加1(假删除)。
客户端对es数据做更新的时候,如果带上了版本号,那带的版本号与es中文档的版本号一致才能修改成功,否则抛出异常。如果客户端没有带上版本号,首先会读取最新版本号才做更新尝试,这个尝试类似于CAS操作,可能需要尝试很多次才能成功。乐观锁的好处是不需要互斥锁的参与。
es节点更新之后会向副本节点同步更新数据(同步写入),直到所有副本都更新了才返回成功。
image分片向副本同步数据
网友评论