springBoot使用ShardingJDBC实现分表

news/2024/4/20 2:59:18/文章来源:https://blog.csdn.net/zxl646801924/article/details/129184094

ShardingSphere的介绍

ShardingSphere是一款起源于当当网内部的应用框架。2015年在当当网内部诞 生,最初就叫ShardingJDBC。2016年的时候,由其中一个主要的开发人员张亮, 带入到京东数科,组件团队继续开发。在国内历经了当当网、电信翼支付、京东数 科等多家大型互联网企业的考验,在2017年开始开源。并逐渐由原本只关注于关系 型数据库增强工具的ShardingJDBC升级成为一整套以数据分片为基础的数据生态 圈,更名为ShardingSphere。到2020年4月,已经成为了Apache软件基金会的顶 级项目。 ShardingSphere包含三个重要的产品,ShardingJDBC、ShardingProxy和 ShardingSidecar。其中sidecar是针对service mesh定位的一个分库分表插件,目 前在规划中。而我们今天学习的重点是ShardingSphere的JDBC这个组 件。 其中,ShardingJDBC是用来做客户端分库分表的产品,而ShardingProxy是用 来做服务端分库分表的产品。这两者定位有什么区别呢?我们看下官方资料中给出 的两个重要的图:

 

 

 

 

1核心概念

分库分表

分库,显而易见,就是一个数据库分成多个数据库,部署到不同机器。

分表,就是一个数据库表分成多个表。

分片

一般在提到分库分表的时候,大多是以水平切分模式(水平分库、分表)为基础来说的,

数据分片将原本一张数据量较大的表例如 t_order 拆分生成数个表结构完全一致的小数据量表 t_order_0、t_order_1、···、t_order_n,每张表只存储原大表中的一部分数据,当执行一条SQL时会通过 分库策略分片策略 将数据分散到不同的数据库、表内。

87a6703b051db1fc739538e1d1230faa.png

数据节点

数据节点是分库分表中一个不可再分的最小数据单元(表),它由数据源名称和数据表组成,例如上图中 order_db_1.t_order_0、order_db_2.t_order_1 就表示一个数据节点。

逻辑表

逻辑表是指一组具有相同逻辑和数据结构表的总称。

比如将订单表 t_order 拆分成 t_order_0 ···  t_order_9 等 10张表。

此时会发现分库分表以后数据库中已不在有 t_order 这张表,取而代之的是 t_order_n,但在代码中写 SQL 依然按 t_order 来写。此时 t_order 就是这些拆分表的逻辑表。

真实表

真实表也就是上边提到的 t_order_n 数据库中真实存在的物理表。

分片键

用于分片的数据库字段。将 t_order 表分片以后,当执行一条SQL时,通过对字段 order_id 取模的方式来决定,这条数据该在哪个数据库中的哪个表中执行,此时 order_id 字段就是 t_order 表的分片健。

 

 实战

以下演示分表,将逻辑订单表 t_order_info 根据订单id字段拆分成3张真实表(t_order_info_0,t_order_info_1,t_order_info_2)

1.创建订单表

 创建好了同时复制另2张表,总共3张表。

2. 创建springboot微服务项目

新建一个微服务项目,引入sharding包

<dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId><version>5.1.2</version>
</dependency>

增加配置

# 配置信息存储方式:内存
spring.shardingsphere.mode.type=Memory# 显示运行的sql
spring.shardingsphere.props.sql.show=true
# 数据源配置
spring.shardingsphere.datasource.names=ds0# 数据源0ds0的配置
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://127.0.0.1:3306/order?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai&useAffectedRows=true
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123456# 表t_order_info
# 分表规则(由于只分表, 不分库, 所以分库直接采用默认的即可)
# 行内表达式分片算法
spring.shardingsphere.rules.sharding.sharding-algorithms.order-inline.type=INLINE
# id列的值%3,等于1就选t_order_info_0表,等于2就选t_order_info_1表,等于3就选t_order_info_2表
spring.shardingsphere.rules.sharding.sharding-algorithms.order-inline.props.algorithm-expression=t_order_info_$->{id % 3}
# 真实表分布,order库的t_order_info_0,t_order_info_1,t_order_info_2表
spring.shardingsphere.rules.sharding.tables.t_order_info.actual-data-nodes=ds0.t_order_info_$->{0..2}
# 以id作为算法的数据值
spring.shardingsphere.rules.sharding.tables.t_order_info.table-strategy.standard.sharding-column=id
# 指定分片算法名称(自定义的名称)
spring.shardingsphere.rules.sharding.tables.t_order_info.table-strategy.standard.sharding-algorithm-name=order-inline

