碼農翻身——Redis:MySQL算老幾?
前言:上一篇《MySQL:快取算什麼東西?》裡挖了一個坑,也有很多人說沒看過癮,今天接著寫,把坑填上,不過得把視角換一下,讓Redis上臺發言。
我知道MySQL看我不順眼,不就是他的好基友Tomcat不怎麼搭理他了嗎? 這能怪我? 誰讓他那麼慢?
張大胖把我Redis安排到這個系統中來,那就是為了提升系統的響應速度,我把資料都暫時放到了記憶體中,每當Tomcat需要的時候直接拿走就是了,都不用聯絡MySQL。只有我這裡沒有資料的時候Tomcat才會給MySQL說一句:“哥們,把這個SQL執行一下啊,把資料告訴我!”
MySQL不死心,不斷使壞,總想著把我給幹掉,恢復他昔日的榮耀和地位。可歷史的車輪滾滾向前,想逆潮流而動,無異於螳臂擋車啊!
有時候我真想把我快取中的資料刪除,讓高併發的訪問都壓到他那裡去, 累死他! 可一想到自己的職業道德,尤其是張大胖那可憐樣,還是忍了吧。
這一天中午,Tomcat發現流量有些異常,之前大部分的資料我都可以處理,這一次大量的請求在我Redis這裡竟然獲取不到資料! Tomcat被迫向MySQL求援:“哥們,這兒有一個SQL啊, 這兒還有一個, 又來一個......”
MySQL剛開始非常高興,滿心歡喜地去執行,可是他很快就發現事情不對, 執行完這些SQL,在資料庫中也查不到資料。他不滿地對Tomcat說:“兄弟,你這是在折騰我嗎? 你看看你這個SQL中where ID = xxxx, 這些ID在資料庫都不存在嘛。”
Tomcat頭也不擡:“又來一個SQL, 還有一個......”
讓我比較佩服的是, MySQL還是比較職業的,儘管有怨氣,他還是不折不扣地執行,很快他就累到了。
整個系統慢如蝸牛,連正常的請求也處理不了。
張大胖趕緊介入,經過一番調查,他發現很多請求故意去查詢那些一定不存在的資料,快取中肯定沒有,於是請求一定會發到MySQL去執行,在流量大時,MySQL就掛掉了。
換句話說:在黑客的精心算計之下,我這個快取成了擺設,快取被穿透了!
張大胖把此事定性為黑客攻擊。
這一次,MySQL終於意識到了我的價值,他出了一個主意:“Redis同學,你把那些不存在的key和對應的空值也快取下來不就行了?下次訪問,就直接返回一個null給這些黑客,別再來找我了。”
我一聽就知道這是個餿主意:“這些key在你那裡都不存在,還讓我快取,那不就是要浪費我的空間嗎? 張大胖給我分配的空間是有限的啊。”
“你不是可以設定資料的有效期嘛,比如過3分鐘就過期,刪除它,空間不就騰出來了。”
“那在這三分鐘內,如果這個key對應的資料真的被加入到了你MySQL當中,那豈不就不一致了?!” 我問道。
MySQL說道:“如果發生這種情況,就可以想辦法清除掉快取中的資料,只是程式邏輯就變得複雜了......”
“退一步來說,假設我快取了他們,那黑客完全可以換一些新的key來攻擊啊!快取中還是沒有,還得去你那裡查,這個辦法不妥!” 我下了結論。
MySQL說:“如果能事先得知這個key是不是在資料庫存在就好了,可是想知道是否存在,那就得把所有的key都放到快取中,Redis,你能受得了嗎?”
我當然受不了。
Tomcat眼前一亮:“你們聽說過布隆過濾器沒有?”
我說:“當然知道了,這是個神奇的資料結構,只需要極少的空間就可以判斷一個元素是不是在一個集合之內,這正好是我們所需要的場景啊:判斷key是否存在。”
(碼農翻身注:布隆過濾器大家可以參考相關資料,這裡不再展開。)
Tomcat說:“對,比如我們可以把所有的使用者ID建立一個布隆過濾器,這樣當那些黑客的請求過來以後,先用這個過濾器攔截一下,如果黑客要訪問的使用者ID不在這個過濾器中,我們就直接把他踢出去了。”
MySQL也是經驗豐富:“可是這個Bloom Filter有誤報啊,即使某個使用者ID不在集合中,他也可能報告說在集合中。這個時候Tomcat就認為這是一個合法的使用者ID,就去Redis中查,不存在,然後到我這裡查,還是不存在。”
我說:“哎呀,一定的誤報也是允許的,沒有完美的事情,總要付出代價不是?”
大家都表示同意。
黑客的攻擊的威脅解除了,日子又恢復了平靜,MySQL意識到了我的價值,也不再嘮嘮叨叨了。
我這個快取的容量是有限的,不可能無限制地增加,所以張大胖新增到快取的資料有一個有效期,過了有效期,我就會把他刪除,騰出空間,讓別的資料使用。
如果是普通的快取資料失效,那就罷了,大不了從資料庫中再去一次就是了。
可是這一次,有個超級熱門的資料失效了,Tomcat組成的叢集中有無數的執行緒都問我要資料,當我告訴他們這個資料已經失效以後,他們扭頭便轉向MySQL,瘋狂地發出SQL語句,問MySQL要資料。
MySQL傻眼了,這麼多的執行緒,每個要發出的SQL都是相同的,可是又不得不執行。
MySQL又一次累倒了,我想他再次體會到了我的重要性。
他對Tomcat說道:“兄弟,給我發這麼多的一模一樣的SQL,你想累死我啊! 你就不能控制一下?只讓一個執行緒發查詢過來,讓其他的等待一下? 那個執行緒取到資料以後,其他執行緒就可以從快取取了!”
Tomcat覺得很有道理,可是現在系統中有多個Tomcat,每個都是平等的,怎麼去選出那個唯一的執行緒呢?
如果是在同一個JVM中還好辦,輕輕鬆鬆用一把程序內的鎖搞定, 可是這分散式的Tomcat,每個都是一個JVM,每個都是一個程序, 怎麼搞?
我說:“這很簡單,我Redis這裡可以提供一個分散式的鎖,誰獲得了這把鎖,誰就可以訪問資料庫。”
MySQL佩服地說:“老弟真是不錯,我服了你了,以後你一定要儘可能的把流量都給擋住,別往我這裡發了,實在是太可怕了!”
Tomcat補充到:“是啊,這Redis快取太重要了!”
突然間他注意到了我還只有一臺機器: “你現在怎麼還是單臺機器? 一個例項? 萬一掛了怎麼辦? 一定得像我一樣,搞叢集,提高可用性啊!”
MySQL說:“啊? 這多嚇人,從今天開始,我將時時刻刻為你祈禱,上帝保佑,你千萬別掛掉。”
與此同時,張大胖開始著手Redis叢集了......
相關閱讀:
轉自碼農翻身微信公眾號文章