Sharding-JDBC分库分表的基本使用

前言

传统的小型应用通常一个项目一个数据库,单表的数据量在百万以内,对于数据库的操作不会成为系统性能的瓶颈。但是对于互联网应用,单表的数据量动辄上千万、上亿,此时通过数据库优化、索引优化等手段,对数据库操作的性能优化可能无济于事,数据库操作势必称为性能的瓶颈。

究其原因,单机数据库系统的连接数、处理能力等都是有限的。对于这种情况,解决的方案有:

1)提升硬件配置。通过使用更好的CPU、更大的带宽、更大的内存等提升系统的处理能力。但该方案成本较高,而且效果不一定明细;

2)使用商用数据库。商用数据库经过高度优化,性能强大且稳定,提供丰富的管理工具等。但该方案成本较高,而且同样会受到硬件配置的影响;

3)分库分表。将数据分散到不同的数据库;在同一个库中,拆分到多个小的表。从而可以分散到多台硬件设备,减轻单机的压力,提升数据库操作的性能。成本相对较低;

分库分表

分库分表的拆分方式分为垂直拆分和水平拆分。

2.1 分库-垂直拆分

垂直拆分库比较简单,在数据库设计层面就可以实现。可以按照业务模块或实际项目需求,把一个项目中的表拆分到多个数据库中。

如用户维度相关的表放在用户库,业务维度相关的表放在业务库。

这样应用可以根据不同的业务连接不同的数据库,如当前的微服务架构,不同微服务可以使用不同的数据库连接。通过多个数据库分担流量,提高查询速度。

2.2 分表-垂直拆分

垂直拆分表是指把一个大表中的字段拆分成多个表,每个表存储其中一部分字段。

如商品信息表,把基本信息存放在商品表,详情等信息存放在商品详情表,两个表直接通过外键进行关联。商品表访问比较频繁,拆分后,单条记录较小,查询效率较高。而商品详情表,通常在查看单个商品时访问,单条记录较大,但通过主键或其他索引,仍然能够保证效率。

实现相对简单,可以有效的提升查询效率。

2.3 分表-水平拆分

水平拆分表是指把一个表中的数据拆分到多个表中。与垂直拆分表的区别是:垂直拆分表是按列(字段)拆分、水平拆分表是按行(记录)拆分。

如订单表,随着业务规模的提升,订单数据不断的膨胀。可以将订单表按会员、商家、下单时间等进行分表存放。

把一个大表拆分成多个按照一定规律分布的小表,每个表的数据量较小,可以有效的提升数据库操作效率。

2.4 分库-水平拆分

水平拆分库是指把一个表中的数据拆分到多个数据库中,数据库采用分布式部署。在水平拆分表的基础上,如果一个库中的表太多,可以通过水平拆分库,将表数据先分库再进行分表。

如订单表,可以先按商家分库,然后再按会员分表。数据插入时,先按商家,找到匹配的数据库,然后再按会员,找到匹配的表,插入对应数据。

通过水平分库分表,针对分库分表时的指定键的查询时,可以大大的提升查询效率。

分库分表带来的问题

3.1 事务一致性

【源码】SpringBoot事务注册原理-CSDN博客

在上面的博文中分享了何为事务以及单数据库事务在SpringBoot中的实现。对于分库分表,事务的一致性处理将变得更加复杂。在单数据库中,事务原子性、隔离性是由数据库来保证,但在多数据库中,事务的特性中只有持久性能够由数据库保证,其他都需要由程序开发者自行实现。

3.2 跨库跨表查询

对于单库单表,无论是关联查询,还是分页、排序,都是在一张表中通过一条SQL即可实现。而进行分表分库之后,数据分布在不同的库、不同的表,实现变得比较复杂。最坏的情况是需要先在不同的库、不同的表中查询数据,进行排序并返回,然后程序中汇总再获取满足条件的数据。

3.3 主键重复

在单库单表中,可以通过数据库提供的自增长设置主键,但在分库分表中,自增长的主键会存在重复。因此需要使用全局唯一的主键。如UUID、雪花算法等。

3.4 公共表

对于一些字典、配置类等数据量较小、变动少,而且需要高频联合查询的表,可以建立公共表。在分库环境中,每个库都保存一份相同的公共表。

Sharding-JDBC介绍

