SpringBoot整合sharding-jdbc 实现分库分表操作

1、简介

Sharding-JDBC 是一款开源的分布式数据库中间件,旨在简化分库分表(Sharding)的实现。它通过透明化的方式将数据分布到多个数据库或表中,同时提供与原生 JDBC 一致的开发体验。Sharding-JDBC 是 Apache ShardingSphere 项目的子模块之一,广泛应用于高并发、大数据量的场景。

核心功能
1、分库分表:

支持水平分库和水平分表,将数据分散到多个数据库或表中,提升系统性能和存储能力。

支持多种分片策略,如按范围、哈希、时间等。

2、读写分离:

支持主从架构的读写分离,将读操作路由到从库,写操作路由到主库,减轻主库压力。

3、分布式事务:

提供基于 XA 和柔性事务(Saga、TCC)的分布式事务支持,确保数据一致性。

4、数据脱敏:

支持对敏感数据进行加密和脱敏处理,保障数据安全。

5、多数据源管理:

支持动态数据源配置和管理,方便扩展和维护。

6、SQL 兼容性:

支持绝大多数 SQL 语法,兼容主流数据库(如 MySQL、PostgreSQL、Oracle 等)。

核心概念
1、逻辑表(Logic Table):

开发中使用的虚拟表名,例如 order。

2、真实表(Actual Table):

数据库中实际存在的表,例如 order_0、order_1。

3、数据节点(Data Node):

数据分片的最小单元,由数据源名称和真实表组成,例如 ds0.order_0。

4、分片键(Sharding Key):

用于分片的字段,例如订单表中的 user_id。

5、分片算法(Sharding Algorithm):

定义如何根据分片键将数据路由到不同的数据库或表。

6、绑定表(Binding Table):

具有相同分片规则的表,例如 order 和 order_item,可以避免跨表查询。

架构设计
Sharding-JDBC 采用轻量级的架构设计,直接嵌入应用程序中,无需额外部署中间件。其核心组件包括:

  • SQL 解析引擎:解析 SQL 语句,提取分片键。

  • 路由引擎:根据分片规则将 SQL 路由到正确的数据节点。

  • 改写引擎:将逻辑 SQL 改写为真实 SQL。

  • 执行引擎:执行 SQL 并合并结果。

使用场景
1、高并发场景:

通过分库分表提升系统的并发处理能力。

2、大数据量场景:

通过数据分片解决单表数据量过大的问题。

3、读写分离场景:

通过读写分离提升数据库的读性能。

4、多租户场景:

通过分库分表实现多租户数据隔离。

2、实战流程

2.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>

  <artifactId>spring-boot-demo-sharding-jdbc</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>spring-boot-demo-sharding-jdbc</name>
  <description>Demo project for Spring Boot</description>

  <parent>
    <groupId>com.xiaoma</groupId>
    <artifactId>spring-boot-demo</artifactId>
    <version>1.0.0-SNAPSHOT</version>
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
      <version>3.1.0</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <dependency>
      <groupId>io.shardingsphere</groupId>
      <artifactId>sharding-jdbc-core</artifactId>
      <version>3.1.0</version>
    </dependency>

    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-all</artifactId>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
  </dependencies>

  <build>
    <finalName>spring-boot-demo-sharding-jdbc</finalName>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

2.2. CustomSnowflakeKeyGenerator.java

package com.xiaoma.sharding.jdbc.config;

import cn.hutool.core.lang.Snowflake;
import io.shardingsphere.core.keygen.KeyGenerator;

public class CustomSnowflakeKeyGenerator implements KeyGenerator {
    private Snowflake snowflake;

    public CustomSnowflakeKeyGenerator(Snowflake snowflake) {
        this.snowflake = snowflake;
    }
    
    @Override
    public Number generateKey() {
        return snowflake.nextId();
    }
}

2.3. DataSourceShardingConfig.java

@Configuration
public class DataSourceShardingConfig {
    private static final Snowflake snowflake = IdUtil.createSnowflake(1, 1);

