php+redis在实际项目中HTTP 500: Internal Server Error故障排除


Posted in PHP onFebruary 05, 2017

问题描述
用户量快速增长,访问量在短时间内翻倍,由于前期容量规划做得比较好,硬件资源可以支撑,可是软件系统方面出现了大问题:
40% 的请求都会返回 HTTP 500: Internal Server Error
通过查看日志,发现错误是在 PHP <-> Redis 的连接处理上
调试处理

第1次
刚开始时并没有找到根本原因,只能尝试各种与错误相关的办法,例如:
增加 PHP 连接数,并把超时时间从 500ms 增加到 2.5s
禁止掉 PHP 设置中的 default_socket_timeout
在主机系统中禁止掉 SYN cookies
检查 Redis 和 Webservers 的文件描述符数量
增加主机系统的 mbuffer
调整 TCP backlog 数量
……

尝试了很多方法,但全部无效

第2次
想在预发布环境中重现这个问题,可惜,还是没成功,应为流量不够大,无法复现

第3次
会不会是代码中没有关闭 Redis 连接呢?
正常来讲,PHP在执行结束时会自动关闭资源连接,但老版本中会有内存泄漏的问题,保险起见,把代码都修改一遍,手动关闭连接
结果还是无效

第4次
怀疑目标:phpredis 这个客户端库
做 A/B 测试,替换回 predis 这个库,部署到数据中心中 20% 的用户量上
得益于良好的代码结构,替换工作很快完成
可结果依旧是无效,但也有好的一面,可以证明 phpredis 没问题嘛

第5次
查看了一下 Redis 的版本,是 v2.6,当时最新版本是 v2.8.9
升级 Redis 试一下吧,升完后还是不行
没事儿,要保持乐观,这不顺便把 Redis 版本升为最新的了

第6次
通过查找大量文档,在官方文档中发现了一个调试好方法 Redis Software Watchdog,打开后执行:

$ redis-cli --latency -p 6380 -h 1.2.3.4
min: 0, max: 463, avg: 2.03 (19443 samples)

查看 Redis 日志:

...
[20398] 22 May 09:20:55.351 * 10000 changes in 60 seconds. Saving...
[20398] 22 May 09:20:55.759 * Background saving started by pid 41941
[41941] 22 May 09:22:48.197 * DB saved on disk
[20398] 22 May 09:22:49.321 * Background saving terminated with success
[20398] 22 May 09:25:23.299 * 10000 changes in 60 seconds. Saving...
[20398] 22 May 09:25:23.644 * Background saving started by pid 42027
...

发现了问题:
每隔几分钟就向硬盘保存一次数据,fork 一个后台存储进行为什么需要大概 400ms(通过上面日志的第1条和第2条的时间可以看出来)

到这儿,终于找到问题的根源了,因为 Redis 实例中有大量的数据,导致每次持久化操作 fork 后台进程时非常耗时,并且在他们的业务中经常修改key,又导致了频繁触发持久化,也就经常产生对 Redis 的阻塞

处理办法:使用单独的 slave 来做持久化

这个 slave 不处理真实的流量请求,唯一的作用就是处理持久化,把之前 Redis 实例上的持久化操作转移到这个 slave 上

效果非常明显,问题基本解决,但有的时候还是会报错

第7次
排查可能阻塞 Redis 的慢查询,发现有地方使用了 keys *

因为 Redis 中的数据越来越多,这个命令自然会产生严重阻塞

可以使用 scan 进行替换

第8次
经过前面的调整,问题已经解决,随后的几个月,即使流量在不断增长,也都抗住了

但他们意识到了新的问题:

现在的方式是,来一个请求就创建一个 Redis 连接,执行几个命令,然后再断开连接,在请求量很大时,这个方式产生了严重的性能浪费,一半以上的命令是用来处理连接操作的,这都超过了业务逻辑上的处理,也使 Redis 变慢

解决方法:引入 proxy,他们选择了 twitter 的 twemproxy,只需要在每个 webserver 上安装代理,twemproxy负责与 Redis 实例进行持久连接,这样就大大减少了连接方面的操作

twemproxy还有两个方便的地方:

支持 memcached
可以阻止非常耗时或者危险的命令,例如 keys、flushall
效果自然很完美,再也不用担心之前的连接错误

第9次
通过数据分片来继续优化:

对不同上下文的数据拆分隔离
对相同上下文的数据进行一致性哈希分片
效果:

减少了每台机器上的请求、负载
提升了缓存的可靠性,不担心节点故障

小结
原文作者写的非常好,详细的描述了他们在 Redis 应用上的成长历程,是很值得参考的实践经验
原文地址http://tech.trivago.com/2017/01/25/learn-redis-the-hard-way-in-production

