ShardingSphere-Hint強制路由剖析

1. 什么是強制路由

一種通過在外部業(yè)務(wù)代碼中指定路由配置的一種方式笋颤,在ShardingSphere中叫做Hint祸憋。如果使用Hint指定了強制分片路由虐急,那么SQL將會無視原有的分片邏輯比庄,直接路由至指定的數(shù)據(jù)節(jié)點操作唉工。

2. Hint強制路由使用場景研乒?
  • 數(shù)據(jù)分片操作,如果分片鍵沒有在SQL或數(shù)據(jù)表中(沒有按表中字段分片)淋硝,而是在業(yè)務(wù)邏輯代碼中雹熬;
  • 讀寫分離操作,如果強制在主庫進行某些數(shù)據(jù)操作谣膳;
    基于Hint的強制主庫路由竿报。可以強制路由走主庫查詢實時數(shù)據(jù)继谚,避免主從同步數(shù)據(jù)延遲烈菌。
3. Hint使用步驟

3.1 編寫分庫或分表路由策略,實現(xiàn)HintShardingAlgorithm接口

// 泛型 Long 代表傳入的 參數(shù)類型為 Long
public class MyHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
    @Override
    public Collection<String> doSharding(
            Collection<String> availableTargetNames,
            HintShardingValue<Long> shardingValue) {
        // 添加分庫或分表路由邏輯
        Collection<String> result = new ArrayList<>();
        for (String each : availableTargetNames){ //代表:分片目標(biāo)花履,對哪些數(shù)據(jù)庫芽世、表分片。如果是對分庫路由诡壁,表示ds0济瓢,ds1;
            for (Long value : shardingValue.getValues()){ // 代表:分片值; 可以HintManager設(shè)置多個分片值妹卿,所以是個集合旺矾。
                if(each.endsWith(String.valueOf(value % 2))){ // 分庫路由,只需要模2夺克,指定是路由到ds0庫箕宙,還是ds1庫
                    result.add(each);
                }
            }
        }
        return result;
    }
}

3.2 在配置文件指定分庫或分表策略

#datasource
spring.shardingsphere.datasource.names=ds0,ds1

spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/demo1
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=root

spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3306/demo2
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=root

#hint
spring.shardingsphere.sharding.tables.city.database-strategy.hint.algorithm-class-name=com.demo.hint.MyHintShardingAlgorithm

3.3 在業(yè)務(wù)代碼中執(zhí)行查詢前使用HintManager指定執(zhí)行策略值

@RunWith(SpringRunner.class)
@SpringBootTest(classes = RunBoot.class)
public class TestHintAlgorithm {

    @Resource
    private CityRepository cityRepository;

    @Test
    public void test1(){
        HintManager hintManager = HintManager.getInstance();
        // 只對庫路由,則只需要hintManager.setDatabaseShardingValue操作
        hintManager.setDatabaseShardingValue(1L); //強制路由到ds${xx%2}
        List<City> list = cityRepository.findAll();
        list.forEach(city->{
            System.out.println(city.getId()+" "+city.getName()+" "+city.getProvince());
        });
    }

}
4. HintManager源碼

HintManager主要使用ThreadLocal管理分片鍵信息懊直,進行hint強制路由扒吁。在代碼中向HintManager添加的配置信息只能在當(dāng)前線程內(nèi)有效。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.shardingsphere.api.hint;

import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import java.util.Collection;
import java.util.Collections;

public final class HintManager implements AutoCloseable {
    // 所有的addDatabaseShardingValue室囊、setDatabaseShardingValue只是對當(dāng)前線程生效
    private static final ThreadLocal<HintManager> HINT_MANAGER_HOLDER = new ThreadLocal();
    private final Multimap<String, Comparable<?>> databaseShardingValues = HashMultimap.create(); // 數(shù)據(jù)庫的路由值信息
    private final Multimap<String, Comparable<?>> tableShardingValues = HashMultimap.create(); // 表的路由值信息
    private boolean databaseShardingOnly; // 是否只強制路由數(shù)據(jù)庫
    private boolean masterRouteOnly; // shi是否只強制路由主數(shù)據(jù)庫

    public static HintManager getInstance() {
        Preconditions.checkState(null == HINT_MANAGER_HOLDER.get(), "Hint has previous value, please clear first.");
        HintManager result = new HintManager();
        HINT_MANAGER_HOLDER.set(result);
        return result;
    }
    // set...與add... 的區(qū)別:就是是否只路由 數(shù)據(jù)庫
    public void setDatabaseShardingValue(Comparable<?> value) {
        this.databaseShardingValues.clear();
        this.databaseShardingValues.put("", value);
        this.databaseShardingOnly = true; // **
    }

    public void addDatabaseShardingValue(String logicTable, Comparable<?> value) {
        this.databaseShardingValues.put(logicTable, value);
        this.databaseShardingOnly = false; // **
    }
    // 表的路由信息
    public void addTableShardingValue(String logicTable, Comparable<?> value) {
        this.tableShardingValues.put(logicTable, value);
        this.databaseShardingOnly = false;
    }

    public static Collection<Comparable<?>> getDatabaseShardingValues() {
        return getDatabaseShardingValues("");
    }

    public static Collection<Comparable<?>> getDatabaseShardingValues(String logicTable) {
        return (Collection)(null == HINT_MANAGER_HOLDER.get() ? Collections.emptyList() : ((HintManager)HINT_MANAGER_HOLDER.get()).databaseShardingValues.get(logicTable));
    }

    public static Collection<Comparable<?>> getTableShardingValues(String logicTable) {
        return (Collection)(null == HINT_MANAGER_HOLDER.get() ? Collections.emptyList() : ((HintManager)HINT_MANAGER_HOLDER.get()).tableShardingValues.get(logicTable));
    }

    public static boolean isDatabaseShardingOnly() {
        return null != HINT_MANAGER_HOLDER.get() && ((HintManager)HINT_MANAGER_HOLDER.get()).databaseShardingOnly;
    }

    public void setMasterRouteOnly() {
        this.masterRouteOnly = true;
    }

    public static boolean isMasterRouteOnly() {
        return null != HINT_MANAGER_HOLDER.get() && ((HintManager)HINT_MANAGER_HOLDER.get()).masterRouteOnly;
    }

    public static void clear() {
        HINT_MANAGER_HOLDER.remove();
    }

    public void close() {
        clear();
    }

    private HintManager() {
    }
}

注意:在讀寫分離結(jié)構(gòu)中,為了避免主從同步數(shù)據(jù)延遲及時獲取剛添加或更新的數(shù)據(jù)魁索,可以采用強制路由走主庫查詢實時數(shù)據(jù)融撞,使用hintManager.setMasterRouteOnly設(shè)置主庫路由即可。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末粗蔚,一起剝皮案震驚了整個濱河市尝偎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖致扯,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肤寝,死亡現(xiàn)場離奇詭異,居然都是意外死亡抖僵,警方通過查閱死者的電腦和手機鲤看,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耍群,“玉大人义桂,你說我怎么就攤上這事〉腹福” “怎么了慷吊?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長曹抬。 經(jīng)常有香客問我溉瓶,道長,這世上最難降的妖魔是什么谤民? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任嚷闭,我火速辦了婚禮,結(jié)果婚禮上赖临,老公的妹妹穿的比我還像新娘胞锰。我一直安慰自己,他們只是感情好兢榨,可當(dāng)我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布嗅榕。 她就那樣靜靜地躺著,像睡著了一般吵聪。 火紅的嫁衣襯著肌膚如雪凌那。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天吟逝,我揣著相機與錄音帽蝶,去河邊找鬼。 笑死块攒,一個胖子當(dāng)著我的面吹牛励稳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播囱井,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼驹尼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了庞呕?” 一聲冷哼從身側(cè)響起新翎,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤程帕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后地啰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體愁拭,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年亏吝,在試婚紗的時候發(fā)現(xiàn)自己被綠了岭埠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡顺呕,死狀恐怖枫攀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情株茶,我是刑警寧澤来涨,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站启盛,受9級特大地震影響蹦掐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜僵闯,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一卧抗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鳖粟,春花似錦社裆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至榄攀,卻和暖如春嗜傅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背檩赢。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工吕嘀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人贞瞒。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓偶房,卻偏偏與公主長得像,于是被迫代替她去往敵國和親憔狞。 傳聞我的和親對象是個殘疾皇子蝴悉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,691評論 2 361

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