当前位置: 首页 > news >正文

Springboot 整合ShardingSphere实现分库分表

简介

分库分表是一种将数据拆分成数据库或表中的策略,例如按照时间的月份创建新的表,将数据隔离开,用于提高查询效率,我们可以使用 Apache 提供的服务 ShardingSphere,用于简化数据库分片、读写分离、分布式事务等复杂场景的管理。

使用步骤一:引入相关依赖

Apache ShardingSphere分为两种模块一种是JDBC一种是proxy,应用于不同的场景和语言,在Java中,我们选择JDBC版本即可,他仅支持Java

<!-- 分库分表 -->
<dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId><version>5.2.0</version>
</dependency>

步骤二:进行相关配置

我们在yaml中可以对shardingSphere进行相对应的配置

spring:# 使用 shardingSphere 空间图片分表shardingsphere:datasource:names: zzx_picture  # 数据库名称zzx_picture: # 数据源名称type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://${picture.mysql.host}:${picture.mysql.port}/${picture.mysql.database}username: ${picture.mysql.username}password: ${picture.mysql.password}rules:sharding:tables:picture: # 配置 picture 表的分片规则actual-data-nodes: zzx_picture.picture  # 动态分表table-strategy:standard:sharding-column: spaceId # 分表字段sharding-algorithm-name: picture_sharding_algorithm  # 使用自定义分片算法sharding-algorithms:picture_sharding_algorithm:type: CLASS_BASEDprops:strategy: standard # 标准分片algorithmClassName: com.zzx.zzxpicturebackend.manager.sharding.PictureShardingAlgorithm # 自定义分片算法props:sql-show: true # 显示分片后的实际 SQL 语句,用于调试mybatis-plus:configuration:map-underscore-to-camel-case: false    # MyBatis 下划线转驼峰# 仅在开发环境开启日志log-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:# 启用 SQL 运行器enable-sql-runner: truedb-config:logic-delete-field: is_delete  # 全局逻辑删除logic-delete-value: 1    # 逻辑删除值(默认为1)logic-not-delete-value: 0  # 逻辑未删除值(默认为0)mapper-locations: classpath:/mapper/**.xml

我们在分片规则 actual-data-nodes配置中只写了基本数据表,因为我们业务需求是创建动态表,所以这里以动态分表为例

这里的配置需要改成自己的数据库的相关配置和MybatisPlus的相关配置

这里的配置需要注意的是:

actual-data-nodes 一般指定的你分表的范围,比如 ds0.table_${0..9999},也可以直接写成ds0.table1,ds0.table2

我们还需要指定分表字段,比如spaceId

需要配置指定一下分片算法类,这个分片算法类主要是需要实现StandardShardingAlgorithm接口,编写doSharding方法,泛型为你分表字段的类型,比如日期就是Data

步骤三:书写相关类

我们需要写两个类,一个类为自己实现分表算法类,一个为动态分表管理相关类

分表算法类:

