暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

elastic search

原创 忽近 2021-08-03
870

Elastic Search

1 Elastic Search增删改查

1.1 新增

请求

PUT /{index}/{type}/{id} { "filed":"value", ... }

响应

{ "_index": "website", "_type": "blog", "_id": "123", "_version": 1, "created": true }

使用post自动生成id

POST /{index}/{type}/ { "title": "My second blog entry", "text": "Still trying this out...", "date": "2014/01/01" }
1.2 修改

请求(覆盖原来的数据)

PUT /website/blog/123 { "title": "My first blog entry", "text": "Just trying this out...", "date": "2014/01/01" }

响应

{ "_index": "website", "_type": "blog", "_id": "123", "_version": 2, "created": false }

指定某个字段进行修改

POST /{index}/{type}/{id}/_update { "doc":{ "filed":"value" } }
1.3 删除
DELETE /{index}/{type}/{id} DELETE {index}
1.4 查询

查询某个索引配置

GET /{index}/_settings

查询所有索引配置

GET _all/_settings

查询某个文档

GET /{index}/{type}/{id}

指定查询某个文档下的某些属性

GET /{index}/{type}/{id}?_source-filed1,filed2,.....

条件查询(查询content字段中包含html的内容)

GET /{index}/{type}/_search?q=content:html
1.5 高级条件查询

精确查询,适合于没有分词器的

GET /{index}/{type}/_search { "query":{ "term":{ "filed":"value" } } }

没有分词器的查询,可以写多个属性值

GET /{index}/{type}/_search { "query":{ "terms":{ "filed":["value1","value2"] } } }

有分词器的查询

GET /{index}/{type}/_search { "query":{ "match":{ "filed":"value" } } }

多个字段查询,只要属性值含有query值的都可以查出来

GET /{index}/{type}/_search { "query":{ "multi_match":{ "query":"xxx" "fileds":["name","description"] } } }

指定查询的字段

GET /{index}/{type}/_search { "query":{ "match":{ "filed":"value" } }, "_source":["name","age"] }

字段名可以模糊匹配(addr --> address)

GET /{index}/{type}/_search { "query":{ "match":{ "filed":"value" } }, "_source":{ "includes":["name","addr"], "excludes":["age"] } }

排序查询

GET /{index}/{type}/_search { "query":{ "match":{ "filed":"value" } }, "sort":[ { "filed":{ "order":"desc" } } ] }

范围查询

GET /{index}/{type}/_search { "query":{ "range":{ "birthday":{ "from":"1998-08-12", "to":"2020-05-10", "include_lower":true, "include_upper":false } } } }

高亮查询

GET /{index}/{type}/_search { "query":{ "match":{ "filed":"value" } }, "highlight":{ "fields":{ "interests":{} } } }

模糊查询

GET /{index}/{type}/_search { "query":{ "fuzzy":{ "filed":"value" } } }

分页查询

GET /{index}/{type}/_search { "query":{ "match":{ "filed":"value" } }, "from":0, "size":10 }

短语精确查询

GET /{index}/{type}/_search { "query":{ "match_phrase":{ "filed":"value" } } }

通配符查询

*代表0个或多个字符

GET /{index}/{type}/_search { "query":{ "wildcard":{ "name":"lis*" } } }

?代表一个字符

GET /{index}/{type}/_search { "query":{ "wildcard":{ "name":"lis?" } } }
1.6 filter查询
GET /{index}/{type}/_search { "query":{ "bool":{ "filter":[ {"term":{"price":40}} ] } } }
1.7 聚合查询
GET /{index}/{type}/_search { "aggs":{ "price_sum":{ "sum":{ "field":"price" } } } }
GET /{index}/{type}/_search { "aggs":{ "price_min":{ "min":{ "field":"price" } } } }
GET /{index}/{type}/_search { "aggs":{ "priceAvg":{ "avg":{ "field":"price" } } } }

某个属性不同值的个数

GET /{index}/{type}/_search { "aggs":{ "price_cardi":{ "cardinality":{ "field":"price" } } } }

属性值相同的分为一组

GET /{index}/{type}/_search { "aggs":{ "price_group":{ "terms":{ "field":"price" } } } }

对唱歌感兴趣,按年龄分组,求每组年龄总和,并按年龄总和排序

