最最基础,要成为同学脑海中,对es的性能优化的一种常识性的知识,以后做es的时候,就是要按照这个最基本的规范来干
1、搜索结果不要返回过大的结果集
es是一个搜索引擎,所以如果用这个搜索引擎对大量的数据进行搜索,并且返回搜索结果中排在最前面的少数结果,是非常合适的。然而,如果要做成类似数据库的东西,每次都进行大批量的查询,是很不合适的。如果真的要做大批量结果的查询,记得考虑用scroll api。
2、避免超大的document
http.max_context_length的默认值是100mb,意味着你一次document写入时,document的内容不能超过100mb,否则es就会拒绝写入。也许你可以将这个参数设置的更大,从而让你的超大的documdent可以写入es,但是es底层的lucene引擎还是有一个2gb的最大限制。
即使我们不考虑引擎层的限制,超大的document在实际生产环境中是很不好的。超大document会耗费更多的网络资源,内存资源和磁盘资源,甚至对那些不要求获取_source的请求,也是一样,因为es需要从_source中提取_id字段,对于超大document这个获取_id字段的过程的资源开销也是很大的。而将这种超大document写入es也会使用大量的内存,占用内存空间的大小甚至会是documdent本身大小的数倍。近似匹配的搜索,比如phrase query,以及高亮显示,对超大document的资源开销会更大,因为这些操作的性能开销直接跟document的大小成正比。
因此对于超大document,我们需要考虑一下,我们到底需要其中的哪些部分。举例来说,如果我们要对一些书进行搜索,那么我们并不需要将整本书的内容就放入es中吧。我们可以仅仅使用每一篇章或者一个段落作为一个document,然后给一个field标识出来这些document属于哪本书,这样每个document的大小不就变小了么。这就可以避免超大document导致的各种开销,同时可以优化搜索的体验。比如说,如果一个用户要搜索两个单词,foo和bar,如果在两个不同的段落中分别匹配了一个单词,肯定匹配效果要比,一个段落中匹配了两个单词,要差。
3、避免稀疏的数据
lucene的内核结构,跟稠密的数据配合起来,性能会更好,举个例子,什么叫稀疏的数据,什么叫稠密的数据?比如有100个document,每个document都有20个field,20个field都有值,这就是稠密的数据。但是如果100个document,每个document的field都不一样,有的document有2个field,有的document有50个field,这就是稀疏的数据。
原因就是,lucene在内部会通过doc id来唯一标识一个document,这个doc id是integer类型,范围在0到索引中含有的document数量之间。这些doc id是用来在lucene内部的api之间进行通信的,比如说,对一个term用一个match query来进行搜索,就会产生一个doc id集合,然后这些doc id会用来获取对应的norm值,以用来计算每个doc的相关度分数。而根据doc id查找norm的过程,是通过每个document的每个field保留一个字节来进行的一个算法,这个过程叫做norm查找,norm就是每个document的每个field保留的一个字节。对于每个doc id对应的那个norm值,可以通过读取es一个内置索引,叫做doc_id的索引,中的一个字节来获取。这个过程是性能很高的,而且可以帮助lucene快速的定位到每个document的norm值,但是同时这样的话document本身就不需要存储这一个字节的norm值了。
在实际运行过程中,这就意味着,如果一个索引有100个document,对于每个field,就需要100个字节来存储norm值,即使100个document中只有10个document含有某个field,但是对那个field来说,还是要100个字节来存储norm值。这就会对存储产生更大的开销,存储空间被浪费的一个问题,而且也会影响读写性能。
下面有一些避免稀疏数据的办法:
(1)避免将没有任何关联性的数据写入同一个索引
我们必须避免将结构完全不一样的数据写入同一个索引中,因为结构完全不一样的数据,field是完全不一样的,会导致index数据非常稀疏。最好将这种数据写入不同的索引中,如果这种索引数据量比较少,那么可以考虑给其很少的primary shard,比如1个,避免资源浪费。
(2)对document的结构进行规范化/标准化
即使我们真的要将不同类型的document写入相同的索引中,还是有办法可以避免稀疏性,那就是对不同类型的document进行标准化。比如说,如果所有的document都有一个时间戳field,不过有的叫做timestamp,有的叫做creation_date,那么可以将不同document的这个field重命名为相同的字段,尽量让documment的结构相同。另外一个,就是比如有的document有一个字段,叫做goods_type,但是有的document没有这个字段,此时可以对没有这个字段的document,补充一个goods_type给一个默认值,比如default。
(3)避免使用多个types存储不一样结构的document
很多人会很喜欢在一个index中放很多个types来存储不同类型的数据。但是其实不是这样的,最好不要这么干,如果你在一个index中有多个type,但是这些type的数据结构不太一样,那么这些type实际上底层都是写到这个索引中的,还是会导致稀疏性。如果多个type的结构不太一样,最好放入不同的索引中,不要写入一个索引中。
(4)对稀疏的field禁用norms和doc_values
如果上面的步骤都没法做,那么只能对那种稀疏的field,禁止norms和doc_values字段,因为这两个字段的存储机制类似,都是每个field有一个全量的存储,对存储浪费很大。如果一个field不需要考虑其相关度分数,那么可以禁用norms,如果不需要对一个field进行排序或者聚合,那么可以禁用doc_values字段。