」MySQL官方驅動「主從分離的神祕面紗(掃盲篇)

假如你往後的工做,須要快速實現MySQL的讀寫分離功能,你必定會想起這篇文章。若是你再次回到這裏,證實你已經迫切須要一個簡單快捷的解決方案了--那就是MySQL官方驅動層實現的讀寫分離,偏小衆,但頗有效。html

JDBC驅動

咱們常用的MySQL驅動jar包,其實默認有很是棒的功能,那就是主從分離和HA。若是你只是須要一個主從分離、failover的功能,不要sharding。一個驅動就夠了,不須要引入什麼中間層。mysql

這個東西就是Replication協議。Mysql JDBC Connector在5.1.X版本以後增長了這些功能,以支持「multi-host」集羣拓撲的訪問範式。這個功能是在驅動層實現的,而既然是驅動層,那就不可避免有一些驅動層的問題(詳見《「分庫分表" ?選型和流程要慎重,不然會失控》sql

咱們日常的jdbc鏈接是這樣數據庫

jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8
複製代碼

而通過協議改造後的jdbc鏈接,長得要長一些、大一些!bash

jdbc:mysql:replication://127.0.0.1:3306,127.0.0.1:3307,127.0.0.1:3308/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=false&loadBalanceStrategy=random
複製代碼

固然,也能夠有ipv6的寫法,更加直觀併發

jdbc:mysql://address=(type=master)(host=master1host),address=(type=master)(host=master2host),address=(type=slave)(host=slave1host)/db
複製代碼

8.0的文檔看這裏: dev.mysql.com/doc/connect… 說明:dom

協議的第一個鏈接,表示主庫Master 後面的一堆鏈接,表示從庫Slave,固然能夠有多個 當你把Master鏈接也放在後面的一堆裏,那麼它也擁有了「讀庫「的屬性了 後面有一堆參數,來控制這全部鏈接,到底要如何相處ide

這樣,所謂的主從分離功能,只要配置好這個鏈接串,就枯木逢春了。高併發

代碼層

首先你要改驅動類,不再是Driver了,而是這個工具

com.mysql.jdbc.ReplicationDriver
複製代碼

這種狀況下 DataSource.getConnection() 獲取的鏈接,其實是ReplicationConnection,這個鏈接是虛擬的,和真實的數據庫鏈接是個1對多的關係,因此記得給每個MySQL都作上相應的機器受權。

那麼如何來區別本次請求是讀是寫呢?靠的實際上是Connection中的readonly屬性,這個屬性是經過請求header中的「readonly」傳遞的。因此若是請求中有寫入操做,這整個的事務就必定是readonly=false的。

對於Spring來講,就可使用@Transactional註解來控制這個屬性了。一個事務不可能跨兩個鏈接,因此是讀是寫,有最高層決定。

如下代碼片斷展現了你應該配置的一些元素,沒錯,就是註解。

public interface UserManager {
    public UserDO get(int id);
    public void insert(UserDO user);
    public void update(int id);
}
複製代碼
@Component
public class UserManagerImpl implements UserManager{
    @Autowired
    private UserDao userDao;
    ...
    @Override
    @Transactional(readOnly = false,propagation = Propagation.REQUIRED)
    public void insert(UserDO user) {
        this.userDao.insert(user);
    }
}
複製代碼

是否是很簡單?等等,別高興太早。

參數

不要以爲是官方驅動,就能夠任性的用。這套jdbc驅動的參數仍是很是豐富的,學習的代價也就高了些。在一些小流量下運行的很好,但在高併發環境下會頻繁發生問題。這裏只挑最重要的說下。

一個虛擬鏈接,對應着一個真正的主庫鏈接和多個從庫鏈接。對於主機的存活和從新上線,咱們要考慮三種狀況:

  • Master都死掉了

  • Slave都死掉了

  • Master、Slave全都死掉了

不管哪一種狀況,MySQL驅動都表現出一些奇怪的行爲,默認參數是很差使的。

你可能覺得驅動也就是管理一下幾個鏈接而已,但狀況比這複雜的多。首先給張圖看下這個複雜的關係。

一、 readFromMasterWhenNoSlaves 當全部的salve死掉後,此參數用來控制主庫是否參與讀。若是從庫的流量很大,配置此參數對主庫有很大風險;但若是你關掉,請求則會快速失敗。 二、 loadBalanceStrategy 策略用來指定從庫的輪詢規則。有輪詢,也有權重,也能夠指定具體的策略實現。當你維護或者遷移某個實例時,先置空流量,這會很是有用。或許,你會給某DB一個預熱的可能。 三、 allowMasterDownConnections 若是主機當機,當鏈接池獲取新的鏈接時,會失敗。但若是打開此參數,則虛擬鏈接只會建立Slave鏈接組,整個鏈接會降級爲只讀,不論你設置了什麼註解。 四、 allowSlavesDownConnections 若是沒有隻讀庫了,是否容許建立新的鏈接。在這種狀況下,此參數開啓,讀操做有很大可能會失敗。 五、 retriesAllDown 當全部的hosts都沒法鏈接時重試的最大次數(依次循環重試),默認爲120。重試次數達到閾值仍然沒法獲取有效連接,將會拋出SQLException。 六、 autoReconnect 實例既然有下線、就有上線。上線之後要可以繼續服務,此參數用來控制斷線狀況下自動重連而不拋出異常。這會破壞事務的完整性,但仍是默認開啓。

然而MySQL驅動提供了更加豐富的參數來控制這個過程,若是你的業務要求比較苛刻,這些參數可能要測個遍纔會放心。 但大多數狀況下,它運行的很好。

管理

僅有配置參數,此協議就算一個半成品而已。所幸,此驅動提供了JMX管理的方式,能夠基於其作一些配置變動之類的功能。市面上並無這種配置管理工具,可能仍是由於它過小衆了。

public abstract void addSlaveHost(String groupFilter, String host) throws SQLException;
 public abstract void removeSlaveHost(String groupFilter, String host) throws SQLException;
 public abstract void promoteSlaveToMaster(String groupFilter, String host) throws SQLException;
 public abstract void removeMasterHost(String groupFilter, String host) throws SQLException;
 public abstract String getMasterHostsList(String group);
 public abstract String getSlaveHostsList(String group);
 public abstract String getRegisteredConnectionGroups();
 public abstract int getActiveMasterHostCount(String group);
 public abstract int getActiveSlaveHostCount(String group);
 public abstract int getSlavePromotionCount(String group);
 public abstract long getTotalLogicalConnectionCount(String group);
 public abstract long getActiveLogicalConnectionCount(String group);
複製代碼

咱們順便提一下阿里的德魯伊數據庫鏈接池。如圖,某些功能,只支持默認的單鏈接,對multi-host支持仍是有限。

結尾

MySQL 5.1.x官方驅動出了這麼個東西之後,其實宣告了不少小公司自研的某些小中間件的死亡。翻來服務,改寫JDBC,不過就是爲了管理個鏈接集合。

本文對象爲專一基礎設施研發的同窗。有人看到的,不過是一堆參數而已;而真正去深刻使用的人,會感到背脊通徹的寒冷。

當它小衆時,你對它不屑一顧。然而當你一旦採用了某種方案,你卻但願全世界都是關於它的描寫。人生歷來就沒那麼幸運,不少坑,還須要本身來踩。 0.

相關文章
相關標籤/搜索