本文へ移動
cccskills
無料GitHub で公開

consistency-multitenancy

设计事务边界、一致性策略、多租户隔离和数据生命周期时使用。适用于高并发写入、幂等、多租户 SaaS、软删归档和审计字段设计。融合 ACID + Saga + 乐观锁 + RLS + Tenant Per Schema。

インストール方法を見る

含まれるファイル(4)

  • SKILL.md12.8 KB
  • references/consistency-multitenancy-guide.md10.1 KB
  • references/tenant-isolation-guide.md857 B
  • templates/consistency-checklist-template.md1.2 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

一致性与多租户(Consistency & Multi-tenancy)

参考来源:Pat Helland《Life beyond Distributed Transactions》、Martin Kleppmann《Designing Data-Intensive Applications》、AWS SaaS 多租户白皮书、Stripe / Slack / Notion 多租户实践。

适用场景

  • 判断事务边界和一致性要求
  • 设计幂等写入、并发冲突处理、乐观锁
  • 设计多租户字段、租户隔离、复合唯一约束
  • 设计软删、归档、审计字段和数据生命周期
  • 评估跨表、跨服务、跨数据库的一致性方案
  • 行级安全(RLS)配置
  • Tenant Per Schema vs Shared Schema 选型

核心原则

1. 把隔离边界和一致性要求显式写入 schema 与约束
   "tenant_id 字段" ≠ "多租户隔离"
   还需要:查询过滤 + 唯一约束 + 权限边界 + 测试

2. 事务不应无限扩大
   边界应等于业务不变量边界

3. 幂等和唯一性优先由数据库约束兜底
   不靠应用代码判重

4. 不在事务里做外部调用
   HTTP / 邮件 / 第三方 API 都不能放事务

5. 强一致 vs 最终一致:业务驱动
   涉及钱:强一致
   不影响财务:最终一致 + 补偿

6. 多租户三层防御:应用层 + 数据库层 + 测试层
   单层防御必定漏

7. 软删除是甜蜜陷阱
   破坏唯一约束、查询要带 WHERE deleted_at IS NULL

事务(ACID)速查

A - Atomicity(原子性):要么全成,要么全败
C - Consistency(一致性):事务前后业务规则一致
I - Isolation(隔离性):并发事务互不干扰
D - Durability(持久性):提交后数据不丢

隔离级别(PostgreSQL / MySQL)

级别脏读不可重复读幻读写偏斜
Read Uncommitted✓✓✓✓
Read Committed(默认)✗✓✓✓
Repeatable Read(PG/MySQL 默认变体)✗✗✗✓
Serializable✗✗✗✗
-- PostgreSQL
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- MySQL
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
建议:
  - 默认 Read Committed 已能满足大多数业务
  - 关键金融场景用 Serializable + 重试
  - 慎用 Read Uncommitted(一般不需要)

事务边界设计

错:事务过大

# 反模式:事务包含外部调用
@transaction.atomic
def create_order(user_id, items):
    order = Order.objects.create(...)
    OrderItem.objects.bulk_create(...)
    
    # 外部 HTTP 调用 → 事务持续 30 秒
    payment_result = call_payment_gateway(order)
    
    if payment_result.success:
        order.status = 'paid'
        order.save()

对:事务最小化 + Saga

# 阶段 1:本地事务(仅 DB 操作)
@transaction.atomic
def create_order(user_id, items):
    order = Order.objects.create(status='pending', ...)
    OrderItem.objects.bulk_create(...)
    return order

# 阶段 2:异步外部调用
def process_payment(order_id):
    order = Order.objects.get(id=order_id)
    result = call_payment_gateway(order)
    
    # 阶段 3:本地事务更新状态
    if result.success:
        order.update(status='paid')
    else:
        order.update(status='failed')
        # 补偿动作(释放库存、退款)
        compensate(order)

幂等性设计

Idempotency-Key 模式(写接口必备)