针对分库分表带来的问题,通过Sharding-JDBC这个框架,都可以有效的解决。

Sharding-JDBC 是一款由当当网开源的分库分表中间件,它可以透明地为 Java 应用程序提供数据库分片功能,无需关心底层的数据库分片细节。它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

在大量社区贡献者的不断迭代下,功能也逐渐完善,现已更名为 ShardingSphere,2020年4⽉16⽇正式成为 Apache 软件基⾦会的顶级项⽬。

ShardingSphere官网:Apache ShardingSphere

Sharding-JDBC入门

以下以订单分表为例,介绍Sharding-JDBC的基本使用。

1)订单表:订单表的字段有订单id、会员id、总价格、状态、下单时间;

2)创建两个订单表,分表为tb_order_1和tb_order_2;

3)分表规则:以订单id分表,id被2整除的放tb_order_1,不能被2整除的放tb_order_2;

5.1 导入sharding-jdbc-spring-boot-starter依赖

<?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">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>Sharing-JDBC-demo</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.1.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.6</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

</project>

引入sharding-jdbc-spring-boot-starter,此处的数据库连接池采用Druid。需要注意的是,不能直接使用druid-spring-boot-starter,否则启动会报错,因为该starter会自动注入SqlSessionFactory和SqlSessionTemplate,和Sharding-JDBC冲突。

5.2 sharding-jdbc配置

# 单数据库,inline分片策略测试
server:
  port: 8080

#sharding-jdbc分片规则配置
spring:
  shardingsphere:
    datasource:
      names: order1 #数据源名称,有几个数据源就写几个名字,和url中的数据库名字保持一致
      order1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/shardingjdbctest?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
        username: root
        password: 123456
    #分表策略
    #按照id分表,id使用雪花算保证全局唯一,具体算法:tb_order是表前缀,拼接上:$->{id % 2}  的值
    sharding:
      tables:
        tb_order: #逻辑表
          actual-data-nodes: order1.tb_order_$->{1..2}  #order1:数据源名称;两个tb_order表,分别为tb_order_1和tb_order_2
          key-generator: # 指定主键生成策略
            column: order_id
            type: SNOWFLAKE
          table-strategy:   #分表策略
            inline:
              sharding-column: order_id   #分片键。对id进行分表
              algorithm-expression: tb_order_$->{order_id % 2 + 1}  #分片算法
    props:
      sql:
        show: true  # 是否打印sql

1)所有sharding-jdbc的相关配置都在spring.shardingsphere开头的属性中;

2)数据源的配置在spring.shardingsphere.datasource开头的属性中;

3)分库分表策略的配置在spring.shardingsphere.sharding.tables开头的属性中;

分库分表策略都是针对表进行的。先指定逻辑表,然后指定分库策略、分表策略等。

5.3 实体类

package com.jingai.sharing.jdbc.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.ToString;

import java.util.Date;

@Data
@ToString
@TableName("tb_order")
public class OrderEntity {

    private long orderId;
    private long memberId;
    private float totalPrice;
    private String status;
    private Date orderTime;

}

在实体类中,@TableName指定配置中的逻辑表。

5.4 Mapper类

package com.jingai.sharing.jdbc.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jingai.sharing.jdbc.entity.OrderEntity;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Options;

public interface OrderMapper extends BaseMapper<OrderEntity> {

    @Insert("insert into tb_order(member_id, total_price, status, order_time) values " +
            "(#{memberId}, #{totalPrice}, #{status}, #{orderTime})")
    @Options(useGeneratedKeys = true, keyProperty = "orderId")
    int insert2(OrderEntity order);
}

在5.2的配置中,通过key-generator设置了逻辑表的主键生成策略为雪花算法。当进行数据插入时,需要编写新的插入接口,不能直接使用Mybatis-plus中的insert()接口。因为在默认的insert()接口中,实体对象的orderId为0,不会走配置的雪花算法。

5.5 Service类

package com.jingai.sharing.jdbc.service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jingai.sharing.jdbc.dao.OrderMapper;
import com.jingai.sharing.jdbc.entity.OrderEntity;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class OrderService extends ServiceImpl<OrderMapper, OrderEntity> {

    @Resource
    private OrderMapper orderMapper;

    public long insert2(OrderEntity order) {
        int rs = orderMapper.insert2(order);
        return rs > 0 ? order.getOrderId() : 0;
    }

}

