本文源码:GitHub·点这里 || GitEE·点这里

一、数据拆分概念

1、场景描述

随着业务发展,数据量的越来越大,业务系统越来越复杂,拆分的概念逻辑就应运而生。数据层面的拆分,主要解决部分表数据过大,导致处理时间过长,长期占用链接,甚至出现大量磁盘IO问题,严重影响性能;业务层面拆分,主要解决复杂的业务逻辑,业务间耦合度过高,容易引起雪崩效应,业务库拆分,微服务化分布式,也是当前架构的主流方向。

2、基本概念

分区模式

针对数据表做分区模式,所有数据,逻辑上还存在一张表中,但是物理堆放不在一起,会根据一定的规则堆放在不同的文件中。查询数据的时候必须按照指定规则触发分区,才不会全表扫描。不可控因素过多,风险过大,一般开发规则中都是禁止使用表分区。

分表模式

单表数据量过大,一般情况下单表数据控制在300万,这里的常规情况是指字段个数,类型都不是极端类型,查询也不存在大量锁表的操作。超过该量级,这时候就需要分表操作,基于特定策略,把数据路由到不同表中,表结构相同,表名遵循路由规则。

分库模式

在系统不断升级,复杂化场景下,业务不好管理,个别数据量大业务影响整体性能,这时候可以考虑业务分库,大数据量场景分库分表,减少业务间耦合度,高并发大数据的资源占用情况,实现数据库层面的解耦。在架构层面也可以服务化管理,保证服务的高可用和高性能。

常用算法

  • 哈希值取余:根据路由key的哈希值余数,把数据分布到不同库,不同表;
  • 哈希值分段:根据路由key的哈希值分段区间,实现数据动态分布;

这两种方式在常规下都没有问题,但是一旦分库分表情况下数据库再次饱和,需要迁移,这时候影响是较大的。

二、关系型分库

1、分库基本逻辑

基于一个代理层(这里使用Sharding-Jdbc中间件),指定分库策略,根据路由结果,找到不同的数据库,执行数据相关操作。

2、数据源管理

把需要分库的数据源统一管理起来。

@Configuration
public class DataSourceConfig {// 省略数据源相关配置/*** 分库配置*/@Beanpublic DataSource dataSource (@Autowired DruidDataSource dataZeroSource,@Autowired DruidDataSource dataOneSource,@Autowired DruidDataSource dataTwoSource) throws Exception {ShardingRuleConfiguration shardJdbcConfig = new ShardingRuleConfiguration();shardJdbcConfig.getTableRuleConfigs().add(getUserTableRule());shardJdbcConfig.setDefaultDataSourceName("ds_0");Map<String,DataSource> dataMap = new LinkedHashMap<>() ;dataMap.put("ds_0",dataZeroSource) ;dataMap.put("ds_1",dataOneSource) ;dataMap.put("ds_2",dataTwoSource) ;Properties prop = new Properties();return ShardingDataSourceFactory.createDataSource(dataMap, shardJdbcConfig, new HashMap<>(), prop);}/*** 分表配置*/private static TableRuleConfiguration getUserTableRule () {TableRuleConfiguration result = new TableRuleConfiguration();result.setLogicTable("user_info");result.setActualDataNodes("ds_${1..2}.user_info_${0..2}");result.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_phone", new DataSourceAlg()));result.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_phone", new TableSignAlg()));return result;}
}

3、指定路由策略

  • 路由到库

根据分库策略的值,基于hash算法,判断路由到哪个库。has算法不同,不但影响库的操作,还会影响数据入表的规则,比如偶数和奇数,导致入表的奇偶性。

public class DataSourceAlg implements PreciseShardingAlgorithm<String> {private static Logger LOG = LoggerFactory.getLogger(DataSourceAlg.class);@Overridepublic String doSharding(Collection<String> names, PreciseShardingValue<String> value) {int hash = HashUtil.rsHash(String.valueOf(value.getValue()));String dataName = "ds_" + ((hash % 2) + 1) ;LOG.debug("分库算法信息:{},{},{}",names,value,dataName);return dataName ;}
}
  • 路由到表

根据分表策略的配置,基于hash算法,判断路由到哪张表。

public class TableSignAlg implements PreciseShardingAlgorithm<String> {private static Logger LOG = LoggerFactory.getLogger(TableSignAlg.class);@Overridepublic String doSharding(Collection<String> names, PreciseShardingValue<String> value) {int hash = HashUtil.rsHash(String.valueOf(value.getValue()));String tableName = "user_info_" + (hash % 3) ;LOG.debug("分表算法信息:{},{},{}",names,value,tableName);return tableName ;}}

上述就是基于ShardingJdbc分库分表的核心操作流程。

三、列式库统计

1、列数数据

