php+redis在實(shí)際項(xiàng)目中HTTP 500: Internal Server Error故障排除
問(wèn)題描述
用戶量快速增長(zhǎng),訪問(wèn)量在短時(shí)間內(nèi)翻倍,由于前期容量規(guī)劃做得比較好,硬件資源可以支撐,可是軟件系統(tǒng)方面出現(xiàn)了大問(wèn)題:
40% 的請(qǐng)求都會(huì)返回 HTTP 500: Internal Server Error
通過(guò)查看日志,發(fā)現(xiàn)錯(cuò)誤是在 PHP <-> Redis 的連接處理上
調(diào)試處理
第1次
剛開始時(shí)并沒(méi)有找到根本原因,只能嘗試各種與錯(cuò)誤相關(guān)的辦法,例如:
增加 PHP 連接數(shù),并把超時(shí)時(shí)間從 500ms 增加到 2.5s
禁止掉 PHP 設(shè)置中的 default_socket_timeout
在主機(jī)系統(tǒng)中禁止掉 SYN cookies
檢查 Redis 和 Webservers 的文件描述符數(shù)量
增加主機(jī)系統(tǒng)的 mbuffer
調(diào)整 TCP backlog 數(shù)量
……
嘗試了很多方法,但全部無(wú)效
第2次
想在預(yù)發(fā)布環(huán)境中重現(xiàn)這個(gè)問(wèn)題,可惜,還是沒(méi)成功,應(yīng)為流量不夠大,無(wú)法復(fù)現(xiàn)
第3次
會(huì)不會(huì)是代碼中沒(méi)有關(guān)閉 Redis 連接呢?
正常來(lái)講,PHP在執(zhí)行結(jié)束時(shí)會(huì)自動(dòng)關(guān)閉資源連接,但老版本中會(huì)有內(nèi)存泄漏的問(wèn)題,保險(xiǎn)起見,把代碼都修改一遍,手動(dòng)關(guān)閉連接
結(jié)果還是無(wú)效
第4次
懷疑目標(biāo):phpredis 這個(gè)客戶端庫(kù)
做 A/B 測(cè)試,替換回 predis 這個(gè)庫(kù),部署到數(shù)據(jù)中心中 20% 的用戶量上
得益于良好的代碼結(jié)構(gòu),替換工作很快完成
可結(jié)果依舊是無(wú)效,但也有好的一面,可以證明 phpredis 沒(méi)問(wèn)題嘛
第5次
查看了一下 Redis 的版本,是 v2.6,當(dāng)時(shí)最新版本是 v2.8.9
升級(jí) Redis 試一下吧,升完后還是不行
沒(méi)事兒,要保持樂(lè)觀,這不順便把 Redis 版本升為最新的了
第6次
通過(guò)查找大量文檔,在官方文檔中發(fā)現(xiàn)了一個(gè)調(diào)試好方法 Redis Software Watchdog,打開后執(zhí)行:
$ 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 ...
發(fā)現(xiàn)了問(wèn)題:
每隔幾分鐘就向硬盤保存一次數(shù)據(jù),fork 一個(gè)后臺(tái)存儲(chǔ)進(jìn)行為什么需要大概 400ms(通過(guò)上面日志的第1條和第2條的時(shí)間可以看出來(lái))
到這兒,終于找到問(wèn)題的根源了,因?yàn)?Redis 實(shí)例中有大量的數(shù)據(jù),導(dǎo)致每次持久化操作 fork 后臺(tái)進(jìn)程時(shí)非常耗時(shí),并且在他們的業(yè)務(wù)中經(jīng)常修改key,又導(dǎo)致了頻繁觸發(fā)持久化,也就經(jīng)常產(chǎn)生對(duì) Redis 的阻塞
處理辦法:使用單獨(dú)的 slave 來(lái)做持久化
這個(gè) slave 不處理真實(shí)的流量請(qǐng)求,唯一的作用就是處理持久化,把之前 Redis 實(shí)例上的持久化操作轉(zhuǎn)移到這個(gè) slave 上
效果非常明顯,問(wèn)題基本解決,但有的時(shí)候還是會(huì)報(bào)錯(cuò)
第7次
排查可能阻塞 Redis 的慢查詢,發(fā)現(xiàn)有地方使用了 keys *
因?yàn)?Redis 中的數(shù)據(jù)越來(lái)越多,這個(gè)命令自然會(huì)產(chǎn)生嚴(yán)重阻塞
可以使用 scan 進(jìn)行替換
第8次
經(jīng)過(guò)前面的調(diào)整,問(wèn)題已經(jīng)解決,隨后的幾個(gè)月,即使流量在不斷增長(zhǎng),也都抗住了
但他們意識(shí)到了新的問(wèn)題:
現(xiàn)在的方式是,來(lái)一個(gè)請(qǐng)求就創(chuàng)建一個(gè) Redis 連接,執(zhí)行幾個(gè)命令,然后再斷開連接,在請(qǐng)求量很大時(shí),這個(gè)方式產(chǎn)生了嚴(yán)重的性能浪費(fèi),一半以上的命令是用來(lái)處理連接操作的,這都超過(guò)了業(yè)務(wù)邏輯上的處理,也使 Redis 變慢
解決方法:引入 proxy,他們選擇了 twitter 的 twemproxy,只需要在每個(gè) webserver 上安裝代理,twemproxy負(fù)責(zé)與 Redis 實(shí)例進(jìn)行持久連接,這樣就大大減少了連接方面的操作
twemproxy還有兩個(gè)方便的地方:
支持 memcached
可以阻止非常耗時(shí)或者危險(xiǎn)的命令,例如 keys、flushall
效果自然很完美,再也不用擔(dān)心之前的連接錯(cuò)誤
第9次
通過(guò)數(shù)據(jù)分片來(lái)繼續(xù)優(yōu)化:
對(duì)不同上下文的數(shù)據(jù)拆分隔離
對(duì)相同上下文的數(shù)據(jù)進(jìn)行一致性哈希分片
效果:
減少了每臺(tái)機(jī)器上的請(qǐng)求、負(fù)載
提升了緩存的可靠性,不擔(dān)心節(jié)點(diǎn)故障
小結(jié)
原文作者寫的非常好,詳細(xì)的描述了他們?cè)?Redis 應(yīng)用上的成長(zhǎng)歷程,是很值得參考的實(shí)踐經(jīng)驗(yàn)
原文地址http://tech.trivago.com/2017/01/25/learn-redis-the-hard-way-in-production
- ThinkPHP5 框架引入 Go AOP,PHP AOP編程項(xiàng)目詳解
- thinkphp5框架前后端分離項(xiàng)目實(shí)現(xiàn)分頁(yè)功能的方法分析
- php項(xiàng)目中類的自動(dòng)加載實(shí)例講解
- docker-compose部署php項(xiàng)目實(shí)例詳解
- PHP如何實(shí)現(xiàn)阿里云短信sdk靈活應(yīng)用在項(xiàng)目中的方法
- Vue 項(xiàng)目中遇到的跨域問(wèn)題及解決方法(后臺(tái)php)
- 在云虛擬主機(jī)部署thinkphp5項(xiàng)目的步驟詳解
- PHP項(xiàng)目多語(yǔ)言配置平臺(tái)實(shí)現(xiàn)過(guò)程解析
相關(guān)文章
比較discuz和ecshop的截取字符串函數(shù)php版
網(wǎng)上看到一篇文章 discuz和ecshop截取字符串的兩個(gè)函數(shù),比較了一下兩個(gè)版本的函數(shù),都各有局限,只能在特定的前提下使用,但是學(xué)習(xí)一下有利于拓寬思路,了解PHP的擴(kuò)展功能2012-09-09
PHP連續(xù)簽到功能實(shí)現(xiàn)方法詳解
這篇文章主要介紹了PHP連續(xù)簽到功能實(shí)現(xiàn)方法,結(jié)合實(shí)例形式詳細(xì)分析了PHP結(jié)合mysql數(shù)據(jù)庫(kù)實(shí)現(xiàn)連續(xù)簽到功能相關(guān)操作技巧,需要的朋友可以參考下2019-12-12
PHP swoole中http_server的配置與使用方法實(shí)例分析
這篇文章主要介紹了PHP swoole中http_server的配置與使用方法,結(jié)合實(shí)例形式分析了swoole中swoole_http_server類的功能及http_server的配置、創(chuàng)建服務(wù)相關(guān)使用技巧,需要的朋友可以參考下2020-03-03
php構(gòu)造方法中析構(gòu)方法在繼承中的表現(xiàn)
這篇文章主要為大家詳細(xì)介紹了php構(gòu)造方法中析構(gòu)方法在繼承中的表現(xiàn),感興趣的小伙伴們可以參考一下2016-04-04
WordPress中查詢文章的循環(huán)Loop結(jié)構(gòu)及用法分析
這篇文章主要介紹了WordPress中查詢文章的循環(huán)Loop結(jié)構(gòu)及用法分析,順帶介紹了提供各種查詢方法的WP_Query類的一些基本情況,需要的朋友可以參考下2015-12-12
php源碼分析之DZX1.5字符串截?cái)嗪瘮?shù)cutstr用法
這篇文章主要介紹了php源碼分析之DZX1.5字符串截?cái)嗪瘮?shù)cutstr用法,實(shí)例分析了DZX1.5中cutstr函數(shù)實(shí)現(xiàn)字符串截取的使用技巧,需要的朋友可以參考下2015-06-06
php正則取img標(biāo)記中任意屬性(正則替換去掉或改變圖片img標(biāo)記中的任意屬性)
因有一項(xiàng)目新聞發(fā)布系統(tǒng),數(shù)據(jù)庫(kù)內(nèi)容字段中存儲(chǔ)的是原圖的路徑(當(dāng)然還有其他文字內(nèi)容啦,內(nèi)容里插圖時(shí),存的是圖片路徑),但前臺(tái)想使用縮略圖,琢磨1小時(shí)余,得到以下結(jié)果,可解決問(wèn)題2013-08-08

