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());
}




