Java ShardingJDBC實(shí)戰(zhàn)演練
一、背景
最近在公司手頭上的項(xiàng)目單表達(dá)到了五千萬(wàn)的規(guī)模,而且日增長(zhǎng)量每天就有10w左右,一個(gè)月就有大概300w的數(shù)據(jù),這樣一直下去過(guò)幾個(gè)月以后表的數(shù)據(jù)很容易就上億了,這樣不利于管理以及在大表的情況下,對(duì)于表的DDL效率也會(huì)相對(duì)下降,和幾個(gè)同事商量了下,于是乎開始做分表的技術(shù)優(yōu)化。
二、優(yōu)化事項(xiàng)
(1)首先先確定使用場(chǎng)景,當(dāng)前表的使用場(chǎng)景更多的是根據(jù)一個(gè)具體的標(biāo)識(shí)值去查詢,范圍查詢的場(chǎng)景頻率相對(duì)低下,在這這種情況下考慮想標(biāo)識(shí)值作為分片鍵去進(jìn)行分表。 具體的算法為:通過(guò)標(biāo)識(shí)值通過(guò)算法算出具體的時(shí)間季度,按季節(jié)進(jìn)行拆分進(jìn)行拆分,也就是一年
record_delivery_log
4個(gè)表record_order_log_202101,record_order_log_202102,record_order_log_202103,record_order_log_202104
拆分前單表數(shù)據(jù)量為 5000w
拆分后單表的數(shù)據(jù)量變成1200w,能夠容忍將來(lái)4~ 5倍的增長(zhǎng)量,符合預(yù)期范圍。
(2)調(diào)研了對(duì)應(yīng)的分庫(kù)分表中間件,目前Sharing-jdbc是最主流的中間件,而且社區(qū)和文檔較完善,故采用Sharing-jdbc作為分表的中間件。
三、具體實(shí)戰(zhàn)
在這里因?yàn)楣卷?xiàng)目不好復(fù)用的原因,用一個(gè)模擬項(xiàng)目來(lái)模擬這次改造。
(1)參照sharing-jdbc文檔對(duì)項(xiàng)目進(jìn)行改造
引入sharing-jdbc對(duì)應(yīng)的pom。
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.0.0-beta</version>
</dependency>
對(duì)應(yīng)的配置文件
#端口
server.port=8080
# 數(shù)據(jù)源ds0
spring.shardingsphere.datasource.name=ds0
# 數(shù)據(jù)源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
# 分片規(guī)則,這里只分表,所以僅指定表的分片規(guī)則
spring.shardingsphere.rules.sharding.tables.record_order_log.actual-data-nodes=ds0.record_order_log_$->{2021..2031}0$->{1..4}
# 指定數(shù)據(jù)庫(kù)的分片鍵,只有一個(gè)庫(kù)所以還是用分表的分片鍵
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 ...
# ...
# 分片規(guī)則(默認(rèn)取模)
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
#本地?cái)?shù)據(jù)庫(kù)鏈接,忽略了springboot自動(dòng)加載后失效
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
注意好分表鍵設(shè)置時(shí)候的表名。
(2)自定義分片鍵策略,根據(jù)order_delivery_id按季度進(jìn)行存儲(chǔ)
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);
// 將時(shí)間戳轉(zhuǎn)為當(dāng)前時(shí)間
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)模擬提供兩個(gè)接口,一個(gè)按id查詢,一個(gè)插入接口。(修改的場(chǎng)景暫時(shí)沒(méi)有,所以不考慮)


