本文目录导读:

我来为你提供一个完整的Java分库分表案例,使用最流行的ShardingSphere框架。
1 技术栈
- Spring Boot 2.7.x
- MyBatis Plus 3.5.x
- ShardingSphere-JDBC 5.2.x
- MySQL 8.0
- HikariCP 连接池
2 分片方案
- 分库策略:根据用户ID取模,分成2个库
- 分表策略:根据订单ID取模,每个库分成2张表
- 广播表:配置字典表,所有库同步
- 绑定表:订单表和订单详情表关联分片
项目结构
sharding-demo/
├── pom.xml
├── src/main/java/
│ └── com/example/sharding/
│ ├── ShardingApplication.java
│ ├── config/
│ │ └── ShardingConfig.java
│ ├── controller/
│ │ └── OrderController.java
│ ├── entity/
│ │ ├── Order.java
│ │ ├── OrderItem.java
│ │ └── Dict.java
│ ├── mapper/
│ │ ├── OrderMapper.java
│ │ ├── OrderItemMapper.java
│ │ └── DictMapper.java
│ └── service/
│ └── OrderService.java
└── src/main/resources/
└── application.yml
核心代码实现
1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
</parent>
<groupId>com.example</groupId>
<artifactId>sharding-demo</artifactId>
<version>1.0.0</version>
<properties>
<java.version>1.8</java.version>
<sharding-jdbc.version>5.2.1</sharding-jdbc.version>
<mybatis-plus.version>3.5.3</mybatis-plus.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- ShardingSphere JDBC -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>${sharding-jdbc.version}</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
2 application.yml
server:
port: 8080
spring:
shardingsphere:
datasource:
names: ds0,ds1
# 数据源0
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/sharding_db0?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root123
max-pool-size: 50
min-pool-size: 5
# 数据源1
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/sharding_db1?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root123
max-pool-size: 50
min-pool-size: 5
rules:
sharding:
# 分片算法配置
sharding-algorithms:
# 数据库分片算法
database-inline:
type: INLINE
props:
algorithm-expression: ds$->{user_id % 2}
# 订单表分片算法
order-table-inline:
type: INLINE
props:
algorithm-expression: t_order_$->{order_id % 2}
# 订单详情表分片算法
order-item-table-inline:
type: INLINE
props:
algorithm-expression: t_order_item_$->{order_id % 2}
# 绑定表配置
binding-tables:
- t_order,t_order_item
# 广播表配置
broadcast-tables:
- t_dict
# 表配置
tables:
# 订单表
t_order:
actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: database-inline
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: order-table-inline
# 订单详情表
t_order_item:
actual-data-nodes: ds$->{0..1}.t_order_item_$->{0..1}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: database-inline
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: order-item-table-inline
# 字典表(广播表)
t_dict:
actual-data-nodes: ds$->{0..1}.t_dict
# 属性配置
props:
sql-show: true # 显示SQL
# MyBatis Plus 配置
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
3 实体类
// Order.java
@Data
@TableName("t_order")
public class Order {
@TableId(type = IdType.INPUT)
private Long orderId;
private Long userId;
private BigDecimal totalAmount;
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
@TableLogic
private Integer deleted;
}
// OrderItem.java
@Data
@TableName("t_order_item")
public class OrderItem {
@TableId(type = IdType.INPUT)
private Long itemId;
private Long orderId;
private Long userId;
private Long productId;
private String productName;
private Integer quantity;
private BigDecimal price;
private LocalDateTime createTime;
@TableLogic
private Integer deleted;
}
// Dict.java(广播表)
@Data
@TableName("t_dict")
public class Dict {
@TableId(type = IdType.AUTO)
private Long id;
private String dictType;
private String dictCode;
private String dictValue;
private Integer status;
}
4 Mapper层
// OrderMapper.java
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
// 自定义分页查询
IPage<Order> selectOrderPage(Page<Order> page, @Param("userId") Long userId);
// 统计用户订单数
@Select("SELECT COUNT(*) FROM t_order WHERE user_id = #{userId}")
Long countByUserId(@Param("userId") Long userId);
}
// OrderItemMapper.java
@Mapper
public interface OrderItemMapper extends BaseMapper<OrderItem> {
// 根据订单ID查询订单详情
@Select("SELECT * FROM t_order_item WHERE order_id = #{orderId}")
List<OrderItem> selectByOrderId(@Param("orderId") Long orderId);
}
// DictMapper.java
@Mapper
public interface DictMapper extends BaseMapper<Dict> {
// 根据类型查字典
@Select("SELECT * FROM t_dict WHERE dict_type = #{dictType} AND status = 1")
List<Dict> selectByType(@Param("dictType") String dictType);
}
5 Service层
@Service
public class OrderService {
@Resource
private OrderMapper orderMapper;
@Resource
private OrderItemMapper orderItemMapper;
/**
* 创建订单(包含订单详情)
*/
@Transactional(rollbackFor = Exception.class)
public Order createOrder(Order order, List<OrderItem> items) {
// 生成订单ID
Long orderId = IdGenerator.generateId();
order.setOrderId(orderId);
order.setStatus(1);
order.setCreateTime(LocalDateTime.now());
order.setUpdateTime(LocalDateTime.now());
order.setDeleted(0);
// 插入订单
orderMapper.insert(order);
// 插入订单详情
for (OrderItem item : items) {
item.setItemId(IdGenerator.generateId());
item.setOrderId(orderId);
item.setUserId(order.getUserId());
item.setCreateTime(LocalDateTime.now());
itemMapper.insert(item);
}
return order;
}
/**
* 查询用户订单列表(分页)
*/
public IPage<Order> getUserOrders(Long userId, int page, int size) {
Page<Order> orderPage = new Page<>(page, size);
IPage<Order> result = orderMapper.selectPage(orderPage,
new LambdaQueryWrapper<Order>()
.eq(Order::getUserId, userId)
.orderByDesc(Order::getCreateTime));
// 遍历订单,查询详情
if (result.getRecords() != null && !result.getRecords().isEmpty()) {
for (Order order : result.getRecords()) {
List<OrderItem> items = orderItemMapper.selectByOrderId(order.getOrderId());
order.setItems(items);
}
}
return result;
}
/**
* 订单统计
*/
public Map<String, Object> getOrderStatistics(Long userId) {
Map<String, Object> result = new HashMap<>();
// 订单总数
Long orderCount = orderMapper.countByUserId(userId);
result.put("orderCount", orderCount);
// 订单总金额
List<Order> orders = orderMapper.selectList(
new LambdaQueryWrapper<Order>()
.eq(Order::getUserId, userId)
.eq(Order::getStatus, 1)
);
BigDecimal totalAmount = orders.stream()
.map(Order::getTotalAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
result.put("totalAmount", totalAmount);
return result;
}
/**
* ID生成器(简化版,生产环境建议使用雪花算法)
*/
public static class IdGenerator {
private static final AtomicLong ID = new AtomicLong(1);
public static Long generateId() {
return System.currentTimeMillis() * 1000000 + ID.getAndIncrement();
}
}
}
6 Controller层
@RestController
@RequestMapping("/api/order")
public class OrderController {
@Resource
private OrderService orderService;
/**
* 创建订单
*/
@PostMapping("/create")
public Result createOrder(@RequestBody CreateOrderRequest request) {
Order order = new Order();
order.setUserId(request.getUserId());
order.setTotalAmount(request.getTotalAmount());
List<OrderItem> items = request.getItems().stream().map(item -> {
OrderItem orderItem = new OrderItem();
orderItem.setProductId(item.getProductId());
orderItem.setProductName(item.getProductName());
orderItem.setQuantity(item.getQuantity());
orderItem.setPrice(item.getPrice());
return orderItem;
}).collect(Collectors.toList());
Order result = orderService.createOrder(order, items);
return Result.success("下单成功", result);
}
/**
* 查询用户订单
*/
@GetMapping("/user/{userId}")
public Result getUserOrders(@PathVariable Long userId,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
IPage<Order> orders = orderService.getUserOrders(userId, page, size);
return Result.success("查询成功", orders);
}
/**
* 订单统计
*/
@GetMapping("/statistics/{userId}")
public Result getStatistics(@PathVariable Long userId) {
Map<String, Object> statistics = orderService.getOrderStatistics(userId);
return Result.success("统计成功", statistics);
}
}
// 请求体类
@Data
public class CreateOrderRequest {
private Long userId;
private BigDecimal totalAmount;
private List<OrderItemRequest> items;
}
@Data
public class OrderItemRequest {
private Long productId;
private String productName;
private Integer quantity;
private BigDecimal price;
}
// 统一响应类
@Data
public class Result {
private Integer code;
private String message;
private Object data;
public static Result success(String message, Object data) {
Result result = new Result();
result.setCode(200);
result.setMessage(message);
result.setData(data);
return result;
}
}
数据库初始化脚本
-- 创建数据库
CREATE DATABASE IF NOT EXISTS sharding_db0 DEFAULT CHARACTER SET utf8mb4;
CREATE DATABASE IF NOT EXISTS sharding_db1 DEFAULT CHARACTER SET utf8mb4;
-- sharding_db0 表结构
USE sharding_db0;
-- 订单表
CREATE TABLE IF NOT EXISTS t_order_0 (
order_id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
status INT DEFAULT 1,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted TINYINT DEFAULT 0,
INDEX idx_user_id (user_id)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS t_order_1 LIKE t_order_0;
-- 订单详情表
CREATE TABLE IF NOT EXISTS t_order_item_0 (
item_id BIGINT PRIMARY KEY,
order_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
product_name VARCHAR(100),
quantity INT NOT NULL,
price DECIMAL(10,2) NOT NULL,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
deleted TINYINT DEFAULT 0,
INDEX idx_order_id (order_id),
INDEX idx_user_id (user_id)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS t_order_item_1 LIKE t_order_item_0;
-- 字典表(广播表)
CREATE TABLE IF NOT EXISTS t_dict (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
dict_type VARCHAR(50) NOT NULL,
dict_code VARCHAR(50) NOT NULL,
dict_value VARCHAR(100) NOT NULL,
status TINYINT DEFAULT 1,
UNIQUE KEY uk_type_code (dict_type, dict_code)
) ENGINE=InnoDB;
-- 初始化字典数据
INSERT INTO t_dict (dict_type, dict_code, dict_value) VALUES
('ORDER_STATUS', '1', '待支付'),
('ORDER_STATUS', '2', '已支付'),
('ORDER_STATUS', '3', '已发货'),
('ORDER_STATUS', '4', '已完成');
-- sharding_db1 表结构(与 db0 相同)
USE sharding_db1;
CREATE TABLE IF NOT EXISTS t_order_0 LIKE sharding_db0.t_order_0;
CREATE TABLE IF NOT EXISTS t_order_1 LIKE sharding_db0.t_order_1;
CREATE TABLE IF NOT EXISTS t_order_item_0 LIKE sharding_db0.t_order_item_0;
CREATE TABLE IF NOT EXISTS t_order_item_1 LIKE sharding_db0.t_order_item_1;
-- 广播表需要手动创建并同步数据
CREATE TABLE IF NOT EXISTS t_dict (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
dict_type VARCHAR(50) NOT NULL,
dict_code VARCHAR(50) NOT NULL,
dict_value VARCHAR(100) NOT NULL,
status TINYINT DEFAULT 1,
UNIQUE KEY uk_type_code (dict_type, dict_code)
) ENGINE=InnoDB;
INSERT INTO t_dict (dict_type, dict_code, dict_value) VALUES
('ORDER_STATUS', '1', '待支付'),
('ORDER_STATUS', '2', '已支付'),
('ORDER_STATUS', '3', '已发货'),
('ORDER_STATUS', '4', '已完成');
测试用例
@SpringBootTest
@RunWith(SpringRunner.class)
public class OrderServiceTest {
@Resource
private OrderService orderService;
@Test
public void testCreateOrder() {
// 创建订单
Order order = new Order();
order.setUserId(1001L);
order.setTotalAmount(new BigDecimal("1999.00"));
List<OrderItem> items = new ArrayList<>();
OrderItem item1 = new OrderItem();
item1.setProductId(1L);
item1.setProductName("iPhone 15");
item1.setQuantity(1);
item1.setPrice(new BigDecimal("5999.00"));
items.add(item1);
OrderItem item2 = new OrderItem();
item2.setProductId(2L);
item2.setProductName("AirPods Pro");
item2.setQuantity(1);
item2.setPrice(new BigDecimal("1899.00"));
items.add(item2);
Order result = orderService.createOrder(order, items);
System.out.println("订单创建成功:" + result);
}
@Test
public void testQueryOrders() {
// 查询用户订单
IPage<Order> orders = orderService.getUserOrders(1001L, 1, 10);
System.out.println("订单总数:" + orders.getTotal());
orders.getRecords().forEach(order -> {
System.out.println("订单号:" + order.getOrderId() +
", 金额:" + order.getTotalAmount() +
", 商品数:" + order.getItems().size());
});
}
@Test
public void testDataDistribution() {
// 测试数据分布
List<Long> userIds = Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L);
for (Long userId : userIds) {
System.out.println("用户 " + userId + " -> 数据库 " + (userId % 2));
}
}
}
分片测试数据验证
-- 验证分库效果 USE sharding_db0; SELECT 'db0' AS database_name, COUNT(*) AS order_count FROM t_order_0; SELECT 'db0' AS database_name, COUNT(*) AS order_count FROM t_order_1; USE sharding_db1; SELECT 'db1' AS database_name, COUNT(*) AS order_count FROM t_order_0; SELECT 'db1' AS database_name, COUNT(*) AS order_count FROM t_order_1; -- 查询某个用户的订单(会自动路由到对应分片) -- 用户ID=1001 应路由到 ds1 EXPLAIN SELECT * FROM t_order WHERE user_id = 1001 AND order_id = 123456;
注意事项
1 分片键选择
- 必须包含分片键的查询才能路由到正确分片
- 全表扫描会广播到所有分片
2 性能优化建议
- 合理设计分片数量(建议2的幂次方)
- 使用绑定表避免跨库join
- 避免分布式事务(可使用柔性事务)
3 常见问题
- 分布式ID:使用雪花算法或号段模式
- 数据迁移:使用elastic-job等工具
- 跨分片查询:使用ShardingSphere的联邦查询或汇总层
这个案例涵盖了分库分表的核心实现,你可以根据实际业务需求进行调整和扩展。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播