背景

近期在项目中需要使用多数据源,其中有一些表的数据量比较大,需要对其进行分库分表;而其他数据表数据量比较正常,单表就可以。

项目中可能使用其他组的数据源数据,因此需要多数据源支持。

经过调研多数据源配置比较方便。在该项目中分库分表的策略比较简单,仅根据一个字段分就可以,因此分库分表方案选用比较流行方便易用的 sharding-jdbc

需要实现的目标是 根据学生姓名字段 student_name 进行分表,但是不需要分库。数据表是student_hist0 - student_hist9

引入 sharding-jdbc maven 依赖

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

数据源配置文件

spring:
application:
name: student-service-provider
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
defaultPropertyInclusion: non_null
deserialization:
FAIL_ON_UNKNOWN_PROPERTIES: false
#对返回的时间进行格式化
datasource:
hikari:
student:
url: jdbc:mysql://127.0.0.1:3306/student_service?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&useTimezone=true&serverTimezone=GMT%2
username: root
password: root123
log1:
url: jdbc:mysql://127.0.0.1:3306/log1?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root123
log2:
url: jdbc:mysql://127.0.0.1:3306/log2?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root123

配置多数据源代码

DataSourceProperties 数据源

import com.zaxxer.hikari.HikariDataSource;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; @Data
@Configuration
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public class DataSourceProperties { private HikariDataSource student;
private HikariDataSource log1;
private HikariDataSource log2; }

DynamicDataSource 动态数据源

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

    /*
当前据源名称
*/
private static final ThreadLocal<String> dataSourceContextHolder = new ThreadLocal<>(); /*
设置数据源名称
*/
public static void setDataSourceName(String dataSourceName) {
dataSourceContextHolder.set(dataSourceName);
} /*
获取据源名称
*/
public static String getDataSourceName() {
return dataSourceContextHolder.get();
} /*
清除当数据源名称
*/
public static void clearDataSource() {
dataSourceContextHolder.remove();
} @Override
protected Object determineCurrentLookupKey() {
return getDataSourceName();
}
}

MultiDataSource 多数据源标记

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MultiDataSource {
String value() default DataSourceConfig.DEFAULT_DATASOURCE_NAME;
}

重点来了,因为是根据表的某一个字段进行分表,该字段是一个字符串类型,因此需要想根据字符串的一致性hash码算出在哪张表上。在sharding-jdbc需要实现 PreciseShardingAlgorithm 类

例如:想要在student.student_hist 表中根据学生姓名进行分表,逻辑表是student_hist,真实表是 student_hist0 - student_hist9

DataSourceConfig.SHARD_MMS_DATASOURCE_TABLE_COUNT=10

import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import java.util.Collection; public class PreciseNodeIdShardingAlgorithm implements PreciseShardingAlgorithm<String> { @Override
public String doSharding(Collection<String> collection, PreciseShardingValue<String> preciseShardingValue) {
for (String tbnm : collection) {
if (tbnm.endsWith("hist" + (getHash(preciseShardingValue.getValue()) % DataSourceConfig.SHARD_MMS_DATASOURCE_TABLE_COUNT))) {
return tbnm;
}
}
throw new UnsupportedOperationException();
} private static int getHash(String str) {
final int p = 16777619;
int hash = (int) 2166136261L;
for (int i = 0; i < str.length(); i++)
hash = (hash ^ str.charAt(i)) * p;
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5; // 如果算出来的值为负数则取其绝对值
if (hash < 0)
hash = Math.abs(hash);
return hash;
}
}

多数据源装配 DataSourceConfig 。需要指定默认数据源,当不需要使用 分表的表时就使用默认的数据源,否则指定需要分表的数据源。

在配置分表策略时如果不需要分库,可以不进行设置 tableRuleConfiguration.setDatabaseShardingStrategyConfig();

