solr 性能优化

本贴最后更新于 2812 天前,其中的信息可能已经沧海桑田

一.配置

1.schema.xml 配置

schema 配置不合理,往往会导致查询性能低,索引占用磁盘、内存空间大的问题。

1)合理设置字段属性
  • 字段是否要检索(indexed),是否要存储(stored),按需配置,避免不必要的空间浪费。
  • 段是否需要根据文本长度算分,是否需要在建索引时设置权重,如果不需要,设置 omitNorms=true
  • omitPositions、omitTermFreqAndPositions,词频信息和打分相关,位置信息和高亮显示相关,当不需要这些功能,则可设置为 true,节省磁盘空间,提升搜索速度。
  • 对于需要排序的字段,使用 docvalues,构造 fieldCache 会进行压缩,节省内存使用。
2)使用正确的数据类型
  • 对于数值类型,使用能正确存储的最小数值类型,更小的数值类型占用更小的磁盘、内存、cpu 缓存,并且处理时的 cpu 周期也更少。
  • 数值类型不要用 string,一个整形占 4 字节,用 string,大小为 1000 以上的整形就占了 4 个字节了。当然,对于只有几个值(比如 0、1、2、3)的可枚举的小整形,可以用 string。
  • 不需要分词的字符型,用 string,不要用 text,text 默认用标准分词器分词。
  • 需要范围查询的数值类型,需要使用 tlong、tint 等分精度索引的类型,范围查询性能是 long、int 等不分精度索引的数值类型的 10 倍。当然,也不能滥用,分精度索引的数值类型比较占用空间,如果没有范围查询的需求,则不需要使用。

2.solrconfig.xml 配置

1)索引目录类型
  • 采用 NRTCachingDirectoryFactory,这种目录类型,小索引会缓存在内存中,减少磁盘 IO。而且这些小文件往往是频繁变化的,放在内存中,则 reopen 的时候,不需要读磁盘,性能会好很多。
  • softcommit 和 hardcommit
    hardcommit 作用是使索引持久化,会 flush 当前正在索引的段到磁盘,比较重,影响查询性能,时间间隔可以设置得较长。没有 hardcommit,机器挂了,索引会否丢失?答案是不会,因为还有 tlog,重启后,会从 tlog 恢复。

softcommit作用是使索引可见,可根据实时性需要设置得适当的长度。需要注意,softcommit会导致searcher层cache失效。索引实时性要求不高的情况下,频率尽可能设置长一点。

2)cache 配置
缓存类型
  • queryResultCache,查询结果缓存,key 由 q 参数、fq 参数、sort 参数组成,value 是 docId 和 score(score 可能没有)的有序集合 DocList。只要 q、fq、sort 参数有一个变化了,则属于不同的 key,不会命中结果。
  • documentCache,document 的缓存,key 是 docId,value 是 document。
  • filterCache,filterQuery 结果缓存,key 是 fq 参数的值,value 是 docId 的无序集合 DocSet。
  • fieldCache,正排索引缓存,可通过 docId 获取字段值。排序、facet、group 等需要正排索引的查询需要用到 fieldCache。fieldCache 是基于段的,一个字段的 fieldCache 是在第一次使用的时候加载到内存中的。Lucene 用一个 weakHashMap 存放已加载的段的 fieldCache,key 为段的 indexReader。因此,fieldCache 是常驻内存,不会自动释放的,除非段被合并,不存在了,才会释放掉。使用时,要注意内存的消耗,避免内存不够用,发生 OOM。
缓存注意事项
  • queryResultCache、documentCache、filterCache 都是 searcher 级别的缓存,searcher 重新打开(softcommit 会触发),则缓存失效。其中 queryResultCache、filterCache 可以配置 autowarn 使 searcher 预热时重新加载。documentCache 的 key 是 docId,重新打开后,document 的 id 已经变化,因此 documentCache 不能进行 autowarn。

  • 对于实时查询(比如 softcommit 频率为 1 秒),最好不要配置 cache,因为缓存失效太快,缓存命中率可能比较低,如果配置了 autowarn,还会导致不停的 autowarn,加重服务器负担。除非查询语句都比较集中,缓存条目很少。

  • 对于有翻页的查询,可以适当调整 queryResultWindowSize 参数,比如一页的大小为 10,则 queryResultWindowSize 设置为 50,则后面 4 页,都会命中缓存,当然参数设置越大占用内存越多。

  • 设置 useFilterForSortedQuery=true,则对于没有按照 score 排序的查询,会用 filterCache 缓存主查询结果(key 为 q 参数),当然,如果主查询的命中数大多比较小,则没必要用这个缓存,可能还没有直接查询快。

  • enableLazyFieldLoading 配置为 true,只读取需要的字段。这个属性要配合 documentCache 使用,即开启了 documentCache,才能发挥 lazyLoading 的作用。举个例子,比如查询出来的条目可能只显示简单的信息,点击具体条目,则需要把整个条目的所有信息展示出来。则点击具体条目去查询时,命中了 cache,获取到的 document 就是有 lazyField 的 document 了,solr 把它返回给调用者时,就会去加载 lazyField 的真实的值。

  • 可配置 firstSearcher、newSearcher 来预热耗时的查询,使查询结果缓存起来,使查询性能更平滑。