package com.zzx.zzxpicturebackend.manager.sharding;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.util.ArrayList;
import java.util.Collection;
import java.util.Properties;/*** 图片分片算法*/
public class PictureShardingAlgorithm implements StandardShardingAlgorithm<Long> {/**** @param collection           所有分表名 是一个固定值,由yaml中 actual-data-nodes 指定* @param preciseShardingValue 分片键值* @return 分表名*/@Overridepublic String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {Long spaceId = preciseShardingValue.getValue();String logicTableName = preciseShardingValue.getLogicTableName();// spaceId 为 null 表示查询所有图片if (spaceId == null || spaceId == 0) {return logicTableName;}// 根据 spaceId 动态生成分表名String realTableName = "picture_" + spaceId;if (collection.contains(realTableName)) {// 找到匹配的目标表return realTableName;}else{// 未找到匹配的目标表,返回逻辑表return logicTableName;}}@Overridepublic Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {return new ArrayList<>();}@Overridepublic Properties getProps() {return null;}@Overridepublic void init(Properties properties) {}
}

如果只实现分表算法类,还不能完成动态分表,

因为 doSharding方法中的 collection 字段是在yaml中 actual-data-nodes直接确定的,所以我们需要自己维护分表列表,并且更新在到 ShardingSphere 中 actual-data-nodes配置中

我们可以写一个动态分表管理类,用于首次加载项目是需要更新分表配置,当项目读取完yaml配置后,然后再把读取到的配置给修改了

最后在这个管理类中创建动态分表的方法,通过拼接SQL 的方式创建出和 picture 表结构一样的分表

因为框架不支持动态维护分表,所以我们需要自己写一个分表管理器,自己维护分表,并且在项目加载完配置文件后动态更新到加载后的配置中

package com.zzx.zzxpicturebackend.manager.sharding;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import com.zzx.zzxpicturebackend.constant.ShardingSphereConstant;
import com.zzx.zzxpicturebackend.exception.BusinessException;
import com.zzx.zzxpicturebackend.exception.ErrorCode;
import com.zzx.zzxpicturebackend.model.enums.SpaceLevelEnum;
import com.zzx.zzxpicturebackend.model.enums.SpaceTypeEnum;
import com.zzx.zzxpicturebackend.model.po.Space;
import com.zzx.zzxpicturebackend.service.SpaceService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection;
import org.apache.shardingsphere.infra.config.rule.RuleConfiguration;
import org.apache.shardingsphere.infra.metadata.database.rule.ShardingSphereRuleMetaData;
import org.apache.shardingsphere.mode.manager.ContextManager;
import org.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
import org.apache.shardingsphere.sharding.route.strategy.ShardingStrategy;
import org.apache.shardingsphere.sharding.rule.ShardingRule;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;/*** 动态分表管理* 首次加载项目时需要更新分表配置*/
@Component
@Slf4j
public class DynamicShardingManager {@Resourceprivate DataSource dataSource;@Resourceprivate SpaceService spaceService;/*** 初始化动态分表*/@PostConstruct // 当这个bean 创建成功之后,会执行这个方法public void initialize() {log.info("初始化动态分表配置");updateShardingTableNodes();}/*** 更新 ShardingSphere 的 actual-data-nodes 动态表名配置*/private void updateShardingTableNodes() {// 现获取所有的表名Set<String> tableNames = fetchAllTableNames();// zzx_picture.picture_53613631563,zzx_picture.picture_436434847934,zzx_picture.picture_48348934873934,String newActualDataNodes = tableNames.stream().map(tableName -> ShardingSphereConstant.MY_DATABASES_NAME + "." + tableName) // 将表名转换为逻辑表名.collect(Collectors.joining(","));log.info("动态分表 actual-data-nodes 配置为:{}", newActualDataNodes);ContextManager contextManager = getContextManager();// 获取 ShardingSphere 的规则元数据ShardingSphereRuleMetaData ruleMetaData = contextManager.getMetaDataContexts().getMetaData().getDatabases().get(ShardingSphereConstant.DATABASES_NAME).getRuleMetaData();Optional<ShardingRule> shardingRule = ruleMetaData.findSingleRule(ShardingRule.class);// 如果存在,则更新if (shardingRule.isPresent()) {ShardingRuleConfiguration ruleConfig = (ShardingRuleConfiguration) shardingRule.get().getConfiguration();List<ShardingTableRuleConfiguration> updateRules = ruleConfig.getTables().stream().map(oldTableRule -> {if (ShardingSphereConstant.LOGIC_TABLE_NAME.equals(oldTableRule.getLogicTable())) {// 设置新的表配置ShardingTableRuleConfiguration newTableRuleConfig = new ShardingTableRuleConfiguration(ShardingSphereConstant.LOGIC_TABLE_NAME, newActualDataNodes);newTableRuleConfig.setDatabaseShardingStrategy(oldTableRule.getDatabaseShardingStrategy());newTableRuleConfig.setTableShardingStrategy(oldTableRule.getTableShardingStrategy());newTableRuleConfig.setKeyGenerateStrategy(oldTableRule.getKeyGenerateStrategy());newTableRuleConfig.setAuditStrategy(oldTableRule.getAuditStrategy());return newTableRuleConfig;}return oldTableRule;}).collect(Collectors.toList());// 更新配置ruleConfig.setTables(updateRules);// 更新规则contextManager.alterRuleConfiguration(ShardingSphereConstant.DATABASES_NAME, Collections.singleton(ruleConfig));// 重新加载数据库contextManager.reloadDatabase(ShardingSphereConstant.DATABASES_NAME);log.info("动态分表配置更新成功");} else {log.error("未找到 ShardingSphere 的分片规则配置,动态分表更新失败");}}/*** 动态创建一张分表** @param space 团队空间*/public void createSpacePictureTable(Space space) {// 仅为旗舰版团队空间创建分表if (space.getSpaceType().equals(SpaceTypeEnum.TEAM.getValue()) && space.getSpaceLevel() == SpaceLevelEnum.FLAGSHIP.getValue()) {Long spaceId = space.getId();String tableName = ShardingSphereConstant.LOGIC_TABLE_NAME + "_" + spaceId;// 创建新表String createTableSql = "CREATE TABLE " + tableName + " LIKE " + ShardingSphereConstant.LOGIC_TABLE_NAME;try {SqlRunner.db().update(createTableSql);// 更新分表updateShardingTableNodes();} catch (Exception e) {log.error("创建分表失败,空间 id = {}", spaceId);e.printStackTrace();throw new BusinessException(ErrorCode.SYSTEM_ERROR, "创建失败,空间 id = " + spaceId);}}}/*** 获取 ShardingSphere 上下文管理器*/private ContextManager getContextManager() {try (ShardingSphereConnection connection = dataSource.getConnection().unwrap(ShardingSphereConnection.class)) {return connection.getContextManager();} catch (SQLException e) {throw new RuntimeException("获取 ShardingSphere 上下文管理器失败", e);}}/*** 获取所有的表名,包括初始表 picture 和 分表 picture_{spaceId}** @return 所有的表名*/private Set<String> fetchAllTableNames() {// 查询对旗舰版的团队空间分表LambdaQueryWrapper<Space> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(Space::getSpaceType, SpaceTypeEnum.TEAM.getValue());// 为了方便测试,目前只对团队空间进行动态分表,实际上线可取消注释queryWrapper.eq(Space::getSpaceLevel, SpaceLevelEnum.FLAGSHIP.getValue());Set<Long> spaceIds = spaceService.list(queryWrapper).stream().map(Space::getId).collect(Collectors.toSet());// 获取所有的表名Set<String> tableNames = spaceIds.stream().map(spaceId -> ShardingSphereConstant.LOGIC_TABLE_NAME + "_" + spaceId).collect(Collectors.toSet());// 添加初始表tableNames.add(ShardingSphereConstant.LOGIC_TABLE_NAME);return tableNames;}}

为了我们的业务便利性,可以在这个管理类中编写创建动态分表的方法,通过拼接SQL 的方式创建出和 picture 表结构一样的分表,在相关的业务代码中执行该方法

至此我们的业务就完成了,在重新梳理一下,我们在yaml进行了相关的配置,然后自己管理创建分表的方法,然后在项目启动时或者执行相关业务通过更新配置文件,实现了动态分表的操作

相关的参考文档:

鱼皮中云图库项目

https://blog.csdn.net/weixin_60523038/article/details/145650882

https://developer.aliyun.com/article/1575676

http://www.dtcms.com/a/492790.html

相关文章:

