Mysql使用ReplicationDriver驅動實現讀寫分離
- 2019 年 10 月 7 日
- 筆記
資料庫的主從複製環境已經配好,該要解決系統如何實現讀寫分離功能了。Mysql的jdbc驅動提供了一種實現ReplicationDriver。
1 資料庫地址的兩種寫法
參考:https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-url-format.html
因為後續配置可能會用到,先介紹一下mysql url中主機地址的兩種寫法。
最簡單的寫法就是 host:port ,如 192.168.5.128:3306,如果埠是預設的3306,也可以不寫,直接使用 192.168.5.128。
更複雜的寫法是指定屬性,格式為 address=(host=host_or_ip)(port=port)(key1=value1)(key2=value2)…(keyN=valueN),如 address=(host=192.168.5.128)(port=3306)(type=master)。
2 數據池配置
參考:https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-master-slave-replication-connection.html
driverClassName: com.mysql.jdbc.ReplicationDriver
url字元串格式:
jdbc:mysql:replication://[master host][:port],[slave host 1][:port][,[slave host 2][:port]]…[/[database]][?propertyName1=propertyValue1[&propertyName2=propertyValue2]…]
資料庫地址的寫法有兩種,上面是第一種簡單的寫法,ReplicationDriver默認將第一個資料庫伺服器作為Master庫,後續其他資料庫伺服器作為從資料庫。
如果資料庫主從設置了多台資料庫作為Master,則url格式為:
jdbc:mysql:replication://address=(type=master)(host=master1host),address=(type=master)(host=master2host),address=(type=slave)(host=slave1host)/database[?propertyName1=propertyValue1[&propertyName2=propertyValue2]…]
3 ReplicationDriver的調用方法
Mysql驅動使用究竟使用 master還是 slave資料庫,取決於 Connection.getReadOnly()的值。如果值為false,則將命令發送到master資料庫,如果值為true,則將命令發送到slave資料庫。當有多台slave資料庫時,使用輪詢調度(round-robin)演算法選擇某一台slave資料庫。
所以,為了讓mysql驅動能夠準確的將命令發送到master或slave,程式碼需要在獲取到數據連接後,執行命令 Connection.setReadOnly(true) 或 Connection.setReadOnly(false)。
以下是mysql官方提供的示例程式碼
import java.sql.Connection;import java.sql.ResultSet;import java.util.Properties;import com.mysql.jdbc.ReplicationDriver;public class ReplicationDriverDemo { public static void main(String[] args) throws Exception { ReplicationDriver driver = new ReplicationDriver(); Properties props = new Properties(); // 從庫上允許自動重連 props.put("autoReconnect", "true"); // 多台從庫使用負載均衡 props.put("roundRobinLoadBalance", "true"); props.put("user", "foo"); props.put("password", "password"); Connection conn = driver.connect("jdbc:mysql:replication://master,slave1,slave2 /test", props); // 在主庫上讀寫,設置 read-only 為 "false" conn.setReadOnly(false); conn.setAutoCommit(false); conn.createStatement().executeUpdate("UPDATE some_table …."); conn.commit(); // 現在在從庫上執行查詢,驅動會自動選擇一台從庫執行查詢 conn.setReadOnly(true); ResultSet rs = conn.createStatement().executeQuery("SELECT a,b FROM alt_table"); ……. }} |
---|
4 Spring TX實現讀寫分離
項目使用Spring Transaction管理事務,註解Transactional已經幫我們做了封裝,註解屬性 readOnly,預設為 false。
/** * {@code true} if the transaction is read-only. * Defaults to {@code false}. * <p>This just serves as a hint for the actual transaction subsystem; * it will <i>not necessarily</i> cause failure of write access attempts. * A transaction manager which cannot interpret the read-only hint will * <i>not</i> throw an exception when asked for a read-only transaction. * @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly() */boolean readOnly() default false; |
---|
簡單用法
@Transactional(readOnly = true)public List<Enterprise> queryList(EnterpriseCriteria criteria) { return enterpriseDao.queryList(criteria);} |
---|
在現有項目上拉了一個分支,使用VMWare上搭建的MariaDB主從庫進行測試,確實能實現讀寫分離。
5 還需要了解的擴展知識
有空時應該進一步讀一下程式碼,看看以下一些內容:
- Transactional的註解,是怎樣逐漸調用到Connection.setReadOnly()的?
- 多個Transactional註解的函數調用時,readOnly參數是否會每次都調用?