ElasticSearch使用总结

本文深入探讨Elasticsearch的高效操作技巧,涵盖增删改查、分词查询、URL请求优化、副本与分片管理、聚合操作及性能调优。特别强调了RESTClient使用细节与多线程处理策略,为大规模数据处理提供实用指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

针对目前工作每日处理上亿数据的经历,作了几点总结


增删改查

  • 增用put(除了自动生成ID的创建文档)
  • 删用delete
  • 改用post
  • 查用get
  • URL只要是加下划线的,都用POST请求

分词查询

term是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇,那么设置索引mapping时,需要注意:是否需要对term查询的字段设置不进行分析,类如:

"name": {
	"type":  "string",
	"index": "not_analyzed"
}
复制代码

那么"name": "Quick Foxes!"内容被存放到索引中将变成[quick, foxes],即进行了分词。那么使用term查询Quick Foxes将不能查到该结果。另外,5.6.8之后,分词not_analyzed改为用keyword代替

搜索格式

  • ip:port/index/type/_search,表示在该index的type中搜索;
  • ip:port/index/_search,表示在该index中搜索;
  • ip:port/_search,表示在全文搜索;

settings vs mappings

  • settings是修改分片和副本数的。
  • mappings是修改字段和类型的。

?pretty作用

URL后加上?pretty表示将返回的json进行格式化,这时解析注意\n

副本与分片

不存在索引时,可以指定副本和分片(不指定默认是5分片,1副本),如果已经存在,则只能修改副本。

操作setting

  • 操作不存在索引:
curl -XPUT '192.168.80.10:9200/liuch/' -d
	'{"settings":{"number_of_shards":3,"number_of_replicas":0}}'
复制代码
  • 操作已存在索引:
curl -XPUT '192.168.80.10:9200/zhouls/_settings' -d
	'{"index":{"number_of_replicas":1}}'
复制代码

操作mapping

  • 操作不存在的索引:
curl -XPUT '192.168.80.10:9200/zhouls' -d
	'{"mappings":{"emp":{"properties":{"name":{"type":"string","analyzer": "ik_max_word"}}}}}'
复制代码
  • 操作已存在的索引:
curl -XPOST http://192.168.80.10:9200/zhouls/emp/_mapping -d
	'{"properties":{"name":{"type":"string","analyzer": "ik_max_word"}}}'
复制代码

ES后面参数:

  • p=param:请求参数方式
  • 空格 -d body:请求体方式
  • v:输出详细
  • h=字段1,字段2:只输出该字段
  • filter_path(如:hits.hits._source.username):只返回指定path的字段,高版本支持设置"_source":["username","pass"]
  • search_type=count:任何时候都不返回hits部分,减少返回内容,只计数(注意,5版本之后已移除该设置)
  • refresh:时刻刷新,为了实时获取最新数据,默认刷新间隔为1秒

size

size设置为0,表示不返回文档的信息,一般用在聚合操作,在只关心聚合的结果而不关心查询结果的情况下使用。

Scroll查询示例:

首次:

ip:port/index/_search?scroll=1m
{
	"size":10
}
复制代码

之后:

ip:port/_search/scroll    //注意此处没有了index,scroll格式也变化了
{
	//注意此处没有了size
	"scroll":1m,
	"scroll_id":"上次查询返回的_scroll_id"
}
复制代码

ID特殊字符

如果根据ID搜索文档时,ID含有特殊字符,如\、/等,可以先用URLEncoder进行编码,编码后仍然查询有效

Term vs Terms

  • 对于查询多个字段,用Terms,而单个字段要用Term
  • 对于排序和script应该用Terms
  • 对于聚合分组,用Terms

Terms查询数组长度过大问题

14.ElasticSearch使用terms查询时,如果值为数组且长度大于1024,可能出现:【maxclauseCount is set to 1024】错误,可以使用filter来解决