二.索引

建议采用离线方式构建索引,再拷贝索引到引擎中。离线构建索引的好处:

  • 避免在线构建索引对在线服务的影响。
  • 不用考虑记录更新的情况,省掉查询老记录是否存在的步骤,性能有提升。
  • 可以使用内存大的机器,在内存中(RAMDirectory)进行构建(内存放不下,可考虑一个段一个段的构建),速度往往是在线构建的好几倍。
  • 离线构建可以打开多个 indexWriter 进行构建(最好多个进程,因为 IndexWriter 存在类上加锁的情况,多个线程还是存在锁等待),然后再合并到一个 index 里,虽然在线索引也可以多个线程并发写,但会存在并发锁的问题。
  • 离线索引追求的是吞吐量,对响应时间要求不高,可按照高吞吐量调配 jvm 参数,而在线索引还得考虑 jvm 对在线查询的影响。
  • 离线构建可减少中间的段的生成,可以调大 ramBufferSzie 和 mergeFactor,避免生成小的段,避免在构建的过程中自动合并,在构建完成后再根据需要触发合并动作,省掉中间的合并过程,速度也有一定的提高。

三.搜索

  • 如果不是所有字段都需要返回,则明确指定需要返回的字段,减少系统开销。
  • 一次返回的记录数不要太多,深度翻页使用 cursorMark 参数提升性能。
  • 查询条件中包含路由字段的,可以先计算出分片,再从分片中获取机器,并指定 shard 来查询,只查询相应的 shard,避免服务端所有 shard 都查询。
  • 合理使用 filterquery,filterquery 结合 filtercache,能把常用的查询条件缓存起来,提升查询效率。对于不使用 filtercache 的情况下,多个 OR 条件组合起来的查询也应该用 filterquery,因为 filterquery 是不算分的,性能会好很多。主查询中的 OR 查询,对于一个 document 并不是有一个 OR 查询条件命中即返回,接着去查下一个 document,而是所有的 OR 条件都去查一遍,记录命中的 OR 条件的数量,由此来计算得分,所以会比在 filterQuery 中查询慢很多。
  • facet query 有几种实现方式,需要根据具体情况选择。
    • enum,通过 filterCache 缓存 field 中每个 term 的 docset,然后和查询结果的 docset 进行交集来计算 term 命中的数量,对于 term 比较少的情况比较合适。这种方式适用于 multiValue。
    • fc,通过 fieldcache 计算,遍历查询结果的 docset,根据 docid 从 fieldcache 获取值进行计算,这里的 fieldcache 是 searcher 级别的,这样能避免每个 segment 的计算结果进行合并,但对于实时查询,searcher 频繁 reopen,需要频繁构造 fieldcache,性能会有波动,可以注册 newSearcher 监听器来解决这个问题。
    • fcs,原来和 fc 一样,但用的是底层 segment 级别的 fieldcache,需要对每个段的计算结果进行合并,性能比 fc 差些,但适用于实时搜索。

四.系统

  • 内存分配
    合理设置 jvm 内存大小,solr 缓存、lucene 缓存、词典文件等都需要加载到内存中,内存设置太小,会造成 gc 频繁。但也不要把内存用得太满,在 jvm 内存足够的情况下,多留点给操作系统做文件缓存,这样索引文件能更多地被操作系统缓存起来,减少磁盘 IO,提升搜索的性能。
  • swap
    系统内存不足时,操作系统会拿交换区(磁盘)来当做内存使用,交换区的访问速度和内存比非常慢,会影响搜索的性能,对 gc 也有较大的影响,jvm 收集交换区的垃圾对象时,常常速度很慢,造成应用停顿。建议内存足够的情况下,把 swap 关掉。如果内存比较紧张,则建议把 swapness 参数的值调小,让操作系统尽量少使用 swap,完全关掉可能会造成操作系统内存不足,进程而被操作系统 kill 掉。
  • 磁盘
    搜索引擎对磁盘的随机访问比较多,推荐使用 ssd 磁盘。
  • Solr
    16 引用 • 22 回帖 • 1 关注
  • 索引
    24 引用 • 28 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...