编程数据架构师

解释如何在 SQL 中实现有效的负载分配(分片/分区)以扩展大表。分区和分片之间有什么区别,并且有哪些潜在的陷阱?

用 Hintsage AI 助手通过面试

答案

大规模数据的分配主要通过两种方式实现:

  1. 分区(partitioning): 在同一个数据库中按照某个键(通常是日期或值的范围)将一个表逻辑上划分为多个段(partition)。这使得对单个分区的操作能够更快执行,加速查找并简化维护。

  2. 分片(sharding): 根据特定算法将数据物理上分割到多个数据库/服务器中——表实际上在不同的集群上被复制,每个集群包含自己的数据段。

分区的优点在于无需维护单独的请求路由业务逻辑,一切对应用程序是“透明”的;缺点是受限于单个数据库管理系统的能力。

分片提供了横向扩展(限制仅取决于服务器数量),但需要复杂的同步、路由和处理“跨分片”的请求。

示例 (PostgreSQL, range-partitioning):

-- 基础分区表 CREATE TABLE orders ( id SERIAL PRIMARY KEY, customer_id INT, order_date DATE ) PARTITION BY RANGE (order_date); CREATE TABLE orders_2023 PARTITION OF orders FOR VALUES FROM ('2023-01-01') TO ('2024-01-01');

问题(隐患)

问题: 是否可以在不阻塞主表的情况下“动态”移动行之间的分区?

答案: 在大多数数据库管理系统中,移动行之间的分区等同于删除和插入——这些操作可能会阻塞行甚至整个表,特别是在触发器或外键参与的情况下。这在大量“滚动”数据时需要特别考虑。

示例:

-- ALTER TABLE ... MOVE PARTITION 通常需要特别关注锁。最好在低负载时间进行。

由于不了解主题细节而导致的真实错误示例


故事 1:在一个项目中,构建了对所有分区的分析报告,而不考虑含有数千个分区的分区表会产生巨大的查询执行计划。因此——执行时间和服务器负载急剧增加。解决方案:增加与实际业务查询轴相符的分区数量,并优化扫描计划。


故事 2:在添加分片时,未考虑到分片之间标识符的不唯一性。在进行跨分片聚合时,常常发生键冲突。


故事 3:自动归档“过时”的分区在未经重新检查外键关系的情况下删除它们,导致与其他表的连接丢失和部分“实时”数据的丢失。此后,整个分区删除逻辑用多重测试连接性进行了重写。