"query":{
	"bool":{
		"must":{
			"match_all":{}
		},
	"filter":{
		"terms":{
			"dip":["0.0.0.0",……]
		}
	}
}
复制代码

Scroll + Scan

使用scroll搜索时,首次搜索和之后的搜索都会返回hit,但是如果加入了scan,需注意

  • 首次不会返回hit,只会返回_scroll_id,然后第二次才开始返回值
  • 比如设置size=1000,则实际返回的数量是size*分片数
  • scanning scroll 查询不支持聚合
  • scanning scroll 查询结果没有排序,结果的顺序是doc入库时的顺序

聚合中的size

聚合中,使用terms分组默认返回每个组的10条数组,如果需要返回所有数据,在terms内添加字段:"size":0,当然也可以指定返回需要的长度的数据

但是注意:size为0是早期版本,0代表全部;5.x,6.x都不允许为0了。那么对于5.x和6.x设置为全量聚合:"size":2147483647

Restclient 查询数据过大

使用restclient,查询数据过大,报:entity content is too long xxxx for the configured buffer limit 104857600

//Overiding the 100MB Buffer Limit to 1GB
HttpEntity entity=new NStringEntity(params,ContentType.APPLICATION_JSON);
response = client.performRequest("POST", endPoint, new HashMap<>(), entity, new HeapBufferedResponseConsumerFactory(1024 * 1024 * 1024));
复制代码

**注意:**es rest clent依赖包版本需要5.6以上才能使用HeapBufferedResponseConsumerFactory

广度优先聚合

在海量数据下使用聚合嵌套聚合导致ES崩溃:考虑使用广度优先聚合:

{ "aggs" :
	{ "actors" :
		{ "terms" :
			{ "field" :"actors" ,"size" :10 ,"collect_mode":"breadth_first"
…………
复制代码

注意:广度优先聚合的内存要求与修剪前每个桶中的文档数量成线性关系,如果每个桶中的文档有数千或数十万,也不考虑使用广度优先。这也是为什么深度优先是默认选择的原因。

_source vs doc

script使用_source['key']速度比doc['key'].value慢。对于上亿数据,实测_source 8秒,doc 0.8秒。

注意:

  • es低版本:doc['key'].value _source['key']
  • es高版本:doc['key'].value params._source['key']
  • 高版本的doc['key'].value中的key不再支持IP类型

ID查找

在数据量非常巨大的情况下,通过ID查找文档最好使用官方的GET index/type/id,而非POST query:{term:{id:14}}

scroll:timevalue vs size

使用scroll时,timevalue应与size成正比,size越大越易超时,故timevalue应越大,不过timevalue最大到5m就差不多了

Scroll自动关闭

ES5.6以上,RestClient Scroll貌似查询到结尾会自动关闭

Scroll查询缓存

ES Scroll查询时,其实返回一批数据后,后面(分片数-1)批数据已经缓存好了,所以后面的(分片数-1)批获取也很快!故查询时,单次查询数量越多整个查询耗时越低,不过也要注意以下情况适当调整单次查询数量:

  • 网络较慢
  • ES负载较大
  • RestClilent方式(数据过大容易造成Timeout或者数据过大接收不了错误【高版本可设置HeapBufferResponseConsumerFactory来调大】)

加快Scroll速度:

  • search_type=scan
  • sort:"_doc" //不过如果size本身就是1的话就没必要了,一条记录不涉及到排序

ES常用字段操作

增加字段:

post  index/_mapping/type
{
	"type":{
		"properties":{
			"字段名":{
				"type":"string",
				"index":"not_analyzed"
			}
		}
	}
}
复制代码

ES按条件更新字段:

post  index/type/_update_by_query
{
	"script":{
		"inline":"if(!ctx._source.containsKey(\"urllevel\")){ctx._source.attending=\"urllevel\"};ctx._source.urllevel=ctx._source.fullurl.split(\"/\").size();"
	},
	"query":{
		"match_all":{}
	}
}
复制代码

ES Groovy截取字符串搜索:

get index/_search
{
	"query":{
		“bool”:{
			“must”:[
				{
					"term":{
						"field":"h-req-refer"
					}
				},
				{
					"script":{
						"inline":"doc['h-req-refer'].value.substring(0,doc['h-req-refer'].value.firstIndexOf('/'))==doc['h-req-host']",
						"lang":"groovy"
					}
				}
			]
		}
	}
}
复制代码

ES聚合后,按照某字段内容总长度进行排序【自定义排序】

http://localhost:9200/index/type/_search   POST
{
	"size":0,
	"aggs":{
		"mydata":{
			"terms":{
				"field":"h-req-data",
				"order":{
				"max_length":"desc"
				},
			"size":3
			},
			"aggs":{
				"max_length":{
					"max":{
						"script":"doc['h-req-data'].value==null?0:doc['h-req-data'].value.split('[^a-zA-Z0-9=/%\\\\+]').sort(a,b->a.length()<==>b.length())[0].length()"
					}
				}
			}
		}
	}
}
复制代码

ES与多线程

处理ES时,并不是线程越多越好,应该让多线程做该做的事情:让多线程去处理逻辑判断,单线程或少量线程去请求IO(网络IO),因为IO总是瓶颈。 读程序:

while 单线程获取ES数据(可分页或scroll分批获取) 多线程逻辑处理ES数据,处理完成后put某个阻塞队列queue

写程序:

while 数据 = queue.take() 单线程将处理后的数据存入ES(增删改等)

ES重试

ES如果数据量巨大,经常发生超时现象(包括scroll时),可代码实现重试操作,就算是scroll超时,scroll_id传入ES,返回数据时超时,也不用担心,该scroll_id仍然可用,可重新使用该scroll_id进行查询。 **注意:**restclient可设置超时时间,而transport默认超时时间很长,但一旦超时,会一直卡死

各关键字查询效率

在布尔查询中,filter参数和must_not参数使用Filter Context,而mustshould使用Query Context,经常使用Filter Context,引擎会自动缓存数据,提高查询性能。

ES的更新

ES的更新分为全局更新和局部更新:

-XPOST 'http://192.168.80.200:9200/zhouls/emp/1/_update' -d
	'{"doc":{"name":"mack"}}'
	
-XPOST 'http://192.168.80.200:9200/zhouls/emp/1' -d
	'{"name":"mack"}'
复制代码

后面的是全局更新,更新后其他字段值将不存在,只有name字段值

ES索引复制:

http://localhost:9200/_reindex   POST
{
	  "source": {
		"index": "old_index"
	},
	"dest": {
		"index": "new_index",
		"op_type": "create"
	}
}
复制代码

painless开启split功能

在painless中没有支持java string的split方法,但可以修改es配置开启正则的split语法

但在实际使用中发现性能损耗很大,一条执行一百毫秒左右的搜索语句,加上split后搜索时间升到七百毫秒左右,所以官方在未支持split方法时也指出本身string的split性能也不是很好,尽量避免使用

ES海量查询技巧

对于ES等大数据库,对于海量数据,查询需要花费时间常查的条件结果集可重入一个“缓存”索引,后续查询时,如果条件包含之前的条件,则可在缓存索引中查询(并带上剩余的条件)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值