GET /{index}/{type}/_search { "query":{ "match":{ "interest":"唱歌" } }, "aggs":{ "age_of_group":{ "terms":{ "field":"age" }, "order":{ "price_sum":"desc" } }, "aggs":{ "price_sum":{ "sum":{ "field":"age" } } } } }
1.8 复合查询

使用bool查询

GET /{index}/{type}/_search { "query":{ "bool":{ "must":{"match":{"interest":"changge"}}, "must_not":{"match":{"interest":"lvyou"}}, "should":[ {"match":{"address":"beijing"}}, {"range":{"birth":{"gte":"1996-02-06"}}} ] }, "filter":{ "range":{"price":{"gte"}:"99"} } } }

content_score经常被用于执行一个filter而没有其他查询(评分查询等)的情况,不计算相关度分数,查询效率高。

GET /{index}/{type}/_search { "query":{ "content_score":{ "filter":{ "term":{"interest":"changge"} } } } }
1.9 MultiGet批量查询

批量获取

GET /_mget { "docs":[ { "_index":"xxx", "_type":"xxx", "_id":"xxx" }, { "_index":"xxx", "_type":"xxx", "_id":"xxx" }, ...... ] }

批量获取,并指定获取某个属性值

GET /_mget { "docs":[ { "_index":"xxx", "_type":"xxx", "_id":"xxx", "_source":"name" }, { "_index":"xxx", "_type":"xxx", "_id":"xxx", "_source":["name","age"] }, ...... ] }

当获取索引和类型相同时,简写方式

GET /{index}/{type}/_mget { "docs":[ { "_id":"xxx" }, { "_id":"xxx", "_type":"xxx" } ] } GET /{index}/{type}/_mget { "ids":["1","2"] }
1.10 Bulk批量操作

index 新增,原文档存在会覆盖

create 新增,原文档存在会报错

delete 删除

update 修改

POST /{index}/{type}/_bulk {"index":{"_id"}:xxx} { "name":"xxx", "price":22 } {"update":{"_index":"xxx","_type":"xxx","id":"xxx"}} { "doc":{ "price":55 } } {"delete":{"_index":"xxx","_type":"xxx","_id":"xxx"}}

2 什么是mapping

2.1 获取mapping
GET /{index}/{type}/_mapping
2.2 mapping的作用

1.自动获取文档字段的类型

2.规定字段的属性

index:是否分词;analyzer:ik指定分词器;ignore_above:当字段值长度超过某个数值,不会被索引;search_analyzer:指定搜索是使用的分词器;

2.3 手动创建mapping

settings可省略

PUT /{index} { "settings":{ "number_of_shards":3, "number_of_replicas":0 }, "mappings":{ "{type}":{ "properties":{ "title":{ "type":"text", "index":true }, "price":{ "type":"double" } } } } }

3 Elastic search原理

3.1 es分布式架构原理

1.分布式架构的透明隐藏特性:

es分布式隐藏复杂的处理机制,我们不用关心数据如何分片,如何创建分片的副本

集群自动发现机制:当前我们启动第二个es进程时,这个节点会自动加入到集群中

shard负载均衡:es进行均衡的分片,确保负载的请求均衡到各个节点

2.扩容机制:

垂直扩容:购置容量更大的机器,替换原有机器

水平扩容:增加节点数

3.自动均衡:

增加和减少节点,es会均衡分配分片

4.主节点的作用:

创建/删除索引,跟踪哪些节点是集群的一部分,决定哪些分片对应分给哪些节点等以管理工作为主。

5.节点对等:

请求到达主节点和其他节点的机会均等

3.2 分片和副本机制

1.每个document对应一个主分片和一个副本

2.主分片和对应副本不在同一台机器

3.主分片数量在创建索引时固定,副分片数量可以随时修改

3.3 水平扩容

​ 适当提高副本数量,可以提高容错性

3.4 容错机制

​ 当服务器宕机,集群状态变为red,es会选举另外节点作为master,把宕机的master对应副本转换为主分片,集群状态变为yellow,因为副本确实,然后重启宕机的es,并把主分片的数据拷贝过来作为副本。

3.5 文档id的生成方式

​ 文档id使用GUID算法,在集群的情况下也不会生成相同的id

3.6 文档修改和删除原理

文档修改:post方式比put方式网络数据传输次数更少,所以性能更高。post耗时短,则并发冲突的可能性降低。

