关于 database-sharding
为数据库水平扩展提供分片策略、路由模板和实施指南的技能,面向设计或维护分片架构的开发者。内容涵盖哈希、范围和目录路由,以及分片键选择、跨分片操作和重平衡。
主要功能
- 01哈希、范围和目录分片策略
- 02分片路由器模板
- 03分片键选择准则
- 04跨分片查询与事务参考
- 05监控和重平衡指导
适用场景
- 01为用户或租户数据选择分片键
- 02构建数据库分片路由层
- 03规划新增分片和数据迁移
- 04排查热点与跨分片查询问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill database-sharding安装后在 Claude Code 中输入/database-sharding确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/cross-shard-queries.mderror-catalog.mdimplementation-patterns.mdrebalancing-guide.mdshard-key-selection.mdsharding-strategies.mdtemplates/cross-shard-aggregation.tsdirectory-router.tshash-router.tsrange-router.ts详细介绍
database-sharding
用于水平扩展的综合数据库分片模式,涵盖哈希、范围和基于目录的策略。
快速入门(10 分钟)
步骤 1:从模板中选择分片策略:
# Hash-based (even distribution)
cat templates/hash-router.ts
# Range-based (time-series data)
cat templates/range-router.ts
# Directory-based (multi-tenancy)
cat templates/directory-router.ts
步骤 2:选择分片键标准:
- ✅ 高基数(数百万个唯一值)
- ✅ 均匀分布(没有单个值超过 5%)
- ✅ 不可变(永不更改)
- ✅ 查询匹配(出现在 80% 以上的 WHERE 子句中)
步骤 3:实现路由器:
import { HashRouter } from './hash-router';
const router = new HashRouter([
{ id: 'shard_0', connection: { host: 'db0.example.com' } },
{ id: 'shard_1', connection: { host: 'db1.example.com' } },
{ id: 'shard_2', connection: { host: 'db2.example.com' } },
{ id: 'shard_3', connection: { host: 'db3.example.com' } },
]);
// Query single shard
const user = await router.query('user_123', 'SELECT * FROM users WHERE id = $1', ['user_123']);
关键规则
✓ 务必做到
| 规则 | 原因 |
|---|---|
| 在查询中包含分片键 | 避免扫描所有分片(慢 100 倍) |
| 监控分片分布 | 在热点导致服务中断前发现它们 |
| 提前规划重新平衡 | 之后无法轻松添加分片 |
| 选择不可变的分片键 | 更改键 = 数据迁移噩梦 |
| 使用生产数据测试分布 | 合成数据会掩盖真实热点 |
| 为数据局部性进行反规范化 | 将相关数据保留在同一分片上 |
✗ 务必避免
| 反模式 | 不良影响 |
|---|---|
| 使用范围分片的顺序 ID | 最新分片会接收所有写入(热点) |
| 将时间戳用作分片键 | 最新分片负载过重 |
| 未使用 2PC 的跨分片事务 | 数据损坏、不一致 |
| 不使用一致性哈希的简单取模 | 添加分片时必须完整重新分片 |
| 可空的分片键 | 对 NULL 的特殊处理会造成热点 |
| 没有分片路由层 | 硬编码分片 = 无法重新平衡 |
7 个最关键的错误
错误 1:分片键选择错误(热点)
症状:一个分片接收了 80% 以上的流量 修复:
// ❌ Bad: Low cardinality (status field)
shard_key = order.status; // 90% are 'pending' → shard_0 overloaded
// ✅ Good: High cardinality (user_id)
shard_key = order.user_id; // Millions of users, even distribution
错误 2:查询中缺少分片键
症状:查询会扫描所有分片(极慢) 修复:
// ❌ Bad: No shard key
SELECT * FROM orders WHERE status = 'shipped'; // Scans all 100 shards!
// ✅ Good: Include shard key
SELECT * FROM orders WHERE user_id = ? AND status = 'shipped'; // Targets 1 shard
错误 3:使用范围分片的顺序 ID
症状:最新分片会接收所有写入 修复:
// ❌ Bad: Range sharding with auto-increment
// Shard 0: 1-1M, Shard 1: 1M-2M, Shard 2: 2M+ → All new writes to Shard 2!
// ✅ Good: Hash-based sharding
const shardId = hash(id) % shardCount; // Even distribution
错误 4:没有重新平衡策略
症状:受困于初始分片数量,无法扩展 修复:
// ❌ Bad: Simple modulo
const shardId = hash(key) % shardCount; // Adding 5th shard breaks ALL keys
// ✅ Good: Consistent hashing
const ring = new ConsistentHashRing(shards);
const shardId = ring.getNode(key); // Only ~25% of keys move when adding shard
错误 5:跨分片事务
症状:数据不一致、部分写入 修复:
// ❌ Bad: Cross-shard transaction (will corrupt)
BEGIN;
UPDATE shard_1.accounts SET balance = balance - 100 WHERE id = 'A';
UPDATE shard_2.accounts SET balance = balance + 100 WHERE id = 'B';
COMMIT; // If shard_2 fails, shard_1 already committed!
// ✅ Good: Two-Phase Commit or Saga pattern
const txn = new TwoPhaseCommitTransaction();
txn.addOperation(shard_1, 'UPDATE accounts SET balance = balance - 100 WHERE id = ?', ['A']);
txn.addOperation(shard_2, 'UPDATE accounts SET balance = balance + 100 WHERE id = ?', ['B']);
await txn.execute(); // Atomic across shards
错误 6:可变的分片键
症状:记录会在分片间移动,导致重复 修复:
// ❌ Bad: Shard by country (user relocates)
shard_key = user.country; // User moves US → CA, now in different shard!
// ✅ Good: Shard by immutable user_id
shard_key = user.id; // Never changes
错误 7:没有监控
症状:热点未被察觉,性能突然下降 修复:
// ✅ Required metrics
- Per-shard record counts (should be within 20%)
- Query distribution (no shard > 40% of queries)
- Storage per shard (alert at 80%)
- Latency p99 per shard
加载 references/error-catalog.md,查看全部 10 个错误及详细修复方法。
分片策略
| 策略 | 最适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 哈希 | 用户数据、均衡负载至关重要 | 无热点、可预测 | 范围查询会分散到多个分片 |
| 范围 | 时序数据、日志、仅追加数据 | 范围查询高效、便于归档 | 最新分片会形成热点 |
| 目录 | 多租户、复杂路由 | 灵活、易于重新平衡 | 查找开销、单点故障 |
加载 references/sharding-strategies.md,查看详细对比和生产环境示例(Instagram、Discord、Salesforce)。
分片键选择标准
| 标准 | 重要性 | 检查方法 |
|---|---|---|
| 高基数 | 关键 | COUNT(DISTINCT shard_key) > shard_count × 100 |
| 均匀分布 | 关键 | 没有值超过总量的 5% |
| 不可变 | 关键 | 值永不更改 |
| 查询匹配 | 高 | 80% 以上的查询包含该键 |
| 数据局部性 | 中 | 相关记录位于一起 |
决策树:
- 面向用户的应用 →
user_id - 多租户 SaaS →
tenant_id - 时序数据/日志 →
timestamp(范围分片) - 产品目录 →
product_id
加载 references/shard-key-selection.md,查看全面的决策树和测试策略。
配置摘要
基于哈希的路由器
import { HashRouter } from './templates/hash-router';
const router = new HashRouter([
{ id: 'shard_0', connection: { /* PostgreSQL config */ } },
{ id: 'shard_1', connection: { /* PostgreSQL config */ } },
]);
// Automatically routes to correct shard
const user = await router.query('user_123', 'SELECT * FROM users WHERE id = $1', ['user_123']);
基于范围的路由器
import { RangeRouter } from './templates/range-router';
const router = new RangeRouter(shardConfigs, [
{ start: Date.parse('2024-01-01'), end: Date.parse('2024-04-01'), shardId: 'shard_q1' },
{ start: Date.parse('2024-04-01'), end: Date.parse('2024-07-01'), shardId: 'shard_q2' },
{ start: Date.parse('2024-07-01'), end: Infinity, shardId: 'shard_q3' },
]);
// Range queries target specific shards
const janEvents = await router.queryRange(
Date.parse('2024-01-01'),
Date.parse('2024-02-01'),
'SELECT * FROM events WHERE created_at BETWEEN $1 AND $2'
);
基于目录的路由器
import { DirectoryRouter } from './templates/directory-router';
const router = new DirectoryRouter(directoryDBConfig, shardConfigs);
// Assign tenant to specific shard
await router.assignShard('tenant_acme', 'shard_enterprise');
// Route automatically
const users = await router.query('tenant_acme', 'SELECT * FROM users');
何时加载参考资料
选择策略
加载 references/sharding-strategies.md,适用于:
- 在哈希、范围和目录策略之间做出选择
- 需要生产环境示例(Instagram、Discord)
- 规划混合方案
选择分片键
加载 references/shard-key-selection.md,适用于:
- 为新项目选择分片键
- 评估现有分片键
- 使用生产数据测试分布
实现
加载 references/implementation-patterns.md,适用于:
- 从头构建分片路由器
- 实现一致性哈希
- 需要处理事务(2PC、Saga)
- 设置监控和指标
跨分片操作
加载 references/cross-shard-queries.md,适用于:
- 需要跨分片聚合(COUNT、SUM、AVG)
- 实现跨分片联接
- 构建跨分片分页
- 优化散播-收集模式
重新平衡
加载 references/rebalancing-guide.md,适用于:
- 添加新分片
- 在分片之间迁移数据
- 规划零停机迁移
- 平衡不均匀的负载
错误预防
加载 references/error-catalog.md,适用于:
- 排查性能问题
- 审查分片架构
- 查看全部 10 个已记录错误及其修复方法
完整设置清单
分片前:
- 使用生产数据测试分片键分布
- 80% 以上的查询包含分片键
- 监控基础设施已就绪
- 已规划重新平衡策略
路由器实现:
- 分片路由层(而非将分片硬编码)
- 每个分片使用连接池
- 错误处理和重试
- 收集指标(每个分片的查询数、延迟)
分片配置:
- 初始设置 4–8 个分片(留有扩展空间)
- 使用一致性哈希或虚拟分片
- 每个分片配置副本(HA)
- 为每个分片制定备份策略
应用变更:
- 所有查询都包含分片键
- 消除跨分片联接(反规范化)
- 遵循事务边界
- 配置连接池
生产示例
之前(单个数据库不堪重负):
// Single PostgreSQL instance
const db = new Pool({ host: 'db.example.com' });
// All 10M users on one server
const users = await db.query('SELECT * FROM users WHERE status = $1', ['active']);
// Query time: 5000ms (slow!)
// DB CPU: 95%
// Disk: 500GB, growing
之后(分片到 8 台服务器):
// Hash-based sharding with 8 shards
const router = new HashRouter([
{ id: 'shard_0', connection: { host: 'db0.example.com' } },
{ id: 'shard_1', connection: { host: 'db1.example.com' } },
// ... 6 more shards
]);
// Query single user (targets 1 shard)
const user = await router.query('user_123', 'SELECT * FROM users WHERE id = $1', ['user_123']);
// Query time: 10ms (500x faster!)
// Query all shards (scatter-gather)
const allActive = await router.queryAll('SELECT * FROM users WHERE status = $1', ['active']);
// Query time: 800ms (parallelized across 8 shards, 6x faster than single)
// Result: Each shard handles ~1.25M users
// DB CPU per shard: 20%
// Disk per shard: 65GB
// Can scale to 16 shards easily (consistent hashing)
已知问题预防
已预防全部 10 个已记录错误:
- ✅ 分片键错误(热点)→ 先测试分布
- ✅ 查询中缺少分片键 → 代码审查、代码检查
- ✅ 跨分片事务 → 使用 2PC 或 Saga 模式
- ✅ 顺序 ID 热点 → 使用基于哈希的分片
- ✅ 没有重新平衡策略 → 从第 1 天起使用一致性哈希
- ✅ 时间戳分片热点 → 采用哈希与范围混合方案
- ✅ 分片键可变 → 选择不可变键(user_id)
- ✅ 没有路由层 → 从一开始就通过路由器进行抽象
- ✅ 没有监控 → 跟踪每个分片的指标
- ✅ 哈希函数较弱 → 使用 MD5、MurmurHash3、xxHash
参见:references/error-catalog.md,了解详细修复方法
资源
模板:
templates/hash-router.ts- 基于哈希的分片templates/range-router.ts- 基于范围的分片templates/directory-router.ts- 基于目录的分片templates/cross-shard-aggregation.ts- 聚合模式
参考资料:
references/sharding-strategies.md- 策略比较references/shard-key-selection.md- 分片键选择指南references/implementation-patterns.md- 路由器实现references/cross-shard-queries.md- 查询模式references/rebalancing-guide.md- 迁移策略references/error-catalog.md- 全部 10 个已记录错误
生产环境示例:
- Instagram:对媒体使用范围分片
- Discord:对消息使用哈希分片
- Salesforce:对组织使用目录分片
经过生产环境验证 | 预防 10 个错误 | MIT License