一、全书概览
系统设计面试是技术面试里公认最难的环节——题目范围又广又模糊,没有标准答案,面试官想看的是你的思考过程而非最终结论。这本书就是专门为应对这种面试写的。
Alex Xu 在 Twitter、苹果这些公司做过大规模分布式系统,他把面试经验总结成了一套"四步法":确定问题范围 → 总体设计 → 细节设计 → 总结。全书 16 章基本都按这个框架展开,每章围绕一个真实系统(短链接、聊天系统、YouTube、Google Drive 等),从零开始一步步搭出完整架构。
中文版(2023 年出版)在英文原版基础上做了适配,新增了支付系统、视频流系统、文件存储系统等章节,更贴近国内大厂面试场景。整本书 300 多张架构图,几乎每页都有图解,阅读门槛不高,属于"看了能上手"的实战型技术书。
这本书的逻辑大概分两部分:
- 前半部分讲分布式系统的通用组件(负载均衡、缓存、消息队列、数据库分片等)
- 后半部分用具体案例串联这些组件,展示完整系统怎么搭
适合 3 年以上经验的后端/全栈工程师,特别是准备面试 FAANG 或国内一线大厂的人。如果你还在写 CRUD,先把基础打牢再看这本书会更有效。
二、逐章要点
第 1 章:从 0 到 100 万用户的扩展
"先让系统能跑起来,再考虑扩展。"
这一章是全书的起点,带读者体验一个 Web 应用从单服务器到百万用户的全过程。
| 阶段 | 架构变化 | 核心问题 | |------|----------|----------| | 单服务器 | 所有东西跑在一台机器上 | 单点故障、性能瓶颈 | | 加数据库 | Web 服务器和数据库分离 | 数据持久化、读写分离 | | 加缓存 | Redis/Memcached 挡住大部分读请求 | 缓存一致性、命中率 | | 加负载均衡 | 多台 Web 服务器水平扩展 | 会话保持、健康检查 | | 加 CDN | 静态资源分发到边缘节点 | 缓存更新、成本控制 | | 微服务 | 按业务域拆分独立服务 | 服务间通信、数据一致性 |
关键决策:SQL vs NoSQL。书里给了一个很实用的判断标准——结构化数据、强一致性、复杂查询用 SQL(MySQL/PostgreSQL);灵活 schema、海量数据、高吞吐用 NoSQL(MongoDB/Cassandra/DynamoDB)。
- [ ] 理解纵向扩展(加硬件)和横向扩展(加机器)的本质区别
- [ ] 能画出单服务器到多层架构的演进图
- [ ] 能说清楚 SQL 和 NoSQL 各自的适用场景
第 2 章:信封背面估算(Back-of-the-Envelope Estimation)
系统设计面试的第一个环节经常是估算:Twitter 每天产生多少条推文?YouTube 每天上传多少视频?存储成本是多少带宽费用是多少?
这一章教你快速做数量级估算。核心方法:
- 2 的幂次法则:1KB ≈ 千字节,1MB ≈ 百万像素照片,1GB ≈ 一部电影
- QPS 估算:日活 100 万 → 日请求 1 亿(每人 100 次)→ 峰值 QPS ≈ 5000(按 20 倍放大系数)
- 存储估算:每条推文平均 1KB,每天 1 亿条 → 每天 100GB,5 年 ≈ 180TB
- 带宽估算:读远大于写,Twitter 的读写比约 100:1
不要追求精确数字,面试官要的是合理的数量级和清晰的推导过程。
- [ ] 能在 30 秒内估算任意 Web 应用的 QPS、存储量、带宽需求
- [ ] 记住常用数据量级:1TB = 1000GB,100 万 = 10
第 3 章:系统设计面试框架
"四步法是全书的核心方法论,也是你在面试中的作战地图。"
Alex Xu 提出的四步法,全书所有案例都按这个结构展开:
-
确定问题范围(Step 1 - Understand the Problem and Establish Design Scope)
- 主动提问:是移动端还是 Web 端?DAU 多少?需要实时还是准实时?
- 明确功能需求和非功能需求(可用性、延迟、一致性等级)
- 写下假设,让面试官确认
-
总体设计(Step 2 - Propose High-Level Design)
- 画出客户端 → API 网关 → 服务层 → 数据层的架构图
- 确定核心数据模型和存储方案
- 估算规模(QPS、存储量)
-
细节设计(Step 3 - Design Deep Dive)
- 选一个 2-3 个核心组件深入讨论
- 讨论数据分区策略、缓存方案、消息队列
- 优化方案:预计算、CDN、异步处理
-
总结(Step 4 - Wrap Up)
- 主动提瓶颈和改进方向
- 讨论故障场景和恢复策略
- 回顾设计,检查有没有遗漏
| 面试阶段 | 时间分配 | 关键产出 | |----------|----------|----------| | 确定范围 | 3-5 分钟 | 需求清单 + 假设 | | 总体设计 | 10-15 分钟 | 高层架构图 + 数据模型 | | 细节设计 | 15-20 分钟 | 2-3 个核心组件的深入设计 | | 总结 | 3-5 分钟 | 瓶颈分析 + 改进方案 |
- [ ] 用四步法完整练习至少 5 道系统设计题
- [ ] 准备一套自己的"开场白模板":先问哪几个问题
第 4 章:设计一个限流器(Rate Limiter)
限流器是面试高频题,也是真实系统中必不可少的组件。核心问题是:如何防止用户或服务过度请求?
三种常见算法:
| 算法 | 原理 | 优点 | 缺点 | |------|------|------|------| | 令牌桶(Token Bucket) | 固定速率生成令牌,请求消耗令牌 | 允许突发流量 | 实现稍复杂 | | 漏桶(Leaky Bucket) | 请求进入队列,固定速率处理 | 输出速率恒定 | 不允许任何突发 | | 固定窗口计数器 | 每个时间窗口内计数 | 实现简单 | 窗口边界可能双倍流量 | | 滑动窗口日志 | 记录每个请求的时间戳 | 精确控制 | 内存占用大 |
书中推荐令牌桶算法,在 Redis 中用 Lua 脚本实现可以保证原子性。分布式限流还有一致性哈希和分级限流(用户级 → API 级 → 系统级)的讨论。
- [ ] 能手写令牌桶算法的伪代码
- [ ] 理解分布式环境下限流器的实现难点
第 5 章:设计一致性哈希(Consistent Hashing)
一致性哈希是分布式系统里数据分片的核心技术,解决了传统哈希在节点增减时大量数据迁移的问题。
原理:把整个哈希空间组织成一个环(0 到 2^32-1),服务器和数据都映射到环上。每个数据顺时针找最近的节点。当增加或删除节点时,只影响相邻的一小段数据。
**虚拟节点(Virtual Nodes)**解决了数据倾斜问题——一台物理服务器映射多个虚拟节点到哈希环上,使数据分布更均匀。例如 3 台服务器各设 100 个虚拟节点,数据分布方差会大幅降低。
一致性哈希的应用场景远不止缓存:数据库分片、负载均衡、分布式存储都用到它。
- [ ] 能手画一致性哈希环,演示增删节点时数据迁移范围
- [ ] 理解虚拟节点如何解决数据倾斜
第 6 章:设计一个键值存储(Key-Value Store)
这章是分布式存储系统的核心教科书,参考了 Amazon Dynamo 和 Google Bigtable 的设计。
核心组件拆解:
- 数据分区:一致性哈希将数据分散到多个存储节点
- 数据复制:每个 key 复制到 N 个节点(通常 N=3),保证高可用
- 一致性模型:从强一致性到最终一致性,不同场景选不同等级
- 一致性协议:Quorum 机制——W + R > N 保证读到的不是旧数据
- 故障处理:反熵(Merkle Tree)、 hinted handoff、向量时钟解决数据冲突
- 写路径 / 读路径:请求从客户端出发经过哪些组件
| 一致性等级 | 延迟 | 可用性 | 典型场景 | |-----------|------|--------|---------| | 强一致性 | 高 | 低 | 金融交易 | | 弱一致性 | 低 | 高 | 社交动态 | | 最终一致性 | 中 | 中 | 购物车 | | 因果一致性 | 中 | 中 | 聊天消息 |
- [ ] 能说清楚 Quorum 协议中 W、R、N 的关系和权衡
- [ ] 理解向量时钟如何解决分布式数据冲突
第 7 章:分布式唯一 ID 生成器
这看起来是个小问题,但在分布式环境下非常有挑战。需求:全局唯一、趋势递增(利于数据库索引)、高可用、低延迟。
书中对比了几种方案:
- UUID:简单但不排序,占用 36 字节,索引效率差
- 数据库自增 ID:单点瓶颈,扩展性差
- Snowflake(雪花算法):Twitter 开源,64 位 = 1bit 符号 + 41bit 时间戳 + 10bit 机器 ID + 12bit 序列号。每毫秒每台机器可生成 4096 个 ID
- Ticket Server:ZooKeeper/etcd 分配分段 ID,数据库存储
Snowflake 是面试中的标准答案,但要能讨论时钟回拨、机器 ID 分配等边界问题。
- [ ] 能手写 Snowflake 算法的 ID 结构和位数分配
- [ ] 能讨论时钟回拨的解决方案(NTP 同步、等待回拨恢复、百度 Leaf 的双 buffer 方案)
第 8 章:设计一个短链接系统(URL Shortener)
短链接是系统设计面试的经典入门题,涉及编码、存储、缓存、重定向等多个知识点。
设计要点:
- ID 生成:Snowflake 或数据库自增,然后用 base62 编码(a-z, A-Z, 0-9)映射为短码
- 存储:NoSQL(Cassandra/DynamoDB),key 是短码,value 是原始 URL + 元数据
- 重定向:301(永久重定向,缓存友好)vs 302(临时重定向,可统计点击)
- 缓存:热点短链放 Redis,命中率通常 80%+
- 自定义短链:额外维护一个用户指定短码到 ID 的映射
一个 7 位的 base62 编码可以表示 62^7 ≈ 3.5 万亿个短链接,完全够用。
- [ ] 能计算 base62 编码的容量上限
- [ ] 理解 301 和 302 重定向的权衡
第 9 章:设计一个网页爬虫(Web Crawler)
网页爬虫的设计难点不在于爬取本身,而在于大规模爬取时的工程挑战:去重、调度、 politeness(礼貌策略)、存储。
核心设计:
- 种子 URL 管理:优先级队列,按重要性排序
- URL 去重:Bloom Filter(布隆过滤器)快速判断是否已爬取
- 分布式爬取:DFS/BFS 策略,工作队列分片
- 礼貌策略:对同一域名限制请求频率(robots.txt)
- 存储:原始 HTML 存对象存储,结构化数据存数据库
Bloom Filter 是这章的高频考点:用位数组 + 多个哈希函数,空间效率极高,但有误判率(可能误报已存在,不会漏报)。
- [ ] 能说清楚 Bloom Filter 的原理和误判率
- [ ] 理解 DFS vs BFS 爬取策略的取舍
第 10 章:设计一个通知系统(Notification System)
通知系统看似简单,实际上要处理多通道(推送、短信、邮件)、多模板、高并发、重试、去重等复杂问题。
架构分三层:
- 通知服务:接收业务方请求,验证、去重、限流
- 消息队列:Kafka 削峰,按通道分队列
- 推送 Worker:APNs(iOS)、FCM(Android)、邮件服务器、短信网关
书中特别强调了"模板管理"的重要性——把通知内容和数据分离,通过模板引擎动态渲染,这样产品经理可以自己改文案而不需要开发介入。
- [ ] 能画出通知系统的三层架构图
- [ ] 理解不同推送通道的特性和限制
第 11 章:设计一个新闻推送系统(News Feed System)
新闻推送(类似 Twitter/微博首页的时间线)是面试中区分候选人水平的高频题。
两种主流架构:
| 方案 | 写扩散(Fan-out on Write) | 读扩散(Fan-out on Read) | |------|--------------------------|-------------------------| | 原理 | 发推时写入所有粉丝的收件箱 | 读推时实时聚合关注人的推文 | | 写成本 | 高(粉丝多的用户写放大严重) | 低(只写一次) | | 读成本 | 低(直接读收件箱) | 高(实时聚合,可能很慢) | | 适合 | 普通用户为主 | 粉丝数差异大的平台 | | 实际案例 | 早期 Twitter | Facebook |
实际系统通常是混合方案:粉丝数低于阈值(如 100 万)用写扩散,超过阈值的大 V 用读扩散 + 缓存。
- [ ] 能说清楚写扩散和读扩散的取舍
- [ ] 能设计一个混合方案处理大 V 场景
第 12 章:设计一个聊天系统(Chat System)
聊天系统是中文版新增的重点章节,因为国内面试考聊天系统的概率极高。
核心挑战:
- 实时通信:WebSocket 长连接,心跳保活,断线重连
- 消息存储:消息数据库(按用户 ID 分片),搜索数据库(Elasticsearch)
- 消息投递:ACK 确认机制,离线消息暂存队列,消息去重
- 群聊优化:写扩散(小群)vs 读扩散(大群),群成员元数据单独存储
- 在线状态:独立的状态服务 + Redis,用户登录时注册,心跳维持
微信的架构设计给了一个很好的参考:每个用户分配一个接入服务器,消息先到接收方所在的服务器,不在线则暂存。群聊消息先存后推(SSS 模式:Store and then push)。
- [ ] 能画出聊天系统的端到端架构
- [ ] 理解 WebSocket 和长轮询的区别
第 13 章:设计搜索自动补全(Search Autocomplete)
Google 搜索框那种"输入即提示"的功能,看起来简单但做到低延迟高准确率需要精心设计。
技术方案:
- Trie(前缀树):内存中维护前缀树,支持前缀搜索
- 数据来源:搜索日志的频率统计,定期更新
- 优化:Trie 太大就分片,按用户地区/语言分;热点查询缓存
- 实时更新:新增热点词通过增量更新 Trie
更高级的方案是用有限状态机(FSA)压缩 Trie,可以把空间降到原来的 1/10。
- [ ] 能实现一个简单的前缀树并分析时间复杂度
- [ ] 理解 Trie 分片和实时更新策略
第 14 章:设计 YouTube
视频流系统是全书最复杂的案例之一,涉及上传、转码、存储、分发、推荐等多个子系统。
架构拆解:
- 上传服务:分块上传,支持断点续传
- 转码服务:异步任务队列,多分辨率转码(1080p/720p/480p/360p)
- 存储:原始视频存对象存储(S3),转码后也存 S3
- CDN 分发:视频缓存在边缘节点,用户就近拉取
- 元数据数据库:视频信息、用户信息、评论(MySQL 分片)
- 推荐系统:独立服务,基于协同过滤和内容分析
带宽是最大的成本。一个 1GB 的视频被 100 万人观看 = 1PB 的出站流量,CDN 费用按 TB 计费,是视频平台最大的运营支出之一。
- [ ] 能估算视频平台的带宽和存储成本
- [ ] 理解视频转码的并行化方案
第 15 章:设计 Google Drive(云盘)
文件存储系统需要解决:文件去重(块级去重节省空间)、大文件分块上传、版本控制、权限管理、搜索。
关键设计决策:
- 块存储:文件切成固定大小的块(如 4MB),相同块只存一份(去重)
- 元数据与数据分离:元数据(文件名、目录结构、权限)用关系数据库,文件内容用对象存储
- 并发控制:文件锁定机制,多人协作时避免冲突
- 通知系统:文件变更事件通过 WebSocket 实时推送给相关用户
Google Drive 的一个精妙设计是"块级去重"——公司里 100 个人存了同一份 PPT,实际只占一份存储空间。这个设计为公司节省了海量存储成本。
- [ ] 理解块级去重的工作原理和节省比例
- [ ] 能设计文件版本控制系统
第 16 章:持续学习
最后一章是资源汇总,推荐了系统设计学习路线:
- 分布式系统理论:《Designing Data-Intensive Applications》(DDIA)——这本是系统设计的"圣经",比 Alex Xu 的书深得多
- 论文阅读:Google 的 Bigtable、MapReduce、Spanner 论文
- 开源项目:研究 Kafka、Redis、ZooKeeper 的源码
- 面试练习:LeetCode 讨论区、Interviewing.io、Pramp 等平台
Alex Xu 还有一个建议值得记住:面试不是考试,是讨论。面试官想看的是你如何分析问题、做出取舍,而不是背诵标准答案。
三、关键概念速查
| # | 概念 | 一句话解释 | 典型应用 | |---|------|-----------|---------| | 1 | CAP 定理 | 一致性、可用性、分区容错性三者只能选两个 | 分布式数据库设计 | | 2 | 一致性哈希 | 把数据和服务节点映射到哈希环上,节点变动时只迁移少量数据 | 缓存分片、数据库分片 | | 3 | Quorum 机制 | W + R > N,写操作至少 W 个副本确认,读操作至少读 R 个 | Dynamo、Cassandra | | 4 | Bloom Filter | 位数组 + 多哈希,O(1) 判断元素是否存在,允许误判不允许漏判 | 爬虫去重、缓存穿透防护 | | 5 | Snowflake | 64 位 ID = 时间戳 + 机器 ID + 序列号,分布式环境下全局唯一递增 | Twitter、订单号生成 | | 6 | Write Fan-out | 写扩散,消息写入时复制到所有接收者 | 聊天系统、新闻推送 | | 7 | CDN | 内容分发网络,静态资源缓存到全球边缘节点 | 视频流、图片、静态页面 | | 8 | Back-of-the-Envelope | 信封背面估算,用数量级快速评估系统规模 | 面试第一题、架构评审 | | 9 | SLA/SLO | 服务等级协议/目标,用 99.9% 可用性等指标量化系统可靠性 | 生产环境运维 |
四、核心框架 / 模型
1. 系统设计面试四步法
确定范围 → 总体设计 → 细节设计 → 总结
↓ ↓ ↓ ↓
需求澄清 架构草图 核心组件深入 瓶颈+改进
这是全书最重要的框架,每道题都应该按这个顺序来。
2. 可扩展性决策树
流量增长怎么办?
├── 读多写少 → 加缓存(Redis/Memcached)
├── 写多读少 → 消息队列异步化 + 数据库分片
├── 两者都多 → 读写分离 + 分库分表 + 缓存
└── 静态内容多 → CDN 分发
3. 数据存储选型矩阵
需要强一致性 + 复杂查询? → MySQL/PostgreSQL
需要灵活 schema + 水平扩展? → MongoDB/Cassandra
需要高吞吐事件流? → Kafka
需要低延迟缓存? → Redis/Memcached
需要全文搜索? → Elasticsearch
需要海量文件存储? → S3/OSS 对象存储
4. Trade-off 分析模板
每个设计决策都要问自己三个问题:
- 这个方案解决了什么问题?
- 引入了什么新问题?(没有银弹)
- 在什么条件下应该换另一个方案?
例如选了最终一致性而不是强一致性,就说明代价是用户可能短暂看到旧数据,但在社交场景下这个代价是可接受的。
五、金句摘录
-
"系统设计没有标准答案,只有权衡取舍。"
-
"Premature optimization is the root of all evil. 先设计一个简单的方案,再根据需求扩展。"
-
"在分布式系统中,失败是常态,不是异常。"
-
"面试不是考试,是讨论。面试官想看你如何分析问题、做出取舍,而不是背诵标准答案。"
-
"一个好的架构师不是什么都懂,而是知道在什么场景下选什么方案。"
-
"不要在面试中沉默。边画边说,让面试官跟着你的思路走。"
-
"每次做设计决策时,都要说清楚你选了什么、为什么选它、放弃了什么。"
六、行动清单
每天必做
- [ ] 复习一个核心概念(如一致性哈希、Quorum、Bloom Filter),用自己的话写一段解释
- [ ] 在白板/纸上画一个系统的架构图(从书中案例开始,逐渐尝试新题目)
- [ ] 花 10 分钟做一道信封估算题(QPS、存储、带宽)
每周安排
- [ ] 用四步法完整练习 2-3 道系统设计题,计时 40 分钟
- [ ] 读一篇系统设计相关的论文或技术博客(推荐 Level Coding、High Scalability)
- [ ] 和同事/朋友做一次 mock interview,互相给反馈
- [ ] 复习本周练习的题目,整理到一个索引文档中
每月推进
- [ ] 通读一个分布式系统开源项目的架构文档(Kafka / Redis / Cassandra 任选一个)
- [ ] 读 DDIA(《Designing Data-Intensive Applications》)的 2-3 个章节,这本书比 Alex Xu 的书深一个量级
- [ ] 整理自己的"系统设计套路清单"——每道题的通用思路模板
- [ ] 回顾自己的设计方案,找找有没有明显的优化空间
七、一句话总结
这本书的本质不是教你"背答案",而是教你一套结构化的思考方法——面对一个模糊的系统设计问题,如何通过四步法有条理地拆解、分析、做取舍。掌握这套方法论,比记住任何具体的架构方案都有用。
八、读者热议
1. "资料来源真实,比国内的真诚"
豆瓣用户 daftei 给了 10 分好评,说这本书的资料几乎都来自各厂的公开博文,Alex 结合自己的经验总结而成,"比国内这些遥遥领先的吹逼分子"靠谱得多。这也说明了这本书的一个优点:案例都是真实生产环境的做法,不是纸上谈兵。
2. "真正要敲开大厂大门,一本书不太够"
豆瓣用户 Honwhy 的书评标题很直白——"真正要敲开大厂的大门这一本书是不太够的"。他认可这本书的价值,但指出门道都是套路,光看书不够,还需要大量的实战练习和项目经验积累。这是一个很中肯的评价。
3. "Tier vs Layer 这个概念终于搞清楚了"
豆瓣用户 Blado 专门写了一篇书评澄清概念:Tier 是物理分层(web server、app server、database server 各在不同机器上),Layer 是逻辑分层(MVC 中的 Controller、Service、Repository)。系统设计中这两个概念经常被混用,这本书帮他理清了这个基本概念。
4. "作为 bytebyego 的粉丝,这书值得看"
有读者提到这本书和 YouTube 博主 bytebyego 的系统设计内容互相补充,bytebyego 的视频更偏图解和直觉理解,这本书更偏结构化框架。两者结合学习效果最好。
豆瓣/社区读者补充
⭐ 微信读书读者「系统设计面试的"万能框架"很实用」
书里教的4步框架(需求澄清→高层设计→深入设计→总结优化)我面试时反复用。面试官问你"设计Twitter",你先花5分钟问清楚需求(读多写少?实时还是延迟?单机还是分布式?),然后画高层架构,再深入某个模块。这个框架帮你避免了一上来就陷入细节的常见错误。
认同度:★★★★★。这个4步框架确实是系统设计面试的核心方法论。关键在于"需求澄清"这一步——大多数候选人跳过了这一步直接开始画架构图。
⭐ 豆瓣读者「Tier vs Layer的概念终于搞清楚了」
Tier是物理分层(web server、app server、database server各在不同机器上),Layer是逻辑分层(MVC中的Controller、Service、Repository)。系统设计中这两个概念经常被混用,这本书帮我理清了这个基本概念。
认同度:★★★★★。这个区分确实重要。面试中说"3-tier架构"和"3-layer架构"是完全不同的东西。
⭐ 微信读书读者「每章最后的"深入探讨"部分最有价值」
每章前面的高层设计部分比较基础,大部分有经验的开发者都能想到。但每章最后的"深入探讨"部分(如一致性哈希的实现细节、消息队列的选型对比、数据库分片策略)才是真正拉开差距的内容。面试官追问时往往就问这些。
认同度:★★★★★。确实如此。面试的前5分钟决定你能不能"过门槛",后面的深入探讨决定你能不能"拿offer"。
⭐ 豆瓣读者「资料来源真实,比国内的真诚」
书中的案例几乎都来自各厂的公开博文,Alex结合自己的经验总结而成。不像某些面试书充斥着编造的面试场景和虚构的面试官对话,这本书的案例都是真实生产环境的做法。
认同度:★★★★☆。资料来源真实确实是优点。但也要注意:大厂的架构方案不一定适合中小公司,面试时不要生搬硬套。
⭐ 微信读书读者「这本书不是架构师的参考书」
要明确这本书的定位:它是面试准备书,不是架构设计指南。如果你想在工作中设计一个真实的分布式系统,应该去看DDIA(《设计数据密集型应用》)和《微服务架构设计模式》。这本书的作用是帮你在45分钟的面试中展现你的系统设计能力。
认同度:★★★★★。定位准确。面试中的系统设计和生产环境中的系统设计是两回事——面试要求的是"在有限时间内做出合理的权衡",而不是"给出完美的方案"。
⭐ 微信读书读者「第2卷比第1卷更深入」
第1卷是入门级,涵盖News Feed、Chat、URL Shortener等经典题目。第2卷更深入,涵盖了Payment System、Search System、Geo-based Service等更贴近实际业务的题目。建议两卷都读,面试准备更充分。
认同度:★★★★☆。第2卷确实更深入,覆盖了更多面试中常见的进阶题目。
笔记生成:2026-04-28 by 喵喵 🐈
相关笔记
同作者仅有此篇 同主题: