12. sharding-jdbc源碼之強制路由

阿飛Javaer歉秫,轉(zhuǎn)載請注明原創(chuàng)出處榆纽,謝謝夺英!

源碼分析

位于sharding-jdbc-core模塊下的包com.dangdang.ddframe.rdb.sharding.hint中皿伺,核心類HintManagerHolder的部分源碼如下:

/**
 * Hint manager holder.
 * <p>Use thread-local to manage hint.</p>
 * @author zhangliang
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class HintManagerHolder {
    
    // hint特性保存數(shù)據(jù)的核心變量缭召,即保存一個HintManager類型對象到ThreadLocal中
    private static final ThreadLocal<HintManager> HINT_MANAGER_HOLDER = new ThreadLocal<>();
    
    /**
     * Set hint manager.
     * @param hintManager hint manager instance
     */
    public static void setHintManager(final HintManager hintManager) {
        Preconditions.checkState(null == HINT_MANAGER_HOLDER.get(), "HintManagerHolder has previous value, please clear first.");
        HINT_MANAGER_HOLDER.set(hintManager);
    }
    
    public static boolean isUseShardingHint() {
        // 判斷當前線程中是否使用了sharding hint--即HintManager中的shardingHint為true
        return null != HINT_MANAGER_HOLDER.get() && HINT_MANAGER_HOLDER.get().isShardingHint();
    }
    
    public static Optional<ShardingValue<?>> getDatabaseShardingValue(final ShardingKey shardingKey) {
        // 如果使用了sharding hint栈顷,那么從ThreadLocal中取數(shù)據(jù)庫的sharding值
        return isUseShardingHint() ? Optional.<ShardingValue<?>>fromNullable(HINT_MANAGER_HOLDER.get().getDatabaseShardingValue(shardingKey)) : Optional.<ShardingValue<?>>absent();
    }
    
    public static Optional<ShardingValue<?>> getTableShardingValue(final ShardingKey shardingKey) {
        // 如果使用了sharding hint逆日,那么從ThreadLocal中取表的sharding值
        return isUseShardingHint() ? Optional.<ShardingValue<?>>fromNullable(HINT_MANAGER_HOLDER.get().getTableShardingValue(shardingKey)) : Optional.<ShardingValue<?>>absent();
    }
    
    public static boolean isMasterRouteOnly() {
        // 是否強制路由主庫--sharding-jdbc的特性之一:強制路由
        return null != HINT_MANAGER_HOLDER.get() && HINT_MANAGER_HOLDER.get().isMasterRouteOnly();
    }
    
    public static boolean isDatabaseShardingOnly() {
        // 是否只是數(shù)據(jù)庫sharding
        return null != HINT_MANAGER_HOLDER.get() && HINT_MANAGER_HOLDER.get().isDatabaseShardingOnly();
    }
    
    /**
     * Clear hint manager for current thread-local.
     */
    public static void clear() {
        // ThreadLocal用完需要清理
        HINT_MANAGER_HOLDER.remove();
    }
}

ThreadLocal中管理的HintManager定義如下:

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class HintManager implements AutoCloseable {
    
    // 數(shù)據(jù)庫強制路由的值
    private final Map<ShardingKey, ShardingValue<?>> databaseShardingValues = new HashMap<>();
    
    // 表強制路由的值
    private final Map<ShardingKey, ShardingValue<?>> tableShardingValues = new HashMap<>();
    
    // 即是否使用了強制路由特性
    @Getter
    private boolean shardingHint;
    
    // 是否強制路由到主數(shù)據(jù)庫
    @Getter
    private boolean masterRouteOnly;
       
    @Getter
    private boolean databaseShardingOnly;

    ... ...
    
    @Override
    public void close() {
        HintManagerHolder.clear();
    }
}

sharding值保存在ThreadLocal中,所以需要在操作結(jié)束時調(diào)用HintManager.close()來清除ThreadLocal中的內(nèi)容萄凤。HintManager實現(xiàn)了AutoCloseable接口室抽,推薦使用try with resource(JDK7新特性,參考Java 7中的Try-with-resources)自動關(guān)閉清理ThreadLocl線程中的數(shù)據(jù)蛙卤。

如何使用

分析了sharding-jdbc的強制路由實現(xiàn)的源碼狠半,接下來說說如何使用這一niubility特性,假定數(shù)據(jù)源定義如下:

private static ShardingDataSource getShardingDataSource() throws SQLException {
    DataSourceRule dataSourceRule = new DataSourceRule(createDataSourceMap());
    TableRule orderTableRule = TableRule
            .builder("t_order")
            .actualTables(Arrays.asList("t_order_0", "t_order_1"))
            .dataSourceRule(dataSourceRule)
            .build();
    TableRule orderItemTableRule = TableRule
            .builder("t_order_item")
            .actualTables(Arrays.asList("t_order_item_0", "t_order_item_1"))
            .dataSourceRule(dataSourceRule)
            .build();
    ShardingRule shardingRule = ShardingRule.builder()
            .dataSourceRule(dataSourceRule)
            .tableRules(Arrays.asList(orderTableRule, orderItemTableRule))
            .bindingTableRules(Collections.singletonList(new BindingTableRule(Arrays.asList(orderTableRule, orderItemTableRule))))
            .databaseShardingStrategy(new DatabaseShardingStrategy("user_id", new ModuloDatabaseShardingAlgorithm()))
            .tableShardingStrategy(new TableShardingStrategy("order_id", new ModuloTableShardingAlgorithm())).build();
    return new ShardingDataSource(shardingRule);
}

