分库分表 相关问题

news/2025/2/1 21:46:16/

问题:分库后,就有多个数据源需要,dbproxy 对机器做代理,一般需要lvs/f5 等手段来实现流量的负载均衡,跨机房可能需要dns分发,例如 mycat 阿里的主键。

就这个问题通过一问一答的方式解答

什么是 dbproxy,为什么要用 dbproxy?

dbproxy(数据库代理)可以理解为在应用层与数据库之间插入的一层代理服务,它的主要作用是“屏蔽后端数据库的复杂性”,对上层提供一个统一的访问接口。

屏蔽数据分片(sharding)的细节:
当数据库做了分库分表(sharding)之后,后端往往会出现多台数据库实例、多套数据源。
如果没有代理层,应用需要自己知道每条 SQL 或者每个主键应该访问哪一台数据库实例。
这样应用层会变得非常复杂,而且维护成本高。

统一的连接管理:
在没有代理的情况下,应用可能需要和多台数据库都建立连接,而且应用可能会开启大量连接,会导致数据库压力上升或者网络开销变大。引入代理层后,可以集中管理连接池,并对数据库连接进行复用、路由等操作。

读写分离、负载均衡:
如果有读写分离需求(主库写、从库读),dbproxy 可以帮我们根据 SQL 类型(读/写)自动路由到不同的数据库实例。此外,如果有多台从库,需要做负载均衡时,也可以在代理层实现。

此外,MyCat、阿里 DRDS(Alibaba 的分布式关系数据库服务)、ShardingSphere 等其实都是常见的分库分表中间件或数据库代理的一些解决方案。

为什么分库就需要多个数据源?

所谓“分库”,可以理解成将原本在单一数据库中的数据,按照一定的规则(比如按照用户 ID 范围、订单 ID 哈希等)拆分到多个数据库实例中。这样做的目的是:

水平扩展:单库的容量或性能达到瓶颈后,将数据分到多台机器可以提高整体吞吐量。
分摊压力:多台数据库服务器分担请求,能够缓解单库/单机的压力。

但是分库带来的一个直接后果就是后端出现了多个数据源:

数据源 1(库 A)
数据源 2(库 B)

数据源 N(库 N)

对应用来说,就不是只连一个“数据库”,而是多个。所以应用需要知道——“我这条 SQL 到底要去哪一个库执行?”。这时如果缺少一个统一的代理或中间件,应用就得自己写很多逻辑来进行路由判断:

if (用户ID % 2 == 0) {
走 数据源A
} else {
走 数据源B
}

而且当分库策略或者分库结构发生改变时,又得改业务逻辑。
所以大部分情况下,会加一个 dbproxy 或类似的中间件,把多库对应用‘伪装’成一个逻辑库,由它来做路由、聚合操作等。

为什么需要 LVS / F5 等做负载均衡?

假设你的 dbproxy 部署了多台实例,或者你的数据库实例本身也有多台,需要对外提供统一的访问地址。这时一般会需要一个负载均衡层,比如:
LVS(Linux Virtual Server):内核层级的负载均衡转发,性能高;
F5:硬件负载均衡设备,很多大公司可能用来做专业的流量调度。

它们可以提供一个虚拟 IP 或者域名,对外只暴露一个地址,而内部则把请求按一定的策略分发到真正的 dbproxy 或数据库后端。这样:

如果某个实例挂了,负载均衡可以自动摘除,保证服务的高可用。
可以让多个实例并行,达到横向扩展的效果。

为什么跨机房可能需要 DNS 分发?

当你的应用或服务需要跨机房(甚至跨地域)部署时,网络拓扑就更加复杂。常见有两种层面的流量调度手段:

在网络或四层/七层负载均衡上做流量转发:

比如同一个业务在北京、上海、广州都有机房,那么应用流量先走到一个全局的负载均衡设备或 GSLB(Global Server Load Balancing),再根据 IP 或者距离、延迟等策略把流量引导到就近的机房。

DNS 级别的负载均衡(域名解析层面):

比如你配置一个域名 db.xxx.com,通过 DNS 轮询或者智能 DNS 的方式,自动把请求解析到离用户最近或健康的机房 IP。
常见的做法叫做 GSLB + DNS 方案,比如一些云厂商的 “智能 DNS” 或者 Akamai、Cloudflare 的 GSLB 服务,都能帮你做全局流量调度。
好处是:不需要手动去改 IP,用户通过域名访问,不同区域的解析得到不同的 IP,可以就近接入,提高访问速度,降低延迟,而且当某个机房出现故障时,DNS 解析可以切换到其他机房 IP。