3.生成代码

使用代码生成器生成代码,订单实体OrderInfo如下,其中自己创建了一个@Sharding 注解用来标识分片键,这里根据id值来分表。

@Getter
@Setter
@ToString
@NoArgsConstructor
public class OrderInfo {/** 主键ID**/@Sharding@JsonFormat(shape = Shape.STRING)private Long id;/** 订单编码**/private String orderCode;/** 订单时间**/private Date orderDate;/** 订单状态**/private Integer orderStatus;
}

@Sharding 注解内容如下

/*** 数据分片*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Sharding {/*** 要识别的信息*/String name() default "";}

OrderInfoMapper.xml 如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cdw.modules.order.mapper.OrderInfoMapper"><resultMap id="OrderInfoMap" type="OrderInfo"><id column="id" property="id"/></resultMap><sql id="baseColumns">o.id,o.order_code,o.order_date,o.stop_time,o.order_status</sql><!-- 根据主键ID查询 --><select id="findById" resultMap="OrderInfoMap">SELECT<include refid="baseColumns" />FROM t_order_info o WHERE o.id = #{id}</select><!-- 列表  --><select id="findList" resultMap="OrderInfoMap">SELECT<include refid="baseColumns" />FROM t_order_info o<if test="id!=null">AND o.id = #{id}</if><if test="orderCode!=null and orderCode!=''">AND o.order_code LIKE concat('%', #{orderCode}, '%')</if><if test="orderDate!=null">AND o.order_date = #{orderDate}</if><if test="orderStatus!=null">AND o.order_status = #{orderStatus}</if></select><!-- 批量插入记录 --><insert id="insertBatch" useGeneratedKeys="true" keyProperty="id" parameterType="OrderInfo">INSERT INTO t_order_info (id,order_code,order_date,order_status) VALUES<foreach  item="emp" separator="," collection="list">(#{emp.id},#{emp.orderCode},#{emp.orderDate},#{emp.orderStatus})</foreach></insert><!-- 批量更新记录 --><update id="updateBatch"><foreach  item="emp" separator=";" collection="list">UPDATE t_order_info<set><if test="emp.orderCode!=null and emp.orderCode!=''">order_code = #{emp.orderCode},</if><if test="emp.orderDate!=null">order_date = #{emp.orderDate},</if><if test="emp.orderStatus!=null">order_status = #{emp.orderStatus},</if></set>WHERE id = #{emp.id}</foreach></update><!-- 批量删除记录 --><delete id="delByIds" parameterType = "java.util.List">DELETE FROM t_order_info WHEREid in<foreach collection="list"  item="item" open="(" separator="," close=")"  >#{item}</foreach></delete></mapper>

OrderInfoService 代码