import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.StandardShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties; @Configuration
public class DataSourceConfig { public static final String DEFAULT_DATASOURCE_NAME = "defaultDataSource";
public static final String MMS_DATASOURCE_NAME = "mmsDatasource";
public static final String SHARD_MMS_DATASOURCE_NAME = "shardMmsDatasource"; public static int SHARD_MMS_DATASOURCE_TABLE_COUNT = 10; @Autowired
private DataSourceProperties properties; @Primary
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(properties.getMms());
// 配置多数据源
Map<Object, Object> dsMap = new HashMap();
dsMap.put(DEFAULT_DATASOURCE_NAME, properties.getStudent());
dsMap.put(MMS_DATASOURCE_NAME, properties.getStudent());
dsMap.put(SHARD_MMS_DATASOURCE_NAME, buildShardDatasources());
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
} public DataSource buildShardDatasources() {
// 配置多数据源
Map<String, DataSource> dsMap = new HashMap();
dsMap.put("shardMms", properties.getMms());
TableRuleConfiguration stuHisTableRuleConfig = new TableRuleConfiguration("student_hist", "shardMms.student_hist${0.." + (SHARD_MMS_DATASOURCE_TABLE_COUNT - 1) + "}");
// tableRuleConfiguration.setDatabaseShardingStrategyConfig();
stuHisTableRuleConfig.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("student_name", new PreciseNodeIdShardingAlgorithm())); ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
shardingRuleConfig.getTableRuleConfigs().add(stuHisTableRuleConfig);
try {
Properties properties = new Properties();
properties.setProperty("sql.show", "true");
return ShardingDataSourceFactory.createDataSource(dsMap, shardingRuleConfig, properties);
} catch (SQLException throwables) {
throwables.printStackTrace();
throw new IllegalArgumentException();
}
} @Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
} }

多数据源切换 DataSourceAspect ,需要使用多数据源切换时,需要在service方法上使用标注方法 MultiDataSource 并指定数据源。

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import java.lang.reflect.Method; @Aspect
@Configuration
@Slf4j
@Order(1)
public class DataSourceAspect { //切入点,service 中所有注解方法
@Pointcut("execution(* com.huitong..service..*.*(..)) && @annotation(com.huitong.app.config.datasource.MultiDataSource)")
public void dataSourceAspect() {
} @Around("dataSourceAspect()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
MultiDataSource ds = method.getAnnotation(MultiDataSource.class);
if (ds != null) {
DynamicDataSource.setDataSourceName(ds.value());
}
try {
return joinPoint.proceed();
} finally {
DynamicDataSource.clearDataSource();
}
}
}

参考文献:

在多数据源中对部分数据表使用shardingsphere进行分库分表的更多相关文章

  1. 分库分表(7)--- SpringBoot+ShardingSphere实现分库分表 + 读写分离

    分库分表(7)--- ShardingSphere实现分库分表+读写分离 有关分库分表前面写了六篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理 ...

  2. 分库分表(5) ---SpringBoot + ShardingSphere 实现分库分表

    分库分表(5)--- ShardingSphere实现分库分表 有关分库分表前面写了四篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理论) 3. ...

  3. Springboot2.x + ShardingSphere 实现分库分表

    之前一篇文章中我们讲了基于Mysql8的读写分离(文末有链接),这次来说说分库分表的实现过程. 概念解析 垂直分片 按照业务拆分的方式称为垂直分片,又称为纵向拆分,它的核心理念是专库专用. 在拆分之前 ...

  4. 【分库分表】sharding-jdbc实践—分库分表入门

    一.准备工作 1.准备三个数据库:db0.db1.db2 2.每个数据库新建两个订单表:t_order_0.t_order_1 DROP TABLE IF EXISTS `t_order_x`; CR ...

  5. 数据量大了一定要分表,分库分表组件Sharding-JDBC入门与项目实战

    最近项目中不少表的数据量越来越大,并且导致了一些数据库的性能问题.因此想借助一些分库分表的中间件,实现自动化分库分表实现.调研下来,发现Sharding-JDBC目前成熟度最高并且应用最广的Java分 ...

  6. SpringCloud微服务实战——搭建企业级开发框架(二十七):集成多数据源+Seata分布式事务+读写分离+分库分表

    读写分离:为了确保数据库产品的稳定性,很多数据库拥有双机热备功能.也就是,第一台数据库服务器,是对外提供增删改业务的生产服务器:第二台数据库服务器,主要进行读的操作. 目前有多种方式实现读写分离,一种 ...

  7. 【大数据和云计算技术社区】分库分表技术演进&最佳实践笔记

    1.需求背景 移动互联网时代,海量的用户每天产生海量的数量,这些海量数据远不是一张表能Hold住的.比如 用户表:支付宝8亿,微信10亿.CITIC对公140万,对私8700万. 订单表:美团每天几千 ...

  8. CRL快速开发框架系列教程十一(大数据分库分表解决方案)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  9. 解读分库分表中间件Sharding-JDBC

    [编者按]数据库分库分表从互联网时代开启至今,一直是热门话题.在NoSQL横行的今天,关系型数据库凭借其稳定.查询灵活.兼容等特性,仍被大多数公司作为首选数据库.因此,合理采用分库分表技术应对海量数据 ...