根據(jù)數(shù)據(jù)源定義可知颤难,數(shù)據(jù)庫的sharding column為user_id神年,表的sharding column為order_id;

1行嗤、強制路由數(shù)據(jù)庫

  • 如何使用
private static void printHintSimpleSelect(final DataSource dataSource) throws SQLException {
    // SQL語句并不涉及任何數(shù)據(jù)庫路由和表路由信息(即where語句中沒有user_id條件和order_id條件)
    String sql = "SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id";
    try (
            HintManager hintManager = HintManager.getInstance();
            Connection conn = dataSource.getConnection();
            PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
        // 強制路由:數(shù)據(jù)庫路由sharding column即user_id的值為10
        hintManager.addDatabaseShardingValue("t_order", "user_id", 10);
        try (ResultSet rs = preparedStatement.executeQuery()) {
            //todo do something
        }
    }
}

由于指定了強制路由數(shù)據(jù)庫的值user_id=10已日,所以只會輸出ds_jdbc_0這個庫中符合條件的數(shù)據(jù)。而ds_jdbc_1會被過濾栅屏;

  • 實現(xiàn)原理
private Collection<String> routeDataSources(final TableRule tableRule) {
    // 首先得到數(shù)據(jù)庫sharding策略飘千,例如:數(shù)據(jù)庫按照列user_id進行sharding
    DatabaseShardingStrategy strategy = shardingRule.getDatabaseShardingStrategy(tableRule);
    // 然后從ThreadLocal中取出sharding的值
    List<ShardingValue<?>> shardingValues = HintManagerHolder.isUseShardingHint() ? getDatabaseShardingValuesFromHint(strategy.getShardingColumns())
            : getShardingValues(strategy.getShardingColumns());
    Collection<String> result = strategy.doStaticSharding(tableRule.getActualDatasourceNames(), shardingValues);
    Preconditions.checkState(!result.isEmpty(), "no database route info");
    return result;
}

2、強制路由表

  • 如何使用
private static void printHintSimpleSelect(final DataSource dataSource) throws SQLException {
    // SQL語句并不涉及任何數(shù)據(jù)庫路由和表路由信息(即where語句中沒有user_id條件和order_id條件)
    String sql = "SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id";
    try (
            HintManager hintManager = HintManager.getInstance();
            Connection conn = dataSource.getConnection();
            PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
        // 強制路由:表路由sharding column即order_id的值為1000
        hintManager.addTableShardingValue("t_order", "order_id", 1000);
        try (ResultSet rs = preparedStatement.executeQuery()) {
            //todo do something
        }
    }
}

由于指定了強制路由表的值order_id=1000栈雳,所以只會輸出所有庫中與t_order_0 匹配的數(shù)據(jù)护奈。而與t_order_1匹配的數(shù)據(jù)會被過濾;

  • 實現(xiàn)原理
private Collection<String> routeTables(final TableRule tableRule, final String routedDataSource) {
    // 首先得到表的sharding策略哥纫,例如:表按照列order_id進行sharding
    TableShardingStrategy strategy = shardingRule.getTableShardingStrategy(tableRule);
    // 然后從ThreadLocal中取出sharding的值
    List<ShardingValue<?>> shardingValues = HintManagerHolder.isUseShardingHint() ? getTableShardingValuesFromHint(strategy.getShardingColumns())
            : getShardingValues(strategy.getShardingColumns());
    Collection<String> result = tableRule.isDynamic() ? strategy.doDynamicSharding(shardingValues) : strategy.doStaticSharding(tableRule.getActualTableNames(routedDataSource), shardingValues);
    Preconditions.checkState(!result.isEmpty(), "no table route info");
    return result;
}

3霉旗、強制路由主庫

  • 如何使用
HintManager hintManager = HintManager.getInstance();
hintManager.setMasterRouteOnly();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蛀骇,隨后出現(xiàn)的幾起案子厌秒,更是在濱河造成了極大的恐慌,老刑警劉巖擅憔,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸵闪,死亡現(xiàn)場離奇詭異,居然都是意外死亡暑诸,警方通過查閱死者的電腦和手機蚌讼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來个榕,“玉大人啦逆,你說我怎么就攤上這事〉崖澹” “怎么了夏志?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我沟蔑,道長湿诊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任瘦材,我火速辦了婚禮厅须,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘食棕。我一直安慰自己朗和,他們只是感情好,可當我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布簿晓。 她就那樣靜靜地躺著眶拉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪憔儿。 梳的紋絲不亂的頭發(fā)上忆植,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天,我揣著相機與錄音谒臼,去河邊找鬼朝刊。 笑死,一個胖子當著我的面吹牛蜈缤,可吹牛的內(nèi)容都是我干的拾氓。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼底哥,長吁一口氣:“原來是場噩夢啊……” “哼咙鞍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起叠艳,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤奶陈,失蹤者是張志新(化名)和其女友劉穎易阳,沒想到半個月后附较,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡潦俺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年拒课,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片事示。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡早像,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肖爵,到底是詐尸還是另有隱情卢鹦,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布劝堪,位于F島的核電站冀自,受9級特大地震影響揉稚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜熬粗,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一搀玖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧驻呐,春花似錦灌诅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至答渔,卻和暖如春关带,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背沼撕。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工宋雏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人务豺。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓磨总,卻偏偏與公主長得像,于是被迫代替她去往敵國和親笼沥。 傳聞我的和親對象是個殘疾皇子蚪燕,可洞房花燭夜當晚...
    茶點故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內(nèi)容