所以,跨机房的负载均衡往往涉及到 DNS 层面的全局调度(GSLB),并配合一定的健康检查或路由策略,使得应用或客户端能够快速接入最近且健康的机房。

关于主键冲突等问题?

分库分表之后,为了保证每台库中的主键(或 ID)全局唯一,通常需要在代理层或者ID 生成服务进行处理。例如:

雪花算法(Snowflake),保证在分布式场景中生成的 ID 全局唯一,而且还能保证有一定的递增趋势。

或者 MyCat/DRDS 自带一些主键策略,像 全局自增主键、多列联合主键 等方式,避免分片数据库中的主键冲突。

这类中间件/代理层也会在一定程度上统一管理或生成主键,屏蔽后面多数据源的冲突问题。

常见的全局唯一主键生成方案?

数据库自增序列/表

通过某个中心数据库表或者序列来获取全局递增的 ID。
缺点:中心化依赖,性能和可用性受限,单点故障风险。

UUID

系统随机生成 128 bit(通常存储为 36 字符的字符串)的标识。
优点:本地生成,无中心依赖。
缺点:存储空间大(影响索引性能),不适合有序索引,难以根据 ID 做路由分片。

Snowflake/雪花算法(Twitter 提出的经典算法)

64 位长整型ID,通常结构是时间戳 + 机器ID + 序列号。
优点:按时间递增,大多是本地生成(需正确配置机器ID、时钟等),性能好,索引有序。
缺点:需要分配 workerId,时钟回拨(时间不同步)会带来复杂度。

号段(Segment)/号段缓存(Leaf)方案

利用数据库的自增列或序列,一次分配一段区间(比如 1000 个ID的区间)给某个节点,节点本地生成 ID。
优点:减少数据库频繁访问,提升性能。
缺点:依赖号段中心库,遇到数据库故障时要有兜底策略。

Redis 原子自增

借助 Redis 的 INCR 命令进行分布式递增;
优点:性能高、实现简单。
缺点:Redis 挂了影响可用性,需要持久化或主从架构保证可靠性。

分布式协调(Zookeeper)

通过 ZK 的顺序节点特性生成全局有序 ID。
缺点:并发高时性能瓶颈明显,不是高并发场景的最佳选择。

实际生产中,雪花算法和号段方案是非常常见的选择。

具体实现细节:以雪花算法为例

雪花算法基础
Snowflake 算法生成的 64 位长整型,通常格式如下(不同实现可能略有差异):

| 1 bit 符号位 | 41 bit 时间戳(毫秒)| 10 bit 工作机器ID | 12 bit 序列号 |

符号位 (1 bit):通常是 0,不用。
时间戳 (41 bit):可以支撑约 69 年的毫秒级时间。
机器ID (10 bit):可以表示 1024 台机器/进程。
序列号 (12 bit):同一毫秒内可生成 4096 个 ID。

生成过程:

取当前时间戳(毫秒级),减去一个自定义起始时间(epoch),得到相对毫秒数。
拼接机器ID 和当毫秒内的序列号。
得到 64 位整形数字(在 Java 中通常是 long 类型)。

机器ID分配
通常会在配置中心或注册中心(Zookeeper、Nacos、Apollo 等)配置每台实例的唯一机器ID。
避免重复机器ID,否则会造成 ID 冲突。

处理时间回拨
如果因为服务器时间突然往回跳,会导致 ID 重复。
一种常见策略:
如果检测到当前时间小于上一次生成 ID 的时间,则等待或直接抛异常,让调用方进行重试;
或在机器ID 上再加一层备用标记,用以覆盖临时回拨(但实现较复杂)。

ID 的数据类型、在数据库如何存储?

无论是雪花算法、号段方案还是其他方式,最终在数据库里通常会用BIGINT类型(MySQL 等常见数据库都是 64 位整型)。为什么?

足够大:64 位可以容纳相当大的数字(9,223,372,036,854,775,807)。
索引效率:数字类型做主键索引效率更高(相比字符串),也有天然顺序(Snowflake 生成的ID大体单调递增)。
通用性好:大多数语言里都有 64 位整型类型(long 类型等)

CREATE TABLE user_order (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
amount DECIMAL(10,2),
create_time TIMESTAMP,

);

在插入数据时,应用侧(或中间件)已经生成好了全局唯一的 ID,直接插入即可。

分库分表/跨机房场景下的路由分片?

