OK,我们按你给的 12 步,一次写完,用简单英文单词 + 少量中文解释,方便你面试直接“填空式”说。
1. 要 verify 的 requirements(按重要程度排序)
针对 4 个 use case:
- List books
- Read online
- Sync reading progress
- Upload ebook by author
表里是“这个系统总体要问清楚的点”,不是 per-API。
| Rank Field What to verify (simple English) |
| 1 | Security | DRM level, encrypt at rest, encrypt in transit, auth method (OAuth/JWT), access control per user/book |
| 2 | Type | Request type: read vs write, idempotent or not, strong auth needed or not |
| 3 | Order | For progress: which update wins if two devices send at same time? (last-write-wins by time? server time or client time?) |
| 4 | Delay | Max P95 for list, read page, sync progress, upload; which flows are “hard real-time” vs “can wait a bit” |
| 5 | lost | What happens if progress update lost? Need retry? client buffer? For upload, can we lose data? (answer: no) |
| 6 | idemp | Idempotency for upload (same file, same request id), progress update (same event not counted twice) |
| 7 | Size & retention | Max ebook file size, max pages, how long to store progress, logs, history; legal retention for books |
| 8 | Region: timeZone | Reader from many regions; which time zone for “last read time”; impact on order of progress |
| 9 | Region: language | Multi-language UI, book metadata locale, search with different language, right-to-left text |
| 10 | Network: resume | Support resume for big file upload / download; offline read then later sync; mobile weak network |
可以面试时一句话打包:
“I want to verify security, request type, order of writes, delay budget, loss tolerance, idempotency, data size and retention, region/timezone/language, and network resume behavior.”
2. 10 个需要提到的“概念 / 算法”(按重要程度)
- DRM (Digital Rights Management)
- CDN distribution
- 用 CDN 推送文件到全球边缘节点,减小延迟、减小源站压力。
- Content-addressable storage (hash key)
- Full-text search index
- Pagination and cursor-based listing
- 列表 books / library 时做分页、cursor。
- Recommendation engine (collaborative filtering / content-based)
- 用行为、内容做推荐(虽然当前 use case 不直接用,也可以顺嘴提)。
- Caching (Redis / in-memory)
- 缓存书列表、书 meta、热书页内容,保护 DB。
- Sharding strategy
- 以 user_id / book_id 分片,水平扩展。
- Resumable upload / download protocol
- 大文件分块上传下载,断点续传,简单说 chunk + offset。
- Event-driven updates
3. Read heavy or write heavy?为什么
- Overall: read-heavy.
- Reason (简单说几句就够用):
- 很多读者在看书、翻页、列出书单。
- 写操作(上传书、更新进度)远少于读操作。
- Typical: one author upload one book once, but thousands readers download and read many times.
4. Use case → API 设计(REST / 是否需要 Auth)
4.1 List books (list my library)
- Style: RESTful
- Method:
GET - Auth: Yes (need user library)
- URL:
/api/v1/users/{userId}/library
- Request payload:
- Query:
page, pageSize, maybe filter
- Response code:
- Response payload (simple):
{
"userId": "u123",
"items": [
{
"bookId": "b1",
"title": "Book Title",
"author": "Author Name",
"coverUrl": "https://...",
"lastReadAt": "2025-01-01T10:00:00Z"
}
],
"nextCursor": "cursor123"
}
4.2 Read online (get page content)
- Style: RESTful
- Method:
GET - Auth: Yes (need check user has right)
- URL:
/api/v1/books/{bookId}/pages/{pageNumber}
- Request payload:
- Query: maybe
format (html/text)
- Response code:
- Response payload:
{
"bookId": "b1",
"pageNumber": 10,
"contentHtml": "<p>...</p>",
"drmToken": "token123"
}
4.3 Sync reading progress
- Style: RESTful
- Method:
POST (or PUT) - Auth: Yes
- URL:
/api/v1/users/{userId}/progress
- Request payload:
{
"bookId": "b1",
"deviceId": "d1",
"currentPage": 10,
"percentage": 0.25,
"eventTime": "2025-01-01T10:00:00Z",
"requestId": "uuid-123" // idempotent key
}
- Response code:
- Response payload:
{
"status": "ok",
"savedPage": 10,
"savedAt": "2025-01-01T10:00:01Z"
}
4.4 Upload ebook by author/publisher
- Style: RESTful (could be multipart)
- Method:
POST - Auth: Yes (author, publisher)
- URL:
- Request payload:
- Multipart or JSON + pre-signed URL:
{
"title": "My Book",
"authorName": "Author A",
"language": "en",
"categories": ["fiction"],
"fileUrl": "https://upload-temp/...",
"requestId": "uuid-456"
}
- Response code:
202 Accepted (async convert + DRM)
- Response payload:
{
"ebookId": "b1",
"status": "processing"
}
5. Core entities(按重要程度排序 + schema)
5.1 User
- Fields:
user_id (PK)emailhashed_passwordrole (READER / AUTHOR / ADMIN)created_atregionlanguage
5.2 Book
- Fields:
book_id (PK)titleauthor_namepublisher_id (FK to User or Org)languagecategories (tags)status (active / removed)created_atupdated_at
5.3 EbookFile / Asset
- Fields:
file_id (PK)book_id (FK)format (epub, mobi, pdf)storage_url (S3 path / object key)size_byteschecksumdrm_typecreated_at
5.4 UserLibraryItem
- Fields:
user_id (PK part)book_id (PK part)added_atsource (bought / free / promo)
5.5 ReadingProgress
- Fields:
user_id (PK part)book_id (PK part)current_pagepercentagedevice_idlast_event_time (from client)updated_at (server time)
5.6 UploadRequest
- Fields:
request_id (PK)uploader_idbook_idraw_file_urlstatus (processing, done, failed)created_atlast_error
6. P95 Latency + Availability + Durability
合并相同 latency 级别:
- Read page / list books
- P95 latency: ≤ 200 ms
- Availability: 99.9% (3 nines)
- Durability: metadata is 99.99% (4 nines)
- Sync reading progress
- P95 latency: ≤ 300 ms (用户能接受小延迟)
- Availability: 99.9%
- Durability: 99.99% (不希望丢进度)
- Upload ebook
- P95 latency (for ACK): ≤ 1 s
- Availability: 99.5% (后台工具可以稍低)
- Durability for book file: “11+ nines” on object store
7. DAU + Peak + Storage 假设
Just one simple set of numbers:
- DAU: 5 million active readers
- Peak factor: 5x (peak vs avg)
- QPS rough:
- List books: say 1 list/day/user →
- avg ≈ 5M / (24*3600) ≈ 60 QPS
- peak ≈ 300 QPS
- Read online page: say 50 pages/day/user →
- avg ≈ 50 * 5M / 86400 ≈ 2900 QPS
- peak ≈ 15K QPS
- Sync progress: say 10 updates/day/user →
- avg ≈ 600 QPS, peak ≈ 3K QPS
- Upload ebook: small, maybe 5K per day →
Storage
- Number of books: 1M ebooks
- Avg size per ebook (multiple formats): 10 MB
- Raw storage: 10 TB (plus replicas, say 30 TB)
- Progress + library:
- Library rows ≈ 5M users * 30 books = 150M rows
- Progress rows ≈ 5M * 10 active books = 50M rows
- Each row ~ 200 bytes → total < ~40 GB
8. This system AP or CP?
- Core answer(简单口语):
- “The system is AP for reading and CP for rights and assets.”
- Details:
- AP: list books, read pages, show maybe slightly stale data, but must stay up.
- CP: upload ebook, manage who owns which book, DRM rights, payment (if any).
- For reading progress, can be AP with last-write-wins, small inconsistency acceptable.
9. Edge cases + solutions(按重要程度)
- Hot book read (HotKey Read)
- 大家都在读同一本新书第一页
- 问题:DB / content service 被打爆
- Solution:CDN 缓存页面内容;本地 in-memory cache;cache key = book_id+page;限流。
- Thundering herd on new release
- 书刚上线,客户端一起拉 library / metadata
- Solution:加前端和 CDN 缓存,后端加 request coalescing(合并同一 cache miss),平滑 rollout。
- Progress update race / order
- 多个设备同时更新同一本书进度
- Solution:带 eventTime,服务器做 last-write-wins;也可写 conflict log;用 server time 校正。
- Delay / Lost for progress sync
- 网络差,进度长时间没上传,或部分事件丢失
- Solution:客户端本地 queue buffer,重试;服务端幂等(requestId);DLQ 记录异常。
- Stale library view (inconsistency)
- 用户刚买书,但列表里暂时看不到
- Solution:写库走主库,读列表可先读 cache;对“刚买完”的请求强制走主库或 read-your-write 缓存。
- 3rd-party DRM / payment fail
- DRM server/down、支付网关失败
- Solution:重试 + backoff;DLQ;manual reconcile;不重复扣费(idempotent key)。
- Cache fail
- Redis 挂了,所有读打到 DB
- Solution:开 circuit breaker,慢启动重建 cache;多分片、多副本;降级返回更小数据。
- DB fail (catalog / library / progress)
- 主库挂掉
- Solution:多副本;自动 failover;读写分离;对只读流量使用只读副本。
- Service fail (upload / convert / content)
- 某微服务挂掉
- Solution:K8s auto-restart;健康检查;蓝绿 / canary 部署;限流保护其他服务。
- Clock issues (client vs server)
- 客户端时间乱,progress order 错
- Solution:优先用 server 时间;client 事件时间只做参考;如差距太大,打标记。
10. <=6 个 services(workflow + DB + QPS + stateful)
10.1 API Gateway / Auth Service
- Responsibility:
- Auth, JWT, routing to other services.
- DB:
- Small UserDB (relational, e.g. MySQL/Postgres)
- Schema:
user_id PK, email, password, role - Sharding key:
user_id - 从 MySQL 单机 → 分片/托管 DB(Aurora / Cloud SQL)
- QPS per 8core/16G: ~1K-2K QPS
- Stateful?: Stateless(session in token)
10.2 Catalog Service
- Responsibility:
- Manage Book metadata, search, list books by filter.
- DB:
- CatalogDB (MySQL/Postgres 或 DynamoDB)
- Schema:
book_id PK, title, author_name, language, categories, status - Sharding key:
book_id - For search: Search index (e.g. ES)
- MySQL → this DB:
- 原因:书数量、读 QPS 变大,需要分片或 NoSQL + 搜索引擎。
- QPS per 8c/16G: ~1K QPS(metadata 查询,不含 search 引擎)
- Stateful?: Stateless 服务,状态在 DB / index。
10.3 Library Service
- Responsibility:
- Manage user library (which user owns which book)
- Provide “list my books” API.
- DB:
- LibraryDB (key-value / wide-column, e.g. DynamoDB / Cassandra)
- Schema (logical):
- PK:
(user_id, book_id) - Fields: added_at, source
- Sharding key:
user_id (保证同一用户在同一分片) - 从 MySQL → NoSQL:
- 原因:行数大,读写分布按 user_id,很适合 key-value;高可用,扩展容易。
- QPS per 8c/16G: ~1K QPS(list + add)
- Stateful?: Stateless(业务状态在 DB)。
10.4 Content Service
- Responsibility:
- Map book/page → file chunks
- Generate signed URL for CDN
- Check DRM before serve.
- DB:
- AssetDB (small relational or key-value)
- Schema:
file_id PK, book_id, format, storage_url, checksum - Sharding key:
book_id or file_id - File content in Object Store (S3-like)
- MySQL → Object store+small metadata DB:
- 原因:大文件不适合放 RDB;对象存储更便宜更可靠。
- QPS per 8c/16G: 2K QPS for signed URL (真正流量在 CDN)
- Stateful?: Stateless。
10.5 Progress Service
- Responsibility:
- Handle progress sync, last-write-wins
- Expose “get last progress” for app.
- DB:
- ProgressDB (NoSQL + cache)
- Logical schema:
- PK:
(user_id, book_id) - Fields: current_page, percentage, device_id, updated_at
- Sharding key:
user_id - Cache: Redis (key =
progress:{user_id}:{book_id}) - MySQL → NoSQL + cache:
- 原因:写多、更新频繁;NoSQL更适合高写入 + TTL。
- QPS per 8c/16G: ~1K-1.5K QPS
- Stateful?: Stateless(状态在 DB + cache)。
10.6 Upload / Ingest Service
- Responsibility:
- Author upload, store raw file, trigger convert + DRM.
- DB:
- UploadDB (small relational) for UploadRequest
- PK:
request_id - Fields: uploader_id, book_id, raw_file_url, status
- Sharding key:
request_id - MySQL 即可:写量不大,但要事务、审计。
- QPS per 8c/16G: < 100 QPS
- Stateful?: Stateless;后台 worker 消费 queue 处理。
11. DB 总结(PK + sharding key + 现实用什么 DB)
- UserDB
- PK:
user_id - Sharding key:
user_id - Real DB: MySQL / Postgres (or managed RDS)
- CatalogDB (Book)
- PK:
book_id - Sharding key:
book_id - Real DB: MySQL/Postgres + Elasticsearch for search.
- LibraryDB (UserLibraryItem)
- PK:
(user_id, book_id) - Sharding key:
user_id - Real DB: DynamoDB / Cassandra
- ProgressDB (ReadingProgress)
- PK:
(user_id, book_id) - Sharding key:
user_id - Real DB: DynamoDB / Cassandra + Redis cache
- AssetDB (EbookFile)
- PK:
file_id - Sharding key:
file_id or book_id - Real DB: small MySQL or DynamoDB
- Content itself: S3-like object store
- UploadDB (UploadRequest)
- PK:
request_id - Sharding key:
request_id - Real DB: MySQL/Postgres
12. 可以优化的地方(按重要程度 + 加什么 component + 在哪里)
- Cache for read-heavy flows
- Add Redis / in-memory cache
- Place: before LibraryDB (list books), before ProgressDB (get progress), before CatalogDB (book meta).
- CDN for content pages
- Add CDN in front of Content Service / Object store
- Place: user → CDN → origin → Content Service
- 减小延迟 + 减轻源站负载 + 抗热点。
- Message queue for async tasks
- Add MQ (Kafka / SQS)
- Place: Upload Service → MQ → Convert/DRM worker; Progress Service → MQ → analytics / recommendation。
- 解耦、削峰。
- Consistency management + reconcile jobs
- Add reconcile worker
- Place: compare LibraryDB vs Payment / DRM rights; fix missing items.
- 定期任务确保书库和权限一致。
- Failure handling components
- Add circuit breaker / retry with backoff
- Place: between services, especially to 3rd-party DRM/payment, cache, DB.
- 防止雪崩,给下游“喘息”。
- Batch / stream for analytics & recos
- Add stream pipeline (Flink / Spark Streaming)
- Place: Progress events → stream → compute “popular books” / “next book recommendation”
- Batch jobs定时更新推荐结果。
- Background compaction / archiving
- Add archive job
- Place: move old logs / old upload records to cold storage, 减少新数据表大小,提升查询性能。
如果你想下一步继续做:
- “只针对 list / read / sync / upload 做 QPS 表 + capacity plan”
- 说一句,我可以帮你把这个系统的 容量估算表、AP/CP 口语回答模板 单独写一版。