分库分表-ShardingSphere

目录

ShardingSphere简介      

基于INLINE行表达式的分片算法

基于CLASS_BASED自定义类的分片算法


ShardingSphere简介      

        Apache ShardingSphere 是一款分布式 SQL 事务和查询引擎,可通过数据分片、弹性伸缩、加密等能力对任意数据库进行增强。它的核心功能有:数据分片、分布式事务、读写分离、联邦查询、数据库加密及脱敏、数据迁移等,本文重点阐述数据分片。

        在大数据,高并发的场景下,数据库会存在性能瓶颈,需要对数据进行分片处理,比如分库分表处理。在分布式应用场景下,每个服务往往对应独立的数据库,天然进行了分库处理。在某个特定的服务中,由于某张表的数据量过大导致系统性能下降,需要对表进行拆分处理,如水平拆分和垂直拆分。

        水平拆分:表结构相同,不同的数据落到不同的表中;垂直拆分:多字段的表拆成多个少字段的表;分表分库常见的解决方案有:shardingsphere和mycat,本文重点阐述基于shardingsphere分表-水平拆分。

基于INLINE行表达式的分片算法

        环境介绍:springboot:2.7.6;shardingsphere:5.2.1;数据源使用Hikari;or mapping框架采用mybatis-plus 3.4.2;数据库mysql,实现基于shardingsphere分表-水平拆分的步骤如下:

        1、建立数据库ds0,建立2张相同结构的订单表

