回顾一下
大约一周前,更新过一次线上,上了分享相册终端页,第二天新鲜事入口一打开,从早上开始就一直开始挂机器,起初认为是压力问题,后来加机器到了快晚上7点的时候,几乎大面积的挂掉,没办法,只能回滚。通过分析,发现是压力测试不到位,因为压力测试不够真实,不够真实是因为数据不够随机,导致缓存为服务减少了压力。
第二天亮哥写了一个足够随机的取数据算法,把算法移植到压测代码上后,进行压测,大概也是夜里7点左右进行,第一次压测不到5分钟就挂了,从现象上看是内存耗尽,FGC频繁。第二次压测大概进行了20分钟左右,又挂了。挂了代表压测足够真实。通过监控工具jconsole发现old区域的曲线一直在往上涨,涨到一定的时候,就挂了。第三次压测大概也进行了20分钟,也挂了,挂了的原因是亮哥无意间用Jstack抓了一个栈,曲线和第二次相似,自己百思不得其解。因为有GC,理论上不会让它一直只涨不跌的。初步认为是什么地方内存发生了泄漏。
第三天就在我还在纠结于内存泄漏这个问题的时候,亮哥从昨晚的堆栈log里找到了答案,堆栈里充满了大量的ReentrantLock,这东西看似不起眼的,有Lock但是栈里却没有Blocked,都被人忽略了。但是跟进代码里一看这把lock锁住了四部对DB的操作,3次查询一次插入。亮哥对我说先把lock干掉,这把锁有问题,代码不能这样写。
@Override
public void save(ShareMap shareMap, int userId) {
lock.lock();
if (findMapId(shareMap.getShareId(), shareMap.getReferId(), userId) == 0){
shareMap.setMapId(idSequenceDAO.nextId(IdSequenceDAO.SHARE_ID));
shareMap.setId(idSequenceDAO.nextId(IdSequenceDAO.SHARE_MAP_ID));
shareMapDAO.insert(shareMap, userId);
}
lock.unlock();
}
因为这把锁在并发量大的时候,完全可以造成无数的线程在这里造成一个临时性的Blocked。为了证明这个问题,我们进行了再一次的压测。这次我们压测从周五下午开始,刚开始一台机器,从监控曲线上看,old区域的内存一直在增加,直到它达到自己的最大内存时,突然一根直线下来降到最大内存的百分之30左右,看到这根曲线我就知道服务正常了。为了达到前一天夜里时候的压测压力,又加了一台机器的流量进行。这次压测我们的代码坚持到了周一我手动把压测服务给杀掉了。通过这次压测,几乎可以确认就是这把lock导致的线上服务大规模的瘫痪。
第一次压测学到的东西:
1、Lock的范围,粒度,以及该不该使用Lock。虽然说这把Lock加上是为了保证下面insert里的某些东西的唯一性,但是为一个概率很小的可能性事件消耗了线上服务的质量,真不值得。
2、服务器挂掉的原因,服务堵了,服务虽然没有Block,但是大大降低了并发效率,导致服务消耗了大量的内存在等待线程排队,使old区域的内存只涨不跌,直到挂掉。
3、知道JVM里面内存分布,内存的分代,知道了什么事FGC,什么是YGC。FGC对服务的影响。
压测成功了,我兴致满满的提测了,测试通过后,就急于更新线上的服务,在我觉得更完了,没事的时候,服务又挂了,这次几乎是一台接一台的挂掉,这次我傻了,心想优化了代码死的还更惨,一头雾水的我真不知掉咋办。想想压测白做了?我承认我当时很低落。低落的不敢看一眼流量图,这让我产生了一种挫败感。
那天夜里我在临近一台机器快要挂掉的时候,用Jstack抓了一个线程堆栈的log,接着回滚线上,然后在抓了一个老版本的jstack log 。
第二天早上一来,我试着对比了两个log。发现新代码的log里充满着大量的findShareMap方法,在往上看就是
com.mysql.jdbc.util.ReadAheadInputStream。我统计了一下com.mysql.jdbc.util.ReadAheadInputStream的行数发现岂有400多行,再对比了一下老代码中的com.mysql.jdbc.util.ReadAheadInputStream,发现才25,我觉得这是一个线索,顺着这条线索我统计了一下findShareMap的方法,发现岂有惊人的1100多行。给我第一感觉就是在等待查表,我看了看代码,发现那条语句就是直接调用DAO的,没有任何其他逻辑,于是我查了查表中的索引,发现这条SQL的查询语句条件居然不是一个索引。我想初步的原因可能已经找到,但是我觉得这仍然不能解释为什么新鲜事路口不开放挂的比上次还快,还恐怖。带着这个疑问,先让DB给我把索引加上了之后,我又进行了一次压测,这次测得不是翻页,而是分享,我用了线上一台机器上的流量直接切到测试机。然后我也没监控了,直接查看log中的findShareMap,这时几乎都在10以下。我觉得问题应该解决了。应该就是少了一个索引导致的线上挂掉。但是这次我仍然不是非常肯定,最后更新一台一台的上去。更一台观察一台,直到所有的都更完。夜里观察了一下高峰的压力。很明显进流量大了很多。由此就解决了我心中的疑问,新鲜事虽然没放开路口,但是仍然有人从列表入口进入查看分享相册。随着分享传播越来越多。量也越来越大。从而不开新鲜事口也可以让线上服务挂掉。
com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:188)
- locked <0x00002aab08030860> (a com.mysql.jdbc.util.ReadAheadInputStream)
at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:2452)
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2906)
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2895)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3438)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1951)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2101)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2554)
- locked <0x00002aab08030a40> (a java.lang.Object)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1761)
at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1912)
- locked <0x00002aab08030a40> (a java.lang.Object)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:93)
at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:648)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:591)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:641)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:670)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:678)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:710)
at net.paoding.rose.jade.provider.jdbc.JdbcImpl.query(JdbcImpl.java:63)
at net.paoding.rose.jade.provider.jdbc.JdbcDataAccess.select(JdbcDataAccess.java:79)
at net.paoding.rose.jade.core.SQLThreadLocalWrapper.select(SQLThreadLocalWrapper.java:48)
at net.paoding.rose.jade.core.SelectOperation.execute(SelectOperation.java:66)
at net.paoding.rose.jade.core.JadeDaoInvocationHandler.invoke(JadeDaoInvocationHandler.java:128)
at $Proxy45.findShareMap(Unknown Source)
at com.renren.app.share.biz.impl.ShareMapBizImpl.findShareMap(ShareMapBizImpl.java:75)
第二次压测学到的东西:
1、心态,遇到问题一定要冷静,淡定。这样才能分析原因。
2、Jstack的使用,分析。
3、线上挂掉的原因总结,查问题的思路
4、更新前先做好策略,应对突发情况。
分享到:
相关推荐
标题中的“百度近期算法更新总结:收录和反链暴涨”指的是百度搜索引擎在其算法上进行的一系列更新,导致许多网站的收录量和反向链接数量显著增加。这些变化可能与百度优化其索引和数据处理能力有关,同时也反映出...
SY4109-2013更新总结[参照].pdf
同时随着新知识的加入,定期更新总结本内容。 **学习重点总结法带来的好处** 1. **提高学习效率** 通过抓住每个学习单元的关键点,避免在大量非重点信息上浪费时间。 2. **加深理解与记忆** 对信息进行深入...
标题中的“INS更新算法总结”指的是惯性导航系统(Inertial Navigation System,简称INS)的更新算法概览。惯性导航系统是一种不依赖外部参考信号的自主式导航技术,通过测量载体在三维空间中的加速度和角速度,经过...
本文主要讲解了Android应用的自动更新...以上知识点总结了Android应用自动更新的核心内容和实现步骤。通过合理的配置和编程逻辑,开发者可以为用户带来便捷的自动更新体验,同时也要注意代码的健壮性和用户体验的优化。
同时,开盘总结也是一个持续的过程,随着项目的进展,团队需要定期更新总结,以反映最新的情况和变化。通过这样的方式,团队能够更好地管理项目,提升工作效率,减少潜在的问题,最终实现项目目标。
标题中的“2019更新工作总结打包4篇打包.zip”表明这是一个包含四份2019年更新的工作总结文档的压缩文件。这类文件通常用于整理和分享个人或团队在过去一年的工作成果、经验总结以及对未来的规划。在IT行业中,工作...
实验报告“数据库原理实验3-数据更新及视图”涵盖了数据库操作的核心方面,特别是SQL语言在数据管理和视图创建中的应用。以下是对实验内容的详细解释: 1. **数据更新语句**: - **UPDATE**:用于修改现有数据表中...
本文将深入探讨“Unity项目热更新方案性能分析总结”这一主题,为开发者提供宝贵的实践经验和性能优化建议。 一、Unity热更新基础 Unity热更新的基本原理是通过分离游戏的核心逻辑和资源,使得部分代码可以在游戏...
关于定期安全性更新报告总结归纳实习调研报告总结归纳工作总结报告总结归纳.pdf
【标题】:“企业-华峰超纤-2020年年终总结(更新后).rar”这个压缩包文件显然包含的是华峰超纤公司在2020年度的工作总结报告。华峰超纤可能是一家专注于超细纤维材料研发、生产和销售的企业,其年度总结报告是对...
推荐系统的总结,后面会持续更新。 推荐概念 信息过滤系统解决信息过载用户需求不明确的问题 * 利用一定的规则将物品排序展示给需求不明确的用户 推荐搜索区别 * 推荐个性化较强,用户被动的接受,希望能够提供持续...
更新-小学二年级体育教学工作总结-表格-(含教学总结、德育总结、业务学习总结、安全总结、综合实践).pdf
前端知识点总结,待更新
这份更新后的总结,通常会包含对以往数据的修正、新增的分析内容或者最新的业务发展动态,以供公司内部管理和对外报告使用。以下是对该PDF文件可能涵盖的关键知识点的详细说明: 1. **公司概况**:东方嘉盛可能在...
总结,子线程中更新UI的方法多样,选择哪种取决于具体需求和项目结构。理解并熟练运用这些技术,可以提高代码的健壮性和用户体验。在获取和处理Message时,Handler-Looper机制是基础,而其他如EventBus和LiveData等...
《wuxinxinggg_raph直刷_更新历史总结》一文详细记录了wuxinxinggg直刷项目的更新历程,特别聚焦于raph800和T7272两个型号设备的直刷过程,为读者提供了宝贵的参考资料。以下是根据标题、描述及部分内容提炼的关键...
oracle使用 merge 更新或插入数据(总结)
将全网的所有资源总结 用了几天后JLINK V8指示灯不亮了,电脑提示无法识别的解决办法 J-link V8 掉固件更新笔记 https://blog.csdn.net/zhaqonianzhu/article/details/121107703 jink v8刷写固件过程中进入bootload...