什麼是讀寫分離?
見名思意,我們就可以知道:讀寫分離主要是為了将資料庫的讀寫操作分散到不同的資料庫節點上。 這樣的話,就能夠小幅提升寫性能,大幅提升讀性能。
一般情況下,我們都會選擇一主多從,也就是一台主資料庫負責寫,其他的從資料庫負責讀。主庫和從庫之間會進行資料同步,以保證從庫中資料的準确性。這樣的架構實作起來比較簡單,并且也符合系統的寫少讀多的特點。
讀寫分離會帶來什麼問題?如何解決?
讀寫分離對于提升資料庫的并發非常有效,但是,同時也會引來一個問題:主庫和從庫的資料存在延遲,比如你寫完主庫之後,主庫的資料同步到從庫是需要時間的,這個時間差就導緻了主庫和從庫的資料不一緻性問題。這也就是主從同步延遲。
解決主從同步延遲問題,我還沒有特别好的一種方案(可能是我太菜了,歡迎補充)。你可以根據自己的業務場景,參考一下下面幾種解決辦法。
1.強制将讀請求路由到主庫處理。
既然你從庫的資料過期了,那我就直接從主庫讀取嘛!這種方案雖然會增加主庫的壓力,但是,實作起來比較簡單,也是我了解到的使用最多的一種方式。
比如 Sharding-JDBC 就是采用的這種方案。通過使用 Sharding-JDBC 的 HintManager 分片鍵值管理器,我們可以強制使用主庫。
HintManager hintManager = HintManager.getInstance();
hintManager.setMasterRouteOnly();
// 繼續JDBC操作
對于這種方案,你可以将那些必須擷取最新資料的讀請求都交給主庫處理。
2.延遲讀取。
既然主從同步存在延遲,那我就在延遲之後讀取,比如主從同步延遲 0.5s,那我就 1s 之後再讀取資料,雖然理論可以解決,但是也很扯淡。
不過,如果是這樣設計業務流程就會好很多:對于一些對資料比較敏感的場景,你可以在完成寫請求之後,避免立即進行請求操作。比如你支付成功之後,跳轉到一個支付成功的頁面,當你點選傳回之後才傳回自己的賬戶。
如何實作讀寫分離?
不論是使用哪一種讀寫分離具體的實作方案,想要實作讀寫分離一般包含如下幾步:
1.部署多台資料庫,選擇其中的一台作為主資料庫,其他的一台或者多台作為從資料庫。
2.保證主資料庫和從資料庫之間的資料是實時同步的,這個過程也就是我們常說的主從複制。
3.系統将寫請求交給主資料庫處理,讀請求交給從資料庫處理。
落實到項目本身的話,常用的方式有兩種:
1.代理方式
我們可以在應用和資料中間加了一個代理層。應用程式所有的資料請求都交給代理層處理,代理層負責分離讀寫請求,将它們路由到對應的資料庫中。
提供類似功能的中間件有 MySQL Router(官方)、Atlas(基于 MySQL Proxy)、Maxscale、MyCat。
2.元件方式
在這種方式中,我們可以通過引入第三方元件來幫助我們讀寫請求。
這也是我比較推薦的一種方式。這種方式目前在各種網際網路公司中用的最多的,相關的實際的案例也非常多。如果你要采用這種方式的話,推薦使用 sharding-jdbc ,直接引入 jar 包即可使用,非常友善。同時,也節省了很多運維的成本。
主從複制原理
MySQL binlog(binary log 即二進制日志檔案) 主要記錄了 MySQL 資料庫中資料的所有變化(資料庫執行的所有 DDL 和 DML 語句)。是以,我們根據主庫的 MySQL binlog 日志就能夠将主庫的資料同步到從庫中。

1.主庫将資料庫中資料的變化寫入到 binlog
2.從庫連接配接主庫
3.從庫會建立一個 I/O 線程向主庫請求更新的 binlog
4.主庫會建立一個 binlog dump 線程來發送 binlog ,從庫中的 I/O 線程負責接收
5.從庫的 I/O 線程将接收的 binlog 寫入到 relay log 中。
6.從庫的 SQL 線程讀取 relay log 同步資料本地(也就是再執行一遍 SQL )。
一般看到 binlog 就要想到主從複制。當然,除了主從複制之外,binlog 還能幫助我們實作資料恢複。
🌕 簡單總結一下:
MySQL 主從複制是依賴于 binlog 。另外,常見的一些同步 MySQL 資料到其他資料源的工具(比如 canal)的底層一般也是依賴 binlog 。