随机推荐

  1. odoo14里面的log模块

    日志记录是分析 Odoo 服务器运行服务时发生的操作的最重要工具之一.此外,在大多数情况下,通过分析服务器日志,我们可以清楚地了解错误或存在的任何错误. 最初,如果我们想使用日志记录选项,我们需要从 ...

  2. JAVA学习笔记之基础概念(一)

    一.Java 简介: Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 面向对象程序设计语言和 Java 平台的总称. 由 James Gosling和同 ...

  3. 2021年BI软件排名,国内外BI软件功能对比

    数据分析是帮助企业深入了解自身业务表现(例如正在做什么或哪块业务需要注意和改进)的重要元素.为了获得更直观的展现,数据分析BI软件可帮助公司通过报告.数据可视化.应用程序等从数据中获取决策数据支撑.没 ...

  4. 基于单机redis的分布式锁实现

    最近我们有个服务经常出现存储的数据出现重复,首先上一个系统流程图: 用户通过http请求可以通知任务中心结束掉自己发送的任务,这时候任务中心会通过MQ通知结束服务去结束任务保存数据,由于任务结束数据计 ...

  5. 创建函数function

    1.创建普通函数 function 函数名称(){ 函数体://封装的代码 } 函数名称()://调用函数 function getSum(){ for(var i=1,sum=0;i<=100 ...

  6. Mongodb集成LDAP授权

    一.环境简介 Mongodb enterprise v4.0.16 OpenLDAP v2.4.44 二.Mongodb集成LDAP的授权过程 客户端指定某种外部验证方式链接Mongodb: Mong ...

  7. appium的安装和环境配置教程

    模拟器安装 夜神模拟器下载地址:https://www.yeshen.com/ 无脑安装 jdk环境 安装jdk 安装教程:https://www.cnblogs.com/yhoil/p/148086 ...

  8. 【LeetCode】424. 替换后的最长重复字符

    424. 替换后的最长重复字符 知识点:字符串,滑动窗口: 题目描述 给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次.在执行上述操作后,找到包含 ...

  9. 腾讯技术团队整理,为什么 Flutter 能最好地改变移动开发

    导语 | Flutter 框架是当下非常热门的跨端解决方案,能够帮助开发者通过一套代码库高效构建多平台精美应用,支持移动.Web.桌面等多端开发.但仍然有很多产品.设计.甚至开发同学并不了解 Flut ...

  10. Python3中的字符串相关操作

    Python3的字符串操作相关函数详解 字符串内建函数 1. capitalize() 将字符串中的第一个字符转换成大写,其他字母变成小写.例: >>> "hello Wo ...