基于时间维度水平拆分的多 TiDB 集群统一数据路由/联邦查询技术的实践

导读

在大数据时代,金融行业面临着日益增长的数据量和复杂的查询需求,尤其是跨库、跨集群的场景。在这种背景下,如何在保证数据一致性、高可用性的同时,实现业务的快速扩展与高效查询,成为了企业数字化转型的关键挑战。本文将深入探讨如何通过 TiDB 分布式数据库,结合自研的轻量级数据路由组件,解决海量数据管理问题。通过按时间维度拆分集群、动态数据路由与高效结果集归并等技术,企业能够实现数据无缝扩展,提升查询效率,并优化资源利用率。同时,本文分享了具体的技术实现,包括如何在多集群环境下进行数据路由、事务管理及跨集群查询,帮助企业在确保稳定性的基础上,支持更高的并发和更复杂的查询需求。


需求背景

某头部银行客户交易明细查询场景服务于全行对私、对公、海外客户,服务的时间范围覆盖了 2014 年至今超 10 年的超长跨度,致使原有保存在关系型数据库、NoSQL 数据库中的整体数据规模达到了可观的单副本 500TB,并且日均增量数据已超 1 亿。不仅如此,业务部门希望在保证服务质量、服务周期的前提下进一步提升差异化和业务敏捷能力,最终选择了具备高可用、高并发、易扩展、透明分布式(全局二级索引)、HTAP 等关键能力的 TiDB 分布式数据库作为其新的数据底座。

由于 TiDB 的设计哲学是在标准硬件基础上通过多副本+Multi Raft 分布式一致性协议实现可用性和性能间的平衡,且副本数至少 3 个起步,部分高等级业务甚至需要 5 副本提供更高的可用性。所以,单副本 500TB 迁移至 TiDB 后即使采用了多层压缩,因副本数的变多整体容量势必会有一定的增长(该场景 5 副本经过压缩约为原始数据的 2 倍)。面对如此大数据规模、面向全渠道/数亿客户、TPS 超过万级/访问延迟毫秒级、并且混合了客户号/账号/机构信息多维度访问的重要业务系统,为了在 SLA、扩展性、整体成本等方面寻求最佳平衡点, 整体数据架构采用了最贴合业务特征的方式做水平拆分,即按交易时间维度拆分为多个物理集群,不同集群可根据 SLA 等级对应不同的资源规格和副本数,并通过应用层的数据路由、联邦查询组件实现跨库背景下的 SQL 访问快速定位、结果集归并、路由策略管理等核心功能

本文的后续内容将聚焦在路由组件的设计与实现,以及与应用框架和 TiDB 的高效整合。

整体设计与选型策略

该案例的跨库访问场景包括基于时间范围的交易明细查询(分页/支持跳页,非分页)、收支记录查询(分页/非跳页)、收支汇总查询,以及基于交易流水号等非时间条件的单笔/多笔查询、单笔/多笔修改、单集群多表 DML 等,可以说涵盖了银行领域该类场景的所有业务。

梳理后共包括以下几类访问模式:

  • 按时间路由-分页追加归并 :对应各类分页查询场景,需要根据查询时间范围确定涉及的集群范围,再结合分页控制类信息(如起始记录数、每页记录数),定位当前分页所在的集群,并对跨集群场景下的结果集进行追加归并。除此之外,该类型还会涉及某分页数据散落在两个相邻集群、WEB 端常见的跳页(即需要动态感知查询范围所涉及的所有集群的数据分布)、以及非时间维度分页等更为复杂的场景。最后这种场景因为排序、分页的过滤规则与多 TiDB 集群的拆分规则不一致,需要更为巧妙的设计以降低多集群磁盘、网络 IO 的放大和稳定性、性能的影响;
  • 按时间路由-汇总归并 :对应汇总查询场景,如实时收支分析、月度/年度收支统计等,需根据查询时间范围确定涉及的集群范围,并将多个集群的查询结果在组件的结果集归并模块中按分组条件进行汇总归并;
  • 轮询路由-追加/汇总归并 :对应单笔/多笔查询、修改场景,由于输入参数能够定位数据的只有非交易时间类字段,如交易流水号,所以宜采用由近到远的方式依次轮询各个集群,直到所有记录都完成遍历,并根据业务类型选择结果集的归并方式,如查询类采用追加、修改类采用汇总。