    /**
     * 需要手动配置事务管理器
     */
    @Bean
    public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "dataSource")
    @Primary
    public DataSource dataSource() throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        // 设置分库策略
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("user_id", "ds${user_id % 2}"));
        // 设置规则适配的表
        shardingRuleConfig.getBindingTableGroups().add("t_order");
        // 设置分表策略
        shardingRuleConfig.getTableRuleConfigs().add(orderTableRule());
        shardingRuleConfig.setDefaultDataSourceName("ds0");
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(new NoneShardingStrategyConfiguration());

        Properties properties = new Properties();
        properties.setProperty("sql.show", "true");

        return ShardingDataSourceFactory.createDataSource(dataSourceMap(), shardingRuleConfig, new ConcurrentHashMap<>(16), properties);
    }

    private TableRuleConfiguration orderTableRule() {
        TableRuleConfiguration tableRule = new TableRuleConfiguration();
        // 设置逻辑表名
        tableRule.setLogicTable("t_order");
        // ds${0..1}.t_order_${0..2} 也可以写成 ds$->{0..1}.t_order_$->{0..1}
        tableRule.setActualDataNodes("ds${0..1}.t_order_${0..2}");
        tableRule.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "t_order_$->{order_id % 3}"));
        tableRule.setKeyGenerator(customKeyGenerator());
        tableRule.setKeyGeneratorColumnName("order_id");
        return tableRule;
    }

    private Map<String, DataSource> dataSourceMap() {
        Map<String, DataSource> dataSourceMap = new HashMap<>(16);

        // 配置第一个数据源
        HikariDataSource ds0 = new HikariDataSource();
        ds0.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds0.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test1?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8");
        ds0.setUsername("root");
        ds0.setPassword("root");

        // 配置第二个数据源
        HikariDataSource ds1 = new HikariDataSource();
        ds1.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds1.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test2?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8");
        ds1.setUsername("root");
        ds1.setPassword("root");

        dataSourceMap.put("ds0", ds0);
        dataSourceMap.put("ds1", ds1);
        return dataSourceMap;
    }

    /**
     * 自定义主键生成器
     */
    private KeyGenerator customKeyGenerator() {
        return new CustomSnowflakeKeyGenerator(snowflake);
    }

}

2.3. SpringBootDemoShardingJdbcApplicationTests.java

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootDemoShardingJdbcApplicationTests {
    @Autowired
    private OrderMapper orderMapper;

    /**
     * 测试新增
     */
    @Test
    public void testInsert() {
        for (long i = 1; i < 10; i++) {
            for (long j = 1; j < 20; j++) {
                Order order = Order.builder().userId(i).orderId(j).remark(RandomUtil.randomString(20)).build();
                orderMapper.insert(order);
            }
        }
    }

    /**
     * 测试更新
     */
    @Test
    public void testUpdate() {
        Order update = new Order();
        update.setRemark("修改备注信息");
        orderMapper.update(update, Wrappers.<Order>update().lambda().eq(Order::getOrderId, 2).eq(Order::getUserId, 2));
    }

    /**
     * 测试删除
     */
    @Test
    public void testDelete() {
        orderMapper.delete(new QueryWrapper<>());
    }

    /**
     * 测试查询
     */
    @Test
    public void testSelect() {
        List<Order> orders = orderMapper.selectList(Wrappers.<Order>query().lambda().in(Order::getOrderId, 1, 2));
        log.info("【orders】= {}", JSONUtil.toJsonStr(orders));
    }
    
}

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

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

相关文章

解锁养生密码,拥抱健康生活

在快节奏的现代生活中&#xff0c;养生不再是一种选择&#xff0c;而是我们保持活力、提升生活质量的关键。它不是什么高深莫测的学问&#xff0c;而是一系列融入日常的简单习惯&#xff0c;每一个习惯都在为我们的健康加分。 早晨&#xff0c;当第一缕阳光洒进窗户&#xff0c…

muduo网络库2

Muduo网络库&#xff1a;底层实质上为Linux的epoll pthread线程池&#xff0c;且依赖boost库。 muduo的网络设计核心为一个线程一个事件循环&#xff0c;有一个main Reactor负载accept连接&#xff0c;然后把连接分发到某个sub Reactor(采用轮询的方式来选择sub Reactor)&…

【大语言模型】【整合版】DeepSeek 模型提示词学习笔记(散装的可以看我之前的学习笔记,这里只是归纳与总结了一下思路,内容和之前发的差不多)

以下是个人笔记的正文内容: 原文在FlowUs知识库上&#xff0c;如下截图。里面内容和这里一样&#xff0c;知识排版好看一点 一、什么是 DeepSeek 1. DeepSeek 简介 DeepSeek 是一家专注于通用人工智能&#xff08;AGI&#xff09;的中国科技公司&#xff0c;主攻大模型研发与…

AWQ和GPTQ量化的区别

一、前言 本地化部署deepseek时发现&#xff0c;如果是量化版的deepseek&#xff0c;会节约很多的内容&#xff0c;然后一般有两种量化技术&#xff0c;那么这两种量化技术有什么区别呢&#xff1f; 二、量化技术对比 在模型量化领域&#xff0c;AWQ 和 GPTQ 是两种不同的量…

ARM Cortex-M3 技术解析:核寄存器R1-R15介绍及使用

ARM Cortex-M3 技术解析&#xff1a;核寄存器R1-R15介绍及使用 作为嵌入式开发领域的经典处理器内核&#xff0c;ARM Cortex-M3&#xff08;CM3&#xff09;凭借其高效能、低功耗和丰富特性&#xff0c;在工业控制、物联网、消费电子等领域广泛应用。而内核寄存器是我们调试代…

ROS ur10机械臂添加140夹爪全流程记录

ROS ur10机械臂添加140夹爪 系统版本&#xff1a;Ubuntu20.04 Ros版本&#xff1a;noetic Moveit版本&#xff1a;moveit-noetic 参考博客&#xff1a; ur3robotiq ft sensorrobotiq 2f 140配置rviz仿真环境_有末端力传感器的仿真环境-CSDN博客 UR5机械臂仿真实例&#xf…

Web自动化之Selenium添加网站Cookies实现免登录

