1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        利用 Sharding-JDBC 解決數(shù)據(jù)庫讀寫分離后,數(shù)據(jù)查詢延時(shí)問題

        共 6220字,需瀏覽 13分鐘

         ·

        2020-08-29 06:04

        點(diǎn)擊上方藍(lán)色“架構(gòu)薈萃”關(guān)注我們,輸入1024,你懂的

        一般熟知 Mysql 數(shù)據(jù)庫的朋友知道,當(dāng)表的數(shù)據(jù)量達(dá)到千萬級時(shí),SQL 查詢會逐漸變的緩慢起來,往往會成為一個系統(tǒng)的瓶頸所在。為了提升程序的性能,除了在表字段建立索引(如主鍵索引、唯一索引、普通索引等)、優(yōu)化程序代碼以及 SQL 語句等常規(guī)手段外,利用數(shù)據(jù)庫主從讀寫分離(Master/Slave)架構(gòu),是一個不錯的選擇。但是在這種分離架構(gòu)中普遍存在一個共性問題:數(shù)據(jù)讀寫一致性問題。

        數(shù)據(jù)讀寫一致性問題
        主從庫同步邏輯
        主庫 Master 負(fù)責(zé)“寫”,會把數(shù)據(jù)庫的 BinLog 日志記錄通過 I/O 線程異步操作同步到從庫(負(fù)責(zé)“讀”),這樣每當(dāng)業(yè)務(wù)系統(tǒng)發(fā)送 select 語句時(shí),會直接路由到從庫去查詢數(shù)據(jù),而不是主庫。

        但是這種同步邏輯有一個比較嚴(yán)重的缺陷:數(shù)據(jù)延時(shí)問題

        我們可以想象一下這樣的場景:

        當(dāng)一段程序在更新完數(shù)據(jù)后,需要立即查詢更新后的數(shù)據(jù),那么真的能查詢到更新后的數(shù)據(jù)嗎?

        答案是:不一定!

        這是因?yàn)橹鲝臄?shù)據(jù)同步時(shí)是異步操作,主從同步期間會存在數(shù)據(jù)延時(shí)問題,平常主庫寫數(shù)據(jù)量比較少的情況下,偶爾會遇到查詢不到數(shù)據(jù)的情況。但是隨著時(shí)間的推移,當(dāng)使用系統(tǒng)的用戶增多時(shí),會發(fā)現(xiàn)這種查詢不到數(shù)據(jù)的情況會變的越來越糟糕。


        Sharding-JDBC
        想必大家并不陌生,Sharding-JDBC 定位為輕量級 Java 框架,在 Java 的 JDBC 層提供的額外服務(wù)。

        它使用客戶端直連數(shù)據(jù)庫,以 jar 包形式提供服務(wù),無需額外部署和依賴,可理解為增強(qiáng)版的 JDBC 驅(qū)動,完全兼容 JDBC 和各種 ORM 框架。

        • 適用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。

        • 支持任何第三方的數(shù)據(jù)庫連接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。

        • 支持任意實(shí)現(xiàn) JDBC 規(guī)范的數(shù)據(jù)庫,目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 標(biāo)準(zhǔn)的數(shù)據(jù)庫。


        讀寫分離特性

        • 提供了一主多從的讀寫分離配置,可獨(dú)立使用,也可配合分庫分表使用。

        • 同個調(diào)用線程,執(zhí)行多條語句,其中一旦發(fā)現(xiàn)有非讀操作,后續(xù)所有讀操作均從主庫讀取。

        • Spring命名空間。

        • 基于Hint的強(qiáng)制主庫路由。


        ShardingSphere-JDBC 官方提供 HintManager 分片鍵值管理器, 通過調(diào)用hintManager.setMasterRouteOnly() 強(qiáng)制路由到主庫查詢,這樣就解決了數(shù)據(jù)延時(shí)問題,無論什么時(shí)候都能夠從主庫 Master 查詢到最新數(shù)據(jù),而不用走從庫查詢。

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

        實(shí)際案例

        核心依賴

        <dependency>   <groupId>io.shardingjdbcgroupId>   <artifactId>sharding-jdbc-coreartifactId>   <version>${sharding-jdbc.version}version>dependency>

        數(shù)據(jù)庫配置

        sharding: jdbc:   data-sources:     mvip:       type: com.alibaba.druid.pool.DruidDataSource       driver-class-name: com.mysql.jdbc.Driver       url: jdbc:mysql://${ha.basedb.mvip.ip}:${ha.basedb.mvip.port}/unicom_portal?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false       username: ${ha.basedb.mvip.username}       password: ${ha.basedb.mvip.password}     svip:       type: com.alibaba.druid.pool.DruidDataSource       driver-class-name: com.mysql.jdbc.Driver       url: jdbc:mysql://${ha.basedb.svip.ip}:${ha.basedb.svip.port}/unicom_portal?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false       username: ${ha.basedb.svip.username}       password: ${ha.basedb.svip.password}   master-slave-rule:     name: ds_ms     master-data-source-name: mvip     slave-data-source-names: svip     load-balance-algorithm-type: round_robin

        數(shù)據(jù)源初始化配置類? ?

        @Data@ConfigurationProperties(prefix = "sharding.jdbc")public class MasterSlaveConfig {    private Map dataSources = new HashMap<>();   private MasterSlaveRuleConfiguration masterSlaveRule;}@ConditionalOnClass(DruidDataSource.class)   @EnableConfigurationProperties(MasterSlaveConfig.class)   @ConditionalOnProperty({           "sharding.jdbc.data-sources.mvip.url",           "sharding.jdbc.master-slave-rule.master-data-source-name"})static class ShardingDruid extends DruidConfig {       @Autowired       private MasterSlaveConfig masterSlaveConfig;       @Bean("masterSlaveDataSource")       public DataSource dataSource() throws SQLException {           masterSlaveConfig.getDataSources().forEach((k, v) -> configDruidParams(v));           Map dataSourceMap = Maps.newHashMap();           dataSourceMap.putAll(masterSlaveConfig.getDataSources());           DataSource dataSource = MasterSlaveDataSourceFactory.createDataSource(dataSourceMap, masterSlaveConfig.getMasterSlaveRule(), Maps.newHashMap());           return dataSource;       }       @Bean       public PlatformTransactionManager txManager(DataSource dataSource) {           return new DataSourceTransactionManager(dataSource);       }       private void configDruidParams(DruidDataSource druidDataSource) {           druidDataSource.setMaxActive(20);           druidDataSource.setInitialSize(1);           // 配置獲取連接等待超時(shí)的時(shí)間           druidDataSource.setMaxWait(10000);           druidDataSource.setMinIdle(1);           // 配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒           druidDataSource.setTimeBetweenEvictionRunsMillis(60000);           // 配置一個連接在池中最小生存的時(shí)間,單位是毫秒 超過這個時(shí)間每次會回收默認(rèn)3個連接           druidDataSource.setMinEvictableIdleTimeMillis(30000);           // 線上配置的mysql斷開閑置連接時(shí)間為1小時(shí),數(shù)據(jù)源配置回收時(shí)間為3分鐘,以最后一次活躍時(shí)間開始算           druidDataSource.setMaxEvictableIdleTimeMillis(180000);           // 連接最大存活時(shí)間,默認(rèn)是-1(不限制物理連接時(shí)間),從創(chuàng)建連接開始計(jì)算,如果超過該時(shí)間,則會被清理           druidDataSource.setPhyTimeoutMillis(15000);           druidDataSource.setValidationQuery("select 1");           druidDataSource.setTestWhileIdle(true);           druidDataSource.setTestOnBorrow(false);           druidDataSource.setTestOnReturn(false);           druidDataSource.setPoolPreparedStatements(true);           druidDataSource.setMaxOpenPreparedStatements(20);           druidDataSource.setUseGlobalDataSourceStat(true);           druidDataSource.setKeepAlive(true);           druidDataSource.setRemoveAbandoned(true);           druidDataSource.setRemoveAbandonedTimeout(180);           try {               druidDataSource.setFilters("stat,slf4j");               List filterList = new ArrayList<>();               filterList.add(wallFilter());               druidDataSource.setProxyFilters(filterList);           } catch (SQLException e) {               e.printStackTrace();           }       }   }

        強(qiáng)制路由到主庫查詢關(guān)鍵代碼:

        public ArticleEntity getWithMasterDB(Long id, String wid) {  HintManager hintManager = HintManager.getInstance() ;  hintManager.setMasterRouteOnly();  ArticleEntity article = baseMapper.queryObject(id, wid);}

        通過強(qiáng)制路由到主庫查詢有個風(fēng)險(xiǎn),對于更新并實(shí)時(shí)查詢業(yè)務(wù)場景比較多,如果都切到主庫查詢,勢必會對主庫服務(wù)器性能造成影響,可能還會影響到主從數(shù)據(jù)同步,所以要根據(jù)實(shí)際業(yè)務(wù)場景評估采用這種方式帶來的系統(tǒng)性能問題。


        另外,如果業(yè)務(wù)層面可以做妥協(xié)的話,盡量減少這種更新并實(shí)時(shí)查詢方式,一種思路是實(shí)時(shí)更新庫,利用 Java Future 特性異步查詢(例如更新后,睡眠1-2秒再查詢),偽代碼如下:

        Callable c1 = new Callable(){  @Override  public String call() throws Exception {    ArticleEntity articleEntity = null    try {?????????Thread.sleep(2000);         articleEntity = articleService.get(id)  ???}?catch?(InterruptedException?e)?{         e.printStackTrace();  ???}    return articleEntity;  }};FutureTask f = new FutureTask(c1);new Thread(f).start();ArticleEntity article = f.get()


        1.?人人都能看懂的 6 種限流實(shí)現(xiàn)方案!

        2.?一個空格引發(fā)的“慘案“

        3.?大型網(wǎng)站架構(gòu)演化發(fā)展歷程

        4.?Java語言“坑爹”排行榜TOP 10

        5. 我是一個Java類(附帶精彩吐槽)

        6. 看完這篇Redis緩存三大問題,保你能和面試官互扯

        7. 程序員必知的 89 個操作系統(tǒng)核心概念

        8. 深入理解 MySQL:快速學(xué)會分析SQL執(zhí)行效率

        9. API 接口設(shè)計(jì)規(guī)范

        10. Spring Boot 面試,一個問題就干趴下了!



        掃碼二維碼關(guān)注我


        ·end·

        —如果本文有幫助,請分享到朋友圈吧—

        我們一起愉快的玩耍!



        你點(diǎn)的每個贊,我都認(rèn)真當(dāng)成了喜歡

        瀏覽 47
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            骚虎网站 | 国产精品人成A片一区二区 | 青青草玖玖爱 | 亚洲欧美另类在线 | 91超碰在线免费观看 | 小舞揉搓难受3d动漫 | 欧美亚洲性爱 | 性爱AV网 | 青青伊人网 | 国产乱妇无码大片在线观看 |