前两天网站访问慢的问题定位过程以及最终解决办法

原创
2018/08/16 10:15
阅读数 1.5W

前提说明:目前开源中国社区在做改版,因此网站同时存在新旧两种不同的版面,例如个人空间就是新的系统。因此整个网站包含两套系统,改版前和改版后,我们把改版前叫老系统,把改版后叫新系统。

大概一周多的时间,经常会感觉网站,包括客户端使用过程中有卡顿的情况,没太在意。但是前天这个卡顿就变得比较严重,动弹里开始出现各种反应访问慢的信息。

检查所有的机器的 CPU 使用率都在一个正常的状态,Tomcat 无特别的异常日志,应用的数据库连接池的忙连接比平时要高好多倍,但是数据库操作一些正常。

检查数据库慢查询日志,比平时多了一些,基本上集中在近期改版新的 SQL 上,把这些慢查询通过索引调整、SQL 优化方式解决后,慢查询不再出现,但是系统响应速度仍旧没有提升。

我们一度怀疑是数据库响应慢,想重启一下社区的 MySQL 数据库,这个数据库已经运行超过 5 年没重启过了!!!但还是忍住了!再试试其他不行再说。

然后发现了一个非常非常慢的操作,就是在客户端上收藏某篇文章的时候,完全没有响应,或者是要十几秒才有反应。

于是在这个收藏功能对应的代码增加了数据监控点,在日志中记录该方法几个逻辑的执行时间。更新到线上进行测试发现,从在界面上点击收藏,到日志的输出中间隔了好几十秒,而真正功能的执行时间很短。看似有很多请求在排队等 Tomcat 处理导致的堵塞,但是 Tomcat 的日志没有关于处理请求队列慢的日志信息。这是其中一个疑点。

日志显示数据库操作执行十几毫秒,缓存操作一两百毫秒,所以可以确定数据库是没问题的(幸亏没重启)。而缓存操作就算是一两百毫秒也不应该导致这个系统拖得那么慢,但是可以明确的是缓存是肯定有问题的。没准其他更复杂的缓存操作耗时要大很多,导致请求处理的卡顿。

OSChina 一直在用 J2Cache 的两级缓存框架,这个框架有很多人在喷,但是喷的人都没用过它。由于 OSChina 现在整个网站同时运行新老两个系统,因为很多页面还没改版完成。老的系统基于 J2Cache 1.x 运行,新的系统基于 J2Cache 2.x。连接的同一个 Redis 服务,使用不同的 database 进行隔离。

所以我们决定搞一个全新的干净的 Redis ,先把老系统切到这个新的 Redis 服务上,切换后先内部测试,首次访问慢一点,再次访问速度就很快了。切到生产上,所有的用户请求的转到这个系统上,访问速度很快,请求的处理速度一般只有几十毫秒。

运行了半天多,速度仍然很稳定。

但是个人空间依然很慢,因为个人空间是新的系统,接到还是之前的 Redis。于是我们又使用了另外一个独立的全新的 Redis 服务重新部署了一套空间,刚部署上线完后速度很快,但是过了半个小时一个小时候,就开始感觉没那么顺畅了。

在观察系统日志时,同时也发现了我们新的业务代码在缓存处理逻辑上的严重问题,例如生成大量动态的 Region ,而实际使用 Region 不应该这样使用,Region 应该事先在配置文件中定义好的。因为在 J2Cache 中动态创建 Region 是一个线程同步的方法,大量并发请求势必导致等待。

于是我们修改了这部分处理逻辑,不再动态创建大量的 Region ,再次更新后访问速度终于恢复了,注意这里是有两个系统。

新系统运行改版后的页面,老系统运行尚未改版的页面,新老系统通过 Nginx 进行路由。

经过一晚上运行之后,新系统感觉虽然没有之前慢,但是也时常感觉不顺畅!!!但是老系统依然很快。

于是现在的问题是:为什么老系统很快,但是新系统运行一段时间后会变慢。

礼拜二早上醒来觉得不服气,在床上操起电脑继续看各种系统日志和运行状态 —— 发现了两者在使用 Redis 服务上的一个区别:

老系统使用 J2Cache 的 hash 模式在 Redis 存储数据,而新系统使用 J2Cache 的 generic 模式记录数据。也就是说老系统操作 Redis 使用 hget/hset 这些方法,Region 对应的是 Redis 的一个 key,而具体的缓存数据是 key 对应的子 key 。而新系统直接是 get/set 这样的方法,也就是说不同 Region 的所有 Key 都揉在一起形成一个巨大的哈希表。

那么会不会是这个原因导致新系统在运行一段时间后变慢呢?理论上是不应该的,因为 Redis 的处理速度不会因为 key 数量的增加而变慢。但我们还是决定一试。

修改 J2Cache 的配置,将 redis.storage 配置值改为 hash ,清理数据,再次更新系统后,到目前运行已经一整天了,一切非常正常。

自此访问速度问题的处理告一段落。

但是仍有几个疑点没有搞清楚:

  1. 为什么 Tomcat 的请求堵塞那么就没得到处理,却没有异常信息,难道是队列还没满?
  2. 为什么 Redis 在 key 的数量达到一定程度后响应速度变慢呢?我们用的是 Redis 3.0.5 版本
  3. 早在 J2Cache 1.x 的时候,当时 Redis 版本还比较低,好像是 2.x ,那时候我测试使用 hash 性能比使用 generic 的低很多,为什么现在反过来了? 

全文完 !

展开阅读全文
加载中
点击加入讨论🔥(87) 发布并加入讨论🔥
打赏
87 评论
93 收藏
44
分享
返回顶部
顶部