How to Implement High‑Performance Database Sharding with ShardingSphere and SpringBoot
This article walks through the complete process of designing, configuring, and coding a sharding solution for loan and repayment tables—including database basics, historical data migration, backend query refactoring, a three‑write strategy, scheduled consistency checks, and full SpringBoot/ShardingSphere implementation details.
Introduction
After a year of writing business code, the author received a request to split the loan and repayment application tables to improve query performance.
Design Overview
Sharding Database Basics
New instance, independent datasource.
No need for a separate database, only tables (50 tables).
Table name pattern: CashRepayApplySplit0${0..9}, CashRepayApplySplit${10..49}.
Sharding column memberId, suffix two‑digit, pad with 0.
Ensure uptTime column has an index.
Historical Data Synchronization
The migration moves existing data from CashRepayInfo to the new split tables. Because the total row count exceeds two hundred million, the DBA handles an initial bulk sync up to a cutoff (e.g., uptTime < now - 5 days), after which developers sync the remaining recent rows.
Backend Query Refactoring
Old single‑table joins are replaced by a hybrid approach: recent two‑three years of data stay in the original CashRepayApply table for normal joins, while older data are queried via memberId from the split tables.
Dynamic Switch and Three‑Write Strategy
A runtime switch controls whether new writes are duplicated to the split tables. The three‑write flow is: write to CashRepayApply, then to CashRepayInfo, and finally to CashRepayApplySplit.
Scheduled Task for Consistency
A daily task scans today’s data in both the main and split tables, compares fields, and raises alerts on mismatches to ensure data integrity.
Implementation Details
Technology Stack
SpringBoot 3.2.4, JDK 19, MySQL 8, ShardingSphere 5.4.1, MyBatis‑Plus 3.5.5.
Maven Dependencies
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core</artifactId>
<version>5.4.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.3</version>
</dependency>application.yml
spring:
application:
name: sharding-jdbc-demo
datasource:
driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
url: jdbc:shardingsphere:classpath:sharding-config.yaml
profiles:
active: default
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImplsharding-config.yaml
dataSources:
ds0:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/tcbiz_ins?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: your_password
rules:
- !SHARDING
tables:
cash_repay_apply:
actualDataNodes: ds0.cash_repay_apply_0${0..9},ds0.cash_repay_apply_${10..49}
tableStrategy:
standard:
shardingColumn: member_id
shardingAlgorithmName: memberId-suffix-mode-50
shardingAlgorithms:
memberId-suffix-mode-50:
type: INLINE
props:
algorithm-expression: cash_repay_apply_${Long.parseLong(member_id) % 50}.padLeft(2,'0')
props:
sql-show: trueMapper Example
@Mapper
public interface CashRepayApplyMapper extends BaseMapper<CashRepayApply> {
@Select("select * from cash_repay_apply where member_id = #{memberId}")
List<CashRepayApply> testSharding(@Param("memberId") String memberId);
}Transactional Annotation for Split Table
@Transactional(propagation = Propagation.REQUIRES_NEW, transactionManager = "transactionManagerSplit")Data Synchronization Service
public void dataHandle(Long startId, Long endId) throws AppBizException {
// batch query from CashRepayInfo, loop, insert/update CashRepayApplySplit,
// handle termination flag from Redis, and continue until all rows are processed.
}Utility to Build Split Entity
private CashRepayApplySplit buildCashRepayApplySplit(CashRepayInfo history) {
CashRepayApplySplit split = new CashRepayApplySplit();
CashRepayApply apply = cashRepayApplyDao.get(history.getRepayNo());
if (apply != null) {
BeanUtils.copyProperties(apply, split, "id");
return split;
}
// ... map fields from CashRepayInfo to split entity ...
return split;
}Conclusion
The author reflects on the learning experience of designing and implementing a sharding solution for billions of rows, emphasizing the importance of architectural planning, cross‑team coordination, and careful handling of both historical and live data.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