CREATE TABLE `t_order_0` (
  `order_id` bigint(20) NOT NULL,
  `user_id` varchar(100) NOT NULL,
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `t_order_1` (
  `order_id` bigint(20) NOT NULL,
  `user_id` varchar(100) NOT NULL,
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

        2、添加项目依赖shardingsphere

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.gingko</groupId>
    <artifactId>data-sharding</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>data-sharding</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.7.6</spring-boot.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.0</version>
        </dependency>
        <!-- springboot默认数据源(HikariCP)-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>
        <!-- 整合MyBatis -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>
        <!-- 整合shardingsphere -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.33</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.gingko.datasharding.DataShardingApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

        3、配置shardingsphere,参考官方配置说明:

        核心的配置:algorithm-expression: t_order_${order_id % 2} #订单号对2取余的结果,决定数据分片到对应的t_order_0还是t_order_1表。

server:
  port: 8080 #配置应用端口
spring:
  shardingsphere:
    # 配置显示sql
    props:
      sql-show: true
    datasource:
      names: ds0 #配置数据源列表,多个数据源使用逗号分割
      ds0: #数据源配置
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost/ds0?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&allowMultiQueries=true
        username: root
        password: 123456
    rules:
      sharding:
        tables:
          t_order: #需要分表的逻辑表名
            actualDataNodes: ds0.t_order_${0..1} #表的真实表列表
            tableStrategy:  # 分片策略配置
              standard:
                shardingColumn: order_id #分表列
                shardingAlgorithmName: t_order_inline #分片算法是 t_order_inline, 这个名称是在下面配置的
        sharding-algorithms: # 配置分片算法
          t_order_inline: # 分片算法名称
            type: INLINE
            props:
              algorithm-expression: t_order_${order_id % 2} #订单号对2取余

        4、entity、mapper、测试类

package com.gingko.datasharding.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_order")
public class Order {

    @TableId
    private long orderId;//订单号
    private String userId;//订单发起用户
}
package com.gingko.datasharding.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gingko.datasharding.entity.Order;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Repository
@Mapper
public interface OrderMapper extends BaseMapper<Order> {

}
package com.gingko.datasharding;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gingko.datasharding.entity.Order;
import com.gingko.datasharding.mapper.OrderMapper;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.List;

@Slf4j
@SpringBootTest
class OrderTest {

    @Resource
    private OrderMapper orderMapper;
    @Test
    void batchAdd() {
        for(int i=0;i<10;i++) {
            Order order = new Order(i,"admin" + i);
            orderMapper.insert(order);
        }
    }

    @Test
    void getByOrderId() {
        Order order = orderMapper.selectById(5);
        log.info("订单信息:{}",order);
    }

    @Test
    void getByUserId() {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.like("user_id","admin");
        List list = orderMapper.selectList(queryWrapper);
        log.info("获取用户的订单信息个数是:{}",list.size());
    }

}

         5、测试

        运行batchAdd方法后,表t_order_0和t_order_1分别插进5条数据,按照order_id%2的规则插入。

 

         运行getByOrderId方法后,由于参数order_id=5,%2=1,所以实际运行的sql只会从表t_order_1查询。

        运行getByUserId方法,查询方法是按照user_id 模糊查询,由于不是按照user_id字段进行数据分表的,所以此查询会同时查询t_order_0和t_order_1表。

基于CLASS_BASED自定义类的分片算法

        实际项目中,数据分片的规则往往基于实际的业务需求,下方示例演示基于user_id数据进行分片,模拟当user_id包含admin时,数据进入t_order_0表,否则数据进入t_order_1表,步骤如下:

        1、修改配置,修改增加自定义的分片算法

        2、实现自定义的分片算法,实现StandardShardingAlgorithm接口,泛型的类型String就是user_id属性的类型

package com.gingko.datasharding.config;
import lombok.extern.slf4j.Slf4j;
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.Collection;
import java.util.Properties;

/**
 * 自定义分片算法,算法基于user_id属性值分片,user_id 包含admin的进入t_order_0表,其他进入t_order_1表
 */
@Slf4j
public class CustomShardingAlgorithm implements StandardShardingAlgorithm<String> {

    @Override
    public String doSharding(Collection<String> collection,
                             PreciseShardingValue<String> shardingValue) {
        String value = shardingValue.getValue();
        log.info("shardingValue = {}",value);
        /**
         * 根据value的值,本项目就是user_id,依据实际的业务规则生成精确的操作的表名actuallyTableName
         * 目前模拟为:user_id 包含admin的进入t_order_0表,其他进入t_order_1表
         */
        String suffixTableName = "";
        if(value.contains("admin")) {
            suffixTableName = "_0";
        }else {
            suffixTableName = "_1";
        }
        String actuallyTableName = shardingValue.getLogicTableName() + suffixTableName;
        if (collection.contains(actuallyTableName)) {
            return actuallyTableName;
        }
        return null;
    }

    /**
     * SPI方式的 SPI名称,配置文件中配置的时候需要用到
     */
    @Override
    public String getType() {
        return "CUSTOM_SPI_BASED";
    }

    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<String> rangeShardingValue) {
        return null;
    }

    @Override
    public Properties getProps() { return null;}

    @Override
    public void init(Properties properties) {}
}

        3、 按照官方文档配置

        在项目的resources目录下建立文件夹META-INF/services,并在该文件夹下创建文件 org.apache.shardingsphere.sharding.spi.ShardingAlgorithm, 文件内写上我们自定义的分片算法:com.gingko.datasharding.config.CustomShardingAlgorithm

        4、测试类测试

        运行后,发现user_id包含admin的都在表 t_order_0中,其他的在表 t_order_1中,符合预期。

### ShardingSphere 分库分表 MySQL 实现与配置教程 ShardingSphere 是一个开源的分布式数据库中间件,支持通过分库分表实现数据水平扩展[^1]。以下详细介绍了如何使用 ShardingSphere 和 MySQL 实现分库分表的配置与实现。 #### 1. 环境准备 在开始配置之前,确保已安装并运行以下环境: - JDK 8 或更高版本。 - MySQL 数据库(建议版本 5.7 或 8.0)。 - Apache ShardingSphere(推荐使用最新稳定版本)。 此外,可以通过 ShardingSphere-JDBC 或 ShardingSphere-Proxy 来接入系统。ShardingSphere-JDBC 是以 Java 驱动的方式嵌入到应用中,而 ShardingSphere-Proxy 则是以独立服务的形式存在[^2]。 #### 2. 数据库和表结构设计 假设需要将日志表 `log` 按照日期进行分片,并且数据存储在两个数据库 `ds0` 和 `ds1` 中。每个数据库中包含多个分片表 `log_YYYYMMDD`。 ```sql -- 在 ds0 中创建表 CREATE TABLE log_202301 ( id BIGINT PRIMARY KEY, content VARCHAR(255), created_date DATE ); -- 在 ds1 中创建表 CREATE TABLE log_202302 ( id BIGINT PRIMARY KEY, content VARCHAR(255), created_date DATE ); ``` #### 3. 数据源配置 ShardingSphere 的核心是数据源配置,定义了分库规则和分表规则。以下是基于 Spring Boot 的配置示例[^4]: ```yaml spring: shardingsphere: datasource: names: ds0,ds1 ds0: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useSSL=false username: root password: root ds1: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3306/test1?serverTimezone=UTC&useSSL=false username: root password: root ``` #### 4. 分片规则配置 分片规则定义了数据如何分布到不同的数据库和表中。以下是一个典型的分片规则配置[^3]: ```yaml rules: sharding: tables: log: actual-data-nodes: ds${0..1}.log_${2023..2024}${01..12} table-strategy: standard: sharding-column: created_date sharding-algorithm-name: log-table-algorithm database-strategy: standard: sharding-column: created_date sharding-algorithm-name: log-database-algorithm sharding-algorithms: log-database-algorithm: type: INLINE props: algorithm-expression: ds${created_date.format('yyyy') % 2} log-table-algorithm: type: INLINE props: algorithm-expression: log_${created_date.format('yyyyMM')} ``` 上述配置表示: - 数据库按照 `created_date` 的年份取模分配到 `ds0` 或 `ds1`。 - 表按照 `created_date` 的年月生成对应的分片表名称。 #### 5. 测试验证 完成配置后,可以通过 SQL 查询验证分片规则是否生效。例如: ```sql INSERT INTO log (id, content, created_date) VALUES (1, 'Test Log', '2023-01-15'); INSERT INTO log (id, content, created_date) VALUES (2, 'Test Log', '2023-02-20'); SELECT * FROM log WHERE created_date = '2023-01-15'; SELECT * FROM log WHERE created_date = '2023-02-20'; ``` 查询结果应分别从 `ds0.log_202301` 和 `ds1.log_202302` 返回数据。 #### 6. 注意事项 - 确保数据库和表已经按照分片规则预先创建好。 - 分片键的选择至关重要,通常选择查询条件中频繁使用的字段作为分片键[^3]。 - 如果需要读写分离,可以结合 ShardingSphere 的读写分离功能进行配置[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值