文档新增:请求一个es节点,成为协调节点,根据数据路由算法,计算数据应该放在哪个分片,通过请求转发到该节点存储数据并同步到副本,最后协调节点给客户端响应。

3.7 写一致性原理和quorum机制

1.任何增删改操作都可以跟上consistency,可以给参数指定的值:

one:只要有一个primary shard是活跃的就可以执行

all:所有primary shard和replica shard都是活跃的才可以执行

quorum:大部分shard是活跃的才执行

2.quorum机制

int((primary shard+replica shard)/2)+1

3.8 文档查询原理

​ 请求到达协调节点,路由算法计算该文档所在的分片,通过轮询算法计算访问主分片或者副本分片的任意一个。

特殊情况:请求的文档还在建立索引的过程中,primary shard上存在,但是replica shard不存在,请求转发到replica shard会提示找不到。

3.9 bulk批量操作json格式解析
{action:{metadata}}\n {requestbody}\n

为什么不使用如下的json格式:

[{ "action":{ }, "data":{ } }]

第二种方式:

1.第二种方式虽然可读性好,但是需要在内存中转换成JSONArray对象,加上原有对象,会占据内存

2.解析json数组的每个json,对每个文档进行路由

3.为路由到同一个shard的多个请求,创建一个请求数组

4.将这个请求数组序列化

5.将序列化后的请求数组发送到对应节点

第一种方式:

1.不用将其转换为json对象,直接按换行符切割,内存中无拷贝

2.对每两个一组的json,读取meta,进行document路由

3.直接将对应json发送到路由上去

3.10 多索引多类型查询
GET /{index1},{index2}/{type1},{type2}/_search
3.11 deep paging问题

当es有多个分片时进行,协调节点会把所有分片符合条件的数据查出来,按照相关度分数排序后,再进行分页。当查询深度越深,这个过程越消耗网络带宽,消耗内存,排序消耗cpu。

3.12 copy_to字段的使用
GET /{index}/{type}?q=html,dicc

当q后面的条件不指定字段名时,es会到每个字段中查询,效率很低,创建mapping时可以把这些相同类型的字段copy_to到同一个字段中,提高性能。

3.13 字符串排序问题

字符串默认进行了分词,排序会报错。解决方法是给interests字段两个值,一个分词,一个不分词,如下:

PUT /{index} { "mapping":{ "properties":{ "interests":{ "type":"text", "fileds":{ "raw":{ "type":"keyword" } }, "fielddata":true } } } } GET /{index}/{type}/_search { "query":{ "match_all":{} }, "sort":[{ "interests.row":{ "order":"desc" } }] }
3.14 es如何计算相关度分数

1.Term Frequency:搜索词在文档出现频率越高,分数越高

2.Inverse Document Frequency:搜索词在所有文档中出现的次数,出现次数越多,相关分数越低

3.Field Length:字段越长,相关度分数越低

4 java操作elastic search

4.1 java代码连接es
// 将client.transport.sniff设置为true即可打开集群节点自动探查功能 Settings settings = Settings.builder().put("client.transport.sniff", true)..put("cluster.name", "elasticsearch").build(); // 只需要指定一个node就行 TransportClient client = new PreBuiltTransportClient(settings); transport.addTransportAddress(new TransportAddress(InetAddress.getByName("192.158.17.138"), 9300));
4.2 新增操作
private static void createEmployee(TransportClient client) throws Exception { IndexResponse response = client.prepareIndex("company", "employee", "1") .setSource(XContentFactory.jsonBuilder() .startObject() .field("name", "jackson") .field("age", 29) .field("position", "wuhan") .field("country", "china") .field("join_date", "2017-01-01") .field("salary", 5000) .endObject()) .get(); System.out.println(response.getResult()); }
4.3 获取员工信息
private static void getEmployee(TransportClient client) throws Exception { GetResponse response = client.prepareGet("company", "employee", "1").get(); System.out.println(response.getSourceAsString()); }
4.4 修改操作
private static void updateEmployee(TransportClient client) throws Exception { UpdateResponse response = client.prepareUpdate("company", "employee", "1") .setDoc(XContentFactory.jsonBuilder() .startObject() .field("position", "tianjin") .endObject()) .get(); System.out.println(response.getResult()); }
4.5 删除操作
private static void deleteEmployee(TransportClient client) throws Exception { DeleteResponse response = client.prepareDelete("company", "employee", "1").get(); System.out.println(response.getResult()); }
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论