为了便于测试,此处省略了Service的接口类。

5.6 Controller类

@RestController
public class OrderController {

    @Resource
    private OrderService orderService;

    @RequestMapping("order")
    public String order(OrderEntity order) {
        order.setOrderTime(new Date());
        long insert = orderService.insert2(order);
        return insert > 0 ? "success" : "fail";
    }

}

访问以上的order接口,系统打印的日志如下:

插入的数据为:

结合日志,执行流程如下:

1)在插入之前,解析逻辑SQL,获取分片键值tb_order;

2)配置的分库分表规则为以order_id作为分表的键,id被2整除的放tb_order_1,不能被2整除的放tb_order_2,以上的order_id为1014578121078210560,所以实际存放的表为tb_order_1;

3)根据实际操作的表tb_order_1,修改SQL语句;

4)执行真实SQL语句;

结尾

限于篇幅,本篇先分享到这里。

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

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

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

相关文章

新手教学系列——【Python开发】不同系统更换pip源的方法

在使用Python进行开发时,你可能会发现使用pip安装包的速度较慢,尤其是在国内进行操作时。为了提高安装速度,我们可以将pip的默认源更换为国内的一些镜像源。本文将详细介绍如何在不同操作系统上进行这一操作,并给出常用的国内镜像源。 为什么要换源 pip默认使用的是官方的…

嵌入式Linux系统编程 — 6.2 signal和 sigaction信号处理函数

目录 1 信号如何处理 2 signal()函数 2.1 signal()函数介绍 2.2 示例程序 3 sigaction()函数 3.1 sigaction()函数介绍 3.2 示例程序 1 信号如何处理 信号通常是发送给对应的进程&#xff0c;当信号到达后&#xff0c; 该进程需要做出相应的处理措施&#xff0c;可以通…

IP地址与电商企业

网购作为我们现代生活不可或缺的部分&#xff0c;现如今电商企业蓬勃发展。 IP地址是网络世界中每一台设备的独特标识符&#xff0c;就像现实世界中每家每户的门牌号。对于电商企业而言&#xff0c;它在很多方面方面发挥着作用。 IP地址能够帮助电商企业精准地确定用户所在的地…

从理论到实践的指南:企业如何建立有效的EHS管理体系?

企业如何建立有效的EHS管理体系&#xff1f;对于任何企业&#xff0c;没有安全就谈不上稳定生产和经济效益&#xff0c;因此建立EHS管理体系是解决企业长期追求的建立安全管理长效机制的最有效手段。良好的体系运转&#xff0c;可以最大限度地减少事故发生。 这篇借着开头这个…

要不要从单片机转Linux?进来看看大神怎么说

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;究竟要不要从单片机转Linu…

对象的引用和常引用

前面曾介绍&#xff1a;一个变量的引用就是变量的别名。实际上&#xff0c;引用是一个指针常量&#xff0c;用来存放该变量的地址。如果形参为变量的引用&#xff0c;实参为变量名&#xff0c;则在调用函数进行虚实结合时&#xff0c;把实参变量的地址传给形参&#xff08;引用…

2024 年江西省研究生数学建模竞赛题目 B题投标中的竞争策略问题---完整文章分享(仅供学习)

问题&#xff1a; 招投标问题是企业运营过程中必须面对的基本问题之一。现有的招投标平台有国家级的&#xff0c;也有地方性的。在招投标过程中&#xff0c;企业需要全面了解招标公告中的相关信息&#xff0c;在遵守招投标各种规范和制度的基础上&#xff0c;选择有效的竞争策…

工业交换机端口统计功能

工业交换机端口统计功能不仅是一项技术手段&#xff0c;更是一双透视企业网络健康状态的慧眼。通过这一功能&#xff0c;企业能够实时捕捉到网络中每一个端口的流量情况&#xff0c;这不仅仅是数据的积累&#xff0c;更是对网络脉搏的精准把握。当网络的每一个脉动都被记录在案…

【每日一练】Python遍历循环

1. 情节描述&#xff1a;上公交车(10个座位)&#xff0c;并且有座位就可以坐下 要求&#xff1a;输入公交卡当前的余额&#xff0c;只要超过2元&#xff0c;就可以上公交车&#xff1b;如果车上有空座位&#xff0c;才可以上。 seat 10 while seat > 0:money int(input(…

