MySQL:硬盤在24 * 7工作中罷工了,我該怎么辦?
雖然他們不承認(rèn), 但我還是這個系統(tǒng)的核心, 因為我保存著這個系統(tǒng)最最重要的東西:數(shù)據(jù)。
為了能讓Tomcat他們訪問, 我提供了幾十個數(shù)據(jù)庫連接——不能提供更多了,因為每個連接都要耗費(fèi)我不少資源。
這些天Tomcat他們實在不像話,數(shù)據(jù)庫讀寫的請求像大海的波濤一樣洶涌澎湃,不斷向我襲來。
996是別想了, 24*7才是殘酷人生。
我沒辦法, 只好拼命地壓榨硬盤,看著他的磁頭在光滑的盤片上滑來滑去,尋找磁道,定位扇區(qū),讀取數(shù)據(jù)。這小伙子挺不錯的, 任勞任怨,但是就是太慢,居然比內(nèi)存慢幾千倍。
很快,連硬盤也招架不住了,他對我說:“MySQL大哥,再這樣下去我就要壞掉了。”
果然,沒過幾天,硬盤病倒了,系統(tǒng)崩潰了。
讀寫分離
第二天我一覺醒來,就發(fā)現(xiàn)系統(tǒng)重啟了,但是有點不對勁,這Tomcat發(fā)來的SQL怎么這么少啊!還都是些Insert, Update, Delete !
硬盤對我說:“你還不知道吧,昨天晚上我們的主人張大胖做了個數(shù)據(jù)庫的讀寫分離!”
“讀寫分離?”
“是啊, 張大胖統(tǒng)計了一下, 我們讀和寫的比例大概是20:1, 非常適合讀寫分離,簡單來說,就是建立多個數(shù)據(jù)庫,你是主庫,主要負(fù)責(zé)寫,還有兩個從庫,主要負(fù)責(zé)讀。這樣我們就沒有多少壓力了。”
“我這里存了這么多數(shù)據(jù), 怎么復(fù)制給另外兩個小弟呢?” 我問道。
“這你不用擔(dān)心,張大胖昨天已經(jīng)給你做了一個快照,他把快照已經(jīng)復(fù)制到了那兩個小弟那里。接下來你只需要把今天早上產(chǎn)生的新的數(shù)據(jù)發(fā)過去就行了。”
基于SQL語句的復(fù)制
正在這個時候,那個叫旺財?shù)男〉芙o我打招呼了: “大哥,你把你那里的執(zhí)行過的Insert, Update, Delete這樣的SQL語句都記錄下來,然后發(fā)給我和小強(qiáng),我們倆要這些SQL在我們自己的數(shù)據(jù)庫上'重放'一下!”
我看了一下自己的配置,果然如此,我只需要把SQL語句發(fā)過去就OK了。
有了兩個小弟的承接讀操作,我的工作大大減輕,又可以和硬盤喝茶聊天了。
可是沒多久,Tomcat氣沖沖地來質(zhì)問我:“你們怎么搞的,數(shù)據(jù)出現(xiàn)不一致了,Order表, rand_num那一列!”
這是怎么回事? 我可是把所有的SQL語句都發(fā)給旺財和小強(qiáng)執(zhí)行了啊,怎么會不一致?
我們?nèi)齻€不敢怠慢, 趕緊翻看最近執(zhí)行的SQL, 尤其是更新Order表, rand_num列相關(guān)的。
終于發(fā)現(xiàn)了罪魁禍?zhǔn)?,就是這個函數(shù): RAND() , 它會返回一個隨機(jī)數(shù), 經(jīng)過處理后,更新到rand_num這一列。
在不同的數(shù)據(jù)庫執(zhí)行,這個函數(shù)返回的值也就不同,這就會導(dǎo)致我們的數(shù)據(jù)不一致了。
我感到非常羞愧,因為數(shù)據(jù)的一致性是我們數(shù)據(jù)庫家族最引以為豪的特性。 在單機(jī)的時候,我們自己就可以通過事務(wù)來保證了。 但是一旦有多個數(shù)據(jù)庫,形成了分布式的環(huán)境,想讓大家都保持一致,怎么會這么麻煩?
我們只好請張大胖手工把數(shù)據(jù)改成一致的, 然后再想新的辦法。
基于行的復(fù)制
小強(qiáng)說道:“大哥,我提議一個新方法,以后你別記錄SQL了,你只記錄SQL的所影響的行和相關(guān)的值,然后把這些日志發(fā)給我們,例如:
對于Insert, 記錄下所有列的新值。
對于Delete,記錄下到底是哪一行被刪除(用主鍵來標(biāo)識)
對于Update,記錄下哪一行被更新(用主鍵來標(biāo)識),以及被更新的列和新值
有了這些日志,我們就可以清楚地知道你那邊到底發(fā)生了什么變化,我們把這些日志應(yīng)用到我們的數(shù)據(jù)庫上就可以了!”
鑒于上一次的教訓(xùn),這次我們仔細(xì)分析各種例外情況,確保沒有問題才正式采用。
我,旺財和小強(qiáng)通力合作,新的復(fù)制方式工作得很好。直到有一天我們遇到了一個Update語句:
- update xxx set flag = 0;
這個語句一下子更新了幾十萬條數(shù)據(jù)。 在之前使用基于SQL的復(fù)制時,記錄下這一條語句就行了。 用現(xiàn)在的方式,得記錄幾十萬條數(shù)據(jù),這太要命了!
怎么辦? 退回到原來的“基于SQL的復(fù)制”,肯定不行!
要不默認(rèn)用SQL復(fù)制? 如果SQL執(zhí)行結(jié)果“不確定”,例如有RAND()函數(shù)調(diào)用,那我們就使用語句復(fù)制。
這是一種混合的模式,雖然麻煩,但也只能如此了。
數(shù)據(jù)延遲
深更半夜Tomcat又來找我:“有個用戶在咱們發(fā)了一個帖子,我在你這里做了Insert 操作,然后用戶刷新頁面的時候,我從旺財那里讀取數(shù)據(jù),卻讀不到! 現(xiàn)在人家來投訴我們了!”
我心想,這家伙也太快了吧, 居然比我復(fù)制數(shù)據(jù)的速度還快。
我又檢查了一下我和旺財之間的復(fù)制通道,由于網(wǎng)絡(luò)原因,確實是有點延遲。
我對Tomcat說:“這是小事情,復(fù)制很快完成,他多刷新幾次肯定就可以了。”
Tomcat怒道:“這是嚴(yán)重的用戶體驗問題,怎么是小事?”
“數(shù)據(jù)復(fù)制延遲多正常啊,反正我們?nèi)齻€能保證最終的一致性!”
Tomcat說:“最終一致性? 在我這里可不行! 我給你們出個主意,我在insert數(shù)據(jù)的時候,你還沒有復(fù)制完成,怎么就給我說已經(jīng)insert成功了? 你必需得等到數(shù)據(jù)復(fù)制完成才能說insert成功!! 你的正確次序應(yīng)該是這樣的。”
旺財一看到這個圖,大驚失色:“萬萬不可, 這樣一來就是同步復(fù)制了,如果網(wǎng)絡(luò)比較慢, 第2.1和第2.2步遲遲不能完成, 那我們大哥就沒法告訴你插入數(shù)據(jù)成功, 用戶連帖子都發(fā)表不了!”
“是啊,這種用戶體驗會更差!” 小強(qiáng)幫腔。
Tomcat說:“我不管,反正是你們的問題!你們數(shù)據(jù)庫得想辦法解決!”
我說:“這個問題啊,本質(zhì)上是數(shù)據(jù)延遲導(dǎo)致的,但是在分布式環(huán)境下這是不可避免的,我們在數(shù)據(jù)庫層面是解決不了的, 你們在應(yīng)用層面多想想辦法吧。”
“能有什么辦法?”
我說: “比如,對于不能容忍延遲的操作,都在我這里(主庫)來讀寫,或者用個什么方法判斷主庫和從庫是不是已經(jīng)一致了。”
“也可以用個取巧的辦法, 讓用戶發(fā)表完帖子后等個幾秒鐘再來刷新......” 旺財補(bǔ)充。
Tomcat嘆了一口氣:“唉,你們這些家伙啊, 只會推卸責(zé)任! 這我可管不了, 我們看看張大胖主人會怎么辦吧!”