1. 程式人生 > >資料庫是否採用讀寫分離方案注意事項

資料庫是否採用讀寫分離方案注意事項

我們怎麼決定,是採用讀寫分離的架構,還是採用sharding的架構?

               總體來講,DBA團隊prefer sharding機制,而不是嚴重依賴於replication based read/write split;
                對於現有的讀寫分離應用,要進行梳理;
                新的讀寫分離的方案,要麼經過架構評審委員會評審,要麼經過開發總監和DBA總監確認; 

讀寫分離的好處

  1. 在相對簡單的付出下(只需要做讀寫分離,相對於sharding 而言,開發簡單很多),可以解決系統的scalability的問題;
  2. 對於寫的高可用相對要求低,對讀的高可用要求/讀的qps 要求非常高(比如使用者登陸,移動的配置型別資訊)可以很容易的實現系統擴充套件新問題
  3. 很容易實現讀庫的99.999%的高可用
  4. 可以某種程度上,降低大查詢/報表類應用,對線上系統的壓力;(比如wms/tms之類,不過不鼓勵這種做法

讀寫分離的壞處:

  1. 應用開發需要清楚知道,什麼時候讀主庫,什麼時候讀從庫;如果主從延遲,導致異常單,應該作為bug處理;
  2. 對於應用開發,某種程度上,對開發的要求更高:不能寫大job(從庫一定delay),不能寫大SQL(從庫也很容易delay);
  3.  需要處理從庫delay 的exception;何時回源主庫;大部分是不能/不應該回源的;集中回源很容易壓死主庫;
  4. 容易讓dev abuse系統;不做過多優化;
  5. 降低主庫的寫容量,當TPS超過6k,從庫就會延遲逐步增加,而主庫的TPS其實可以去到1.5w甚至2w

總體判斷的guideline:

  1. 讀寫比率,必須在10:1 以上(建議20:1以上),才有可能考慮讀寫分離;不然原則上,不同意讀寫分離,不管如何還是要求避免延遲,比如:防止延遲的時候突發failover,那要麼拋棄延遲的資料(如果硬體故障,大事務產生的大量binlog來不及傳送,必然丟資料),要麼等延遲過去拉長了故障恢復時間
  2. 讀寫分離的資料庫,必須在讀庫前面,有LVS,來虛擬化讀庫的IP;原則上,10:1的讀寫分離,讀庫的壓力會大大於寫庫;也需要多個數據庫伺服器來支撐;也需要LVS來掩蓋和解決後端資料庫伺服器的維護問題(基本上讀庫可以邏輯上always online) 
  3.  讀寫分離的應用,程式要能夠handle延遲的情況,
    必須能夠容忍讀庫delay 3~5s; 如果不能容忍,請不要連線從庫;
  4.  讀寫分離的主庫,在系統設計支撐的容量,3年內,寫庫應該不需要sharding 拆分;不然,就是白白引入先讀寫分離,然後在主庫sharding的複雜度;
  5. 讀寫分離的應用,不能因為讀寫分離了,就可以濫用系統;在主庫的大事務,和在從庫的大SQL,都會導致slave 的較大規模的delay; 
  6. 讀寫分離的應用,對於一個transaction裡面的或者有強一致性依賴的,不能一下子去從庫,一下子去主庫;一個transaction 裡面的資訊,必須都是consistent的;屬於ms級別的;主從複製必然有這個delay的;
  7. 原則上,讀寫分離的資料庫,儘量不要讓主庫過於大(2個TB);
  8. 讀寫分離,不應該作為報表的標準解決方案;報表型別的應用(後端系統,比如vis, tps, ods, tms, wms等較多)原則上,應該考慮通過hadoop 來解決MySQL天然不適合報表型別的應用;線上系統和報表系統,天然應該分離;
  9. 讀寫分離應用: 對於cache側有可能回源的應用, 可以考慮選擇讀寫分離, 所有的讀回源必須到 LVS vip

做了分庫分表的資料庫,不做讀寫分離

原因:
(1)從庫是用於容災HA的,如果承擔了流量壓力,那麼談不上HA了,當從庫掛了,從庫的流量到了主庫,主庫可能承擔不了從庫的壓力一起掛了,那麼從庫一掛,就大家都掛
(2)如果從庫要再做HA,就大大增加了硬體成本的投入以及維護成本,比如最低配的1主2從,你有10個分庫就多10臺從庫以及負載均衡裝置
  (3)  目前機房機櫃是稀缺資源,就算有錢買機器,也不一定能拿到機櫃資源

過去的例子,讀寫分離之後,由於應用設計的問題,帶來的問題

  1. 訂單/Coupon的問題;一個交易裡面,一會兒讀從庫,一會兒讀主庫;在部分有幾秒或者十幾秒delay的場景下,發生異常;
    1. 訂單例子
      業務場景:使用者訂單支付成功第三方支付平臺回撥流程中,pay域呼叫訂單介面更新訂單支付狀態,主庫更新成功從庫同步存在延遲,pay域進一步呼叫審單介面進行稽核(其中稽核資料包含支付狀態的判斷),讀取從庫訂單資料支付狀態仍處於未支付狀態,此時訂單審單流程異常。
      1. 原始訂單資料<order_sn,pay_status> = <15032483118418,0> (pay_status 0:未支付,1:支付成功)
      2. 訂單支付成功後,訂單主庫資料<15032483118418,1> 從庫資料<15032483118418,0>
      3. 審單時讀取從庫訂單資料<15032483118418,0>,判斷訂單支付狀態時出現異常
    2. coupon例子
      業務場景:coupon.api在大促前為了降低主庫壓力,應文湧和超哥要求,在變更餘額時判斷餘額金額的查詢由主庫遷到從庫,並做了資料庫主從一致性判斷,當資料庫主從不一致時,不能變更會員賬戶餘額。但是訂單退款其他操作是沒有判斷資料庫主從一致性的,所以導致當退款操作時,資料庫主從不同步時其他金額退款成功,禮品卡唯品卡賬戶退款是失敗的。這段時間資料庫主從一致性不穩定,導致出現異常單。

      解決方案:
      在訂單退款的時候,禮品卡唯品卡賬戶從主庫來獲取。

 常見的slave delay的原因:

  1. 大job,一個update 幾萬條几十萬條記錄的;
  2. 一個update好多條記錄(100+),而且是全表掃描型別的,沒有合適的where 條件可以走索引的;因為我們是基於mixed的複製,每一條都要跑一邊這個全表掃描/低效的索引掃描;這個問題在slave會放大;現在我們統一基於row模式,該問題不存在。
  3. 表沒有主鍵,導致從庫複製每一條記錄的DML都會做全表掃描
  4. slave 大SQL,比如頻繁的大量的複雜join,耗盡磁碟的io能力或者耗光CPU資源;slave delay;
  5. DBA維護操作,比如大表的online DDL,或者dba 晚上的歸檔的job; --應該禁止;我們online ddl要求控制速度;歸檔也要控制速度和併發;麻煩的是兩個同時kick off的時候;
  6. 網路或者系統問題;比如跨機房網路,同機房網路不穩定,系統不穩定(機器故障);
  7. MySQL bug; 
  8. 頻繁對Text欄位的表的讀寫,會把IO資源耗光,哪怕是flash卡
  9. 一些框架,查詢也會發起事務,如set autocommit=0,又不關閉連線,到釋出需要做ddl變更時,就會導致表鎖延遲

 有讀寫分離的系統(判斷標準:QPS最高是否超過10)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~以下為草稿~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   什麼樣的讀寫比率,和讀寫絕對值,我們決定採用 讀寫分離架構?讀寫分離,我們在業務開發層面讓他們aware,還是我們開發proxy中介軟體,隱藏讀寫分離對於業務的複雜度?
   我們規劃主庫多少的tps,在接下來可以預見的將來(3年,或者100x的流量增幅)的成長下,可以採用讀寫分離架構?
                   讀寫分離的好處是什麼:
                                                      在讀寫比率很懸殊的情況下,做讀寫分離,讀通過LVS-MySQL的模式,可以做到幾乎always online;而且後端系統維護,我們可以對業務透明;業務不會下線;
                                                       後端系統可以平滑擴容;對前端無感知;
                   壞處是什麼:
                                        必須寫的量足夠小;不然容易一個batch操作導致slave跟不上,或者大促情況下,寫的絕對值增加導致從庫可能的跟不上的情況;
                                      資料庫相對比較小;如果讀寫分離的資料庫很大,那麼會有每個都是幾個TB的讀庫;

程式碼邏輯需要容錯性,因為master-slave複製存在延遲的可能性
1、核心的讀在mysql master上,如需要讀取資料後判斷是否更新狀態,避免因為slave延遲導致判斷失誤
2、非核心的讀,都在mysql slave上,如使用者自發的查詢,1)查詢自己拿到多少獎勵;2)查詢自己的錢包餘額;3)查詢自己的會員資訊等

所以,一個transaction,必須不能拆分;

多個transaction,或者業務上,不會再10,100ms內發生的兩個query,就可以讀寫分離查閱從庫;