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