1.一般建议
b.避免大的文档。
2. 如何提高索引速度
b.使用多线程发送数据。
在索引初始化(大量导入文档)的时候,可以关闭refresh_interval。当产品允许较大的不可搜索时间,可以将index.refresh_interval
设置为30s,提高索引速度。
e.关闭swapping。swap会极大地降低es的索引速度。
Swap分区(即交换区)在系统的物理内存不够用的时候,把硬盘空间中的一部分空间释放出来,以供当前运行的程序使用。
那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间被临时保存到Swap分区中,等到那些程序要运行时,再从Swap分区中恢复保存的数据到内存中。
f.给文件系统缓存分配足够多的内存。文件系统换行用来处理io操作,至少要将物理机一半的内存分配给文件系统缓存。比如物理机内存64g,那么至少分配32g给文件系统缓存,剩下的内存才考虑分配给es。
{
"_index": "sales",
"_type": "_doc",
"_id": "xb7IY4cB6Rdc8HbDycuE", // auto-generated id
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 10,
"_primary_term": 1
}
h.使用更好的硬件。比如SSD,或者Amazon的Elastic Block Storage。
indices.memory.index_buffer_size = 512M,大于512M没有更多提升效果。
3.如何提到搜索速度
a.给文件系统缓存分配足够多的内存。
Linux的文件预读readahead,指Linux系统内核将指定文件的某区域预读进页缓存起来,便于接下来对该区域进行读取时,不会因缺页(page fault)而阻塞。因为从内存读取比从磁盘读取要快很多。
预读可以有效的减少磁盘的寻道次数和应用程序的I/O等待时间,是改进磁盘读I/O性能的重要优化手段之一。使用命令lsblk查看readahead值。
c.使用更好的硬件。
文档模型 | 对比普通查询 |
nested query | 慢几倍 |
parent query | 慢几百倍 |
join query | 应当避免 |
e.尽可能少的查询字段。在越多的字段上匹配,查询速度就越慢。在索引的时候可以将需要查询的多个字段聚合到一个字段中。使用copy_to 可以自动实现这一功能,以下示例将name和plot字段聚合到name_and_plot字段中。
PUT movies
{
"mappings": {
"properties": {
"name_and_plot": {
"type": "text"
},
"name": {
"type": "text",
"copy_to": "name_and_plot"
},
"plot": {
"type": "text",
"copy_to": "name_and_plot"
}
}
}
}
f.预先索引数据。比如如果想对price字段做range聚合,那么预先计算出单个文档的price范围,那么就能将range聚合转化成terms聚合。这样确实能提高效率,但是不太灵活。
PUT index/_doc/1
{
"designation": "spoon",
"price": 13
}
range聚合查询:
GET index/_search
{
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 10 },
{ "from": 10, "to": 100 },
{ "from": 100 }
]
}
}
}
}
另一种做法,预先计算price_range:
PUT index
{
"mappings": {
"properties": {
"price_range": {
"type": "keyword"
}
}
}
}
PUT index/_doc/1
{
"designation": "spoon",
"price": 13,
"price_range": "10-100"
}
使用terms聚合:
GET index/_search
{
"aggs": {
"price_ranges": {
"terms": {
"field": "price_range"
}
}
}
}
g.尽可能将字段自定义为keyword。对于数字类型的字段,es对其range查询做了优化。在term层级的查询下,keyword字段比数字类型要好。
1.不需要对这些数据进行range查询
如果实在不清楚哪个好,可以用 multi-field为数字类型的字段同时定义数字类型和keyword类型。
script_scorequery。
实时查询(秒级):
PUT index/_doc/1
{
"my_date": "2016-05-11T16:30:55.328Z"
}
GET index/_search
{
"query": {
"constant_score": {
"filter": {
"range": {
"my_date": {
"gte": "now-1h",
"lte": "now"
}
}
}
}
}
}
分钟级查询:
GET index/_search
{
"query": {
"constant_score": {
"filter": {
"range": {
"my_date": {
"gte": "now-1h/m",
"lte": "now/m"
}
}
}
}
}
}
j.对只读索引进行force-merge。在时序索引中,过期的索引都是只读的,将其合并成一个段能加快查询速度。
PUT index
{
"mappings": {
"properties": {
"foo": {
"type": "keyword",
"eager_global_ordinals": true
}
}
}
}
l.预热文件系统缓存。设置index.store.preload
参数即可。注意,必须确保文件系统缓存足够大,否则会让查询变得更慢。
n.使用preference进行缓存使用优化。es中有非常多的缓存,比如文件系统缓存(最重要),请求缓存,查询缓存,但是这些缓存都是在节点层面。默认情况下es会使用round-robin算法分配查询到不同的分片上去,这样缓存就失效了。
GET /_search?preference=_shards:2,3
{
"query": {
"match": {
"title": "elasticsearch"
}
}
}
o.更多的复制分片会提升吞吐量(但并不一定)。在系统资源充足的情况下,复制分片越多吞吐量会越高。但是过多的分片会让故障恢复变得更慢。
GET /my-index-000001/_search
{
"profile": true,
"query" : {
"match" : { "message" : "GET /search" }
}
}
{
"took": 25,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 5,
"relation": "eq"
},
"max_score": 0.17402273,
"hits": [...]
},
"profile": {
"shards": [
{
"id": "[2aE02wS1R8q_QFnYu6vDVQ][my-index-000001][0]",
"searches": [
{
"query": [
{
"type": "BooleanQuery",
"description": "message:get message:search",
"time_in_nanos" : 11972972,
"breakdown" : {
"set_min_competitive_score_count": 0,
"match_count": 5,
"shallow_advance_count": 0,
"set_min_competitive_score": 0,
"next_doc": 39022,
"match": 4456,
"next_doc_count": 5,
"score_count": 5,
"compute_max_score_count": 0,
"compute_max_score": 0,
"advance": 84525,
"advance_count": 1,
"score": 37779,
"build_scorer_count": 2,
"create_weight": 4694895,
"shallow_advance": 0,
"create_weight_count": 1,
"build_scorer": 7112295
},...
q.使用 index_phrases
加速phrase query。index_phrases,会将两个单词的组合单独索引,这样可以加速phrase query。
index_phrases 加速prefix query。同上。
mapping如下:
UT bicycles
{
"mappings": {
"properties": {
"cycle_type": {
"type": "constant_keyword",
"value": "bicycle"
},
"name": {
"type": "text"
}
}
}
}
PUT other_cycles
{
"mappings": {
"properties": {
"cycle_type": {
"type": "keyword"
},
"name": {
"type": "text"
}
}
}
}
查询语句:
GET bicycles,other_cycles/_search
{
"query": {
"bool": {
"must": {
"match": {
"description": "dutch"
}
},
"filter": {
"term": {
"cycle_type": "bicycle"
}
}
}
}
}
在查询bicycles索引时,es会将查询语句自动转换为:
GET bicycles,other_cycles/_search
{
"query": {
"match": {
"description": "dutch"
}
}
}
4.磁盘优化
a.禁用不需要的特性。
PUT index
{
"mappings": {
"properties": {
"foo": {
"type": "integer",
"index": false
}
}
}
}
es会对text类型的字段存储一些打分信息,如果不需要对这些字段进行打分,可以将其设置为match_only_text类型
PUT index
{
"mappings": {
"dynamic_templates": [
{
"strings": {
"match_mapping_type": "string",
"mapping": {
"type": "keyword"
}
}
}
]
}
}
c.监控分片大小。越大的分片能更有效地存储数据。但是分片越大,故障恢复也会越慢。
e.使用best_compression进行压缩。es默认使用 LZ4 进行压缩,使用best_compression可以提升压缩比率,但是会影响数据存取性能。
g.shrink 索引。即收缩索引,将当前索引重新索引成分片数更少的索引。分片越大,存储效率越高。
1.索引必须只读。
3.索引状态必须是健康的。
i.使用索引排序来提升文档的压缩性能。排序后相似的文档会放在一起,es能根据他们的特性有效地进行压缩。
PUT my-index-000001
{
"settings": {
"index": {
"sort.field": "date",
"sort.order": "desc"
}
},
"mappings": {
"properties": {
"date": {
"type": "date"
}
}
}
}
j.索引文档时保证json字段顺序一致。es在存储的时候将多个文档压缩成一成block,如果json文档顺序一致,es能更好的对更长的相同的字符串进行压缩。
5.分片大小
1.将索引分片大小保持在10G~50G之间
2.平均下来每G堆内存下不要超过20个分片。