-- 幂等性表
CREATE TABLE idempotency_keys (
  key varchar(128) PRIMARY KEY,
  request_hash varchar(64) NOT NULL,
  response_body jsonb,
  status varchar(32) NOT NULL,  -- 'processing' / 'completed' / 'failed'
  created_at timestamptz DEFAULT now(),
  expires_at timestamptz NOT NULL DEFAULT (now() + INTERVAL '24 hours')
);

CREATE INDEX idx_idempotency_expires ON idempotency_keys(expires_at);

业务唯一约束(最强幂等)

-- 同一订单不能支付两次
CREATE TABLE payments (
  id bigserial PRIMARY KEY,
  order_id bigint NOT NULL,
  amount_cents bigint NOT NULL,
  status varchar(32) NOT NULL,
  created_at timestamptz DEFAULT now(),
  
  -- 业务唯一:每个订单只能有一条成功的支付
  CONSTRAINT uq_order_payment UNIQUE (order_id, status)
    DEFERRABLE INITIALLY DEFERRED
);

乐观锁(version 字段)

-- 表设计
CREATE TABLE orders (
  id bigserial PRIMARY KEY,
  status varchar(32) NOT NULL,
  version integer NOT NULL DEFAULT 1,
  ...
);

-- 更新时校验版本
UPDATE orders
SET status = 'paid', version = version + 1
WHERE id = ? AND version = ?;

-- 影响 0 行 → 版本冲突 → 应用层重试或返回 409

悲观锁(SELECT FOR UPDATE)

-- 高冲突场景:直接锁行
BEGIN;
SELECT * FROM accounts WHERE id = ? FOR UPDATE;
UPDATE accounts SET balance = balance - 100 WHERE id = ?;
COMMIT;
何时用:
  乐观锁:冲突率低(< 5%),重试代价小
  悲观锁:冲突率高,必须串行

多租户隔离三层防御

第一层:应用层

# 中间件:每个查询都带 tenant_id
class TenantMiddleware:
    def process_request(self, request):
        request.tenant_id = get_tenant_from_token(request)

# ORM 自动过滤
class TenantAwareManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(tenant_id=current_tenant_id())

第二层:数据库层(RLS / 视图)

-- PostgreSQL Row Level Security(强烈推荐)
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON orders
  USING (tenant_id = current_setting('app.current_tenant_id')::bigint);

-- 应用每次连接时设置
SET LOCAL app.current_tenant_id = '123';
SELECT * FROM orders;  -- 自动过滤

第三层:测试层

# 跨租户越权测试用例
def test_cross_tenant_isolation():
    user_a = create_user(tenant=1)
    user_b = create_user(tenant=2)
    
    order_a = create_order(user=user_a)  # tenant 1 的订单
    
    # 用 user_b 的身份尝试访问 user_a 的订单
    response = client.get(f'/orders/{order_a.id}', as_user=user_b)
    assert response.status_code == 404  # 或 403

多租户数据模型

Shared Schema(共享 Schema,最常用)

-- 所有租户共享表,靠 tenant_id 区分
CREATE TABLE orders (
  id bigserial PRIMARY KEY,
  tenant_id bigint NOT NULL REFERENCES tenants(id),
  ...
);

-- 复合唯一(必须含 tenant_id)
CREATE UNIQUE INDEX uq_orders_number
  ON orders(tenant_id, order_number);

优势:

  • 简单
  • 资源利用高(单库支持千个租户)
  • 升级一次性

劣势:

  • 单租户故障可能影响全部
  • 大租户 vs 小租户性能干扰
  • 跨租户查询风险

适合:SaaS 中小租户、共用功能

Schema Per Tenant(每租户独立 Schema)

-- 每个租户一个 schema
CREATE SCHEMA tenant_acme;
CREATE TABLE tenant_acme.orders (...);

CREATE SCHEMA tenant_globex;
CREATE TABLE tenant_globex.orders (...);

优势:

  • 隔离更强
  • 备份恢复粒度细
  • 大租户性能可控

劣势:

  • 升级复杂(每个 schema 都要 migrate)
  • 资源消耗大
  • 应用层切换 schema 复杂

