开源搜索引擎Solr是一款非常优秀的搜素引擎,只要一些简单的配置就能进行使用,大大减少了开发时间。

在我工作的环境中,整站的商品搜索业务都是依托于Solr,在Solr的使用上沉淀了不少宝贵的开发经验。随着公司商品数据规模不断的扩大,针对Solr的二次开发难度也在不断的增大,在过去的几年时间内,我把大量的数据放在索引构建上,从之前的DB模式24小时都无法完成全量的构建,到现在使用hive + avro + mapreduce把全量构建压缩到3小时以内,从之前的DB增量模式经常出现更新失败,更新延迟,到现在使用kafka+redis的模式保证更新数据不丢实,更新的延迟在1分钟以内。整个全量增量的索引架构都是可以横向扩展。

在索引构建告一段落后,查询又出现了问题,经常可以看到Solr每隔20分钟,会有timeout情况的出现。查看了下日志,在那段报错的时间段,正好是slave从master上获取增量数据的时间(replication配置了每隔20分钟slave replication一次数据到master),而Solr中许多内置的缓存都开的比较大。当数据更新后,Solr会对Cache进行重新的预热,在这个时候,有大量的内存对象会被换入换出,可能在这个点触发了full gc。

为了验证是否是full gc的可能,首先第一步获取full的信息,命令:

使用lsof命令根据端口找出pid(当然linux有很多其他方式,条条大路通罗马),然后使用jstat -gc命令获取信息。图中该Solr程序已经运行了196hrs35min,可以看出FGC一共执行了356次,一共花费6331s,平均一次停顿18s,这种停顿在java中有个专有名词——stop the world(STW),顾名思义在这18秒内,所有的业务代码都会stop给GC让出资源。结合程序启动的时间,平均每30分钟有一次18秒的卡顿,无论solr本身的性能再优越18秒足以让client time,问题也就这样终于被发现了。

由于Solr进程没有进行full gc的制定,所以都是java6的默认配置,java6默认的配置对于Solr这种低延迟的场景显然是不适用的,所以需要选择一款合适的GC。GC的两大标准:吞吐量 & 响应时间。就service来说,响应时间优先级要高于吞吐量,对于上述的垃圾回收而言,串行垃圾回收和并行垃圾回收不适合service的场景,对于CMS和G1,虽然都是以响应时间优先,但是在内部实现上差异很大,G1是oracle唯一还在继续开发优化的垃圾回收器,支持并发并行操作,在保证响应时间的前提下,尽可能的提高了吞吐量,所以更建议对于大型service服务使用G1作为默认的垃圾回收。由于G1在jdk6上还是个不稳定的版本,所以需要将JDK升到7.4以上。

关于G1的介绍有很多,就不介绍了,主要介绍下调优的一些思路

1. 请打开gc log日志

gc log日志在gc调优的过程中非常关键,很多参数的调整都需要以gc log日志的反馈作为根据,从而判断参数是否生效。
     参数:-XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintAdaptiveSizePolicy -Xloggc:./gc.log

2. 根据gc日志的返回,找到full gc的原因
     

从上述的GC信息中可以看到,是因为无法分配内存导致FGC的触发。在这里先简答介绍下G1的模型,G1和其他GC算法不同的是,G1使用region把内存分为多块,每个region的大小可以根据-XX:G1HeapGegionSize进行设置,最大不超过32m,每个region有唯一一个角色(unused,young,old),由于Solr需要大量的内存作为cache,所以我将该值设置为最大,即:-XX:G1HeapGegionSize=32m,G1当要为对象申请内存空间的时候,回去region列表中找合适的region进行分配,但是这里有个特殊的场景,当一个内存对象很大,大到一个region无法满足之后,G1会查找一片连续的region用于存放该对象,并且这些region标记为old,跳过young gc。在G1算法内,如果一个对象超过了region的1/2,就认为是一个大对象。再回到上面的日志可以发现,Solr需要申请1个G的内存空间,而由于region的大小为32M,即需要32个连续的region。但是我从内存的使用上来看,申请了100G的内存,实际只使用了30G就出现了这种场景,原因是g1内存分配不适连续分配,导致内存碎片,所以即使还有很多余量,但是无法找到32个连续的region,导致强制执行了fgc。找到了根本原因后就简单了。

3. 参数调整

G1对内存的压缩只在两种情况下进行,第一个中情况是fgc,这种事我们应该尽力避免的,第二种是在初始话标记阶段,所以调优的目标是希望能在适当的时间触发该阶段。在补充了G1的整个执行周期,G1首先上来进行young gc,会清理一部分内存数据,把旧的数据迁移到old region,随着内存不断的增加,每次做完一次young gc后,会根据-XX:InitiatingHeapOccupancyPercent判断是否需要进入初始化标记阶段,所以这个参数在G1大对象调优上非常关键,我最后调整为:-XX:InitiatingHeapOccupancyPercent=30,即到达最大内存的30%开始执行初始化标记。当完成标记后,会进行clean up在clean up阶段old区域的内存会被回收压缩,达到目的。在后面会进入mixed gc,反复执行多次满足条件后再返回到young gc,一个周期就这么完成了。

当然还有很多参数的调整没有给出,但是我觉得还是希望大家自己去研究,很多参数的调整需要针对特殊的业务,千万不可生搬硬套。最后的调整也起到了非常好的效果,从一天上万的报错降低到100个以内,也得到了CTO和其他老大们的肯定。

参考资料

https://blogs.oracle.com/g1gc/entry/g1gc_logs_how_to_print

http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/G1GettingStarted/index.html

https://software.intel.com/en-us/blogs/2014/06/18/part-1-tuning-java-garbage-collection-for-hbase

http://www.oracle.com/technetwork/cn/community/developer-day/2-jdk7-g1-2083344-zhs.pdf

从Solr卡顿到G1垃圾回收相关推荐

  1. G1垃圾回收算法概述

    垃圾回收都是基于分区进行的.G1在实现垃圾回收时一共提供了3种回收的方法,分别是新生代回收(称为Young GC, YGC),混合回收(称为Mixed GC),全回收(称为Full GC, FGC). ...

  2. 万字长文教你看懂java G1垃圾回收日志

    文章目录 一.如何在idea打印G1日志 二.G1基础参数 三.G1新生代收集 1. 四个关键信息 2. 列出了新生代收集中并行收集的详细过程 3.列出了新生代GC中的一些任务: 4.包含一些扩展功能 ...

  3. 什么是G1垃圾回收算法

    转载自 什么是G1垃圾回收算法 为解决CMS算法产生空间碎片和其它一系列的问题缺陷,HotSpot提供了另外一种垃圾回收策略,G1(Garbage First)算法,通过参数 -XX:+UseG1GC ...

  4. G1垃圾回收日志分析

    标准 gc 日志 使用G1垃圾回收器最难的地方是读懂回收日志.G1回收虽然也是分代的,但是打印出来的日志却不像其他回收器那样明显.所以需要好好配置JVM参数才行.先看下面一段日志,这段日志是通过配置好 ...

  5. 垃圾回收之G1收集过程

    G1 中提供了 Young GC.Mixed GC 两种垃圾回收模式,这两种垃圾回收模式,都是 Stop The World(STW) 的. G1 没有 fullGC 概念,需要 fullGC 时,调 ...

  6. 垃圾回收器之 G1 垃圾回收器

    4.4 G1 定义:Garbage First 2004论文发布 2009 JDK 6u14 体验 2012 JDK 7u4 官方支持 2019 JDK9 默认 (废弃了之前的 CMS 垃圾回收器) ...

  7. Java G1 GC 垃圾回收深入浅出

    1. G1概览 G1 GC 全称是Garbage First Garbage Collector,垃圾优先垃圾回收器,以下简称G1.G1是HotSpot JVM的短停顿垃圾回收器.其实关于G1的论文早 ...

  8. G1垃圾回收器在并发场景调优

    一.序言 目前企业级主流使用的Java版本是8,垃圾回收器支持手动修改为G1,G1垃圾回收器是Java 11的默认设置,因此G1垃圾回收器可以用很长时间,现阶段垃圾回收器优化意味着针对G1垃圾回收器优 ...

  9. 一文搞懂G1垃圾回收器

    G1是从JDK9之后的默认垃圾回收器,其功能强大,性能优异,不过目前市面的材料不算多,很多都是抄来抄去,讲得也不太清楚.经过仔细阅读oracle官网以及相关的材料,从整体上梳理了G1的过程,希望这一文 ...

最新文章

  1. 【swjtu】数据结构实验9_多种排序算法的实现
  2. DAY9-字符串笔记整理2018-1-19
  3. Codeforces 374A - Inna and Pink Pony
  4. 可应用于实际的14个NLP突破性研究成果(三)
  5. 【原创】MySQL里求给定的时间是所在月份的第几个礼拜
  6. MySQL无法远程连接解决方案
  7. 前端开发中,对图片的优化技巧有哪些?
  8. Nginx配置SSL报错 nginx: [emerg] unknown directive ssl
  9. EasyBert,基于Pytorch的Bert应用
  10. 基于python的tensorflow_Python深度学习:基于TensorFlow
  11. 【报告分享】连锁经营行业全面费用管理数字化白皮书-亿欧汇联易(附下载)
  12. python 递归 和 动态规划 DP算法两种方法求解 最长回文子串问题
  13. 朱晔的互联网架构实践心得S1E1:Pilot
  14. 苹果推送通知办事教程 Apple Pushnb
  15. NetSuite 未实现汇兑损益
  16. UltraScale Architecture Clocking Resources
  17. Android Display 之 HAL Gralloc
  18. oracle数据库报ora-01691错误--表空间容量溢出解决办法
  19. Java 基础 --- Java 历史背景、体系特点以及实现原理
  20. 茂名石化BPM应用实践 ——业务协同及服务共享平台建设和应用

热门文章

  1. C#调用C++接口 -- delegate实现函数指针
  2. NPDP-《产品组合管理》
  3. Ubuntu 20.04增加Deepin源以安装应用商城内最新版的微信、钉钉
  4. 关于NetKeeper(创翼)升级后,无法共享WIFI问题的解决-猎豹、360都可
  5. pr字幕模板 炫酷科技蓝色流线条动画pr模板
  6. java关闭ftp 连接_Java语言实现简单FTP软件------gt;连接管理模块的实现:主机与服务器之间的连接与关闭操作(八) - 移动编程 - ITeye博客...
  7. 车辆管理与购买系统/车辆管理系统/车辆销售系统
  8. 手机存储卡文件怎么恢复
  9. OpenAL的Mingw编译
  10. 2022 WordPress All Pic Local v0.2 下载外链图片到本地