springboot个人证书管理系统-计算机毕业设计源码16679

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了个人证书管理系统的开发全过程。通过分析个人证书管理系统管理的不足&#xff0c;创建了一个计算机管理个人证书管理系统的方案。文章介绍了个人证书管理系统的系…

计算机组成原理 | CPU子系统(4)MIPS32架构-单周期处理器设计

R型运算指令通路 I型运算指令通路 I型访存指令数据通路 I型分支 J型j指令 重新布局 继续整合通路&#xff1a;MUX多路选择器 控制方式 硬连接控制方式&#xff1a;依靠电路 微命令控制&#xff1a;将指令转换为微命令 控制信号的整理和编码 控制系统的两级控制方案 ALU控制器&…

大模型时代的基础架构,大模型算力中心建设指南重磅来袭!

什么是最畅销商品&#xff1f;什么是高毛利商品&#xff1f; 我们来看一个例子&#xff1a; 一件T恤使用成本为100元的原料&#xff0c;价格为140元。另一件T恤使用成本为80元的原料&#xff0c;但在样式、颜色、图案的设计上比较有特色&#xff0c;价格也为140元。 当这两件…

【BES2500x系列 -- RTX5操作系统】深入探索CMSIS-RTOS RTX -- 同步与通信篇 -- 消息队列和邮箱处理 --(四)

&#x1f48c; 所属专栏&#xff1a;【BES2500x系列】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f49…

面向航天器大数据安全传输的发布/订阅系统设计

源自&#xff1a;系统工程与电子技术 作者&#xff1a;覃润楠 彭晓东 谢文明 惠建江 冯渭春 姜加红 注&#xff1a;若出现无法显示完全的情况&#xff0c;可 V 搜索“人工智能技术与咨询”查看完整文章 摘 要 针对航天器试验任务过程监控的在轨故障诊断状态检测、健…

5款简洁干净,功能强悍,专注实用的软件

​ 电脑上的各类软件有很多&#xff0c;除了那些常见的大众化软件&#xff0c;还有很多不为人知的小众软件&#xff0c;专注于实用功能&#xff0c;简洁干净、功能强悍。 1.音量控制利器——EarTrumpet ​ EarTrumpet是一款专为Windows用户设计的音量控制软件。它允许用户轻松…

等保测评应该选择什么样的SSL证书

选择适合等保测评的SSL证书&#xff0c;需考虑证书的加密强度、认证机制以及是否满足国家相关的密码技术要求 1、证书类型&#xff1a;应选择符合国家或行业标准的SSL证书&#xff0c;这些证书通常采用RSA、DSA或ECC等国际认可的加密算法。同时&#xff0c;考虑到国内特定的合规…

【C语言】常见的字符串函数

©作者:末央&#xff06; ©系列:C语言初阶(适合小白入门) ©说明:以凡人之笔墨&#xff0c;书写未来之大梦 目录 strlen函数模拟实现 strstr子串查找函数模拟实现 strtok字符串分割 strlen函数 strlen函数是一个用于求字符串长度的库函数。它的参数是被求长度的字…

免费分享:2000-2021年全国分省250mNDVI数据集(附下载方法)

NDVI (Normalized Difference Vegetation Index)归一化植被指数&#xff0c;又称标准化植被指数。是目前应用最广泛的植被指数&#xff0c;与植被的分布呈线性相关&#xff0c;是植被生长状态和空间分布的最佳指示因子&#xff0c;也是遥感估算植被覆盖度(FVC&#xff0c;Fract…

VMware Workstation 安装 Centos 虚拟机

1. 下载 VMware Workstation 直接上网找官网下载即可 2. 下载 Centos 镜像 阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 3.打开 VMware 创建虚拟机 3.1点击创建虚拟机 3.2 选择自定义安装 3.3 选择使用 Workstation 的版本 版本越高兼容性越低但性能越好&#xff0c;一…

APP性能测试

1、性能测试分类&#xff1a;&#xff08;CPU&#xff0c;内存&#xff0c;流量&#xff0c;时间&#xff08;启动耗时计算&#xff09;&#xff0c;电量&#xff0c;流畅度&#xff08;帧率&#xff09;&#xff09;&#xff0c;稳定性&#xff08;崩溃&#xff0c;闪退&#…