在相对庞大的数据分析时,通常会选择生成一张大宽表,并且存放到列式数据库中,为了保证高效率执行,可能会把数据分到不同的库和表中,结构一样,基于多线程去统计不同的表,然后合并统计结果。

基本原理:多线程并发去执行不同的表的统计,然后汇总统计,相对而言统计操作不难,但是需要适配不同类型的统计,比如百分比,总数,分组等,编码逻辑相对要求较高。

2、列式数据源

基于ClickHouse数据源,演示案例操作的基本逻辑。这里管理和配置库表。

核心配置文件

spring:datasource:type: com.alibaba.druid.pool.DruidDataSource# ClickHouse数据01ch-data01:driverClassName: ru.yandex.clickhouse.ClickHouseDriverurl: jdbc:clickhouse://127.0.0.1:8123/query_data01tables: ch_table_01,ch_table_02# ClickHouse数据02ch-data02:driverClassName: ru.yandex.clickhouse.ClickHouseDriverurl: jdbc:clickhouse://127.0.0.1:8123/query_data02tables: ch_table_01,ch_table_02

核心配置类

@Component
public class ChSourceConfig {public volatile Map<String, String[]> chSourceMap = new HashMap<>();public volatile Map<String, Connection> connectionMap = new HashMap<>();@Value("${spring.datasource.ch-data01.url}")private String dbUrl01;@Value("${spring.datasource.ch-data01.tables}")private String tables01 ;@Value("${spring.datasource.ch-data02.url}")private String dbUrl02;@Value("${spring.datasource.ch-data02.tables}")private String tables02 ;@PostConstructpublic void init (){try{Connection connection01 = getConnection(dbUrl01);if (connection01 != null){chSourceMap.put(connection01.getCatalog(),tables01.split(","));connectionMap.put(connection01.getCatalog(),connection01);}Connection connection02 = getConnection(dbUrl02);if (connection02 != null){chSourceMap.put(connection02.getCatalog(),tables02.split(","));connectionMap.put(connection02.getCatalog(),connection02);}} catch (Exception e){e.printStackTrace();}}private synchronized Connection getConnection (String jdbcUrl) {try {DriverManager.setLoginTimeout(10);return DriverManager.getConnection(jdbcUrl);} catch (Exception e) {e.printStackTrace();}return null ;}
}

3、基本任务类

既然基于多线程统计,自然需要一个线程任务类,这里演示count统计模式。输出单个线程统计结果。

public class CountTask implements Callable<Integer> {private Connection connection ;private String[] tableArray ;public CountTask(Connection connection, String[] tableArray) {this.connection = connection;this.tableArray = tableArray;}@Overridepublic Integer call() throws Exception {Integer taskRes = 0 ;if (connection != null){Statement stmt = connection.createStatement();if (tableArray.length>0){for (String table:tableArray){String sql = "SELECT COUNT(*) AS countRes FROM "+table ;ResultSet resultSet = stmt.executeQuery(sql) ;if (resultSet.next()){Integer countRes = resultSet.getInt("countRes") ;taskRes = taskRes + countRes ;}}}}return taskRes ;}}

4、线程结果汇总

这里主要启动线程的执行,和最后把每个线程的处理结果进行汇总。

@RestController
public class ChSourceController {@Resourceprivate ChSourceConfig chSourceConfig ;@GetMapping("/countTable")public String countTable (){Set<String> keys = chSourceConfig.chSourceMap.keySet() ;if (keys.size() > 0){ExecutorService executor = Executors.newFixedThreadPool(keys.size());List<CountTask> countTasks = new ArrayList<>() ;for (String key:keys){Connection connection = chSourceConfig.connectionMap.get(key) ;String[] tables = chSourceConfig.chSourceMap.get(key) ;CountTask countTask = new CountTask(connection,tables) ;countTasks.add(countTask) ;}List<Future<Integer>> countList = Lists.newArrayList();try {if (countTasks.size() > 0){countList = executor.invokeAll(countTasks) ;}} catch (InterruptedException e) {e.printStackTrace();}Integer sumCount = 0 ;for (Future<Integer> count : countList){try {Integer countRes = count.get();sumCount = sumCount + countRes ;} catch (Exception e) {e.printStackTrace();}}return "sumCount="+sumCount ;}return "No Result" ;}
}

5、最后总结

关系型分库,还是列式统计,都是基于特定策略把数据分开,然后路由找到数据,执行操作,或者合并数据,或者直接返回数据。

四、源代码地址

GitHub·地址
https://github.com/cicadasmile/data-manage-parent
GitEE·地址
https://gitee.com/cicadasmile/data-manage-parent

推荐阅读:数据管理

序号 标题
01 数据源管理:主从库动态路由,AOP模式读写分离
02 数据源管理:基于JDBC模式,适配和管理动态数据源
03 数据源管理:动态权限校验,表结构和数据迁移流程

数据源管理 | 关系型分库分表,列式库分布式计算相关推荐

  1. 关系型数据库分库分表中间件之选型

    写在前面 本文主要介绍关系型数据库分库分表的中间件,主要包含中间件介绍.选项及其对比.虽然市面上很多分库分表中间件,但是大多数都是不友好或者社区活跃度不高的项目,当然还是有很多淘汰的中间件.目前,在实 ...

  2. mysql为什么要分库_mysql为什么要分库分表?

    1 基本思想之什么是分库分表? 从字面上简单理解,就是把原本存储于一个库的数据分块存储到多个库上,把原本存储于一个表的数据分块存储到多个表上. 2 基本思想之为什么要分库分表? 单表操作数据量有最优值 ...

  3. 淘宝TDDL——Matrix层的分库分表配置与实现

    http://gao-xianglong.iteye.com/blog/2019729/ 目录 一.互联网当下的数据库拆分过程 二.TDDL的架构原型 三.下载TDDL的Atom层和Group层源代码 ...

  4. 分库分表Sharding-JDBC最佳实践专题

    一 Mysql数据库架构演变历史 单机 请求量大查询慢 单机故障导致业务不可用 主从 数据库主从同步,从库可以水平扩展,满足更大读需求 但单服务器TPS,内存,IO都是有限的 双主 用户量级上来后,写 ...

  5. 分库分表之淘宝TDDL的原理

    分库分表之淘宝TDDL的原理 一.互联网当下的数据库拆分过程 二.TDDL的架构原型 三.下载TDDL的Atom层和Group层源代码 四.Diamond简介 五.Diamond的安装和使用 六.动态 ...

  6. 自己动手写一个分库分表中间件(三)数据源路由实现

    相关文章: 自己动手写一个分库分表中间件(一)思考 自己动手写一个分库分表中间件(二)数据源定义和分片代理层设计 排查项目中读写分离失效原因 小议 Java 内省机制 注:本文内容暂不涉及事务相关的问 ...

  7. MySQL 分库分表实践

    文章目录 一.为什么要分库分表 二.库表太大产生的问题 三.垂直拆分 1. 垂直分库 2. 垂直分表 四.水平分表 1. 配置水平分表 2. 测试水平分表 一.为什么要分库分表 数据库架构演变 刚开始 ...

  8. 订单表的分库分表方案设计(大数据)

    原创文章,转载注明出处   一.两种方案分库分表 一般业界,对订单数据的分库分表,笔者了解,有两类思路:按照订单号来切分.按照用户id来切分. 方案一.按照订单号来做hash分散订单数据 把订单号看作 ...

  9. 分库分表全面了解分析

    2019独角兽企业重金招聘Python工程师标准>>> 前言 内容来源 本文内容均来源于网络,涉及地址 为什么要分库分表(个人理解,希望能与大家共勉) https://blog.cs ...

最新文章

  1. 阿里达摩院再造AI抗疫技术:20秒判读CT影像,识别准确率达96%
  2. PL/SQL 游标
  3. 动态slimmable网络:高性能的网络轻量化方法!对比slimmable涨点5.9%
  4. window.print 固定表头不影响_Excel中的表头,你会处理吗
  5. 通过Web Service获取天气预报并朗读
  6. SaaS系统给企业带来了哪些优势
  7. boost::contract模块实现可选结果的测试程序
  8. P1101 单词方阵(DFS)
  9. extern 全局变量在不同的文件使用方法(static)
  10. Nuget多项目批量打包上传服务器的简明教程
  11. 移动端H5 页面 input 获取焦点不灵敏
  12. pytorch 向量转化为one-hot编码
  13. ChengDu University Mental Health Test 需求分析文档
  14. c语言随机数猜数游戏
  15. Sentinel 2 哨兵2号 基本介绍
  16. 基金投资基本常识【狂神说】
  17. 移动APP云测试平台测评分析
  18. 【2022网易雷火】游戏研发笔试-AC代码及题目分享
  19. WebApi编程(一)-DOM
  20. Linux服务器百万并发实现与问题排查

热门文章

  1. 测试学开发——第一课:环境搭建与页面开发介绍。
  2. 2.6宽带接入技术ADSL
  3. 什么是堆栈?内存中划分出按FIL0方式操作的特殊区域,叫做堆栈
  4. 操作系统之文件管理:9、磁盘的结构与磁盘调度算法(先来先服务FCFS、最短寻找时间优先SSTF、扫描算法SCAN、循环扫描算法C-SCAN、LOOK调度算法、C-LOOK调度算法)
  5. 程序是怎样跑起来的:第一章-对程序员来说CPU是什么
  6. VirtualAllocEx 跨进程读写数据 代码注入
  7. 一个java中HashMap和HashSet的应用实例
  8. USACO-Section2.1 Hamming Codes(深度优先搜索)
  9. Shell生成随机uuid
  10. Jenkins远程命令执行漏洞(CVE-2018-1000861复现)