/*** 获取基础订单信息分页列表** @author lockie* @date: 2022-8-10 09:20* @param: param*/
public PageInfo<OrderInfoVO> getOrderInfoPage(OrderInfo param) {param.enablePage();return toPageInfo(orderInfoMapper.findList(param), OrderInfoVO.class);
}/** 查询列表**/
public List<OrderInfoVO> getOrderInfoList(OrderInfo param) {return copyToList(orderInfoMapper.findList(param), OrderInfoVO.class);
}/** 根据ID获取详情**/
public OrderInfoVO getOrderInfoById(Long id) {return orderInfoMapper.findById(id);
}/** 新增**/
public Integer insertOrderInfo(OrderInfo orderInfo) {return insertBatchOrderInfo(Collections.singletonList(orderInfo));
}/** 批量新增**/
public Integer insertBatchOrderInfo(List<OrderInfo> orderInfoList) {if (CollectionUtils.isEmpty(orderInfoList)) {return null;}try {List<List<OrderInfo>> data = ShardingUtil.shardingData(shardingCount.getShardingTableCount(), orderInfoList);data.forEach(s -> {log.info("orderInfoMapper.insertBatchOrderInfo:[{}]", JsonUtil.toJson(s));orderInfoMapper.insertBatch(s);});} catch (Exception e) {throw new RuntimeException(e);}return 1;
}

其中新增操作时重要的就是新增到哪个表去,ShardingUtil就是根据id%3 取余然后找到对应的表