  • 网站建设与管理教学计划新开传奇网站手游
  • 基于51单片机的频率和幅度调节多功能波形发生器系统设计
  • 海口网站制作网站网站在百度上搜索不到
  • 网站添加站长统计代码焦作网站建设哪家好
  • 广州展厅设计公司排名seo搜索优化
  • 做家装壁纸的网站黄山网站建设找哪家
  • 做app和网站哪个比较好湛江企业建站程序
  • 泰州网站制作套餐软文推广多少钱一篇
  • 长宁企业网站制作宝安中心区房价
  • 网站内页模板南高齿网站是谁做的
  • 怎么创建网站详细流程用ps网站首页怎么做
  • 免费代理服务器网站高端网站设计费用
  • 清新县城乡规划建设局网站阿里巴巴电脑版
  • 南昌高端网站开发费用表网站建设写
  • 专业的免费网站建设哪家网站别人能打开我打不开
  • 反馈机制只有“汇报”,如何建立双向反馈
  • SpringBoot房屋租赁系统
  • 网站做不做双解析宝塔wordpress ssl证书
  • 品牌网站建设推荐大蝌蚪做网站用php还是node
  • 常州网站建设工作室怎么被百度收录
  • 帝国cms网站地图xml怎样经营好一个网站
  • 做网站用的腾讯云服务器北京网站建设有哪些
  • 服务网站运营方案旅游网站建设属于什么以及学科
  • 东莞在线网站制作平台网站开发流程分析
  • 懂网络维护和网站建设的专业网页设计模板与效果图
  • 网站建设好的公司哪家好网站建设和维护要点
  • 江苏省电力建设一公司网站wordpress网站插件下载
  • 天津企业网站排名优化设计学习
  • 网站设计的发展趋势黄页网站推广方案
  • 门户网站的盈利模式怎样围绕网站专题发展来做ppt