Java ShardingJDBC實戰演練
一、背景
最近在公司手頭上的項目單表達到瞭五千萬的規模,而且日增長量每天就有10w左右,一個月就有大概300w的數據,這樣一直下去過幾個月以後表的數據很容易就上億瞭,這樣不利於管理以及在大表的情況下,對於表的DDL效率也會相對下降,和幾個同事商量瞭下,於是乎開始做分表的技術優化。
二、優化事項
(1)首先先確定使用場景,當前表的使用場景更多的是根據一個具體的標識值去查詢,范圍查詢的場景頻率相對低下,在這這種情況下考慮想標識值作為分片鍵去進行分表。 具體的算法為:通過標識值通過算法算出具體的時間季度,按季節進行拆分進行拆分,也就是一年
record_delivery_log
4個表record_order_log_202101,record_order_log_202102,record_order_log_202103,record_order_log_202104
拆分前單表數據量為 5000w
拆分後單表的數據量變成1200w,能夠容忍將來4~ 5倍的增長量,符合預期范圍。
(2)調研瞭對應的分庫分表中間件,目前Sharing-jdbc是最主流的中間件,而且社區和文檔較完善,故采用Sharing-jdbc作為分表的中間件。
三、具體實戰
在這裡因為公司項目不好復用的原因,用一個模擬項目來模擬這次改造。
(1)參照sharing-jdbc文檔對項目進行改造
引入sharing-jdbc對應的pom。
<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId> <version>5.0.0-beta</version> </dependency>
對應的配置文件
#端口 server.port=8080 # 數據源ds0 spring.shardingsphere.datasource.name=ds0 # 數據源ds0的配置 spring.shardingsphere.datasource.ds0.type=com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.ds0.driverClassName=com.mysql.cj.jdbc.Driver spring.shardingsphere.datasource.ds0.url=jdbc:mysql://localhost:3306/world1?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2b8 spring.shardingsphere.datasource.ds0.username=root spring.shardingsphere.datasource.ds0.password=123456 # 分片規則,這裡隻分表,所以僅指定表的分片規則 spring.shardingsphere.rules.sharding.tables.record_order_log.actual-data-nodes=ds0.record_order_log_$->{2021..2031}0$->{1..4} # 指定數據庫的分片鍵,隻有一個庫所以還是用分表的分片鍵 spring.shardingsphere.rules.sharding.tables.record_order_log.database-strategy.standard.sharding-column=order_delivery_id spring.shardingsphere.rules.sharding.tables.record_order_log.database-strategy.standard.sharding-algorithm-name=database-inline # 指定分表的分片鍵 spring.shardingsphere.rules.sharding.tables.record_order_log.table-strategy.standard.sharding-column=order_delivery_id spring.shardingsphere.rules.sharding.tables.record_order_log.table-strategy.standard.sharding-algorithm-name=table-inline # Omit t_order_item table rule configuration ... # ... # 分片規則(默認取模) spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.type=INLINE spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.props.algorithm-expression=ds0 spring.shardingsphere.rules.sharding.sharding-algorithms.table-inline.type=CLASS_BASED spring.shardingsphere.rules.sharding.sharding-algorithms.table-inline.props.strategy=STANDARD spring.shardingsphere.rules.sharding.sharding-algorithms.table-inline.props.algorithmClassName=com.cus.shd.sharingjdbc.config.OrderDeliveryIdShardingAlgorithm spring.shardingsphere.props.sql.show=true #mybatis-plus?? mybatis-plus.mapper-locations=classpath:mappers/*.xml mybatis-plus.type-aliases-package=com.cus.shd.sharingjdbc.model mybatis-plus.configuration.map-underscore-to-camel-case=true # sql?? mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl #本地數據庫鏈接,忽略瞭springboot自動加載後失效 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/world1?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2b8 spring.datasource.username=root spring.datasource.password=123456
註意好分表鍵設置時候的表名。
(2)自定義分片鍵策略,根據order_delivery_id按季度進行存儲
package com.cus.shd.sharingjdbc.config; import org.apache.commons.lang.StringUtils; import org.apache.shardingsphere.sharding.api.sharding.ShardingAutoTableAlgorithm; import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue; import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue; import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Collection; /** * @author ASUS * @Description 自定義分片策略 * @Date 2021/11/6 22:20 **/ public class OrderDeliveryIdShardingAlgorithm implements StandardShardingAlgorithm<Long> { @Override public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) { String orderDeliveryId = shardingValue.getValue().toString(); orderDeliveryId = orderDeliveryId.substring(0,orderDeliveryId.length() - 4); // 將時間戳轉為當前時間 LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(Long.valueOf(orderDeliveryId)/1000, 0, ZoneOffset.ofHours(8)); String availableTargetName; int month = localDateTime.getMonthValue(); LocalDateTime nowTime = LocalDateTime.now(); int year = nowTime.getYear(); if(month >= 1 && month < 3){ availableTargetName = "01"; }else if(month >= 3 && month < 6){ availableTargetName = "02"; }else if(month >= 6 && month < 9){ availableTargetName = "03"; }else { availableTargetName = "04"; } if(StringUtils.isEmpty(availableTargetName)){ return null; } return String.format("%s_%s%s",shardingValue.getLogicTableName(),year,availableTargetName); } @Override public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) { return availableTargetNames; } @Override public void init() { } @Override public String getType() { return "ORDER_DELIVERY_ID"; } }
(3)模擬提供兩個接口,一個按id查詢,一個插入接口。(修改的場景暫時沒有,所以不考慮)
新增的時候做瞭模擬插入,能夠根據分片算法將數據存儲到對應的表,達到效果。
查詢同理。
(4)sharing-jdbc 不會自動的進行創建表,所以需在後臺維護一個定時任務,到瞭一定的季度點就要進行建表操作。(需確保生產環境的應用程序對應的數據庫賬號是否有建表權限)
<update id="createNewTable" parameterType="String"> CREATE TABLE ${tableName} SELECT * FROM record_order_log WHERE 1=2 </update>
四、遇到的問題
1、引入sharing-jdbc包的時候報錯瞭。這裡debug到源碼發現是mybatisPlus的自動啟動器(MybatisPlusAutoConfiguration)有指定單一數據源類(spring中數據源不能有多個實現類)的時候才會啟動,因為sharing的引入造成瞭多數據源(多datasource),所以這個就不會啟動瞭,導致瞭實例化mapper的時候報錯瞭。解決方案是在SpringBoot的啟動類的註解加上
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,DruidDataSourceAutoConfigure.class})
忽略掉SpringBoot數據源自動裝配以及Druid數據源的自動裝配,把所有的數據源實例化交給sharing-jdbc
2、部分項目存在歷史遺留的問題,如果是mybatis或者hibernate的情況下,不想徹底引入sharding-jdbc數據源的話,個人覺得可以使用多數據源的形式來進行改造,去擴展需要使用分表的一些數據庫操作,切換對應的sharding數據源進行數據庫操作。具體可以參考switchDataSource目錄下的一些切換數據源的代碼。
3、給自己的疑問
忽略瞭DataSourceAutoConfiguration.class後,sharing-jdbc是如何整合mybatis-plus的?
答:其實也不難,相當於數據源這個對象原本由SpringBoot自帶的數據源自動註入進行註入,現在換成瞭Sharding的自動裝配(ShardingSphereAutoConfiguration)來進行註入,相當於換瞭整個數據源的一套東西,用的也是sharding整套的東西。
所以在改造的時候需要檢查一下是否對舊的項目存在影響。
五、項目源碼地址
cus-sharding-jdbc: sharding-jdbc springboot實戰
到此這篇關於Java ShardingJDBC實戰演練的文章就介紹到這瞭,更多相關Java ShardingJDBC內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 使用sharding-jdbc實現水平分庫+水平分表的示例代碼
- 利用Sharding-Jdbc進行分庫分表的操作代碼
- 透明化Sharding-JDBC數據庫字段加解密方案
- 使用sharding-jdbc實現水平分表的示例代碼
- ShardingSphere jdbc實現分庫分表核心概念詳解