新增的時(shí)候做了模擬插入,能夠根據(jù)分片算法將數(shù)據(jù)存儲(chǔ)到對(duì)應(yīng)的表,達(dá)到效果。
查詢同理。
(4)sharing-jdbc 不會(huì)自動(dòng)的進(jìn)行創(chuàng)建表,所以需在后臺(tái)維護(hù)一個(gè)定時(shí)任務(wù),到了一定的季度點(diǎn)就要進(jìn)行建表操作。(需確保生產(chǎn)環(huán)境的應(yīng)用程序?qū)?yīng)的數(shù)據(jù)庫(kù)賬號(hào)是否有建表權(quán)限)
<update id="createNewTable" parameterType="String">
CREATE TABLE ${tableName} SELECT * FROM record_order_log WHERE 1=2
</update>
四、遇到的問(wèn)題
1、引入sharing-jdbc包的時(shí)候報(bào)錯(cuò)了。這里debug到源碼發(fā)現(xiàn)是mybatisPlus的自動(dòng)啟動(dòng)器(MybatisPlusAutoConfiguration)有指定單一數(shù)據(jù)源類(spring中數(shù)據(jù)源不能有多個(gè)實(shí)現(xiàn)類)的時(shí)候才會(huì)啟動(dòng),因?yàn)閟haring的引入造成了多數(shù)據(jù)源(多datasource),所以這個(gè)就不會(huì)啟動(dòng)了,導(dǎo)致了實(shí)例化mapper的時(shí)候報(bào)錯(cuò)了。解決方案是在SpringBoot的啟動(dòng)類的注解加上
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,DruidDataSourceAutoConfigure.class})
忽略掉SpringBoot數(shù)據(jù)源自動(dòng)裝配以及Druid數(shù)據(jù)源的自動(dòng)裝配,把所有的數(shù)據(jù)源實(shí)例化交給sharing-jdbc


2、部分項(xiàng)目存在歷史遺留的問(wèn)題,如果是mybatis或者h(yuǎn)ibernate的情況下,不想徹底引入sharding-jdbc數(shù)據(jù)源的話,個(gè)人覺(jué)得可以使用多數(shù)據(jù)源的形式來(lái)進(jìn)行改造,去擴(kuò)展需要使用分表的一些數(shù)據(jù)庫(kù)操作,切換對(duì)應(yīng)的sharding數(shù)據(jù)源進(jìn)行數(shù)據(jù)庫(kù)操作。具體可以參考switchDataSource目錄下的一些切換數(shù)據(jù)源的代碼。
3、給自己的疑問(wèn)
忽略了DataSourceAutoConfiguration.class后,sharing-jdbc是如何整合mybatis-plus的?
答:其實(shí)也不難,相當(dāng)于數(shù)據(jù)源這個(gè)對(duì)象原本由SpringBoot自帶的數(shù)據(jù)源自動(dòng)注入進(jìn)行注入,現(xiàn)在換成了Sharding的自動(dòng)裝配(ShardingSphereAutoConfiguration)來(lái)進(jìn)行注入,相當(dāng)于換了整個(gè)數(shù)據(jù)源的一套東西,用的也是sharding整套的東西。

所以在改造的時(shí)候需要檢查一下是否對(duì)舊的項(xiàng)目存在影響。
五、項(xiàng)目源碼地址
cus-sharding-jdbc: sharding-jdbc springboot實(shí)戰(zhàn)
到此這篇關(guān)于Java ShardingJDBC實(shí)戰(zhàn)演練的文章就介紹到這了,更多相關(guān)Java ShardingJDBC內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot多數(shù)據(jù)源切換實(shí)現(xiàn)代碼(Mybaitis)
實(shí)際工作中我們會(huì)遇到springboot項(xiàng)目初始化啟動(dòng)時(shí)候,不能指定具體連接哪個(gè)數(shù)據(jù)源的時(shí)候,不同的接口連接不同的數(shù)據(jù)源或者前端頁(yè)面指定連接某個(gè)數(shù)據(jù)源等等情況,就會(huì)遇到動(dòng)態(tài)數(shù)據(jù)源切換的問(wèn)題,需要的朋友可以參考下2022-04-04
java web response提供文件下載功能的實(shí)例講解
下面小編就為大家分享一篇java web response提供文件下載功能的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
使用IDEA創(chuàng)建一個(gè)vert.x項(xiàng)目的方法
這篇文章主要介紹了使用IDEA創(chuàng)建一個(gè)vert.x項(xiàng)目的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-09-09
java實(shí)現(xiàn)中綴表達(dá)式轉(zhuǎn)后綴的方法
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)中綴表達(dá)式轉(zhuǎn)后綴的表達(dá)式方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11
Java?EasyExcel利用填充模版動(dòng)態(tài)生成多個(gè)sheet頁(yè)
這篇文章主要為大家詳細(xì)介紹了Java?EasyExcel如何利用填充模版動(dòng)態(tài)生成多個(gè)sheet頁(yè),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12
mybatis使用foreach語(yǔ)句實(shí)現(xiàn)IN查詢(三種)
這篇文章主要介紹了mybatis使用foreach語(yǔ)句實(shí)現(xiàn)IN查詢(三種),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12