适合:百级租户、企业版

Database Per Tenant

每个租户独立数据库实例

优势:
  - 完全隔离
  - 性能 / 备份 / 安全独立

劣势:
  - 资源消耗大
  - 运维复杂

适合:大型企业客户、合规要求强(PII / 医疗)

唯一性约束设计

多租户场景

-- 错:用户邮箱全局唯一
CREATE UNIQUE INDEX uq_users_email ON users(email);
-- 不同租户不能有同名邮箱

-- 对:租户内唯一
CREATE UNIQUE INDEX uq_users_email_tenant
  ON users(tenant_id, email);

软删除场景

-- 错:软删后无法重新注册
CREATE UNIQUE INDEX uq_users_email
  ON users(tenant_id, email);
-- 同 email 的删除用户阻止新注册

-- 对:部分索引
CREATE UNIQUE INDEX uq_users_email_active
  ON users(tenant_id, email)
  WHERE deleted_at IS NULL;

软删除 vs 硬删除决策

软删除(deleted_at):
  ✅ 用户可恢复
  ✅ 关联数据完整
  ✅ 审计需要
  ❌ 唯一约束复杂
  ❌ 查询都要带 WHERE
  ❌ 数据膨胀

硬删除:
  ✅ 简单
  ✅ 性能好
  ❌ 不可恢复
  ❌ 关联数据连锁删除

归档(删除前移到归档表):
  ✅ 主表干净
  ✅ 历史数据可查
  ❌ 跨表查询复杂

推荐:
  - 用户数据:软删除(GDPR 30 天后硬删)
  - 业务数据:软删除 + 6 个月归档
  - 临时数据(缓存 / 会话):硬删除
  - 审计日志:永不删除

数据生命周期

-- 推荐字段
CREATE TABLE orders (
  id bigserial PRIMARY KEY,
  -- ... 业务字段 ...
  
  -- 生命周期字段
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  deleted_at timestamptz,                            -- 软删
  archived_at timestamptz,                           -- 归档
  retention_until timestamptz,                       -- 保留期限
  
  -- 审计字段
  created_by bigint REFERENCES users(id),
  updated_by bigint REFERENCES users(id),
  deleted_by bigint REFERENCES users(id),
  
  -- 乐观锁
  version integer NOT NULL DEFAULT 1
);

标准流程

1. 识别业务不变量和并发写入场景
   - 哪些状态不能同时存在
   - 哪些操作不能重复执行
   - 哪些字段必须唯一
   ↓
2. 判断强一致、最终一致或补偿机制
   - 涉及钱 → 强一致
   - 通知 / 报表 → 最终一致
   - 跨服务 → Saga
   ↓
3. 设计事务边界、锁策略、唯一约束、版本字段
   - 事务最小化
   - 数据库约束兜底
   - 乐观锁优先
   ↓
4. 设计租户字段、复合约束、默认过滤规则
   - 三层防御
   - 复合唯一含 tenant_id
   - RLS 兜底
   ↓
5. 设计删除、归档、审计和恢复策略
   - 软删 + 部分索引
   - 归档表
   - 审计日志
   ↓
6. 输出一致性和隔离检查清单

多租户检查表

项目检查点
租户键每张租户数据表是否有明确 tenant_id 字段
查询是否默认带 tenant_id 过滤(中间件 / RLS)
唯一性唯一约束是否包含租户维度
后台操作跨租户操作是否有审计和授权
测试是否有跨租户越权回归用例
备份单租户备份恢复是否可行
监控单租户性能 / 错误是否可观测
删除租户注销时数据如何处理(GDPR)

配套模板

  • templates/consistency-checklist-template.md — 一致性 + 多租户 + 软删除 + 审计 + RLS 配置完整清单

质量自检

