摘要:最近的互聯(lián)網(wǎng)線上事故發(fā)生比較頻繁,年月號順豐發(fā)生了一起線上刪庫事件,在這里就不介紹了。最后的最后,線上操作的任何一條命令,再小心也不為過,因為由于你的一個符號而引起的事故可能是你所承擔(dān)不起的。
摘要: 使用 Redis 的開發(fā)者必看,吸取教訓(xùn)啊!
原文:Redis 的 KEYS 命令引起 RDS 數(shù)據(jù)庫雪崩,RDS 發(fā)生兩次宕機,造成幾百萬的資金損失
作者:陳浩翔
Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。
最近的互聯(lián)網(wǎng)線上事故發(fā)生比較頻繁,2018 年 9 月 19 號順豐發(fā)生了一起線上刪庫事件,在這里就不介紹了。
在這里講述一下最近發(fā)生在我公司的事故,以及如何避免,并且如何處理優(yōu)化。
間接原因還有很多,技術(shù)跟不上業(yè)務(wù)的發(fā)展,由每日百萬量到千萬級是一個大的跨進(jìn),公司對于系統(tǒng)優(yōu)化的處理優(yōu)先級不高,技術(shù)開發(fā)人手的短缺
第一次宕機2018 年 9 月 13 號某個點,公司某服務(wù)化項目的 RDS 實例連接飆升,CPU 升到 100%,拒絕了其他應(yīng)用的所有請求服務(wù)
整個過程如下:
監(jiān)控報警,顯示 RDS 的 CPU 使用率達(dá)到 80%以上,DBA 介入,準(zhǔn)備 KILL 慢 SQL
1 分鐘內(nèi),沒有發(fā)現(xiàn)明顯阻塞的 SQL,CPU 持續(xù)上升到 99%
5 分鐘內(nèi),大量應(yīng)用報警,并且拒絕服務(wù),RDS 的監(jiān)控顯示出現(xiàn)大量慢 SQL,聯(lián)系服務(wù)器數(shù)據(jù)庫提供商進(jìn)行協(xié)助
8 分鐘內(nèi),進(jìn)行數(shù)據(jù)庫主備切換(業(yè)務(wù)會受損,但是也沒辦法,沒有定位到問題)
9 分鐘內(nèi),部分業(yè)務(wù)恢復(fù),但是一些業(yè)務(wù)訂單的回調(diào)消息堆積超過 20w,備庫的 CPU 使用率也持續(xù)上升
15 分鐘內(nèi),備庫 CPU 使用率超過 97%,業(yè)務(wù)再次中斷,進(jìn)行切回主庫,并進(jìn)行限流
20 分鐘內(nèi),關(guān)閉一些次要應(yīng)用的流量入口
25 分鐘內(nèi),主庫 CPU 使用率恢復(fù)正常
30 分鐘內(nèi),逐步開啟關(guān)閉的限流應(yīng)用
35 分鐘內(nèi),所有應(yīng)用恢復(fù)正常
接下來就是與服務(wù)器數(shù)據(jù)庫提供商成立應(yīng)急小組緊急優(yōu)化可能出現(xiàn)的慢 SQL,雖然說可能解決了一些慢 SQL,但此次并沒有定位到具體的問題,也就為幾天后再次發(fā)生宕機事件埋下了伏筆
事故影響
某服務(wù)化項目服務(wù)不可用幾十分鐘,造成訂單數(shù)減少幾十萬筆,損失百萬資金。
原因分析
當(dāng)時是沒有定位到具體的原因的,但是下面的原因也是一部分可能引起宕機的情況。
某服務(wù)化項目的業(yè)務(wù)增速非常快,在高峰期,數(shù)據(jù)庫 QPS 突破 35000,系統(tǒng)處于高負(fù)荷狀態(tài)。
在高峰期如果同時執(zhí)行幾個全表掃描的 SQL,會造成數(shù)據(jù)庫壓力急劇上升,應(yīng)用超時增多,前端應(yīng)用超時,用戶重試,流量飆升,形成了雪崩效應(yīng)。
主要原因在與一些老項目的 SQL 查詢性能較差,并且使用的主庫,對數(shù)據(jù)庫影響較大。數(shù)據(jù)庫 QPS 太高,但是緩存方案因為人手原因一直沒有落地,慢 SQL 的問題處理優(yōu)先級應(yīng)該提升
改進(jìn)方案
針對每個應(yīng)用建一個數(shù)據(jù)庫賬號,嚴(yán)格按照規(guī)范使用
緩存優(yōu)化方案即時落地,慢 SQL 問題優(yōu)先處理,集中處理目前已經(jīng)發(fā)現(xiàn)的慢 SQL(查詢時間超過 1S)
升級數(shù)據(jù)庫配置
遷移非核心業(yè)務(wù)到新的 RDS 實例中去
第二次宕機由于上一次的宕機原因未找到,所以此次的宕機是可以預(yù)見的。
2018 年 9 月 19 號,還是一樣的"配方",還是原來的"味道"。同一個 RDS,CPU 飆升至 100%,接下來就是拒絕服務(wù),宕機。當(dāng)然,有了第一次的經(jīng)驗,直接主從切換,在幾十秒左右就恢復(fù)了所有業(yè)務(wù),但還是嚴(yán)重影響了公司的業(yè)務(wù)和形象。
原因分析
恢復(fù)業(yè)務(wù)后,公司緊急召開了緊急事故研究會議,當(dāng)然,我的級別是參與不了的。公司的高管,高層技術(shù)架構(gòu)、DBA、各個項目的主負(fù)責(zé)人一起進(jìn)行了會議。
在此次會議中,經(jīng)過查看各個項目的日志,后臺的監(jiān)控數(shù)據(jù),發(fā)現(xiàn)在那臺 RDS 數(shù)據(jù)庫 CPU 飆升時,有一臺 Redis 數(shù)據(jù)庫內(nèi)存將近 100%,然后急劇下降。聯(lián)系第一次的宕機情況,也是類似的。
接下來就是聯(lián)系服務(wù)器數(shù)據(jù)庫提供商,將那臺 Redis 最近一周的命令全部調(diào)用出來,最后發(fā)現(xiàn),在那個時間點運行了一條keys *...*命令。公司的一個工程師執(zhí)行 keys 模糊的匹配命令是為了清理沒用的鍵,但是沒有考慮到keys *進(jìn)行模糊匹配引發(fā) Redis 鎖,造成 Redis 鎖住,CPU 飆升,引起了所有調(diào)用鏈路的超時并且卡住,等 Redis 鎖的那幾秒結(jié)束,所有的請求流量全部請求到 RDS 數(shù)據(jù)庫中,使數(shù)據(jù)庫產(chǎn)生了雪崩,使數(shù)據(jù)庫宕機。
改進(jìn)方案
所有線上操作,全部要經(jīng)過運維通過后方可執(zhí)行,運維部門逐步快速收回各項權(quán)限
新增 Redis 實例,進(jìn)行分離
如果有使用類似 keys 正則命令需求,使用 scan 命令代替
總結(jié)該事件中出現(xiàn)的兩次事故,完全是由于人為操作引起的,如果那位工程師,看過 Redis 的開發(fā)規(guī)范,會發(fā)現(xiàn)是建議禁用 keys 命令的。另外,有線上的命令操作,一定要經(jīng)過運維評估后方可進(jìn)行操作,估計那個工程師是老員工吧,有權(quán)限,然后直接就進(jìn)行操作了。
另外,公司的業(yè)務(wù)發(fā)展確實很快,技術(shù)跟不上,這是非常非常危險的,極大的增加了宕機的概率。
在業(yè)務(wù)量不大的情況下,那位工程師的操作是完全沒什么問題的,畢竟并發(fā)也不大,但是現(xiàn)在,隨著公司的發(fā)展,業(yè)務(wù)量的成倍成倍增加,技術(shù)的擴(kuò)展卻沒有隨著增長那么快。
公司的技術(shù)人手不足也是一方面,絕大多數(shù)人都是邊維護(hù)老項目邊做新功能,但是對于項目的重構(gòu)優(yōu)化,人手卻少了很多,項目優(yōu)化的優(yōu)先級不高,這也是很大的一個原因,極有可能出現(xiàn)類似的情況,新服務(wù)化構(gòu)建迫在眉睫。
最后的最后,線上操作的任何一條命令,再小心也不為過,因為由于你的一個符號而引起的事故可能是你所承擔(dān)不起的。
Redis 開發(fā)建議最后附上 Redis 的一些開發(fā)規(guī)范和建議
1. 冷熱數(shù)據(jù)分離,不要將所有數(shù)據(jù)全部都放到 Redis 中
雖然 Redis 支持持久化,但是 Redis 的數(shù)據(jù)存儲全部都是在內(nèi)存中的,成本昂貴。建議根據(jù)業(yè)務(wù)只將高頻熱數(shù)據(jù)存儲到 Redis 中【QPS 大于 5000】,對于低頻冷數(shù)據(jù)可以使用 MySQL/ElasticSearch/MongoDB 等基于磁盤的存儲方式,不僅節(jié)省內(nèi)存成本,而且數(shù)據(jù)量小在操作時速度更快、效率更高!
2. 不同的業(yè)務(wù)數(shù)據(jù)要分開存儲
不要將不相關(guān)的業(yè)務(wù)數(shù)據(jù)都放到一個 Redis 實例中,建議新業(yè)務(wù)申請新的多帶帶實例。因為 Redis 為單線程處理,獨立存儲會減少不同業(yè)務(wù)相互操作的影響,提高請求響應(yīng)速度;同時也避免單個實例內(nèi)存數(shù)據(jù)量膨脹過大,在出現(xiàn)異常情況時可以更快恢復(fù)服務(wù)! 在實際的使用過程中,redis 最大的瓶頸一般是 CPU,由于它是單線程作業(yè)所以很容易跑滿一個邏輯 CPU,可以使用 redis 代理或者是分布式方案來提升 redis 的 CPU 使用率。
3. 存儲的 Key 一定要設(shè)置超時時間
如果應(yīng)用將 Redis 定位為緩存 Cache 使用,對于存放的 Key 一定要設(shè)置超時時間!因為若不設(shè)置,這些 Key 會一直占用內(nèi)存不釋放,造成極大的浪費,而且隨著時間的推移會導(dǎo)致內(nèi)存占用越來越大,直到達(dá)到服務(wù)器內(nèi)存上限!另外 Key 的超時長短要根據(jù)業(yè)務(wù)綜合評估,而不是越長越好!
4. 對于必須要存儲的大文本數(shù)據(jù)一定要壓縮后存儲
對于大文本【+超過 500 字節(jié)】寫入到 Redis 時,一定要壓縮后存儲!大文本數(shù)據(jù)存入 Redis,除了帶來極大的內(nèi)存占用外,在訪問量高時,很容易就會將網(wǎng)卡流量占滿,進(jìn)而造成整個服務(wù)器上的所有服務(wù)不可用,并引發(fā)雪崩效應(yīng),造成各個系統(tǒng)癱瘓!
5. 線上 Redis 禁止使用 Keys 正則匹配操作
Redis 是單線程處理,在線上 KEY 數(shù)量較多時,操作效率極低【時間復(fù)雜度為 O(N)】,該命令一旦執(zhí)行會嚴(yán)重阻塞線上其它命令的正常請求,而且在高 QPS 情況下會直接造成 Redis 服務(wù)崩潰!如果有類似需求,請使用 scan 命令代替!
6. 可靠的消息隊列服務(wù)
Redis List 經(jīng)常被用于消息隊列服務(wù)。假設(shè)消費者程序在從隊列中取出消息后立刻崩潰,但由于該消息已經(jīng)被取出且沒有被正常處理,那么可以認(rèn)為該消息已經(jīng)丟失,由此可能會導(dǎo)致業(yè)務(wù)數(shù)據(jù)丟失,或業(yè)務(wù)狀態(tài)不一致等現(xiàn)象發(fā)生。
為了避免這種情況,Redis 提供了 RPOPLPUSH 命令,消費者程序會原子性的從主消息隊列中取出消息并將其插入到備份隊列中,直到消費者程序完成正常的處理邏輯后再將該消息從備份隊列中刪除。同時還可以提供一個守護(hù)進(jìn)程,當(dāng)發(fā)現(xiàn)備份隊列中的消息過期時,可以重新將其再放回到主消息隊列中,以便其它的消費者程序繼續(xù)處理。
7. 謹(jǐn)慎全量操作 Hash、Set 等集合結(jié)構(gòu)
在使用 HASH 結(jié)構(gòu)存儲對象屬性時,開始只有有限的十幾個 field,往往使用 HGETALL 獲取所有成員,效率也很高,但是隨著業(yè)務(wù)發(fā)展,會將 field 擴(kuò)張到上百個甚至幾百個,此時還使用 HGETALL 會出現(xiàn)效率急劇下降、網(wǎng)卡頻繁打滿等問題【時間復(fù)雜度 O(N)】,此時建議根據(jù)業(yè)務(wù)拆分為多個 Hash 結(jié)構(gòu);或者如果大部分都是獲取所有屬性的操作,可以將所有屬性序列化為一個 STRING 類型存儲!同樣在使用 SMEMBERS 操作 SET 結(jié)構(gòu)類型時也是相同的情況!
8. 根據(jù)業(yè)務(wù)場景合理使用不同的數(shù)據(jù)結(jié)構(gòu)類型
目前 Redis 支持的數(shù)據(jù)庫結(jié)構(gòu)類型較多:字符串(String),哈希(Hash),列表(List),集合(Set),有序集合(Sorted Set), Bitmap, HyperLogLog 和地理空間索引(geospatial)等,需要根據(jù)業(yè)務(wù)場景選擇合適的類型。
常見的如:String 可以用作普通的 K-V、計數(shù)類;Hash 可以用作對象如商品、經(jīng)紀(jì)人等,包含較多屬性的信息;List 可以用作消息隊列、粉絲/關(guān)注列表等;Set 可以用于推薦;Sorted Set 可以用于排行榜等!
9. 命名規(guī)范
雖然說 Redis 支持多個數(shù)據(jù)庫(默認(rèn) 32 個,可以配置更多),但是除了默認(rèn)的 0 號庫以外,其它的都需要通過一個額外請求才能使用。所以用前綴作為命名空間可能會更明智一點。
另外,在使用前綴作為命名空間區(qū)隔不同 key 的時候,最好在程序中使用全局配置來實現(xiàn),直接在代碼里寫前綴的做法要嚴(yán)格避免,這樣可維護(hù)性實在太差了。
如:系統(tǒng)名:業(yè)務(wù)名:業(yè)務(wù)數(shù)據(jù):其他
但是注意,key 的名稱不要過長,盡量清晰明了,容易理解,需要自己衡量
10. 線上禁止使用 monitor 命令
禁止生產(chǎn)環(huán)境使用 monitor 命令,monitor 命令在高并發(fā)條件下,會存在內(nèi)存暴增和影響 Redis 性能的隱患
11. 禁止大 string
核心集群禁用 1mb 的 string 大 key(雖然 redis 支持 512MB 大小的 string),如果 1mb 的 key 每秒重復(fù)寫入 10 次,就會導(dǎo)致寫入網(wǎng)絡(luò) IO 達(dá) 10MB;
12. redis 容量
單實例的內(nèi)存大小不建議過大,建議在 10~20GB 以內(nèi)。redis 實例包含的鍵個數(shù)建議控制在 1kw 內(nèi),單實例鍵個數(shù)過大,可能導(dǎo)致過期鍵的回收不及時。
13. 可靠性
需要定時監(jiān)控 redis 的健康情況:使用各種 redis 健康監(jiān)控工具,實在不行可以定時返回 redis 的 info 信息。客戶端連接盡量使用連接池(長鏈接和自動重連)。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/17996.html
摘要:最近的互聯(lián)網(wǎng)線上事故發(fā)生比較頻繁,年月號順豐發(fā)生了一起線上刪庫事件,在這里就不介紹了。最后的最后,線上操作的任何一條命令,再小心也不為過,因為由于你的一個符號而引起的事故可能是你所承擔(dān)不起的。 摘要: 使用 Redis 的開發(fā)者必看,吸取教訓(xùn)啊! 原文:Redis 的 KEYS 命令引起 RDS 數(shù)據(jù)庫雪崩,RDS 發(fā)生兩次宕機,造成幾百萬的資金損失 作者:陳浩翔 Fundebu...
摘要:最近安全事故瀕發(fā)啊,前幾天發(fā)生了順豐高級運維工程師的刪庫事件,今天又看到了工程師在線執(zhí)行了危險命令導(dǎo)致某公司損失萬。。該公司表示,如再犯類似事故,將直接開除,并表示之后會逐步收回運維部各項權(quán)限。 最近安全事故瀕發(fā)啊,前幾天發(fā)生了《順豐高級運維工程師的刪庫事件》,今天又看到了 PHP 工程師在線執(zhí)行了 Redis 危險命令導(dǎo)致某公司損失 400 萬。。 什么樣的 Redis 命令會有如此...
摘要:給我們帶來便利的同時,使用過程中會存在什么問題呢,本文將簡單加以總結(jié)。避免使用內(nèi)存過大的實例。如果主線程距離上一次的成功超過,為了數(shù)據(jù)安全會阻塞直到后臺線程執(zhí)行完完成。 redis可以滿足很多的應(yīng)用場景,而且因為將所有數(shù)據(jù)都放到內(nèi)存中,所以它的讀寫性能很好,很多公司都在使用redis。redis給我們帶來便利的同時,使用過程中會存在什么問題呢,本文將簡單加以總結(jié)。 阻塞問題 r...
閱讀 1084·2021-10-08 10:04
閱讀 3522·2021-08-05 10:01
閱讀 2278·2019-08-30 11:04
閱讀 1794·2019-08-29 15:29
閱讀 836·2019-08-29 15:12
閱讀 1670·2019-08-26 12:11
閱讀 3114·2019-08-26 11:33
閱讀 1163·2019-08-26 10:23