數據庫中間件 Sharding-JDBC 源碼分析 —— JDBC實現與讀寫分離

摘要: 原創出處 http://www.iocoder.cn/Sharding-JDBC/jdbc-implement-and-read-write-splitting/ 「芋道源碼」歡迎轉載,保留摘要,謝謝!

本文主要基於 Sharding-JDBC 1.5.0 正式版


關注微信公衆號:【芋道源碼】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源碼分析文章列表

  2. RocketMQ / MyCAT / Sharding-JDBC 中文註釋源碼 GitHub 地址

  3. 您對於源碼的疑問每條留言將得到認真回覆。甚至不知道如何讀源碼也可以請教噢

  4. 新的源碼解析文章實時收到通知。每週更新一篇左右

  5. 認真的源碼交流微信羣。


1. 概述

本文主要分享 JDBC讀寫分離 的實現。爲什麼會把這兩個東西放在一起講呢?客戶端直連數據庫的讀寫分離主要通過獲取讀庫和寫庫的不同連接來實現,和 JDBC Connection 剛好放在一塊。

OK,我們先來看一段 Sharding-JDBC 官方對自己的定義和定位

Sharding-JDBC定位爲輕量級java框架,使用客戶端直連數據庫,以jar包形式提供服務,未使用中間層,無需額外部署,無其他依賴,DBA也無需改變原有的運維方式,可理解爲增強版的JDBC驅動,舊代碼遷移成本幾乎爲零。

可以看出,Sharding-JDBC 通過實現 JDBC規範,對上層提供透明化數據庫分庫分表的訪問。 黑科技?實際我們使用的數據庫連接池也是通過這種方式實現對上層無感知的提供連接池。甚至還可以通過這種方式實現對 Lucene、MongoDB 等等的訪問。

扯遠了,下面來看看 Sharding-JDBC jdbc 包的結構:

  • unsupported:聲明不支持的數據操作方法

  • adapter:適配類,實現和分庫分表無關的方法

  • core:核心類,實現和分庫分表相關的方法

根據 core 包,可以看出分到四種我們超級熟悉的對象

  • Datasource

    -w640


  • Connection

    -w640


  • Statement

    -w640


  • ResultSet

    -w640


實現層級如下:JDBC 接口 <=(繼承)== unsupported抽象類 <=(繼承)== unsupported抽象類 <=(繼承)== core


本文內容順序

  1. unspported 包

  2. adapter 包

  3. 插入流程,分析的類:

  • ShardingDataSource

  • ShardingConnection

  • ShardingPreparedStatement(ShardingStatement 類似,不重複分析)

  • GeneratedKeysResultSet、GeneratedKeysResultSetMetaData

查詢流程,分析的類:
  • ShardingPreparedStatement

  • ShardingResultSet

讀寫分離,分析的類:
  • MasterSlaveDataSource


Sharding-JDBC 正在收集使用公司名單:傳送門
你的登記,會讓更多人蔘與和使用 Sharding-JDBC。傳送門
Sharding-JDBC 也會因此,能夠覆蓋更多的業務場景。傳送門
登記吧,騷年!傳送門

2. unspported 包

unspported 包內的抽象類,聲明不支持操作的數據對象,所有方法都是 thrownewSQLFeatureNotSupportedException() 方式。

public abstract class AbstractUnsupportedGeneratedKeysResultSet extends AbstractUnsupportedOperationResultSet {    @Override    public boolean getBoolean(final int columnIndex) throws SQLException {        throw new SQLFeatureNotSupportedException("getBoolean");    }    // .... 省略其它類似方法}public abstract class AbstractUnsupportedOperationConnection extends WrapperAdapter implements Connection {    @Override    public final CallableStatement prepareCall(final String sql) throws SQLException {        throw new SQLFeatureNotSupportedException("prepareCall");    }   // .... 省略其它類似方法}

3. adapter 包

adapter 包內的抽象類,實現和分庫分表無關的方法。

考慮到第4、5兩小節更容易理解,本小節貼的代碼會相對多

3.1 WrapperAdapter


3.2 AbstractDataSourceAdapter

AbstractDataSourceAdapter,數據源適配類。

直接點擊鏈接查看源碼。

3.3 AbstractConnectionAdapter

AbstractConnectionAdapter,數據庫連接適配類。

3.5 AbstractPreparedStatementAdapter

AbstractPreparedStatementAdapter,預編譯語句對象的適配類。

3.6 AbstractResultSetAdapter

AbstractResultSetAdapter,代理結果集適配器。

4. 插入流程

插入使用分佈式主鍵例子代碼如下:

// 代碼僅僅是例子,生產環境下請注意異常處理和資源關閉String sql = "INSERT INTO t_order(uid, nickname, pid) VALUES (1, '2', ?)";DataSource dataSource = new ShardingDataSource(shardingRule);Connection conn = dataSource.getConnection();PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); // 返回主鍵需要  Statement.RETURN_GENERATED_KEYSps.setLong(1, 100);ps.executeUpdate();ResultSet rs = ps.getGeneratedKeys();if (rs.next()) {    System.out.println("id:" + rs.getLong(1));}

調用 #executeUpdate() 方法,內部過程如下

5. 查詢流程

單純從 core 包裏的 JDBC 實現,查詢流程 #executeQuery()#execute() 基本一致,差別在於執行多結果集歸併

6. 讀寫分離

建議前置閱讀:《官方文檔 —— 讀寫分離》

當你有讀寫分離的需求時,將 ShardingRule 配置對應的數據源 從 ShardingDataSource 替換成 MasterSlaveDataSource。我們來看看 MasterSlaveDataSource 的功能和實現。

支持一主多從的讀寫分離配置,可配合分庫分表使用

同一線程且同一數據庫連接內,如有寫入操作,以後的讀操作均從主庫讀取,用於保證數據一致性。
  • ShardingConnection 獲取到的數據源是 MasterSlaveDataSource 時,調用 MasterSlaveDataSource#getConnection() 方法獲取真實的數據源


  • 通過 #isMasterRoute() 判斷是否讀取主庫,以下三種情況會訪問主庫:




    • 非查詢語句 (DQL)

    • 數據源在當前線程訪問過主庫:通過線程變量 DML_FLAG 實現

    • 強制主庫:程序裏調用 HintManager.getInstance().setMasterRouteOnly() 實現



666. 彩蛋

沒有彩蛋
沒有彩
沒有

下一篇,《分佈式事務(一)之最大努力型》走起。老司機,趕緊上車。

道友,分享一個朋友圈可好?不然交個道姑那~~敏感詞~~你。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章