□ 列出业务不变量
□ 说明事务边界和失败补偿
□ 幂等键、唯一约束、版本字段明确
□ 多租户三层防御(应用 + 数据库 + 测试)
□ 唯一约束包含 tenant_id
□ 软删除有部分唯一索引
□ 删除、归档、审计、恢复策略明确
□ 事务不包含外部调用
□ 隔离级别选择有依据
□ 跨服务一致性方案(Saga / 事件)
□ RLS 配置(PG)兜底
□ 与安全工作流协作评审

常见坑

  1. 有 tenant_id 但唯一约束没带 tenant_id——同 email 跨租户冲突
  2. 后台管理接口绕过租户过滤——越权读取
  3. 长事务包外部调用——锁持有 30 秒,影响并发
  4. 幂等只靠前端防重复点击——后端裸奔
  5. 软删后唯一约束和恢复逻辑冲突——无法重新注册
  6. 乐观锁 version 不增——更新成功但无法检测并发
  7. 隔离级别用 Read Uncommitted——读到未提交数据
  8. 跨表强一致用分布式事务——2PC 性能差,应该 Saga
  9. 多租户共享表无 RLS 兜底——应用层一漏全漏
  10. 审计日志在主表——主表膨胀,审计被污染
  11. 删除级联到所有关联表——删一个用户影响 50 张表
  12. 保留期限不明——GDPR 违规
  13. 跨租户备份恢复无方案——单租户问题影响所有
  14. 测试不覆盖跨租户越权——上线后才发现

与其他 skill 的协作

上游:
  schema-design → 提供表结构基础
  api-designer → 提供权限边界

下游:
  index-access-pattern → 设计租户维度索引
  data-operations-safety → 评估跨租户操作和数据修复风险
  security-engineer → 评审敏感数据和权限边界
  backend-engineer → 实现租户中间件
  qa-engineer → 跨租户越权回归用例

相关参考

  • references/consistency-multitenancy-guide.md — ACID 深度、Saga 模式、SaaS 多租户三种架构对比、RLS 实战、PostgreSQL 隔离级别真相、Slack / Notion 真实案例

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

A/B 测试设计与分析时使用。适用于产品实验、功能验证、转化率优化。融合假设驱动 + 样本量计算 + 置信度判断。

日本語の概要は準備中です。原文の説明を表示しています。

zhaoxuya520/AI-Fullstack-Delivery-Workflow532026年5月19日 更新

上线前业务方/产品方验收时使用。适用于功能交付确认、UAT、发布会签。融合 BDD Given-When-Then、Specification by Example、ATDD。

日本語の概要は準備中です。原文の説明を表示しています。

zhaoxuya520/AI-Fullstack-Delivery-Workflow532026年5月19日 更新

设计无障碍可访问的界面时使用。适用于满足 WCAG 标准、键盘导航、屏幕阅读器支持。优先使用 WCAG 2.2 AA 级标准 + 键盘 + 焦点 + 对比度 + ARIA。

日本語の概要は準備中です。原文の説明を表示しています。

zhaoxuya520/AI-Fullstack-Delivery-Workflow532026年5月19日 更新

实现可访问性(无障碍)时使用。覆盖 WCAG 2.2 AA / ARIA / 键盘导航 / 屏幕阅读器 / 颜色对比 / 焦点管理。融合 React Aria / Radix UI / Headless UI 实践 + axe-core 自动化测试。

日本語の概要は準備中です。原文の説明を表示しています。

zhaoxuya520/AI-Fullstack-Delivery-Workflow532026年5月19日 更新

AI 模型部署时使用。适用于推理服务 / GPU / TensorRT / 监控 / 扩缩容。

日本語の概要は準備中です。原文の説明を表示しています。

zhaoxuya520/AI-Fullstack-Delivery-Workflow532026年5月19日 更新

数据分析报告输出时使用。适用于业务分析报告、周报、专项分析、决策支持。融合洞察 + 建议 + 行动项。

日本語の概要は準備中です。原文の説明を表示しています。

zhaoxuya520/AI-Fullstack-Delivery-Workflow532026年5月19日 更新

zhaoxuya520 のスキルをすべて見る

このスキルの問題を報告する