MYSQL系列-分库分表(二):Spring动态数据源实现分库分表落地实践-上

系列文档参考 MYSQL系列-整体架构介绍
目前市面上有很多比较成熟的分库分表中间件,可以帮助我们解决分库分表后多数据源问题

目前常用的就是下面三种,接下来3个章节我们来一一介绍

  1. 「 Spring 动态数据源 」
  2. 「shardingsphere(前身 sharding-jdbc)」
  3. 「Mycat」

原始需求

有一个会员积分系统,承载线上业务会员积分能力,随着用户数的增长,会员积分相关表数据量急剧增长,单表存储已经不能满足业务需求,需要对积分相关表进行分库分表处理,以适应日益增长的数据量。

因积分数据量巨大,先需要对同一个表进行水平分库分表处理

为啥要分库分表处理?

  • 如果仅分库不分表,那么可能需要100个库或者更多,然而数据库连接池是db实例级别的,如果每个数据库连接池最小连接数是10,那么最少也需要1000个常驻线程,会把本地线程撑爆
  • 如果仅分表不分库,那么高峰时期的流程都打到同一db上,这样也是无法横向扩展的

拆分结果类似如下(示例拆成2个库,每个库2张分表):

设计思路

基于Spring boot + mybatics + druid + 动态数据源,支持读写分离,配置表落在积分库1

实现方案

数据库表脚本

新建了2个库point_shard1、point_shard2 其中point_balance在1 2库各两张分表point_balance1 point_balance2

param_config是配置表,落在1库

