<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 6220字,需瀏覽 13分鐘

           ·

          2020-08-29 06:04

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

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

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

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

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

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

          答案是:不一定!

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


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

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

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

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

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


          讀寫分離特性

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

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

          • Spring命名空間。

          • 基于Hint的強制主庫路由。


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

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

          實際案例

          核心依賴

          <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);           // 配置獲取連接等待超時的時間           druidDataSource.setMaxWait(10000);           druidDataSource.setMinIdle(1);           // 配置間隔多久才進行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒           druidDataSource.setTimeBetweenEvictionRunsMillis(60000);           // 配置一個連接在池中最小生存的時間,單位是毫秒 超過這個時間每次會回收默認3個連接           druidDataSource.setMinEvictableIdleTimeMillis(30000);           // 線上配置的mysql斷開閑置連接時間為1小時,數(shù)據(jù)源配置回收時間為3分鐘,以最后一次活躍時間開始算           druidDataSource.setMaxEvictableIdleTimeMillis(180000);           // 連接最大存活時間,默認是-1(不限制物理連接時間),從創(chuàng)建連接開始計算,如果超過該時間,則會被清理           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();           }       }   }

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

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

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


          另外,如果業(yè)務層面可以做妥協(xié)的話,盡量減少這種更新并實時查詢方式,一種思路是實時更新庫,利用 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 種限流實現(xiàn)方案!

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

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

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

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

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

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

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

          9. API 接口設計規(guī)范

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



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


          ·end·

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

          我們一起愉快的玩耍!



          你點的每個贊,我都認真當成了喜歡
          、
          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品福利在线播放 | jizz少妇| 天堂av网在线 | 伊人激情网| 亚洲无码手机在线观看 |