在场景分类的基础上,还需要结合集群间数据生命周期管理策略的要求进一步细化相关设计:

  • 集群拆分和容量规划 :如“需求背景”章节所述,TiDB 多副本整体数据规模接近 PB 级,并且访问频度、SLA 等级随数据热度的降低也会显著下降,所以,按照热、温、冷水平拆分了三类集群,并在分片规则上引入了“相对时间”、“绝对时间”的概念。比如,热集群以开区间的形式存储了相对时间为最近一年的数据,即[now,-365),温集群以半开区间的形式存储了相对时间一年前到绝对时间为 20xx 年的少量数据,而冷集群则以固定区间存储了更长时间范围的数据。这样做的好处是只有热、温集群之间涉及数据转储 ETL 作业,简化了管理和运维成本,同时最为重要的热集群能保证容量相对稳定,而温集群则预留了足够的空间使整体方案具备较好的扩展性。如图 1 所示;

图 1-集群拆分方式

图 1:集群拆分方式
  • 集群间数据冗余设计 :热、温集群间的 ETL 作业会遵循“热集群导出[交易日期 < (now - 365d)]数据–>温集群导入–>热集群清理[交易日期 < (now - 365d)]”的时序,也就是数据转储是有时延的、并且存在因异常导致时延增大的可能。如果热、温集群“开口部分的相对时间”卡的过死,会出现 ETL 导入完成前客户查不到数据的“幻象”。所以,在设计上引入了名义时间、冗余时间、实际时间的概念,比如热集群名义最小时间(lowerValue)为[now - 365d],考虑到上述的时延因素,将冗余时间(overlapping)设定为 1 天,即该集群实际存储并提供服务的最小时间为[now - 365d - overlapping],相应的,温集群提供服务的最大时间也为该值,如图 2 中的绿色虚线。不难看出,只要冗余时间设定的相对合理、稳妥,就可以覆盖 ETL 产生的各类边界场景。

图 2:集群冗余设计

图 2:集群冗余设计

综上,考虑到该案例在业务上同时包含客户号/账号/机构、以及时间/非时间多种组合维度,在技术上对于数据分片的灵活性、数据冗余也有特殊需求。所以,传统的数据路由类组件(如 ShardingSphere)难以满足定制化需求,并且代码规模和维护成本也较高,最终采用了自研轻量级数据路由 SDK 组件的方式。

核心技术实现

该自研组件的设计理念与 ShardingSphere 低业务侵入的方式有所不同,本质上也是为了在“工期紧、任务重”的客观情况下寻求最优解。所以,整体思想是在现代化 Java 类应用经典的基于数据库连接池+ORM 框架+Spring 事务控制的基础上,通过少量编码(10 行左右),以 Builder 方式构建参数对象、并通过 Java 静态方法执行实际的 SQL 逻辑,再结合 TiDB 的 HTAP 能力做集群内的混合处理。

图 3:代码逻辑架构

图 3:代码逻辑架构

整体逻辑架构包括了应用框架、业务代码、以及以 jar 包形式集成在应用框架和业务代码中的数据路由 SDK。各部分简要说明如下:

应用框架

基于现有开发框架(如 Spring Boot)增加多数据源配置,包括多个集群的 Spring Bean 定义、以及继承 spring-jdbc AbstractRoutingDataSource 抽象类并实现 determineCurrentLookupKey 方法以提供多数据源的切换能力;

业务代码

主要变化包括 ORM 框架(如 Mybatis)的 SQL 语句部分按规则预留供路由组件改写的动态参数,以及调用方式改为通过路由组件提供的入口方法,对于仅访问热集群或无需改写 SQL 参数的场景,路由组件会调用业务侧上送的回调函数直接执行原始的 ORM 操作。对于跨集群的场景,由路由组件基于反射技术调用上送的 Mybatis mapper 中对应的业务 SQL,并将执行结果处理后返回调用方;

