背景

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

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

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

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

引入 sharding-jdbc maven 依赖

  1. <dependency>
  2. <groupId>org.apache.shardingsphere</groupId>
  3. <artifactId>sharding-jdbc-core</artifactId>
  4. <version>4.1.1</version>
  5. </dependency>

数据源配置文件

  1. spring:
  2. application:
  3. name: student-service-provider
  4. jackson:
  5. date-format: yyyy-MM-dd HH:mm:ss
  6. time-zone: GMT+8
  7. defaultPropertyInclusion: non_null
  8. deserialization:
  9. FAIL_ON_UNKNOWN_PROPERTIES: false
  10. #对返回的时间进行格式化
  11. datasource:
  12. hikari:
  13. student:
  14. url: jdbc:mysql://127.0.0.1:3306/student_service?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&useTimezone=true&serverTimezone=GMT%2
  15. username: root
  16. password: root123
  17. log1:
  18. url: jdbc:mysql://127.0.0.1:3306/log1?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false
  19. username: root
  20. password: root123
  21. log2:
  22. url: jdbc:mysql://127.0.0.1:3306/log2?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false
  23. username: root
  24. password: root123

配置多数据源代码

DataSourceProperties 数据源

  1. import com.zaxxer.hikari.HikariDataSource;
  2. import lombok.Data;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. import org.springframework.context.annotation.Configuration;
  5. @Data
  6. @Configuration
  7. @ConfigurationProperties(prefix = "spring.datasource.hikari")
  8. public class DataSourceProperties {
  9. private HikariDataSource student;
  10. private HikariDataSource log1;
  11. private HikariDataSource log2;
  12. }

DynamicDataSource 动态数据源

  1. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  2. public class DynamicDataSource extends AbstractRoutingDataSource {
  3. /*
  4. 当前据源名称
  5. */
  6. private static final ThreadLocal<String> dataSourceContextHolder = new ThreadLocal<>();
  7. /*
  8. 设置数据源名称
  9. */
  10. public static void setDataSourceName(String dataSourceName) {
  11. dataSourceContextHolder.set(dataSourceName);
  12. }
  13. /*
  14. 获取据源名称
  15. */
  16. public static String getDataSourceName() {
  17. return dataSourceContextHolder.get();
  18. }
  19. /*
  20. 清除当数据源名称
  21. */
  22. public static void clearDataSource() {
  23. dataSourceContextHolder.remove();
  24. }
  25. @Override
  26. protected Object determineCurrentLookupKey() {
  27. return getDataSourceName();
  28. }
  29. }

MultiDataSource 多数据源标记

  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.RetentionPolicy;
  4. import java.lang.annotation.Target;
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @Target({ElementType.METHOD})
  7. public @interface MultiDataSource {
  8. String value() default DataSourceConfig.DEFAULT_DATASOURCE_NAME;
  9. }

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

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

