1. 程式人生 > >Mycat中介軟體---MySQL讀寫分離

Mycat中介軟體---MySQL讀寫分離

MySQL主從複製的幾種方案

資料庫讀寫分離對於大型系統或者訪問量很高的網際網路應用來說,是必不可少的一個重要功能。 從資料庫的角度來說,對於大多數應用來說,從集中到分佈,最基本的一個需求不是資料儲存的瓶頸,而是在於計算的瓶頸,即SQL查詢的瓶頸,我們知道,正常情況下,Insert SQL就是幾十個毫秒的時間內寫入完成,而系統中的大多數Select SQL則要幾秒到幾分鐘才能有結果,很多複雜的SQL,其消耗伺服器CPU的能力超強,不亞於死迴圈的威力。在沒有讀寫分離的系統上,很可能高峰時段的一些複雜SQL查詢就導致資料庫伺服器CPU爆表,系統陷入癱瘓,嚴重情況下可能導致資料庫崩潰。因此,從保護資料庫的角度來說,我們應該儘量避免沒有主從複製機制的單節點資料庫。  對於MySQL來說,標準的讀寫分離是主從模式,一個寫節點Master後面跟著多個讀節點,讀節點的數量取決於系統的壓力,通常是1-3個讀節點的配置,如下圖所示: 這裡寫圖片描述

  MySQL支援更多的主從複製的拓撲關係,如下圖所示,但通常我們不會採用雙向主從同步以及環狀的拓撲:  MySQL主從複製的原理如下: 這裡寫圖片描述  第一步是在主庫上記錄二進位制日誌(稍後介紹如何設定)。在每次準備提交事務完成數 據更新前,主庫將資料更新的事件記錄到二進位制日誌中。MySQL會按事務提交的順序 而非每條語句的執行順序來記錄二進位制日誌。在記錄二進位制日誌後,主庫會告訴儲存引 擎可以提交事務了。 下一步,備庫將主庫的二進位制日誌複製到其本地的中繼日誌中。首先,備庫會啟動一個 工作執行緒,稱為I/O執行緒,I/O執行緒跟主庫建立一個普通的客戶端連線,然後在主庫上啟 動一個特殊的二進位制轉儲(binhg dump、執行緒(該執行緒沒有對應的SQL命令),這個二 進位制轉儲執行緒會讀取主庫上二進位制日誌中的事件。它不會對事件進行輪詢。如果該執行緒 追趕上了主庫,它將進入睡眠狀態,直到主庫傳送訊號量通知其有新的事件產生時才會 被喚醒,備庫I/O執行緒會將接收到的事件記錄到中繼日誌中。  備庫的SQL執行緒執行最後一步,該執行緒從中繼日誌中讀取事件並在備庫執行,從而實現 備庫資料的更新。當SQL執行緒追趕上I/O執行緒時,中繼日誌通常已經在系統快取中,所 以中繼日誌的開銷很低。SQL執行緒執行的事件也可以通過配置選項來決定是否寫入其自 己的二進位制日誌中,它對於我們稍後提到的場景非常有用。這種複製架構實現了獲取事件和重放事件的解耦,允許這兩個過程非同步進行。也就是說 I/o執行緒能夠獨立於SQL執行緒之外工作。但這種架構也限制了複製的過程,其中最重要 的一點是在主庫上併發執行的査詢在備庫只能序列化執行,因為只有一個SQL執行緒來重 放中繼日誌中的事件。後面我們將會看到,這是很多工作負載的效能瓶頸所在。雖然有 一些針對該問題的解決方案,但大多數使用者仍然受制於單執行緒。MySQL5.6以後,提供了基於GTID多開啟多執行緒同步複製的方案,即每個庫有一個單獨的(sql thread)  進行同步複製,這將大大改善MySQL主從同步的資料延遲問題,配合Mycat分片,可以更好的將一個超級大表的資料同步的時延降低到最低。此外,用GTID避免了在傳送 binlog 邏輯上依賴檔名和物理偏移量,能夠更好的支援自動容災切換,對運維人員來說應該是一件令人高興的事情,因為傳統的方式裡,你需要找到binlog和POS點,然後change master to指向,而不是很有經驗的運維,往往會將其找錯,造成主從同步複製報錯,在mysql5.6裡,無須再知道binlog和POS點,需要知道master的IP、埠,賬號密碼即可,因為同步複製是自動的,mysql通過內部機制GTID自動找點同步。  即使是併發複製機制、仍然無法避免主從資料庫的資料瞬間不同步的問題,因此又有了一種增強的方案,即galera for mysql、percona-cluster或者mariadb cluster等叢集機制,他們是一種多主同步複製的模式,可以在任意節點上進行讀寫、自動控制成員,自動刪除故障節點、自動加入節點、真正給予行級別的併發複製等強大能力!  下圖是其原理圖,通常是採用3個MySQL節點作為一個Cluster,即提供了3倍的資料庫讀的併發能力.galera for mysql叢集這種方式,是犧牲了資料的寫入速度,以換取最大程度的資料併發訪問能力,類似Mycat裡的全域性表,並且保證了資料同時存在幾個有效的副本,從而具有非常高的可靠性,因此在某種程度上,可以替代Oracle的一些關鍵場景,**目前開源中介軟體中,只有Mycat很完美的支援了galera for mysql叢集模式。 這裡寫圖片描述

MySQL主從複製的幾個問題

MySQL主從複製並不完美,存在著幾個由來已久的問題,首先一個問題是複製方式:

  • 基於SQL語句的複製(statement-based replication, SBR),
  • 基於行的複製(row-based replication, RBR),
  • 混合模式複製(mixed-based replication, MBR)。
  • 基於SQL語句的方式最古老的方式,也是目前預設的複製方式,後來的兩種是MySQL 5以後才出現的複製方式。 RBR 的優點:
  • 任何情況都可以被複制,這對複製來說是最安全可靠的
  • 和其他大多數資料庫系統的複製技術一樣
  • 多數情況下,從伺服器上的表如果有主鍵的話,複製就會快了很多

RBR的缺點:

  • binlog 大了很多
  • 複雜的回滾時 binlog 中會包含大量的資料
  • 主伺服器上執行 UPDATE 語句時,所有發生變化的記錄都會寫到 binlog 中,而 SBR 只會寫一次,這會導致頻繁發生 binlog  的併發寫問題
  • 無法從 binlog 中看到都複製了寫什麼語句

SBR 的優點:

  • 歷史悠久,技術成熟

  • binlog檔案較小

  • binlog中包含了所有資料庫更改資訊,可以據此來稽核資料庫的安全等情況

  • binlog可以用於實時的還原,而不僅僅用於複製

  • 主從版本可以不一樣,從伺服器版本可以比主伺服器版本高

SBR 的缺點:

  • 不是所有的UPDATE語句都能被複制,尤其是包含不確定操作的時候。

  • 複製需要進行全表掃描(WHERE 語句中沒有使用到索引)的 UPDATE 時,需要比 RBR 請求更多的行級鎖

  • 對於一些複雜的語句,在從伺服器上的耗資源情況會更嚴重,而 RBR 模式下,只會對那個發生變化的記錄產生影響

  • 資料表必須幾乎和主伺服器保持一致才行,否則可能會導致複製出錯

  • 執行復雜語句如果出錯的話,會消耗更多資源

選擇哪種方式複製,會影響到複製的效率以及伺服器的損耗,甚以及資料一致性性問題,目前其實沒有很好的客觀手手段去評估一個系統更適合哪種方式的複製,Mycat未來希望能通過智慧調優模組給出更科學的建議。  第二個問題是關於主從同步的監控問題,Mysql有主從同步的狀態資訊,可以通過命令show slave status獲取,除了獲知當前是否主從同步正常工作,另外一個重要指標就是Seconds_Behind_Master,從字面理解,它表示當前MySQL主從資料的同步延遲,單位是秒,但這個指標從DBA的角度並不能簡單的理解為延遲多少秒,感興趣的同學可以自己去研究,但對於應用來說,簡單的認為是主從同步的時間差就可以了,另外,當主從同步停止以後,重新啟動同步,這個數值可能會是幾萬秒,取決於主從同步停止的時間長短,我們可以認為資料此時有很多天沒有同步了,而這個數值越接近零,則說明主從同步延遲最小,我們可以採集這個指標並匯聚曲線圖,來分析我們的資料庫的同步延遲曲線,然後根據此曲線,給出一個合理的閥值,主從同步的時延小於閥值時,我們認為從庫是同步的,此時可以安全的從從庫讀取資料。Mycat未來將支援這種優化,讓應用更加可靠的讀取到預期的從庫資料。

Mycat支援的讀寫分離

1.配置mysql端主從的資料自動同步,mycat不負責任何的資料同步問題。  2.Mycat配置讀寫分離,具體引數參加前面章節。

<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native"> 
<heartbeat>select user()</heartbeat> 
<!-- can have multi write hosts --> 
<writeHost host="hostM1" url="localhost:3306" user="root" password="123456"> 
<!-- can have multi read hosts --> 
<readHost host="hostS1" url="localhost2:3306" user="root" password="123456" weight="1" /> 
</writeHost> </dataHost> 
或者 
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native"> 
<heartbeat>select user()</heartbeat> 
<!-- can have multi write hosts --> 
<writeHost host="hostM1" url="localhost:3306" user="root" password="123456"> </writeHost> 
<writeHost host="hostS1" url="localhost:3307" user="root" password="123456"> 
</writeHost> </dataHost>

以上兩種取模第一種當寫掛了讀不可用,第二種可以繼續使用,事務內部的一切操作都會走寫節點,所以讀操作不要加事務,如果讀延時較大,使用根據主從延時的讀寫分離,或者強制走寫節點: 應用強制走寫:  一個查詢SQL語句以/balance/註解來確定其是走讀節點還是寫節點。 1.6以後添加了強制走讀走寫處理: 強制走從:

/*!mycat:db_type=slave*/ select * from travelrecord
 /*#mycat:db_type=slave*/ select * from travelrecord 
強制走寫: 
/*#mycat:db_type=master*/ select * from travelrecord /*!mycat:db_type=master*/ select * from travelrecord

根據主從延時切換:  1.4開始支援MySQL主從複製狀態繫結的讀寫分離機制,讓讀更加安全可靠,配置如下: MyCAT心跳檢查語句配置為 show slave status ,dataHost 上定義兩個新屬性: switchType=”2” 與 slaveThreshold=”100”,此時意味著開啟MySQL主從複製狀態繫結的讀寫分離與切換機制,Mycat心跳機制通過檢測 show slave status 中的 “Seconds_Behind_Master”, “Slave_IO_Running”, “Slave_SQL_Running” 三個欄位來確定當前主從同步的狀態以及Seconds_Behind_Master主從複製時延, 當Seconds_Behind_Master>slaveThreshold時,讀寫分離篩選器會過濾掉此Slave機器,防止讀到很久之前的舊資料,而當主節點宕機後,切換邏輯會檢查Slave上的Seconds_Behind_Master是否為0,為0時則表示主從同步,可以安全切換,否則不會切換。

<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="2" slaveThreshold="100"> 
<heartbeat>show slave status </heartbeat> 
<!-- can have multi write hosts --> 
<writeHost host="hostM1" url="localhost:3306" user="root" password="123456"> </writeHost> 
<writeHost host="hostS1" url="localhost:3316" user="root" password="123456" /> 
</dataHost>

1.4.1 開始支援MySQL 叢集模式,讓讀更加安全可靠,配置如下: MyCAT心跳檢查語句配置為 show status like ‘wsrep%’ , dataHost 上定義兩個新屬性: switchType=”3” 此時意味著開啟MySQL叢集複製狀態狀態繫結的讀寫分離與切換機制,Mycat心跳機制通過檢測叢集複製時延時,如果延時過大或者叢集出現節點問題不會負載改節點。

 <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="3" >
 <heartbeat> show status like ‘wsrep%’</heartbeat>
  <writeHost host="hostM1" url="localhost:3306" user="root"password="123456"> </writeHost> 
 <writeHosthost="hostS1"url="localhost:3316"user="root"password="123456" ></writeHost> </dataHost>