首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring 多資料源動態路由方案:基於 AbstractRoutingDataSource 實現

     編輯:Java 相關知識

業務背景

大型系統常將數據按業務維度分片存儲於多個資料庫實例,提升水平擴展性。此時應用需根據請求上下文(如用戶 ID、租戶 ID)動態決定數據源,避免將選擇邏輯滲透到 DAO 或 Service 層。

數據分片架構 邏輯滲透問題

解決方案:Proxy 模式封裝虛擬 DataSource

引入一個虛擬 DataSource 代理,內部維護多個真實 DataSource,根據客戶端提供的上下文動態路由。Spring 2.x 提供 AbstractRoutingDataSource 作為基礎框架。

Proxy 模式架構

核心實作

public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final Logger log = Logger.getLogger(DynamicDataSource.class);

    @Override
    protected Object determineCurrentLookupKey() {
        // 從 ThreadLocal 獲取當前用戶標識
        String userId = (String) DbContextHolder.getContext();
        // 業務邏輯:根據 userId 計算目標數據源 ID
        Integer dataSourceId = getDataSourceIdByUserId(userId);
        return dataSourceId; // 返回作為 Map key 的查找鍵
    }

    private Integer getDataSourceIdByUserId(String userId) {
        // 實際可為取模、範圍映射、配置表查詢等策略
        return Math.abs(userId.hashCode()) % 3; // 示例:3 個庫取模
    }
}

DbContextHolder 為簡單的 ThreadLocal 持有器,在 Filter 或 Interceptor 中根據請求設置/清除。

Spring 配置範例

<bean id="dataSource" class="com.example.DynamicDataSource">
  <property name="targetDataSources">
    <map key-type="java.lang.Integer">
      <entry key="0" value-ref="dataSource0"/>
      <entry key="1" value-ref="dataSource1"/>
      <entry key="2" value-ref="dataSource2"/>
    </map>
  </property>
  <property name="defaultTargetDataSource" ref="dataSource0"/>
</bean>

<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
  <property name="configLocation" value="classpath:sqlmap/sql-map-config.xml"/>
  <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="UserInfoDAO" class="com.example.dao.impl.UserInfoDAO">
  <property name="sqlMapClient" ref="sqlMapClient"/>
</bean>

關鍵優勢

  • 業務代碼零侵入:DAO 僅依賴單一 dataSource Bean,無感知路由邏輯
  • 靈活擴展:新增庫只需配置 Map,無需修改 Java 代碼
  • 支援任意路由策略:用戶 ID、租戶、地區、讀寫分離等均可在 determineCurrentLookupKey 實現

注意事項

  1. 確保 DbContextHolder 在請求開始時設置、結束時清除,防止線程池複用導致數據源錯亂
  2. 事務邊界內不可切換數據源,需在事務啟動前確定路由鍵
  3. 連接池參數(大小、超時)需根據各庫負載獨立調優
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved