最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

sharding-jdbc讀寫(xiě)分離原理詳細(xì)解析

 更新時(shí)間:2023年12月15日 10:34:00   作者:那個(gè)天真的人  
這篇文章主要介紹了sharding-jdbc讀寫(xiě)分離原理詳細(xì)解析,很多時(shí)候,為了應(yīng)付DB的高并發(fā)讀寫(xiě),我們會(huì)采用讀寫(xiě)分離技術(shù),讀寫(xiě)分離指的是利用數(shù)據(jù)庫(kù)主從技術(shù)(把數(shù)據(jù)復(fù)制到多個(gè)節(jié)點(diǎn)中),分散讀多個(gè)庫(kù)以支持高并發(fā)的讀,需要的朋友可以參考下

前言

很多時(shí)候,為了應(yīng)付DB的高并發(fā)讀寫(xiě),我們會(huì)采用讀寫(xiě)分離技術(shù)。讀寫(xiě)分離指的是利用數(shù)據(jù)庫(kù)主從技術(shù)(把數(shù)據(jù)復(fù)制到多個(gè)節(jié)點(diǎn)中),分散讀多個(gè)庫(kù)以支持高并發(fā)的讀,而寫(xiě)只在master庫(kù)上。DB的主從技術(shù)只負(fù)責(zé)對(duì)數(shù)據(jù)進(jìn)行復(fù)制和同步,而讀寫(xiě)分離技術(shù)需要業(yè)務(wù)應(yīng)用自身去實(shí)現(xiàn)。sharding-jdbc通過(guò)簡(jiǎn)單的開(kāi)發(fā),可以方便的實(shí)現(xiàn)讀寫(xiě)分離技術(shù)。本篇主要介紹其實(shí)現(xiàn)的原理。

sharding-jdbc讀寫(xiě)分離特性說(shuō)明

sharding-jdbc官方對(duì)其支持的讀寫(xiě)分離技術(shù)進(jìn)行了說(shuō)明:

支持項(xiàng) 提供了一主多從的讀寫(xiě)分離配置,可獨(dú)立使用,也可配合分庫(kù)分表使用。 同個(gè)調(diào)用線程,執(zhí)行多條語(yǔ)句,其中一旦發(fā)現(xiàn)有非讀操作,后續(xù)所有讀操作均從主庫(kù)讀取。 Spring命名空間。 基于Hint的強(qiáng)制主庫(kù)路由。

不支持范圍 主庫(kù)和從庫(kù)的數(shù)據(jù)同步。 主庫(kù)和從庫(kù)的數(shù)據(jù)同步延遲導(dǎo)致的數(shù)據(jù)不一致。 主庫(kù)雙寫(xiě)或多寫(xiě)。

簡(jiǎn)單說(shuō)明 sharding-jdbc實(shí)現(xiàn)讀寫(xiě)分離技術(shù)的思路比較簡(jiǎn)潔,不支持類(lèi)似主庫(kù)雙寫(xiě)或多寫(xiě)這樣的特性,但目前來(lái)看,已經(jīng)可以滿足一般的業(yè)務(wù)需求了。

讀寫(xiě)分離實(shí)現(xiàn)demo

庫(kù)和表的設(shè)計(jì)結(jié)構(gòu)如下:

這里寫(xiě)圖片描述

簡(jiǎn)單的java代碼示例:

public final class MasterSlaveMain {
    public static void main(final String[] args) throws SQLException {
        DataSource dataSource = getShardingDataSource();
        printSimpleSelect(dataSource);
    }
    private static void printSimpleSelect(final DataSource dataSource) throws SQLException {
        String sql = "SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.user_id=? AND o.order_id=?";
        try (
                Connection conn = dataSource.getConnection();
                PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
            preparedStatement.setInt(1, 10);
            preparedStatement.setInt(2, 1001);
            try (ResultSet rs = preparedStatement.executeQuery()) {
                while (rs.next()) {
                    System.out.println(rs.getInt(1));
                    System.out.println(rs.getInt(2));
                    System.out.println(rs.getInt(3));
                }
            }
        }
    }
    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))
                .databaseShardingStrategy(new DatabaseShardingStrategy("user_id", new ModuloDatabaseShardingAlgorithm()))
                .tableShardingStrategy(new TableShardingStrategy("order_id", new ModuloTableShardingAlgorithm())).build();
        return new ShardingDataSource(shardingRule);
    }
    private static Map<String, DataSource> createDataSourceMap() throws SQLException {
        Map<String, DataSource> result = new HashMap<>(2, 1);
        Map<String, DataSource> slaveDataSourceMap1 = new HashMap<>(2, 1);
        slaveDataSourceMap1.put("ds_0_slave_0", createDataSource("ds_0_slave_0"));
        slaveDataSourceMap1.put("ds_0_slave_1", createDataSource("ds_0_slave_1"));
        result.put("ds_0", MasterSlaveDataSourceFactory.createDataSource("ds_0", "ds_0_master", createDataSource("ds_0_master"), slaveDataSourceMap1));
        Map<String, DataSource> slaveDataSourceMap2 = new HashMap<>(2, 1);
        slaveDataSourceMap2.put("ds_1_slave_0", createDataSource("ds_1_slave_0"));
        slaveDataSourceMap2.put("ds_1_slave_1", createDataSource("ds_1_slave_1"));
        result.put("ds_1", MasterSlaveDataSourceFactory.createDataSource("ds_1", "ds_1_master", createDataSource("ds_1_master"), slaveDataSourceMap2));
        return result;
    }
    private static DataSource createDataSource(final String dataSourceName) {
        BasicDataSource result = new BasicDataSource();
        result.setDriverClassName(com.mysql.jdbc.Driver.class.getName());
        result.setUrl(String.format("jdbc:mysql://localhost:3306/%s", dataSourceName));
        result.setUsername("root");
        result.setPassword("123456");
        return result;
    }
}

讀寫(xiě)分離實(shí)現(xiàn)原理

一般我們是這樣來(lái)執(zhí)行sql語(yǔ)句的:

Connection conn = dataSource.getConnection();
PreparedStatement preparedStatement = conn.prepareStatement(sql);
preparedStatement.executeQuery();

這是利用原生jdbc操作數(shù)據(jù)庫(kù)查詢語(yǔ)句的一般流程,獲取一個(gè)連接,然后生成Statement,最后再執(zhí)行查詢。那么sharding-jdbc是在哪一塊進(jìn)行擴(kuò)展從而實(shí)現(xiàn)讀寫(xiě)分離的呢?

想一下,想要實(shí)現(xiàn)讀寫(xiě)分離,必然會(huì)涉及到多個(gè)底層的Connection,從而構(gòu)造出不同連接下的Statement語(yǔ)句,而很多第三方軟件,如Spring,為了實(shí)現(xiàn)事務(wù),調(diào)用dataSource.getConnection()之后,在一次請(qǐng)求過(guò)程中,可能就不會(huì)再次調(diào)用getConnection方法了,所以在dataSource.getConnection中做讀寫(xiě)擴(kuò)展是不可取的。為了更好的說(shuō)明問(wèn)題,看下面的例子:

Connection conn = getConnection();
PreparedStatement preparedStatement1 = conn.prepareStatement(sql1);
preparedStatement1.executeQuery();
Connection conn2 = getConnection();
PreparedStatement preparedStatement2 = conn2.prepareStatement(sql2);
preparedStatement2.executeUpdate();

一次請(qǐng)求過(guò)程中,為了實(shí)現(xiàn)事務(wù),一般的做法是當(dāng)線程第一次調(diào)用getConnection方法時(shí),獲取一個(gè)底層連接,然后存儲(chǔ)到ThreadLocal變量中去,下次就直接在ThreadLocal中獲取了。為了實(shí)現(xiàn)一個(gè)事務(wù)中,針對(duì)一個(gè)數(shù)據(jù)源,既可能獲取到主庫(kù)連接,也可能獲取到從庫(kù)連接,還能夠切換,sharding-jdbc在PreparedStatement(實(shí)際上為ShardingPreparedStatement)的executeXX層進(jìn)行了主從庫(kù)的連接處理。

下圖為sharding-jdbc執(zhí)行的部分流程:

這里寫(xiě)圖片描述

sharding-jdbc使用ShardingPreparedStatement來(lái)替代PreparedStatement,在執(zhí)行ShardingPreparedStatement的executeXX方法時(shí),通過(guò)路由計(jì)算,得到PreparedStatementUnit單元列表,然后執(zhí)行后合并結(jié)果返回,而PreparedStatementUnit只不過(guò)封裝了原生的PreparedStatement。讀寫(xiě)分離最關(guān)鍵的地方在上圖標(biāo)綠色的地方,也就是生成PreparedStatement的地方。

在使用SQLEcecutionUnit轉(zhuǎn)換為PreparedStatement的時(shí)候,有一個(gè)重要的步驟就是必須先獲取Connection,源碼如下:

public Connection getConnection(final String dataSourceName, final SQLType sqlType) throws SQLException {
        if (getCachedConnections().containsKey(dataSourceName)) {
            return getCachedConnections().get(dataSourceName);
        }
        DataSource dataSource = shardingContext.getShardingRule().getDataSourceRule().getDataSource(dataSourceName);
        Preconditions.checkState(null != dataSource, "Missing the rule of %s in DataSourceRule", dataSourceName);
        String realDataSourceName;
        if (dataSource instanceof MasterSlaveDataSource) {
            NamedDataSource namedDataSource = ((MasterSlaveDataSource) dataSource).getDataSource(sqlType);
            realDataSourceName = namedDataSource.getName();
            if (getCachedConnections().containsKey(realDataSourceName)) {
                return getCachedConnections().get(realDataSourceName);
            }
            dataSource = namedDataSource.getDataSource();
        } else {
            realDataSourceName = dataSourceName;
        }
        Connection result = dataSource.getConnection();
        getCachedConnections().put(realDataSourceName, result);
        replayMethodsInvocation(result);
        return result;
    }

如果發(fā)現(xiàn)數(shù)據(jù)源對(duì)象為MasterSlaveDataSource類(lèi)型,則會(huì)使用如下方式獲取真正的數(shù)據(jù)源:

public NamedDataSource getDataSource(final SQLType sqlType) {
    if (isMasterRoute(sqlType)) {
        DML_FLAG.set(true);
        return new NamedDataSource(masterDataSourceName, masterDataSource);
    }
    String selectedSourceName = masterSlaveLoadBalanceStrategy.getDataSource(name, masterDataSourceName, new ArrayList<>(slaveDataSources.keySet()));
    DataSource selectedSource = selectedSourceName.equals(masterDataSourceName) ? masterDataSource : slaveDataSources.get(selectedSourceName);
    Preconditions.checkNotNull(selectedSource, "");
    return new NamedDataSource(selectedSourceName, selectedSource);
}
private static boolean isMasterRoute(final SQLType sqlType) {
    return SQLType.DQL != sqlType || DML_FLAG.get() || HintManagerHolder.isMasterRouteOnly();
}

有三種情況會(huì)認(rèn)為一定要走主庫(kù):

1. 不是查詢類(lèi)型的語(yǔ)句,比如更新字段

2. DML_FLAG變量為true的時(shí)候

3. 強(qiáng)制Hint方式走主庫(kù)

當(dāng)執(zhí)行了更新語(yǔ)句的時(shí)候,isMasterRoute()==true,這時(shí)候,Connection為主庫(kù)的連接,并且引擎會(huì)強(qiáng)制設(shè)置DML_FLAG的值為true,這樣一個(gè)請(qǐng)求后續(xù)的所有讀操作都會(huì)走主庫(kù)。 有些時(shí)候,我們想強(qiáng)制走主庫(kù),這時(shí)候在請(qǐng)求最開(kāi)始執(zhí)行Hint操作即可,如下所示:

HintManager hintManager = HintManager.getInstance();
hintManager.setMasterRouteOnly();

在獲取數(shù)據(jù)源的時(shí)候,如果走的是從庫(kù),會(huì)使用從庫(kù)負(fù)載均衡算法類(lèi)進(jìn)行處理,該類(lèi)的實(shí)現(xiàn)比較簡(jiǎn)單,如下所示:

public final class RoundRobinMasterSlaveLoadBalanceStrategy implements MasterSlaveLoadBalanceStrategy {
    private static final ConcurrentHashMap<String, AtomicInteger> COUNT_MAP = new ConcurrentHashMap<>();
    @Override
    public String getDataSource(final String name, final String masterDataSourceName, final List<String> slaveDataSourceNames) {
        AtomicInteger count = COUNT_MAP.containsKey(name) ? COUNT_MAP.get(name) : new AtomicInteger(0);
        COUNT_MAP.putIfAbsent(name, count);
        count.compareAndSet(slaveDataSourceNames.size(), 0);
        return slaveDataSourceNames.get(count.getAndIncrement() % slaveDataSourceNames.size());
    }
}

其實(shí)就是一個(gè)簡(jiǎn)單的輪循機(jī)制進(jìn)行從庫(kù)的負(fù)載均衡。

總結(jié)

sharding-jdbc進(jìn)行主從讀寫(xiě)分離的特性實(shí)現(xiàn)比較簡(jiǎn)潔易懂,對(duì)spring這種上層框架而言是無(wú)感知的,而且由于它是在路由得到SQLExecutionUtil后再處理的,所以使用了讀寫(xiě)分離特性,可以同時(shí)使用分庫(kù)分表。

到此這篇關(guān)于sharding-jdbc讀寫(xiě)分離原理詳細(xì)解析的文章就介紹到這了,更多相關(guān)sharding-jdbc讀寫(xiě)分離內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot中的CSRF攻擊及預(yù)防方法

    SpringBoot中的CSRF攻擊及預(yù)防方法

    CSRF攻擊是一種常見(jiàn)的網(wǎng)絡(luò)攻擊方式,可以通過(guò)欺騙用戶來(lái)執(zhí)行惡意操作,在Spring Boot應(yīng)用程序中,我們可以采取多種措施來(lái)預(yù)防CSRF攻擊,本文將給大家介紹一下CSRF攻擊以及如何預(yù)防攻擊,需要的朋友可以參考下
    2023-07-07
  • IntelliJ IDEA 使用經(jīng)驗(yàn)總結(jié)(推薦)

    IntelliJ IDEA 使用經(jīng)驗(yàn)總結(jié)(推薦)

    這篇文章主要介紹了IntelliJ IDEA 使用經(jīng)驗(yàn)總結(jié),非常不錯(cuò),具有參考價(jià)值,需要的朋友可以參考下
    2018-02-02
  • Spring獲取當(dāng)前類(lèi)在容器中的beanname實(shí)現(xiàn)思路

    Spring獲取當(dāng)前類(lèi)在容器中的beanname實(shí)現(xiàn)思路

    這篇文章主要介紹了Spring獲取當(dāng)前類(lèi)在容器中的beanname,實(shí)現(xiàn)思路只需繼承BeanNameAware接口,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • Java多線程之線程狀態(tài)詳解

    Java多線程之線程狀態(tài)詳解

    這篇文章主要介紹了Java多線程 線程狀態(tài)原理詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2021-09-09
  • Java11?中基于嵌套關(guān)系的訪問(wèn)控制優(yōu)化問(wèn)題

    Java11?中基于嵌套關(guān)系的訪問(wèn)控制優(yōu)化問(wèn)題

    在?Java?語(yǔ)言中,類(lèi)和接口可以相互嵌套,這種組合之間可以不受限制的彼此訪問(wèn),包括訪問(wèn)彼此的構(gòu)造函數(shù)、字段、方法,接下來(lái)通過(guò)本文給大家介紹Java11中基于嵌套關(guān)系的訪問(wèn)控制優(yōu)化問(wèn)題,感興趣的朋友一起看看吧
    2022-01-01
  • Spring Boot如何使用Undertow代替Tomcat

    Spring Boot如何使用Undertow代替Tomcat

    這篇文章主要介紹了Spring Boot如何使用Undertow代替Tomcat,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • springboot項(xiàng)目中controller層與前端的參數(shù)傳遞方式

    springboot項(xiàng)目中controller層與前端的參數(shù)傳遞方式

    這篇文章主要介紹了springboot項(xiàng)目中controller層與前端的參數(shù)傳遞方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • Java基礎(chǔ)之Maven詳解

    Java基礎(chǔ)之Maven詳解

    這篇文章主要介紹了Java基礎(chǔ)之Maven詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • SpringBoot使用RESTful接口詳解

    SpringBoot使用RESTful接口詳解

    RESTful是一種web軟件風(fēng)格,它不是標(biāo)準(zhǔn)也不是協(xié)議,它不一定要采用,只是一種風(fēng)格,它倡導(dǎo)的是一個(gè)資源定位(url)及資源操作的風(fēng)格,這篇文章主要介紹了SpringBoot使用RESTful接口
    2022-10-10
  • java實(shí)現(xiàn)潛艇大戰(zhàn)游戲源碼

    java實(shí)現(xiàn)潛艇大戰(zhàn)游戲源碼

    潛艇大戰(zhàn)游戲相信大家都玩過(guò),是一款非常有趣的小游戲,那么基于代碼是如何實(shí)現(xiàn)的呢?今天小編給大家?guī)?lái)一篇教程幫助大家學(xué)習(xí)java實(shí)現(xiàn)潛艇大戰(zhàn)游戲,感謝的朋友一起看看吧
    2021-06-06

最新評(píng)論

邢台县| 瑞安市| 广元市| 沙洋县| 封开县| 平乡县| 临颍县| 大关县| 张家界市| 黄陵县| 龙里县| 北安市| 吉安市| 曲周县| 遂昌县| 苗栗县| 桑植县| 西乌珠穆沁旗| 红河县| 仁怀市| 晋江市| 女性| 大英县| 金堂县| 台北市| 诏安县| 溧阳市| 临沧市| 宁南县| 涞水县| 界首市| 来凤县| 海晏县| 清苑县| 靖州| 仙居县| 沂水县| 广州市| 池州市| 隆化县| 青浦区|