基于 ID 的分库分表

常见做法是基于 ID 取模(Hash)或区间进行路由。例如:

Hash 路由:库索引 = (id % 库数),表索引 = (id % 表数) 等;
区间路由:如 0 ~ 9999999 存在库0,10000000 ~ 19999999 存在库1,等等。

如果使用雪花算法,往往取其中的低位来做 id % n,这样分散度较好(也要注意避免某些位都是 0 导致不均衡)。

注意:如果将来要扩容分库数,从 2 库扩展到 4 库,ID 取模算法也要做迁移或映射,否则会导致数据不在预期的库中。可以考虑使用一致性哈希或中间件提供的线上扩容功能。

基于 ID 分配机房(跨机房存储)

如果每个机房都存储不相交的数据片,通常也可以通过取模或区间来决定某条数据应该进哪个机房。

例如:id % 2 == 0 存在北京机房,id % 2 == 1 存在上海机房。
或者更精细的分段,比如 ID 前 X 位代表机房编号,这在定制的雪花算法实现里也可以做到(将机器ID 部分的一部分位数当作“机房ID”+“机器ID”)。

但是这种做法会导致“跨机房 JOIN/查询”难度较大。如果一个用户的数据需要经常跨机房访问,就要考虑多机房同步或“同城多活”方案,复杂度会增加。

具体举例:MyCat / ShardingSphere / DRDS?

一些常见的分库分表中间件内部也会内置 ID 生成器,或者可以集成第三方 ID 生成服务,以实现全局主键:

MyCat

提供了多种主键策略,比如自增 ID、UUID、前缀自增等,但很多团队会结合雪花算法自己生成,然后写到 MyCat 路由。
路由规则可自定义(Groovy 表达式等)。

ShardingSphere

可以配置内置的雪花算法生成器 (Snowflake) 或者你自定义的算法;
在 sharding 配置中指定 keyGenerator 的实现类;
还可以定义分片算法(standard/inline/hint 等),在 inline 中写 t_order_$->{order_id % 4} 之类的表达式。

阿里 DRDS / PolarDB-X

本身也提供全局自增 ID和雪花算法两种模式,可以在控制台上进行配置。

如果使用雪花算法,ID 会不会重复?

只要每台机器的 workerId 唯一,时间戳不回拨,序列号在同毫秒内不溢出,就不会重复。
时间回拨可以通过“等待”或“抛异常重试”来处理。

ID 是不是严格递增?

雪花算法是趋势递增,同一毫秒内生成多个 ID,后者序列号更大,毫秒切换后则跳到新的时间区间。但由于多线程并发或多机并发,全局的顺序性不一定 100% 按数值递增,只有单机单毫秒内是顺序的。
如果要绝对顺序,可以用数据库序列或 ZK 顺序节点,但要考虑性能。

ID 占用多少存储空间?

64 位整型在 MySQL 中一般是 BIGINT (8 字节),相比 32 位INT 多 4 个字节,但是在实际生产中一般都能接受。

修改分库分表策略,ID 取模规则怎么变?

需要迁移数据或使用中间件提供的在线扩容功能;
在早期设计就要考虑可扩容,比如使用 “虚拟桶” 思路(先对 ID 取模 128,再把桶映射到实际库表),扩容时只需改变桶与库表的映射关系,减少大规模数据迁移。

跨机房多活场景,主键还会冲突吗?

如果每个机房都用各自的雪花算法,但机器ID 没做好区分,可能出现冲突;
需要为不同机房分配不同机房ID段,然后再在机房内部用机器ID + 序列号;
或者用统一的中心化 ID 服务(注意多活容灾)

订单表量很大,会不会因为主键太大影响性能?

通常使用雪花算法或递增 ID,都能让主键在InnoDB聚簇索引中大致有序插入,插入性能较好;
如果索引随机性大(如 UUID) 反而会导致数据页分裂、性能下降。

在分布式事务下 ID 是怎么处理的?

通常先拿 ID(本地生成或分布式 ID 服务),再进行插入,这样不依赖全局事务;
避免了“插入时再获取自增ID”,减少锁竞争和分布式事务带来的性能问题

公司订单量爆炸增长,单库性能不足,如何拆分数据库?

先做读写分离,缓解读压力。
如果还是不够,进一步垂直拆分(用户、订单等分不同数据库),再进行水平拆分(根据订单ID或用户ID哈希拆分)。
引入分库分表中间件(MyCat、ShardingSphere)或云厂商 DRDS/PolarDB-X,实现自动路由、主键生成。
同时考虑搜索、统计类场景,可能要引入ES、大数据平台等。