DataSourceConfig.SHARD_MMS_DATASOURCE_TABLE_COUNT=10

  1. import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
  2. import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
  3. import java.util.Collection;
  4. public class PreciseNodeIdShardingAlgorithm implements PreciseShardingAlgorithm<String> {
  5. @Override
  6. public String doSharding(Collection<String> collection, PreciseShardingValue<String> preciseShardingValue) {
  7. for (String tbnm : collection) {
  8. if (tbnm.endsWith("hist" + (getHash(preciseShardingValue.getValue()) % DataSourceConfig.SHARD_MMS_DATASOURCE_TABLE_COUNT))) {
  9. return tbnm;
  10. }
  11. }
  12. throw new UnsupportedOperationException();
  13. }
  14. private static int getHash(String str) {
  15. final int p = 16777619;
  16. int hash = (int) 2166136261L;
  17. for (int i = 0; i < str.length(); i++)
  18. hash = (hash ^ str.charAt(i)) * p;
  19. hash += hash << 13;
  20. hash ^= hash >> 7;
  21. hash += hash << 3;
  22. hash ^= hash >> 17;
  23. hash += hash << 5;
  24. // 如果算出来的值为负数则取其绝对值
  25. if (hash < 0)
  26. hash = Math.abs(hash);
  27. return hash;
  28. }
  29. }

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

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

  1. import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
  2. import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
  3. import org.apache.shardingsphere.api.config.sharding.strategy.StandardShardingStrategyConfiguration;
  4. import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.context.annotation.Primary;
  9. import org.springframework.jdbc.datasource.DataSourceTransactionManager;
  10. import org.springframework.transaction.PlatformTransactionManager;
  11. import javax.sql.DataSource;
  12. import java.sql.SQLException;
  13. import java.util.HashMap;
  14. import java.util.Map;
  15. import java.util.Properties;
  16. @Configuration
  17. public class DataSourceConfig {
  18. public static final String DEFAULT_DATASOURCE_NAME = "defaultDataSource";
  19. public static final String MMS_DATASOURCE_NAME = "mmsDatasource";
  20. public static final String SHARD_MMS_DATASOURCE_NAME = "shardMmsDatasource";
  21. public static int SHARD_MMS_DATASOURCE_TABLE_COUNT = 10;
  22. @Autowired
  23. private DataSourceProperties properties;
  24. @Primary
  25. @Bean(name = "dynamicDataSource")
  26. public DataSource dynamicDataSource() {
  27. DynamicDataSource dynamicDataSource = new DynamicDataSource();
  28. // 默认数据源
  29. dynamicDataSource.setDefaultTargetDataSource(properties.getMms());
  30. // 配置多数据源
  31. Map<Object, Object> dsMap = new HashMap();
  32. dsMap.put(DEFAULT_DATASOURCE_NAME, properties.getStudent());
  33. dsMap.put(MMS_DATASOURCE_NAME, properties.getStudent());
  34. dsMap.put(SHARD_MMS_DATASOURCE_NAME, buildShardDatasources());
  35. dynamicDataSource.setTargetDataSources(dsMap);
  36. return dynamicDataSource;
  37. }
  38. public DataSource buildShardDatasources() {
  39. // 配置多数据源
  40. Map<String, DataSource> dsMap = new HashMap();
  41. dsMap.put("shardMms", properties.getMms());
  42. TableRuleConfiguration stuHisTableRuleConfig = new TableRuleConfiguration("student_hist", "shardMms.student_hist${0.." + (SHARD_MMS_DATASOURCE_TABLE_COUNT - 1) + "}");
  43. // tableRuleConfiguration.setDatabaseShardingStrategyConfig();
  44. stuHisTableRuleConfig.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("student_name", new PreciseNodeIdShardingAlgorithm()));
  45. ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
  46. shardingRuleConfig.getTableRuleConfigs().add(stuHisTableRuleConfig);
  47. try {
  48. Properties properties = new Properties();
  49. properties.setProperty("sql.show", "true");
  50. return ShardingDataSourceFactory.createDataSource(dsMap, shardingRuleConfig, properties);
  51. } catch (SQLException throwables) {
  52. throwables.printStackTrace();
  53. throw new IllegalArgumentException();
  54. }
  55. }
  56. @Bean
  57. public PlatformTransactionManager transactionManager() {
  58. return new DataSourceTransactionManager(dynamicDataSource());
  59. }
  60. }

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

  1. import lombok.extern.slf4j.Slf4j;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.Around;
  4. import org.aspectj.lang.annotation.Aspect;
  5. import org.aspectj.lang.annotation.Pointcut;
  6. import org.aspectj.lang.reflect.MethodSignature;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.core.annotation.Order;
  9. import java.lang.reflect.Method;
  10. @Aspect
  11. @Configuration
  12. @Slf4j
  13. @Order(1)
  14. public class DataSourceAspect {
  15. //切入点,service 中所有注解方法
  16. @Pointcut("execution(* com.huitong..service..*.*(..)) && @annotation(com.huitong.app.config.datasource.MultiDataSource)")
  17. public void dataSourceAspect() {
  18. }
  19. @Around("dataSourceAspect()")
  20. public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
  21. MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  22. Method method = signature.getMethod();
  23. MultiDataSource ds = method.getAnnotation(MultiDataSource.class);
  24. if (ds != null) {
  25. DynamicDataSource.setDataSourceName(ds.value());
  26. }
  27. try {
  28. return joinPoint.proceed();
  29. } finally {
  30. DynamicDataSource.clearDataSource();
  31. }
  32. }
  33. }

参考文献:

在多数据源中对部分数据表使用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. python的代码块和if条件表达式

    代码块和if条件表达式 代码块 什么是代码块 以冒号作为开始,用缩进来划分作用域. 在之后的学习当中,使用if条件语句.for.while循环语句.定义函数.定义类等诸多地方都会涵盖代码的概念. 什么 ...

  2. Python urllib翻译笔记一

    22.5.urllib- URL处理模块urllib 是一个收集几个模块以处理URL的包: urllib.request 用于打开和阅读URL urllib.error 包含由urllib.reque ...

  3. MySQL问题定位-性能优化之我见

    前言 首先任何一个数据库不是独立存在的,也不是凭空想象决定出来的. 数据库的架构离不开应用的场景.所以,为了解决某些深入的问题,首先你得掌握数据库的原理与架构.原理掌握得越深入,越能帮助你定位复杂与隐 ...

  4. selenium 鼠标,键盘操作

    1.鼠标操作 导包:from selenium.webdriver.common.action_chains import ActionChains 1.context_click()        ...

  5. 简明易懂,将细节隐藏,面向新手树立web开发概念——学完Java基础语法,超快速上手springboot+mybatiJavaWeb开发

    简明易懂,将细节隐藏,面向新手树立web开发概念 --学完Java基础语法,超快速上手JavaWeb开发 Web本质(先忽视各种协议) Web应用可以理解为浏览器和服务器之间的交互. 我们可以看一个简 ...

  6. (数据科学学习手札126)Python中JSON结构数据的高效增删改操作

    本文示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 在上一期文章中我们一起学习了在Python ...

  7. 网络编程之TCP客户端开发和TCP服务端开发

    开发 TCP 客户端程序开发步骤 创建客户端套接字对象 和服务端套接字建立连接 发送数据 接收数据 关闭客户端套接字 import socket if __name__ == '__main__': ...

  8. Java练习——加减乘除计算器实现

    Java练习--计算器(加减乘除)  package method; import java.util.Scanner; /*  写一个计算器 实现加减乘除四个功能   并且能够用循环接收新的数据,通 ...

  9. Python 列表解析式竟然支持异步?

    PEP原文:https://www.python.org/dev/peps/pep-0530 PEP标题:PEP 530 -- Asynchronous Comprehensions PEP作者:Yu ...

  10. 遗传算法 TSP(Python代码)

    该代码是本人根据B站up主侯昶曦的代码所修改的. 原代码github地址:https://github.com/Houchangxi/heuristic-algorithm/blob/master/T ...