PHP 相关文章推荐
同台服务器使用缓存APC效率高于Memcached的演示代码
Feb 16 PHP
php 模拟get_headers函数的代码示例
Apr 27 PHP
PHP实现阳历到农历转换的类实例
Mar 07 PHP
PHP模拟asp中response类实现方法
Aug 08 PHP
CentOS下搭建PHP环境与WordPress博客程序的全流程总结
May 07 PHP
php 实现一个字符串加密解密的函数实例代码
Nov 01 PHP
php微信公众号开发之现金红包
Apr 16 PHP
swoole_process实现进程池的方法示例
Oct 29 PHP
微信公众平台开发教程⑥ 微信开发集成类的使用图文详解
Apr 10 PHP
Laravel 错误提示本地化的实现
Oct 22 PHP
PhpStorm的使用教程(本地运行PHP+远程开发+快捷键)
Mar 26 PHP
Thinkphp框架使用list_to_tree 实现无限级分类列出所有节点示例
Apr 04 PHP
php实现给二维数组中所有一维数组添加值的方法
Feb 04 #PHP
PHP进制转换实例分析(2,8,16,36,64进制至10进制相互转换)
Feb 04 #PHP
php加密之discuz内容经典加密方式实例详解
Feb 04 #PHP
yii2实现 &quot;上一篇,下一篇&quot; 功能的代码实例
Feb 04 #PHP
PHP正则表达式匹配替换与分割功能实例浅析
Feb 04 #PHP
/etc/php-fpm.d/www.conf 配置注意事项
Feb 04 #PHP
PHP正则替换函数preg_replace()报错:Notice Use of undefined constant的解决方法分析
Feb 04 #PHP
You might like
删除无限级目录与文件代码共享
2006/07/12 PHP
PHP遍历二维数组的代码
2011/04/22 PHP
php实现按文件名搜索文件的远程文件查找器
2014/05/10 PHP
ThinkPHP3.1新特性之查询条件预处理简介
2014/06/19 PHP
PHP Header失效的原因分析及解决方法
2016/11/16 PHP
PHP+AJAX 投票器功能
2017/11/11 PHP
jQuery中eq()方法用法实例
2015/01/05 Javascript
在JavaScript中call()与apply()区别
2016/01/22 Javascript
Node.js利用Net模块实现多人命令行聊天室的方法
2016/12/23 Javascript
js中数组的常用方法小结
2016/12/30 Javascript
vue.js使用v-pre与v-html输出HTML操作示例
2018/07/07 Javascript
Angular5中提取公共组件之radio list的实例代码
2018/07/10 Javascript
对vux点击事件的优化详解
2018/08/28 Javascript
vue+canvas实现炫酷时钟效果的倒计时插件(已发布到npm的vue2插件,开箱即用)
2018/11/05 Javascript
微信小程序中的canvas 文字断行和省略号显示功能的处理方法
2018/11/14 Javascript
jQuery+PHP+Ajax实现动态数字统计展示功能
2019/12/25 jQuery
vue同个按钮控制展开和折叠同个事件操作
2020/07/29 Javascript
JavaScript实现鼠标移入随机变换颜色
2020/11/24 Javascript
[26:21]浴火之凤-TI4世界冠军Newbee战队纪录片
2014/08/07 DOTA
[00:31]2016完美“圣”典风云人物:国士无双宣传片
2016/12/04 DOTA
python paramiko实现ssh远程访问的方法
2013/12/03 Python
Python使用Pycrypto库进行RSA加密的方法详解
2016/06/06 Python
浅谈python 四种数值类型(int,long,float,complex)
2016/06/08 Python
python内置函数:lambda、map、filter简单介绍
2017/11/16 Python
解决Python的str强转int时遇到的问题
2018/04/09 Python
Python3.6实现带有简单界面的有道翻译小程序
2019/04/16 Python
python实现大文件分割与合并
2019/07/22 Python
tensorflow保持每次训练结果一致的简单实现
2020/02/17 Python
使用Python打造一款间谍程序的流程分析
2020/02/21 Python
python实现猜数游戏(保存游戏记录)
2020/06/22 Python
Python用户自定义异常的实现
2020/12/25 Python
Set里的元素是不能重复的,那么用什么方法来区分重复与否呢?
2016/08/18 面试题
学雷锋志愿服务月活动总结
2014/03/09 职场文书
公司承诺书格式范文
2015/04/28 职场文书
活动总结模板大全
2015/05/11 职场文书
python 使用Tensorflow训练BP神经网络实现鸢尾花分类
2021/05/12 Python