/*** 分表工具类* 批量操作ShardingSphere本就不支持, 因此封装这个工具用于处理该问题* 该工具仅仅支持取模模式* @author lockie* @date: 2022-7-30 19:09*/
@Slf4j
public class ShardingUtil {private static String fieldName = "";private static PropertyDescriptor sourceProperty = null;/*** 根据数据大小以及分片数量得到初始化分片桶** @author lockie*/private static <T> List<List<T>> init(int listCount) {List<List<T>> retList = new ArrayList<>(listCount);// 默认先占位for (int i = 0; i < listCount; i++) {retList.add(new ArrayList<>());}return retList;}/*** 数据分片** @author lockie* @date: 2022-9-6 14:37*/public static <T> List<List<T>> shardingData(int tableCount, List<T> data) throws RuntimeException, InvocationTargetException, IllegalAccessException {if (tableCount == 0) {throw new RuntimeException("tableCount 不能为0!");}List<List<T>> retData = init(tableCount);// 如果是1的话, 则表示没有分表, 直接封装数据返回即可if (tableCount == 1) {retData.set(0, data);retData.removeIf(CollectionUtils::isEmpty);log.info("处理 retData 返回值3{}", JsonUtil.toJson(retData));return retData;}// 基础类型标记boolean isBasicType = false;// 获取类Class<?> clazz = data.get(0).getClass();// 判断是否为基本类型的包装类if (clazz.getSimpleName().equals("Long") || clazz.getSimpleName().equals("Integer")) {isBasicType = true;}// 标记是否有分表操作boolean hasAnnotation = false;// 获取类型for (T t : data) {Object value = null;if (!isBasicType) {// 检验字段名称是否有值, 没有的话循环一遍属性查找if (StringUtils.isEmpty(fieldName)) {Class<?> tClass = t.getClass();List<Field> fields = new ArrayList<>();while (tClass != null) {fields.addAll(Arrays.asList(tClass.getDeclaredFields()));tClass = tClass.getSuperclass();}for (Field field : fields) {Sharding sharding = field.getAnnotation(Sharding.class);if (null == sharding) {continue;}fieldName = field.getName();hasAnnotation = true;break;}}// 如果没标注分表注解的话, 则默认将原始数据封装好丢出去if (!hasAnnotation) {retData.add(data);retData.removeIf(CollectionUtils::isEmpty);log.info("处理 retData 返回值2{}", JsonUtil.toJson(retData));return retData;}// 根据字段名称获取这个属性sourceProperty = BeanUtils.getPropertyDescriptor(clazz, fieldName);value = sourceProperty.getReadMethod().invoke(t);} else {value = t;}// 取模操作int index = (int) (Long.parseLong(String.valueOf(value)) % tableCount);List<T> valueList = retData.get(index);// 添加到对应的List中valueList.add(t);}// 重置状态sourceProperty = null;fieldName = "";// 删除空ListretData.removeIf(CollectionUtils::isEmpty);log.info("处理 retData 返回值1{}", JsonUtil.toJson(retData));return retData;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_73209.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 622.设计循环队列

设计你的循环队列实现。 循环队列是一种线性数据结构&#xff0c;其操作表现基于 FIFO&#xff08;先进先出&#xff09;原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里&a…

注意啦!如何通过广告吸引客户直接下单?

2023年跨境电商越来越突出&#xff0c;据业内相关人士称&#xff0c;在未来几年与跨境电商相关的政策仍会继续倾斜甚至加大力度&#xff0c;因此各行各业都响应政策&#xff0c;在新政策落实之前致力于平台的转型升级&#xff0c;做新时代创新型的高质量发展&#xff0c;其实细…

Linux下的命令执行绕过技巧合集(渗透测试专用)

一、通配符* 代表『0个到无穷多个』任意字符&#xff0c;包括空字符? 代表『一定有一个』任意字符[ ] 同样代表『一定有一个在括号内』的字符(非任意字符)。例如 [abcd] 代表『一定有一个字符&#xff0c; 可能是 a, b, c, d 这四个任何一个』[ - ]若有减号在中括号内时&#…

(考研湖科大教书匠计算机网络)第六章应用层-第五节:文件传送协议FTP

获取pdf&#xff1a;密码7281专栏目录首页&#xff1a;【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一&#xff1a;概述二&#xff1a;工作原理三&#xff1a;控制连接与数据连接本节对应视频如下 【计算机网络微课堂&#xff08;有字幕无背景音乐版&#xff09;】…

求职3个月,简历大多都石沉大海,一听是手工测试都纷纷摇头....太难了

距离被上家公司裁员已经过去了3个月了&#xff0c;3个月的求职经历真的让我痛不欲生&#xff0c;我也从中理解感叹到了很多&#xff0c;想写出来&#xff0c;告诫跟我一样的经历的人。 我今年26岁&#xff0c;大学是一所普通的大专&#xff0c;学的是机电专业&#xff0c;如何…

Python自动化测试框架封装和调用

封装与调用函数与参数化前言 面实现了参数的关联&#xff0c;那种只是记流水账的完成功能&#xff0c;不便于维护&#xff0c;也没什么可读性&#xff0c;接下来这篇可以把每一个动作写成一个函数&#xff0c;这样更方便了。参数化的思维只需记住一点&#xff1a;不要写死 登录…

类与对象(this 关键字、构造器)

目录一、面向对象二、类与对象三、对象内存图四、成员变量和局部变量区别五、this关键字六、构造器/构造方法一、面向对象 一种编程思想:也就是说我们要以何种思路&#xff0c;解决问题&#xff0c;以何种形式组织代码 当解决一个问题的时候&#xff0c;面向对象会把事物抽象成…

分享app的测试技巧

前言 今天笔者想和大家来唠唠app测试&#xff0c;现在的app有非常的多&#xff0c;这些app都是需要经过测试之后才能发布到应用市场中&#xff0c;app已经成为了我们日常生活中不可或缺的一部分了&#xff0c;但它的功能必须强大&#xff0c;才能受到消费者的重视&#xff0c;…

已解决from cryptography.hazmat.backends import default_backend导包错误

已解决Python连接FTPS抛出异常&#xff1a;CryptographyDeprecationWarning: Python 3.6 is no longer supported by the Python core team. Therefore, support for it is deprecated in cryptography. The next release of cryptography (40.0) will be the last to support …

pyaudio声卡信息中hostApi是什么意思?

hostApi是声卡驱动协议&#xff0c;声卡驱动模式&#xff0c;有如下很多类。下面的类型是网上找的PortAudio的类&#xff0c;不不确定是不是python的。typedef enum PaHostApiTypeId{paInDevelopment0, /* use while developing support for a new host API */paDirectSound1,p…

深度学习之“制作自定义数据”--torch.utils.data.DataLoader重写构造方法。

深度学习之“制作自定义数据”–torch.utils.data.DataLoader重写构造方法。 前言&#xff1a; ​ 本文讲述重写torch.utils.data.DataLoader类的构造方法&#xff0c;对自定义图片制作类似MNIST数据集格式&#xff08;image, label&#xff09;&#xff0c;用于自己的Pytorc…

推荐系统从入门到入门(3)——基于MapReuduce与Spark的分布式推荐系统构建

本系列博客总结了不同框架、不同算法、不同界面的推荐系统&#xff0c;完整阅读需要大量时间&#xff08;又臭又长&#xff09;&#xff0c;建议根据目录选择需要的内容查看&#xff0c;欢迎讨论与指出问题。 目录 系列文章梗概 系列文章目录 三、MapReduce 1.MapReduce详…

【视频】海康摄像头、NVR网络协议简介

1、软硬件整体架构 2、涉及的网络协议 3、协议简介 3.1 海康私有协议 设备发现SADP:进行设备的发现、激活、修改网络参数、忘记密码等; SDK:4200、系统平台的接入前端设备,协议不对外开放,但对外提供接口库; ISAPI:Intelligent Security API(智能安全API),基于HTTP传输…

2023新的一年软件测试还值得学习吗?

最近因为疫情等各种原因&#xff0c;大厂裁员&#xff0c;失业等等频频受到关注。不解释&#xff0c;确实存在&#xff0c;各行各业都很难&#xff0c;但是&#xff0c;说软件测试行业不吃香&#xff0c;我还真不认同&#xff08;不是为培训机构说好话&#xff0c;大环境不好&a…

Odoo丨Odoo框架源码研读三:异常处理与定制化开发

Odoo丨Odoo框架源码研读三&#xff1a;异常处理与定制化开发 Odoo源码研读的第三期内容&#xff1a;异常处理与定制化开发。 *异常处理* Odoo中的Exception是对Python内置异常做了继承和封装&#xff0c;设定了自己核心的几个Exception。 而对异常的处理和Python内置异常的…

Spring 之bean的生命周期

文章目录IOCBean的生命周期运行结果实例演示实体类实例化前后置代码初始化的前后置代码application.xml总结今天我们来聊一下Spring Bean的生命周期&#xff0c;这是一个非常重要的问题&#xff0c;Spring Bean的生命周期也是比较复杂的。IOC IOC&#xff0c;控制反转概念需要…

Flutter+【三棵树】

定义 在Flutter中和Widgets一起协同工作的还有另外两个伙伴&#xff1a;Elements和RenderObjects&#xff1b;由于它们都是有着树形结构&#xff0c;所以经常会称它们为三棵树。 这三棵树分别是&#xff1a;Widget、Element、RenderObject Widget树&#xff1a;寄存烘托内容…

SigmaPlot科学绘图工具:ROC曲线分析及AUC组间差异的显著性分析

目的 初步使用SigmaPlot科学绘图工具&#xff1b;进行ROC曲线绘制并分析检验变量AUC组间差异性是否显著 软件下载及安装 SigmaPlot下载安装按照这个教程即可&#xff1a;https://www.hhkxxw.com/24799.html 快速通道&#xff1a;SigmaPlot下载链接&#xff1a;百度网盘链接…

DC220V冲击继电器RCJ-3

系列型号 RCJ-2型冲击继电器&#xff1b; RCJ-2/48VDC冲击继电器 RCJ-2/110VDC冲击继电器 RCJ-2/220VDC冲击继电器 RCJ-2/100VAC冲击继电器 RCJ-2/127VAC冲击继电器 RCJ-2/220VAC冲击继电器 RCJ-3/220VAC冲击继电器 RCJ-3型冲击继电器 RCJ-3/127VAC冲击继电器 RCJ-3/100VAC冲…

FastCGI sent in stderr: "PHP message: PHP Fatal error

服务器php7.2卸载安装7.4之后,打开网站一直无法访问,查看nginx错误日志发现一直报这个错误:2023/02/23 11:12:55 [error] 4735#0: *21 FastCGI sent in stderr: &#xff02;PHP message: PHP Fatal error: Uncaught ReflectionException: Class translator does not exist in …