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 相关文章推荐
PHP UTF8编码内的繁简转换类
Jul 20 PHP
Php获取金书网的书名的实现代码
Jun 11 PHP
PHP字符编码问题之GB2312 VS UTF-8解决方法
Jun 23 PHP
如何用php获取文件名后缀
Jun 09 PHP
CentOS6.5 编译安装lnmp环境
Dec 21 PHP
Mac OS下配置PHP+MySql环境
Feb 25 PHP
是 WordPress 让 PHP 更流行了 而不是框架
Feb 03 PHP
Android App中DrawerLayout抽屉效果的菜单编写实例
Mar 21 PHP
php实现的二叉树遍历算法示例
Jun 15 PHP
thinkphp ajaxfileupload实现异步上传图片的示例
Aug 28 PHP
Laravel框架实现的上传图片到七牛功能详解
Sep 06 PHP
laravel入门知识点整理
Sep 15 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
php获取url字符串截取路径的文件名和扩展名的函数
2010/01/22 PHP
输入值/表单提交参数过滤有效防止sql注入的方法
2013/12/25 PHP
php+highchats生成动态统计图
2014/05/21 PHP
PHP异常处理Exception类
2015/12/11 PHP
PHP与SQL语句常用大全
2016/12/10 PHP
php面向对象基础详解【星际争霸游戏案例】
2020/01/23 PHP
通过js脚本复制网页上的一个表格的不错实现方法
2006/12/29 Javascript
window.location.href中url中数据量太大时的解决方法
2013/12/23 Javascript
jquery中的$(document).ready()使用小结
2014/02/14 Javascript
JavaScript中的无阻塞加载性能优化方案
2014/10/10 Javascript
jQuery模拟物体自由落体运动(附演示与demo源码下载)
2016/01/21 Javascript
详解Javascript继承的实现
2016/03/25 Javascript
微信小程序scroll-view实现横向滚动和上拉加载示例
2017/03/06 Javascript
深入理解JavaScript继承的多种方式和优缺点
2017/05/12 Javascript
基于js原生和ajax的get和post方法以及jsonp的原生写法实例
2017/10/16 Javascript
jQuery读取本地的json文件(实例讲解)
2017/10/31 jQuery
利用JQUERY实现多个AJAX请求等待的实例
2017/12/14 jQuery
node前端开发模板引擎Jade的入门
2018/05/11 Javascript
iview在vue-cli3如何按需加载的方法
2018/10/31 Javascript
vue如何限制只能输入正负数及小数
2019/07/04 Javascript
Openlayers绘制地图标注
2020/09/28 Javascript
如何在vue中使用HTML 5 拖放API
2021/01/14 Vue.js
Python中关键字is与==的区别简述
2014/07/31 Python
Python映射拆分操作符用法实例
2015/05/19 Python
Python3.x对JSON的一些操作示例
2017/09/01 Python
利用python和百度地图API实现数据地图标注的方法
2019/05/13 Python
Html5插件教程之添加浏览器放大镜效果的商品橱窗
2016/01/07 HTML / CSS
亚洲领先的旅游体验市场:Voyagin
2019/11/23 全球购物
美国最大的烧烤架和户外生活用品专业零售商:Barbeques Galore
2021/01/09 全球购物
成龙霸王洗发水广告词
2014/03/14 职场文书
八项规定整改方案
2014/10/01 职场文书
电工实训报告总结
2014/11/05 职场文书
人事任命书范本
2015/09/21 职场文书
文案策划岗位个人自我评价(范文)
2019/08/08 职场文书
Nginx内网单机反向代理的实现
2021/11/07 Servers
apache ftpserver搭建ftp服务器
2022/05/20 Servers