多数据源,读写分离,分库分表,基本上已经是现在任何一个项目的基本配置了,在之前的文章
Spring多数据源实现https://blog.csdn.net/wangerrong/article/details/131910740 里讲了多数据源的实现,其实已经包含了读写分离(master和slave分别配置主库和只读库就好了),现在就记录下分库分表的实现过程。
1,引入jdbc, mybatis, shardingsphere等依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId><version>2.4.2</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>8.0.30</version>
</dependency>
<!-- jpa持久化工具 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId><version>2.7.6</version>
</dependency>
<!-- 必须引入的包 ShardingSphere -->
<dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId><version>5.2.0</version>
</dependency>
2,建立数据库和数据表
3,添加分库分表配置到bootsrap.yml
server:port: 9102
# NaCos
spring:application:name: deviceserviceshardingsphere:# 数据源配置datasource:# 数据源名称,多数据源以逗号分隔names: db0,db1db0:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://xxxx:3306/db_0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=trueusername: rootpassword: xxxxxxxxxdb1:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://yyyy:3306/db_1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=trueusername: rootpassword: yyyyyyyyy# 分片规则配置rules:sharding:# 分片算法配置sharding-algorithms:database-inline:# 分片算法类型type: INLINEprops:# 分片算法的行表达式(算法自行定义,此处为方便演示效果)algorithm-expression: db$->{order_id%10 > 4?1:0}# 关键,查询时是否在所有分库分表中查询allow-range-query-with-inline-sharding: truetable-inline:# 分片算法类型type: INLINEprops:# 分片算法的行表达式algorithm-expression: t_order_$->{order_id % 3}# 关键,查询时是否在所有分库分表中查询allow-range-query-with-inline-sharding: truetables:# 逻辑表名称t_order:# 行表达式标识符可以使用 ${...} 或 $->{...},但前者与 Spring 本身的属性文件占位符冲突,因此在 Spring 环境中使用行表达式标识符建议使用 $->{...}actual-data-nodes: db${0..1}.t_order_${0..2}# 分库策略database-strategy:standard:# 分片列名称sharding-column: order_id# 分片算法名称sharding-algorithm-name: database-inline# 分表策略table-strategy:standard:# 分片列名称sharding-column: order_id# 分片算法名称sharding-algorithm-name: table-inline# 属性配置props:# 展示修改以后的sql语句sql-show: trueprofiles:active: dev
---
spring:config:activate:on-profile: devimport: optional:config\application-dev.yml
4, 实体类和mapper
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;@Data
@TableName("t_order")
public class Order {private long orderId;private String createdTime;
}
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;@Data
@TableName("t_device")
public class Device {private long deviceId;private String name;private String createdTime;
}
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xxx.domain.Order;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;@Mapper
@Repository
public interface OrderMapper extends BaseMapper<Order> {}
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xxx.domain.Device;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;@Mapper
@Repository
public interface DeviceMapper extends BaseMapper<Device> {
}
import com.xxx.domain.Order;
import org.springframework.stereotype.Service;import java.util.List;@Service
public interface OrderService {public void saveOrder(Order order);public List<Order> list();public void delete(long orderId);public void update(long orderId);
}
import com.xxx.domain.Device;
import org.springframework.stereotype.Service;@Service
public interface DeviceService {public void saveDevice(Device device);
}
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xxx.domain.Order;
import com.xxx.mapper.OrderMapper;
import com.xxx.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class OrderServiceImpl implements OrderService {@Autowiredprivate OrderMapper orderMapper;@Overridepublic void saveOrder(Order order) {orderMapper.insert(order);}@Overridepublic void delete(long orderId) {QueryWrapper qw = new QueryWrapper<Order>();qw.eq("order_id",orderId);orderMapper.delete(qw);}@Overridepublic void update(long orderId) {Order order = new Order();order.setOrderId(orderId);order.setCreatedTime("2200-07-26 00:00:01");QueryWrapper qw = new QueryWrapper<Order>();qw.eq("order_id",orderId);orderMapper.update(order,qw);}@Overridepublic List<Order> list() {QueryWrapper qw = new QueryWrapper<Order>();qw.ge("order_id",0);qw.orderByAsc("order_id");return orderMapper.selectList(qw);}
}
import com.xxx.domain.Device;
import com.xxx.mapper.DeviceMapper;
import com.xxx.service.DeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class DeviceServiceImpl implements DeviceService {@Autowiredprivate DeviceMapper deviceMapper;@Overridepublic void saveDevice(Device device) {deviceMapper.insert(device);}
}
import com.xxx.common.UserContext;
import com.xxx.domain.Device;
import com.xxx.domain.Order;
import com.xxx.service.DeviceService;
import com.xxx.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;@RestController
@RefreshScope
@Slf4j
@RequestMapping("/device")
public class DeviceController extends AbstractController{@Autowiredprivate OrderService orderService;@Autowiredprivate DeviceService devieService;@GetMapping("/save")public String save(@RequestParam("orderId") int orderId){Order order = new Order();order.setOrderId(orderId);DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");String dateStr = sdf.format(new Date());order.setCreatedTime(dateStr);orderService.saveOrder(order);Device device = new Device();device.setDeviceId(orderId);device.setName(orderId+"_"+dateStr);device.setCreatedTime(dateStr);devieService.saveDevice(device);return "success";}@GetMapping("/delete")public String delete(@RequestParam("orderId") long orderId) {orderService.delete(orderId);return "success";}@GetMapping("/update")public String update(@RequestParam("orderId") int orderId) {orderService.update(orderId);return "success";}@GetMapping("/list")public List<Order> list() {return orderService.list();}
}
上面代码是一个简单的验证:
1, 通过对做了分库分表配置的t_order表的增删改查,验证分库分表后的增删改查是否生效,验证通过。
2,验证对没做分库分表配置的t_device表的增加,验证正常数据库操作(默认走spring.shardingsphere.datasource.names: db0,db1配置里的第一个数据库)是否生效,验证通过。
5,总结
Springboot + shardingsphere 实现分库分表的需求,比较简单,但是要想真正地投入实用,还有很多路要走,比如实现自定义分库分表规则的算法,自定义分布式表的主键ID,基于分库分表后的排序(默认已实现按分库分表的列进行排序)和分页等等等。
码字不易,记得点赞关注哟!