系统设计:日历应用

发布: (2026年1月7日 GMT+8 04:41)
5 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的具体文本内容(文章正文、代码块除外),我将为您翻译成简体中文并保持原有的 Markdown 格式。

功能需求

  • 创建事件、修改事件、取消事件
  • 查看日历(每日、每周或每年)
  • 设置重复会议
  • 通过电子邮件发送任何更改的通知

非功能性需求

  • 高可用性 → 一致性(同步事件的最终一致性)
  • 应支持 10亿 用户
  • 查看日历的低延迟(读多写少 > 写多读少)

数据模型

  • 用户
  • 事件
  • 重复

API

POST /events/

{
  "title": "Meeting title",
  "userId": "creator_id",          // creator
  "userIds": ["participant1", "participant2"], // optional list of attendees
  "startTime": "2025-01-10T15:00:00Z",
  "endTime": "2025-01-10T16:00:00Z",
  "content": {                     // JSON blob: video call link, description, etc.
    "videoCallLink": "https://example.com/meet/123",
    "description": "Discuss project status"
  },
  "recurrence": "weekly|biweekly|monthly|yearly" // optional
}

GET /events?startDay=&endDay=

返回一个事件列表,这些事件位于请求的日期范围内。

高层设计

(概述组件、缓存层、数据库和客户端同步。)

Source:

深入探讨

1. 存储每日或循环事件

事件创建与存储

案例 1 – 简单(一次性)事件
示例:医生预约

event_idowner_idtitlestart_time_utcend_time_utcrruletzversion
evt_101user_1医生预约2025‑01‑10 15:002025‑01‑10 16:00NULLUTC1

返回该行并直接渲染;无需展开。

案例 2 – 循环事件(每周)
示例:团队同步

event_idowner_idtitlestart_time_utcend_time_utcrruletzversion
evt_201user_1团队同步2025‑01‑06 10:002025‑01‑06 11:00FREQ=WEEKLY;BYDAY=MOUTC1

GET 行为(周视图) – 获取基础行,仅在请求的时间范围内展开规则,在内存中生成出现实例。

案例 3 – 带异常的循环事件(一次取消)
示例:2025‑01‑20 的团队同步被取消

event_exceptions 表:

exception_idevent_idexception_datetype
ex_301evt_2012025‑01‑20已取消

GET 行为(2025‑01‑20 那一周) – 展开每周规则,匹配异常,剔除该实例,返回其余事件。

存储概览

  • 一次性事件 → 1 行
  • 多年周期的每周事件 → 1 行
  • 每周 + N 异常 → 1 + N 行

结果:存储最小化。

2. 视图生成与低延迟

用户流程: 服务器向客户端发送事件列表。

优化 – Redis 缓存

  1. 客户端请求事件。
  2. 服务器检查 Redis:
    • 缓存命中 → 返回缓存的事件。
    • 缓存未命中 → 查询数据库,填充 Redis,返回结果。

优点:读取快速,降低 DB 负载。
缺点:缓存失效复杂,存在最终一致性窗口。

结论: 混合方案 – 客户端本地展开 + SQLite 离线存储,既能实现低延迟,又能提供离线能力。

3. 冲突检测与锁策略

  • 乐观锁(默认) – 争用低,适用于个人日历;服务层需要重试逻辑。
  • 悲观锁 – 适用于高争用日历;锁定多行以避免竞争条件。

4. 多设备同步(推送 & 拉取)

  • 拉取(增量同步) – 用于冷启动、重新连接、错过的更新。
  • 推送(SSE / 推送通知) – 获取更新的规则,在本地重新展开,更新 SQLite + UI。SSE 更受青睐:单向、轻量、对电池友好。
  • 混合模型 – 推送保证新鲜度,拉取保证正确性。

5. 数据库选择:SQL vs NoSQL

SQL 的优势

  • 写入量适中,关系型数据库足以扩展。
  • 事务简化冲突检查。
  • 循环查询(RRULE 展开)适配关系模型。
  • 强一致性(或可配置的最终一致性)更易保证。

NoSQL 的权衡

  • 没有事务时冲突检查困难。
  • 循环查询不适配。
  • 默认是最终一致性,需要额外工作确保正确性。
  • 为获得相同保证而导致实现复杂度提升。
Back to Blog

相关文章

阅读更多 »

系统设计快速指南

系统设计是规模的语言,每位工程师都需要掌握它。我创建了这份 1 页的 Quick Guide,帮助你解码复杂的系统设计主题……

EP 6.3:主从架构

概述 在系统设计中,Master‑Slave 或 Leader‑Follower 架构是一种用于实现可伸缩性和高可用性的基本模式,尤其…

别再像2015年那样写API

我们已经进入2025年,仍有许多代码库把 API 视为简单的“返回 JSON 的端点”。如果你的 API 设计仍停留在基本的 CRUD 路由上,你正……