路由组件

  • 配置管理
    • 参数配置 :基于应用框架(如 Spring Boot)配置文件的定制化配置,比如是否启用、默认数据源(热集群)BeanName、动态数据源事务管理器 BeanName、跨库 DML 重试次数和间隔、SQL 执行时间打印标志、非交易时间类特殊查询场景的启用阈值、路由配置热更新配置等;路由配置:主要配置项包括每个集群的上下界的名义日期、日期类型(相对 or 绝对)、基于日期排序的集群顺序(为了按日期正序或倒序遍历)、版本信息等。配置信息会在应用启动时加载到 JVM 内存中,并通过版本信息来触发配置表变更时的热更新。表结构如代码 1 所示;
    • 路由配置 :主要配置项包括每个集群的上下界的名义日期、日期类型(相对 or 绝对)、基于日期排序的集群顺序(为了按日期正序或倒序遍历)、版本信息等。配置信息会在应用启动时加载到JVM内存中,并通过版本信息来触发配置表变更时的热更新。表结构如代码 1 所示;
    create table route_config
    (
        DATASOURCE_NAME  varchar(20) not null PRIMARY KEY COMMENT '数据源名称',
        LOWER_VALUE      varchar(8)  not null COMMENT '下限日期',
        UPPER_VALUE      varchar(8)  not null COMMENT '上限日期',
        LOWER_VALUE_TYPE int         not null COMMENT '下限日期类型: 1-相对值, 2-绝对值',
        UPPER_VALUE_TYPE int         not null COMMENT '上限日期类型: 1-相对值, 2-绝对值',
        LOWER_OVERLAPS   int         not null COMMENT '下游集群重叠日期天数',
        UPPER_OVERLAPS   int         not null COMMENT '上游集群重叠日期天数',
        STATUS           int         not null COMMENT '配置项状态: 1-正常, 2-失效',
        EFFECTIVE_DATE   varchar(8)  not null COMMENT '生效日期',
        EXPIRE_DATE      varchar(8)  not null COMMENT '失效日期',
        ORDER_BY_DATE    int         not null COMMENT '配置项日期排序',
        VERSION          int         not null COMMENT '版本号'
    ) comment = '路由配置表';
    ```
代码片段 1:路由配置表
  • 动态路由解析 :采用两段式路由的机制。第一段先通过请求参数中的业务类型、查询的日期范围,结合动态计算的每个集群当前的上下界时间,确定当前请求涉及的集群范围。第二段是进行更细粒度的路由,对于只涉及热集群的场景,直接透传返回并在不改写参数的情况下回调原始 SQL;对于多集群场景,则会涉及业务类型、是否按时间排序、正序/倒序、是否跳页多个维度的组合。如表 1 所示:

动态路由解析

  • 多数据源 SQL 执行
    • 透传回调 :当路由解析结果只涉及热集群时,直接透传返回、并在不改写 SQL 参数的情况下回调原始 SQL;
    • 多数据源执行 :按路由解析排序后的结果在多个集群依次执行 SQL,执行前涉及 SQL 参数的改写(包括日期、分页,即业务参数到集群级物理参数的改写)、基于 ThreadLocal 切换数据源,并通过反射的方式执行实际的 SQL 方法。非查询类场景还涉及事务管理,同时,考虑到业务操作幂等性的特点,为降低整体复杂度和维护成本,对于个别的跨集群 DML 操作并未引入应用侧的分布式事务。最后会按集群维度将结果集追加合并;
  • 结果集归并:
    • 分页查询场景 :对于按交易日期排序的简单场景,只需将多个集群的结果集按顺序追加归并即可。对于非交易日期排序的复杂场景,则需要根据排序字段、各字段的正序/倒序规则,对集群内局部有序的所有结果集进行整体重排序,算法模型采用的是稳定性较好的插入排序;
    • 聚合查询 :对于可以汇总归并的算子,如 sum、count、max、min 等,会将多集群的全局结果集按照分组字段进行汇总或比较;对于avg类算子,则需要先将多个结果集的 sum、count 结果分别累加,再用“total sum / total count”得到最终的 avg 结果;
    • 单笔/多笔查询或修改 :单笔类操作由于只涉及一个集群,所以无需二次归并;多笔场景采用上述类似的方式做追加或汇总归并。

总结与展望

通过该组件与 TiDB 分布式数据库的有效结合, 可以实现近乎无容量上限的超大规模数据管理 ,尤其是对于重要程度高、吞吐量大、业务敏捷性强、数据冷热特征明显的业务系统。不仅能够支撑面向内外部客户业务无损的多维度、不受分片键制约的灵活高效访问,还可以有效控制和平衡单集群的负载、容量、资源利用率、稳定性等关键指标,在不增加过多复杂性的前提下实现更强的整体扩展能力。

当然,组件的现有功能更多是聚焦在当前的客户场景,未来可以按需在功能性、易用性、高性能等方面进一步优化和提升。以实现在无损支撑现有业务功能的基础上,大大提升系统在数智化背景下的业务扩展、迭代能力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/950091.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

概率论 期末 笔记

第一章 随机事件及其概率 利用“四大公式”求事件概率 加法公式 减法 条件概率公式 全概率公式与贝叶斯公式 伯努利概型求概率 习题 推导 一维随机变量及其分布 离散型随机变量&#xff08;R.V&#xff09;求分布律 利用常见离散型分布求概率 连续型R.V相关计算 利用常见连续…

把vue项目或者vue组件发布成npm包或者打包成lib库文件本地使用

将vue项目发布成npm库文件&#xff0c;第三方通过npm依赖安装使用&#xff1b;使用最近公司接了一个项目&#xff0c;这个项目需要集成到第三方页面&#xff0c;在第三方页面点击项目名称&#xff0c;页面变成我们的项目页面&#xff1b;要求以npm库文件提供给他们&#xff1b;…

《空舞的巨兽》官方学习版

一个以被遗忘之地为背景的原创故事&#xff0c;这是一个充满悲剧的没落王国。扮演外地战士雷恩猎人&#xff08;玩家&#xff09;&#xff0c;踏上危险的任务&#xff0c;结束困扰你自己和村庄的诅咒。你唯一的希望就是杀死不可杀死的可怕巨兽。 《空舞的巨兽》官方版 https:/…

go-zero框架快速入门

文章目录 go-zero 简介安装goctl安装go-zero启动go-zero API语言定义结构体API定义路由API格式化对齐 生成代码生成基本逻辑代码生成数据库model文件 go-zero 简介 go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性&#xff0c;经…

切忌 SELECT *,就算表只有一列

原文地址 尽量避免 SELECT *&#xff0c;即使在单列表上也是如此 – 如果你现在不同意这一点&#xff0c;读完这篇文章&#xff0c;你可能就要动摇了。 2012年的一个故事 这是我 12 年前&#xff08;约 2012-2013 年&#xff09;在客户后台应用程序中遇到的一个真实故事。 当…

了解RabbitMQ中的Exchange:深入解析与实践应用

在分布式系统设计中&#xff0c;消息队列&#xff08;Message Queue&#xff09;扮演着至关重要的角色&#xff0c;而RabbitMQ作为开源消息代理软件的佼佼者&#xff0c;以其高性能、高可用性和丰富的功能特性&#xff0c;成为了众多开发者的首选。在RabbitMQ的核心组件中&…

【linux系统之redis6】redis的基础命令使用及springboot连接redis

redis的基础命令很多&#xff0c;大部分我们都可以在官网上找到&#xff0c;真的用的时候可以去官网找&#xff0c;不用全部记住这些命令 redis通用的基础命令的使用 代码测试 string类型常见的命令 key值的结构&#xff0c;可以区分不同的需求不同的业务名字 hash类型 创建…

基于FPGA的交通信号灯实现 (verilog极简实现)

本文分享利用FPGA实现的交通信号灯&#xff0c;FPGA型号为野火征途Pro开发板&#xff0c;具体功能如下&#xff1a; 此项目旨在模拟东西和南北两路口交通信号灯&#xff0c;初始态两路口均为红灯亮&#xff0c;接着&#xff0c;东西路口绿灯亮&#xff0c;南北路口红灯亮&…

在K8S上部署OceanBase的最佳实践

在K8S上部署OceanBase的最佳实践 目录 1. 背景与选型 1.1 为什么选择OB1.2 为什么选择ob-operator实现OB on K8S 2. 部署实操 2.1 环境准备2.2 安装 ob-operator2.3 配置 OB 集群2.4 配置 OBProxy 集群2.5 Headless Service 和 CoreDNS 配置2.6 监控与运维 2.6.1 Promethues部…

unity开发之shader 管道介质流动特效

效果 shader graph 如果出现下面的效果&#xff0c;那是因为你模型的问题&#xff0c;建模做贴图的时候没有设置好UV映射&#xff0c;只需重新设置下映射即可

【JavaWeb】2. 通用基础代码

以下内容来源&#xff1a;编程导航。 无论在任何后端项目中&#xff0c;都可以复用的代码。 1、自定义异常 自定义错误码&#xff0c;对错误进行收敛&#xff0c;便于前端统一处理。 &#x1f4a1; 这里有 2 个小技巧&#xff1a; 自定义错误码时&#xff0c;建议跟主流的错…

Excel 技巧04 - 如何计算两个时间之差 (★)

本文讲了如何通过Excel计算两个时间的时间差。 1&#xff0c;计算两个时间的时间差 比如 5&#xff1a;50 ~ 19&#xff1a;40 a&#xff09;&#xff0c;用公式 相减 这样默认算出来的是机械的时间加减&#xff0c;即它们之间相差了 13小时50分钟 b&#xff09;&#xff0c;…

win下搭建elk并集成springboot

一、ELK 是什么&#xff1f; ELK 实际上是三个工具的集合&#xff0c;Elasticsearch Logstash Kibana&#xff0c;这三个工具组合形成了一套实用、易用的监控架构&#xff0c;很多公司利用它来搭建可视化的海量日志分析平台。 ElasticSearch ElasticSearch 是一个基于 Lucen…

JUC--线程池

线程池 七、线程池7.1线程池的概述7.2线程池的构建与参数ThreadPoolExecutor 的构造方法核心参数线程池的工作原理 Executors构造方法newFixedThreadPoolnewCachedThreadPoolnewSingleThreadExecutornewScheduledThreadPool(int corePoolSize) 为什么不推荐使用内置线程池&…

Java到底是值传递还是引用传递????

在搞懂这个问题之前, 我们要首先了解什么是值传递, 什么是引用传递? 值传递: 传递的是数据的副本&#xff0c;修改副本不会影响原始数据。引用传递: 传递的是数据的引用&#xff08;地址&#xff09;&#xff0c;修改引用会直接影响原始数据. 也就是说&#xff0c;值传递和引…

屏幕显示技术再突破!海信RGB- Mini LED,让色彩“活”起来

文 | 智能相对论 作者 | 佘凯文 在今天&#xff0c;屏幕显示技术的日新月异&#xff0c;让每次技术革新都引领行业迈向新的高度。 从黑白到彩色&#xff0c;从标清到高清&#xff0c;再到超高清&#xff0c;回顾曾经彩电显示的技术升级&#xff0c;不仅都极大地提升了观众的…

豆包ai 生成动态tree 增、删、改以及上移下移 html+jquery

[豆包ai 生成动态tree 增、删、改以及上移下移 htmljquery) 人工Ai 编程 推荐一Kimi https://kimi.moonshot.cn/ 推荐二 豆包https://www.doubao.com/ 实现效果图 html 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF…

基于SMT32U575RIT单片机-中断练习

任务 查看手册对所有的拓展板上和相对应的底板的引脚对应的端口找到以下结论 通过STM32MX软件对各个引脚进行相应的配置 1.第一种切换模式电脑发送 #include "main.h" #include "icache.h" #include "usart.h" #include "gpio.h"/*…

HNU人工智能期末复习知识点整理

考纲 选择题 ( 30 分 ) (30分) (30分)&#xff1a; 15 15 15个单选 选择题范围为 PPT 内容&#xff0b;课本内容 计算、简答、推理题 ( 70 分 ) (70分) (70分)&#xff1a; 4 4 4个大题&#xff0c;每个大题 2 ∼ 3 2 \sim 3 2∼3小问 4 4 4个大题分别为&#xff1a;机器学习、…

设计DCDC的 Layout的秘诀

很多DCDC芯片的手册都有对应的PCB Layout设计要求&#xff0c;有些还会提供一些Layout示意图&#xff0c;都是大同小异的。 比如我随便列几点buck的设计要点&#xff1a; 1、输入电容器和二极管在与IC相同的面&#xff0c;尽可能在IC最近处。 2、电感靠近芯片的SW&#xff0c;输…