乐趣区

ShardingJDBC之分库分表

一、Sharding-JDBC 简介

Sharding-JDBC 是 ShardingSphere 的第一个产品,也是 ShardingSphere 的前身。它定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

  • 适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。
  • 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。
  • 支持任意实现 JDBC 规范的数据库。目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 标准的数据库。

使用 Sharding-JDBC 可以实现数据分片、读写分离、分布式事务等需求。

本篇文章主要介绍通过 Sharding-JDBC 实现数据分片,即分库分表。

Sharding-JDBC 提供了 4 种配置方式,用于不同的使用场景。本文只介绍 Spring 命名空间配置和 SpringBoot 配置。

二、准备工作

在虚拟机上部署两台 Mysql 服务器,IP 地址分别为 192.168.0.150,192.168.0.152。

然后创建数据库,数据库名分别为 sharding_order,shard_order 以示区分。

在 sharding_order,shard_order 中均创建两张结构一致的表,表名分别为 t_order_1,t_order_2。建表语句如下:

CREATE TABLE `t_order_1` (
  `id` int NOT NULL,
  `order_amount` decimal(10,2) NOT NULL,
  `order_status` int NOT NULL,
  `user_id` int NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

三、创建项目

在 IDEA 中新建 SpringBoot 项目,选择以下依赖

然后通过 MyBatis Generator 创建实体类和 Dao 层接口及 Mapper 文件。配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!-- 配置文件路径 -->
    <properties resource="dbconfig.properties"/>

    <!-- 数据库驱动包路径 已经在 Maven 的 pom 配置过了,这里就不需要了 -->
    <!--<classPathEntry location="${drive.class.path}"/>-->

    <context id="MySQLTables" targetRuntime="MyBatis3Simple">
        <!-- 关闭注释 -->
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <!-- 是否去除自动生成的注释 true:是:false: 否 -->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!-- 数据库连接信息 -->
        <jdbcConnection driverClass="${jdbc.driver}" connectionURL="${jdbc.url}" userId="${jdbc.username}"
                        password="${jdbc.password}">
        </jdbcConnection>
        <!--
        默认 false,把 JDBC DECIMAL 和 NUMERIC 类型解析为 Integer
            true,把 JDBC DECIMAL 和 NUMERIC 类型解析为 java.math.BigDecimal
         -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>


        <!-- 生成的 model 包路径 -->
        <javaModelGenerator targetPackage="${model.package}" targetProject="src\main\java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!-- 生成 xml mapper 文件 路径 -->
        <sqlMapGenerator targetPackage="${xml.mapper.package}" targetProject="src\main\resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>

        <!-- 生成的 Dao 接口 的包路径 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="${dao.package}" targetProject="src\main\java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!-- 对应数据库表名 -->
        <table schema="sharding_order" tableName="t_order_1" domainObjectName="Order"></table>
    </context>
</generatorConfiguration>

项目创建完成后的结构如下:

四、Spring 命名空间配置

在 pom 文件中引入 Sharding-JDBC 命名空间配置的依赖

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-namespace</artifactId>
    <version>4.1.1</version>
</dependency>

在 resources 目录下新建配置文件 sharding-jdbc.xml, 内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sharding="http://shardingsphere.apache.org/schema/shardingsphere/sharding"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://shardingsphere.apache.org/schema/shardingsphere/sharding
                        http://shardingsphere.apache.org/schema/shardingsphere/sharding/sharding.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/tx
                        http://www.springframework.org/schema/tx/spring-tx.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">
    
    <!-- 配置数据源,对应分片的数据库,有几个数据库就需要配置几个数据源 -->
    <!-- 这里数据源使用的是 HikariDataSource,也可以使用其他的数据源 -->
    <bean id="ds0" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://192.168.0.150:3306/sharding_order?serverTimezone=Asia/Shanghai&amp;useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value=""/>
    </bean>
    <bean id="ds1" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://192.168.0.152:3306/shard_order?serverTimezone=Asia/Shanghai&amp;useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value=""/>
    </bean>
    
    <!-- 配置 sharding-jdbc 的数据源 -->
    <sharding:data-source id="shardingDataSource">
        <!--data-source-names 数据源 Bean 列表,多个 Bean 以逗号分隔 -->
        <sharding:sharding-rule data-source-names="ds0,ds1">
            <!--table-rules 表分片规则配置对象 -->
            <sharding:table-rules>
                <!--logic-table 逻辑表名称 -->
                <!--actual-data-nodes 由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持 inline 表达式。-->
                <sharding:table-rule logic-table="t_order" actual-data-nodes="ds$->{0..1}.t_order_$->{1..2}" database-strategy-ref="databaseShardingStrategy" table-strategy-ref="tableShardingStrategy" />
            </sharding:table-rules>
        </sharding:sharding-rule>
    </sharding:data-source>

    <!-- 数据库分片策略, 使用 user_id 作为分片的列,user_id 对 2 取模,user_id 为奇数的分到 ds1, 偶数的则分到 ds0-->
    <sharding:inline-strategy id="databaseShardingStrategy" sharding-column="user_id" algorithm-expression="ds$->{user_id % 2}"/>
    <!-- 表分片策略, 使用 id 作为分片的列,id 对 2 取模然后加 1,id 为奇数的分到 t_order_2, 偶数的则分到 t_order_1-->
    <sharding:inline-strategy id="tableShardingStrategy" sharding-column="id" algorithm-expression="t_order_$->{id % 2 + 1}"/>

    <!-- 配置 Mybatis 的 SqlSessionFactoryBean-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="shardingDataSource"/>
        <property name="mapperLocations" value="classpath*:/mapper/*.xml"/>
    </bean>

</beans>

在启动类上添加 @ImportResource@MapperScan注释

在测试类中创建测试方法

@SpringBootTest
class ShardingDemoApplicationTests {
    @Resource
    OrderMapper orderMapper;

    @Test
    void contextLoads() {}

    @Test
    public void testOrder(){Order order=new Order();
        order.setId(21);
        order.setOrderAmount(BigDecimal.TEN);
        order.setOrderStatus(0);
        order.setUserId(1);
        orderMapper.insert(order);
    }

}

这里 id 设置为 21,user_id 设置为 1,根据前面配置的分库规则使用 user_id 取模结果为 1,所以该记录会落在数据源 ds1 中,即 152 服务器上的数据库。根据分表的规则 id 取模再加 1,结果为 2,所以该记录最终落在 152 数据库 shard_order 数据库中的 t_order_2 表中。

再将 id 设置为 22,user_id 设置为 2,该记录最终应落在 150 数据库 sharding-order 的 t_order_1 中。

五、SpringBoot 配置

将 Spring 命名空间配置的依赖删除或注释,引入 SpringBoot 配置的依赖

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

在 application.properties 中添加以下配置

spring.shardingsphere.datasource.names=ds0,ds1

spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbcUrl=jdbc:mysql://192.168.0.150:3306/sharding_order
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=

spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbcUrl=jdbc:mysql://192.168.0.152:3306/shard_order
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=

spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds$->{0..1}.t_order_$->{1..2}
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression=t_order_$->{id % 2 + 1}
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression=ds$->{user_id % 2}

mybatis.mapper-locations=/mapper/*.xml

将启动类上的 @ImportResource 删除或者注释

测试方法同 Spring 命名空间配置的方式。

退出移动版