Solr性能优化
1. 配置文件
1.1 schema.xml配置
schema 配置不合理,往往会导致查询性能低,索引占用磁盘、内存空间大的问题
- 合理设置域属性
- 域是否要检索(indexed),是否要存储(stored),按需配置,避免不必要的空间浪费。
- 域是否需要根据文本长度算分,是否需要在建索引时设置权重,如果不需要,设置omitNorms=true
- omitPositions、omitTermFreqAndPositions,词频信息和打分相关,位置信息和高亮显示相关,当不需要这些功能,则可设置为 true,节省磁盘空间,提升搜索速度。
- 对于需要排序的字段,使用 docValues,构造 fifieldCache 会进行压缩,节省内存使用
- 使用正确的数据类型
- 对于数值类型,使用能正确存储的最小数值类型,更小的数值类型占用更小的磁盘、内存、CPU缓存,并且处理时的 CPU 周期也更少。
- 数值类型不要用 string,一个整型占 4 字节,用 string,大小为 1000 以上的整型就占了 4 个字节了。当然,对于只有几个值(比如 0、1、2、3)的可枚举的,可以用 string。
- 不需要分词的域,用 string,不要用 text,text 默认用标准分词器分词。
- 需要范围查询的数值类型,需要使用 plong、pint 等分精度索引的类型,范围查询性能是不分精度索引的数值类型的 10 倍。当然,也不能滥用,分精度索引的数值类型比较占用空间,如果没有范围查询的需求,则不需要使用。
1.2 solrconfig.xml
-
索引目录类型
-
采用 NRTCachingDirectoryFactory,这种目录类型,小索引会缓存在内存中,减少磁盘 IO。而且这些小文件往往是频繁变化的,放在内存中,则 reopen 的时候,不需要读磁盘,性能会好很多。
-
autoSoftCommit 和 autoCommit
hardcommit 作用是使索引持久化,会 flflush 当前正在索引的段到磁盘,比较重,影响查询性能,时间间隔可以设置得较长。没有 hardcommit,机器挂了 ,重启后,会从 tlog 恢复。
softcommit作用是使索引可见,可根据实时性需要设置适当的长度。需要注意,softcommit会导致searcher层cache失效。索引实时性要求不高的情况下,频率尽可能设置长一点
-
-
cache配置
-
缓存类型
1.queryResultCache,查询结果缓存,key 由 q 参数、fq 参数、sort 参数组成,value 是 docId 和 score(score 可能没有)的有序集合 DocList。只要 q、fq、sort 参数有一个变化了,则属于不同 的 key,不会命中结果。 2.documentCache,document 的缓存,key 是 docId,value 是 document。 3.filterCache,filterQuery 结果缓存,key 是 fq 参数的值,value 是 docId 的无序集合 DocSet。 4.fieldCache,正排索引缓存,可通过 docId 获取字段值。排序、facet、group 等需要正排索引的 查询需要用到 fieldCache。fieldCache 是基于段的,一个字段的 fieldCache 是在第一次使用的时 候加载到内存中的。Lucene 用一个 weakHashMap 存放已加载的段的 fieldCache,key 为段的 indexReader。因此,fieldCache 是常驻内存,不会自动释放的,除非段被合并,不存在了,才会 释放掉。使用时,要注意内存的消耗,避免内存不够用,发生 OOM。
-
缓存注意事项
1.queryResultCache、documentCache、filterCache 都是 searcher 级别的缓存,searcher 重新 打开(softcommit 会触发),则缓存失效。其中 queryResultCache、filterCache 可以配置 autowarm 使 searcher 预热时重新加载。documentCache 的 key 是 docId,重新打开后, document 的 id 已经变化,因此 documentCache 不能进行 autowarm。 <filterCache class="solr.LRUCache" size="16384" initialSize="4096" autowarmCount="4096"/> 2.对于实时查询(比如 softcommit 频率为 1 秒),最好不要配置 cache,因为缓存失效太快,缓存 命中率可能比较低,如果配置了 autowarm,还会导致不停的 autowarm,加重服务器负担。除 非查询语句都比较集中,缓存条目很少。 3.对于有翻页的查询,可以适当调整 queryResultWindowSize 参数,比如一页的大小为 10,则 queryResultWindowSize 设置为 50,则后面 4 页,都会命中缓存,当然参数设置越大占用内存越 多 4.enableLazyFieldLoading 配置为 true,只读取需要的字段。这个属性要配合 documentCache 使 用,即开启了 documentCache,才能发挥 lazyLoading 的作用。 举个例子,比如查询出来的条目可能只显示简单的信息,点击具体条目,则需要把整个条目的所有信 息展示出来。则点击具体条目去查询时,命中了 cache,获取到的 document 就是有 lazyField 的 document 了,Solr 把它返回给调用者时,就会去加载 lazyField 的真实的值。 5.可配置 firstSearcher、newSearcher 来预热耗时的查询,使查询结果缓存起来,使查询性能更平 滑。 <listener event="newSearcher" class="solr.MyQuerySenderListener">
-
2. 索引构建
建议采用离线方式构建索引 ,离线构建索引的好处:
- 避免在线构建索引对在线服务的影响。
- 不用考虑记录更新的情况,省掉查询老记录是否存在的步骤,性能有提升。
- 可以使用内存大的机器,在内存中(RAMDirectory)进行构建(内存放不下,可考虑一个段一个段的构建),速度往往是在线构建的好几倍。
- 离线构建可以打开多个 indexWriter 进行构建(最好多个进程,因为 IndexWriter 存在类上加锁的情况,多个线程还是存在锁等待),然后再合并到一个 index 里,虽然在线索引也可以多个线程并发写,但会存在并发锁的问题。
- 离线索引追求的是吞吐量,对响应时间要求不高,可按照高吞吐量调配 jvm 参数,而在线索引还得考虑 jvm 对在线查询的影响。
- 离线构建可减少中间的段的生成,可以调大 ramBufffferSzie 和 mergeFactor,避免生成小的段,避免在构建的过程中自动合并,在构建完成后再根据需要触发合并动作,省掉中间的合并过程,速度也有一定的提高。
3. 搜索
-
如果不是所有字段都需要返回,则明确指定需要返回的字段,减少系统开销
-
一次返回的记录数不要太多,深度翻页使用 cursorMark 参数提升性能
//查询优化:游标查询 public void cursorQuery() throws Exception { //solr查询封装 SolrQuery sq = new SolrQuery(); sq.setRows(2);//设置游标一次读的数量 sq.set("q", "*:*");//按条件检索 sq.setSort("id", SolrQuery.ORDER.asc);//根据主键排序 String cursorMark = CursorMarkParams.CURSOR_MARK_START;//游标初始化 boolean done = false; while (!done) { sq.set(CursorMarkParams.CURSOR_MARK_PARAM, cursorMark);//变化游标条件 QueryResponse rsp = solrClient.query(sq);//执行多次查询读取 String nextCursorMark = rsp.getNextCursorMark();//获取下次游标 // 做一些操作数据的事 for (SolrDocument sd : rsp.getResults()) { System.out.println(sd.get("id")); } //如果两次游标一样,说明数据拉取完毕,可以结束循环了 if (cursorMark.equals(nextCursorMark)) { done = true; } cursorMark = nextCursorMark; } }
-
查询条件中包含路由字段的,可以先计算出分片,再从分片中获取机器,并指定 shard 来查询,只查询相应的 shard,避免服务端所有 shard 都查询
String shards = "192.168.1.215:8983/solr/a,192.168.1.214:8983/solr/b"; solrParams.set("shards", shards);//设置shard
-
合理使用 filterquery,filterquery 结合 filterCache,能把常用的查询条件缓存起来,提升查询效率。对于不使用 filterCache 的情况下,多个 OR 条件组合起来的查询也应该用 filterquery,因为filterquery 是不算分的,性能会好很多。主查询中的 OR 查询,对于一个 document 并不是有一个 OR 查询条件命中即返回,接着去查下一个 document,而是所有的 OR 条件都去查一遍,记录命中的 OR 条件的数量,由此来计算得分,所以会比在 filterQuery 中查询慢很多
4. 系统
-
内存分配
合理设置 jvm 内存大小,Solr 缓存、lucene 缓存、词典文件等都需要加载到内存中,内存设置太小,会造成 gc 频繁。但也不要把内存用得太满,在 jvm 内存足够的情况下,多留点给操作系统做文件缓存,这样索引文件能更多地被操作系统缓存起来,减少磁盘 IO,提升搜索的性能
方法一:直接修改配置文件参数 打开solr\bin目录下的solr.in.sh脚本文件 搜索找到 SOLR_HEAP 或者 SOLR_JAVA_MEM ,然后修改 SOLR_HEAP="1024m" 或者 SOLR_JAVA_MEM="-Xms512m -Xmx512m" 方法二:修改JVM内存存执 打开 tomcat\bin下面的catalina.sh文件,加入 set JAVA_OPTS= -Xms1024m -Xmx1024m 方法三:修改系统环境变量 设置CATALINA_OPTS 或者 JAVA_OPTS CATALINA_OPTS="-Xms128m -Xmx1024m -XX:PermSize=64M -XX:MaxPermSize=512M " 方法四:在启动时直接设置JVM大小 ./solr -m 2g
-
swap
系统内存不足时,操作系统会拿交换区(磁盘)来当做内存使用,交换区的访问速度和内存比非常慢,会影响搜索的性能,对 gc 也有较大的影响,jvm 收集交换区的垃圾对象时,常常速度很慢,造成应用停顿。建议内存足够的情况下,把 swap 关掉。如果内存比较紧张,则建议把 swapness参数的值调小,让操作系统尽量少使用 swap,完全关掉可能会造成操作系统内存不足,进程而被操作系统 kill 掉
如果内存够大,应当告诉linux不必太多的使用SWAP分区,可以通过修改swappiness的数值。swappiness=0的时候表示最大限度使用物理内存,然后才是swap空间,swappiness=100的时候表示积极的使用swap分区,并且把内存上的数据及时的搬运到swap空间里面。 现在一般1个G的内存可修改为10,2个G的可改为5,甚至是0。具体这样做: 1.查看你的系统里面的swappiness $cat/proc/sys/vm/swappiness 2.修改swappiness值为10 $sudo sysctl vm.swappiness=10 但是这只是临时性的修改,在你重启系统后会恢复默认的,为长治久安,还要更进一步: $sudo gedit /etc/sysctl.conf 在这个文档的最后加上这样一行: vm.swappiness=10
-
磁盘
搜索引擎对磁盘的随机访问比较多,推荐使用 ssd 磁盘