“为什么要用 MyCat / ShardingSphere,而不是自己在应用里写路由逻辑?”

避免业务代码与分库分表策略耦合,后期维护成本高。
中间件往往提供成熟的功能,如全局主键生成、分片路由、读写分离、分布式事务支持等。
便于在线扩容、迁移,减少对应用的改动。

问道 谈到多数据源 / 分库分表 / 负载均衡 / 跨机房 / DNS 分发 ?

业务背景:为什么要分库/分表,为什么要做负载均衡,为什么要跨机房。

技术实现:
分库分表中间件的核心功能(路由、读写分离、主键生成);
负载均衡(LVS/F5/HAProxy)的工作原理和如何部署;
DNS/GSLB 进行跨机房流量调度的思路。

关键挑战:分布式事务、数据一致性、主键冲突、迁移扩容、故障切换、DNS 缓存等。

实践经验:
是否有线上规模案例?遇到哪些坑,比如 SQL 分片错误、热点数据、跨机房延迟等。
如何进行监控与报警,比如 metrics、日志、慢 SQL。

扩展思考:
会不会继续往微服务、消息队列、大数据分析方向演进?如何结合 Spring Cloud 或云平台服务?

项目实践或线上真实数据(QPS、TPS、库规模、节点数、机房分布)

深挖底层原理,可以谈一些LVS 的四层转发机制 (DR/NAT 模式)、F5 处理 SSL 卸载、DB 中间件如何做 SQL 解析和执行计划等底层知识。

场景设计,你可以尝试描述如何保证当机房A挂了以后,机房B能够无缝顶上,涉及到数据同步、应用配置、DNS 切换、监控报警等全链路内容。


http://www.ppmy.cn/news/1568521.html

相关文章

LeetCode--84. 柱状图中最大的矩形【单调栈】

84. 柱状图中最大的矩形 正文 题目如下 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。 这道题暴力很简单,但是时间复杂度是O(N^2)&#xf…

虚拟串口技术

本文来自智谱清言 --------------------------------- 虚拟串口是一种软件技术,它模拟了物理串口(COM口)(Communication Port)的功能,允许计算机与其他设备或应用程序通过串行通信协议进行数据交换,而不需要实际的物…

【架构面试】二、消息队列和MySQL和Redis

MQ MQ消息中间件 问题引出与MQ作用 常见面试问题:面试官常针对项目中使用MQ技术的候选人提问,如如何确保消息不丢失,该问题可考察候选人技术能力。MQ应用场景及作用:以京东系统下单扣减京豆为例,MQ用于交易服和京豆服…

分层多维度应急管理系统的设计

一、系统总体架构设计 1. 六层体系架构 #mermaid-svg-QOXtM1MnbrwUopPb {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-QOXtM1MnbrwUopPb .error-icon{fill:#552222;}#mermaid-svg-QOXtM1MnbrwUopPb .error-text{f…

LLM - 大模型 ScallingLaws 的设计 100B 预训练方案(PLM) 教程(5)

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/145356022 免责声明:本文来源于个人知识与公开资料,仅用于学术交流,欢迎讨论,不支持转载。 Scaling Laws (缩放法则) 是大模型领域中,用于描述 模型性能(Loss) 与…

0基础跟德姆(dom)一起学AI 自然语言处理22-fasttext文本分类

1 文本分类介绍 1.1 文本分类概念 文本分类的是将文档(例如电子邮件,帖子,文本消息,产品评论等)分配给一个或多个类别. 当今文本分类的实现多是使用机器学习方法从训练数据中提取分类规则以进行分类, 因此构建文本分…

讯飞绘镜(ai生成视频)技术浅析(四):图像生成

1. 技术架构概述 讯飞绘镜的图像生成技术可以分为以下几个核心模块: 文本理解与视觉元素提取:解析脚本中的场景描述,提取关键视觉元素(如人物、场景、物体等)。 视觉元素生成:根据文本描述生成具体的视觉元素(如人物、场景、物体等)。 分镜画面生成:将视觉元素组合成…

HTML5 常用事件详解

在现代 Web 开发中,用户交互是提升用户体验的关键。HTML5 提供了丰富的事件机制,允许开发者监听用户的操作(如点击、拖动、键盘输入等),并触发相应的逻辑处理。本文将详细介绍 HTML5 中的常用事件,包括鼠标…