在使用Selenium进行Web自动化时&#xff0c;添加网站Cookies是实现免登录的一种高效方法。通过模拟浏览器行为&#xff0c;我们可以将已登录状态的Cookies存储起来&#xff0c;并在下次自动化测试或爬虫任务中直接加载这些Cookies&#xff0c;从而跳过登录步骤。 Cookies简介 …

【落羽的落羽 数据结构篇】树、二叉树

文章目录 一、树1. 树的概念和结构2. 树的相关术语 二、二叉树1. 概念与结构2. 满二叉树3. 完全二叉树4. 二叉树的性质5. 二叉树的存储结构 一、树 1. 树的概念和结构 之前我们学习了线性表&#xff0c;今天我们再来接触一种全新的数据结构——树。 树是一种非线性的数据结构…

数据结构(陈越,何钦铭) 第四讲 树(中)

4.1 二叉搜索树 4.1.1 二叉搜索树及查找 Position Find(ElementTyoe X,BinTree BST){if(!BST){return NULL;}if(X>BST->Data){return Find(X,BST->Right)}else if(X<BST->Data){return Find(X,BST->Left)}else{return BST;} } Position IterFind(ElementTyp…

【原创工具】同文件夹PDF文件合并 By怜渠客

【原创工具】同文件夹PDF文件合并 By怜渠客 原贴&#xff1a;可批量合并多个文件夹内的pdf工具 - 吾爱破解 - 52pojie.cn 他这个存在一些问题&#xff0c;并非是软件内自主实现的PDF合并&#xff0c;而是调用的pdftk这一工具&#xff0c;但楼主并没有提供pdftk&#xff0c;而…

Kafka系列之:记录一次源头数据库刷数据,造成数据丢失的原因

Kafka系列之:记录一次源头数据库刷数据,造成数据丢失的原因 一、背景二、查看topic日志信息三、结论四、解决方法一、背景 源头数据库在很短的时间内刷了大量的数据,部分数据在hdfs丢失了 理论上debezium数据采集不会丢失,就需要排查数据链路某个节点是否有数据丢失。 数据…

llama.cpp 一键运行本地大模型 - Windows

文章目录 llama.cpp 一键运行本地大模型 - Windows嘿&#xff0c;咱来唠唠 llama.cpp 这玩意儿&#xff01;gguf 格式是啥&#xff1f;咱得好好说道说道基座模型咋选&#xff1f;所需物料&#xff0c;咱得准备齐全咯核心命令&#xff0c;得记牢啦运行方式咋选&#xff1f;测试应…

BGP状态和机制

BGP邻居优化 为了增加稳定性,通常建议实验回环口来建立邻居。更新源:建立邻居和邻居所学习到的路由的下一跳。多跳:EBGP邻居建立默认选哟直连,因为TTL=1,如果非直连,必须修改TTL。命令备注peer 2.2.2.2 connect-interface lo1配置更新源peer 2.2.2.2 ebgp-max-hop 2配置T…

Holoens2开发报错记录02_通过主机获取彩色和深度数据流常见错误

01.E1696 E1696 无法打开源文件 “stdio.h” 解决方法&#xff1a; 更新一下SDK 1&#xff09;打开Visual Studio Installer&#xff0c;点击修改 2&#xff09;安装详细信息中自己系统对应的SDK&#xff0c;点击修改即可 02.WinError 10060 方法来源 解决方法&#xff1a…

labview关于计时器的使用

通过使用计时器函数&#xff0c;可以对采集和保存实现很好的控制&#xff0c;因为之前通过等待函数有出现程序卡死的情况&#xff0c;这里用到定时器函数来实现时间控制。 根据用户输入的采集频率&#xff0c;和采集的单位来确定是否上次采集的时间间隔减去这次计时器的时间是…

go语言环境下载与配置(Windows)

下载 Go下载 - Go语言中文网 - Golang中文社区 建议在D盘中创建文件夹安装到 D 盘 &#xff0c;方便进行管理&#xff0c;然后进行傻瓜式安装。 安装 验证安装 go version 安装成功 配置环境变量 winE --> 右击此电脑 --> 选择属性 --> 高级系统设置 --> 点击…

低延迟,高互动:EasyRTC的全场景实时通信解决方案

在数字化时代&#xff0c;实时通信技术已成为连接人与人、人与设备的重要桥梁。无论是在线教育、远程医疗、智能家居&#xff0c;还是企业协作&#xff0c;高效的实时互动体验都是提升效率和满意度的关键。而 EasyRTC&#xff0c;作为领先的实时通信解决方案&#xff0c;凭借其…

车载诊断架构 --- LIN节点路由转发注意事项

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…

浏览器深度解析:打造极速、安全、个性化的上网新体验

在数字化时代,浏览器作为我们获取信息、娱乐休闲的重要工具,其性能与功能直接影响着我们的上网体验。今天,我将为大家介绍一款备受好评的浏览器——Yandex浏览器,并深入解析其独特功能与优势,帮助大家更好地了解并选择这款上网神器。 一、知名公司背书,开源项目融合 Yan…