sql 复制代码
CREATE DATABASE if not exists point_shard1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE if not exists point_shard2 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
use point_shard1;
DROP TABLE IF EXISTS `point_balance1`;
CREATE TABLE IF NOT EXISTS `point_balance1` (
    `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    `uid` BIGINT(20) NOT NULL COMMENT 'uid',
    `type` SMALLINT(5) NOT NULL COMMENT '积分类型',
    `value` BIGINT(20) NOT NULL COMMENT '积分值',
    `expire_time` DATE NOT NULL COMMENT '过期时间',
    `modify_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
    `country` VARCHAR(20) NOT NULL DEFAULT 'CN' COMMENT '国家',
    PRIMARY KEY (`id`),
    UNIQUE KEY idx_uid_and_expire_time(`uid`,`expire_time`)
    ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户余额表1';

DROP TABLE IF EXISTS `param_config`;
CREATE TABLE param_config (
                              `id` INT AUTO_INCREMENT NOT NULL COMMENT '自增ID',
                              `param_key` VARCHAR (50) NOT NULL COMMENT '配置KEY',
                              `param_key_desc` VARCHAR (200) DEFAULT NULL COMMENT '配置KEY说明',
                              `param_value` VARCHAR (1024) NOT NULL COMMENT '配置value',
                              `param_value_type` SMALLINT (2) NOT NULL DEFAULT 0 COMMENT '参数类型',
                              `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
                              `modify_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
                              `country` VARCHAR (20) NOT NULL DEFAULT 'CN' COMMENT '国家',
                              PRIMARY KEY (`id`),
                              INDEX idx_param_key_country (param_key, country)
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8 COMMENT = '配置参数表' ;

maven相关引用

因为涉及spring boot+mybatics+druid,以及调试使用junit,sl4j日志打印,最终引用包如下

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.7.9</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
        <version>2.7.9</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.0</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.16</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.9.5</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.5</version>
    </dependency>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib-nodep</artifactId>
        <version>3.2.10</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>2.7.9</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j2</artifactId>
        <version>2.7.9</version>
    </dependency>
</dependencies>

mybatics相关配置

mybatics配置信息我选择放在xml配置文件中,放在\resources\spring\spring_jdbc.xml 首先druid配置数据库连接串信息,如下

xml 复制代码
<bean id="db_w_1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    <property name="url" value="${master.jdbc.url1}"/>
    <property name="username" value="${master.jdbc.username}"/>
    <property name="password" value="${master.jdbc.password}"/>

    <!-- Druid连接池属性配置 -->
    <property name="initialSize" value="5"/>
    <property name="maxActive" value="20"/>
    <property name="minIdle" value="5"/>
    <property name="maxWait" value="60000"/>
    <!-- 添加更多的属性配置 -->
    <property name="filters" value="config"/>
    <property name="validationQuery" value="select 1"/>
    <property name="timeBetweenEvictionRunsMillis" value="600000"/>
    <property name="minEvictableIdleTimeMillis" value="600000"/>
    <property name="defaultAutoCommit" value="true"/>
    <property name="testOnBorrow" value="true"/>
    <property name="testOnReturn" value="true"/>
    <property name="testWhileIdle" value="true"/>
    <property name="removeAbandoned" value="true"/>
    <property name="removeAbandonedTimeout" value="1800"/>
    <property name="logAbandoned" value="true"/>
</bean>

共有写库:db_w_1 db_w_2 读库:db_r_1 db_r_2

自己实现的数据源ShardingDataSource,路由策略ContextDbRouter,公共类(包含分库分表算法)DataSourceUtil以及其他的一些通用配置实现,具体配置如下

xml 复制代码
<bean id="shardingDataSource" class="com.toby.dynamic.data.source.db.ShardingDataSource"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="shardingDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" order="2"/>
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="shardingDataSource"/>
    <property name="configLocation" value="classpath:sqlmap-config.xml"/>
    <property name="mapperLocations">
        <array>
            <value>classpath*:sqlmap/*.xml</value>
            <value>classpath*:sqlmap/config/*.xml</value>
        </array>
    </property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.toby.dynamic.data.source.db.dao"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
</bean>
<!-- 配置的分库分表以及读写库数据源-->
<util:map id="datasourceMap" key-type="java.lang.String" value-type="java.util.Map">
    <entry key="MASTER">
        <map key-type="java.lang.String" value-type="java.lang.String">
            <entry key="1" value="db_w_1"/>
            <entry key="2" value="db_w_2"/>
            <!-- default 默认数据源写库-->
            <entry key="-1" value="db_w_1"/>
        </map>
    </entry>
    <entry key="READ">
        <map>
            <entry key="1" value="db_r_1"/>
            <entry key="2" value="db_r_2"/>
            <!-- default 默认数据源读库-->
            <entry key="-1" value="db_r_1"/>
        </map>
    </entry>
</util:map>
<!-- 分库分表路由实现-->
<bean class="com.toby.dynamic.data.source.db.route.ContextDbRouter">
    <property name="dataSourceGroup" ref="datasourceMap"/>
</bean>

分库核心实现

UML类图如下

未完待续

相关推荐
paopaokaka_luck4 分钟前
基于Spring Boot+Vue的多媒体素材管理系统的设计与实现
java·数据库·vue.js·spring boot·后端·算法
程序猿麦小七37 分钟前
基于springboot的景区网页设计与实现
java·spring boot·后端·旅游·景区
蓝田~1 小时前
SpringBoot-自定义注解,拦截器
java·spring boot·后端
theLuckyLong1 小时前
SpringBoot后端解决跨域问题
spring boot·后端·python
.生产的驴1 小时前
SpringCloud Gateway网关路由配置 接口统一 登录验证 权限校验 路由属性
java·spring boot·后端·spring·spring cloud·gateway·rabbitmq
小扳1 小时前
Docker 篇-Docker 详细安装、了解和使用 Docker 核心功能(数据卷、自定义镜像 Dockerfile、网络)
运维·spring boot·后端·mysql·spring cloud·docker·容器
v'sir1 小时前
POI word转pdf乱码问题处理
java·spring boot·后端·pdf·word
李少兄1 小时前
解决Spring Boot整合Redis时的连接问题
spring boot·redis·后端
码上一元6 小时前
SpringBoot自动装配原理解析
java·spring boot·后端
锐策8 小时前
〔 MySQL 〕数据库基础
数据库·mysql