-
程序设计是组装代码,系统设计是组装服务
- 程序设计的组件是变量、函数、类等;系统设计的组件是服务器、数据库、缓存、队列、事件总线、代理等
-
如果一个系统很长时间不出错,它的设计就是良好的
- 如果你进一步看了代码,脱口而出:“哈,这比我想的要简单”,或者“这个部分不用我操心,即使出问题也容易解决”,它的设计就是优秀的
-
良好的系统设计,总是从一个有效的简单系统发展而来, 千万不要从零开始设计一个复杂的系统
-
系统设计的难点在于状态。尽量采用无状态组件,最小化“有状态组件”的数量。状态的复杂性在于,你无法简单地重启服务,一旦出错,往往需要手动修复状态
-
数据库是最重要的系统组件,用来保存和管理状态
- 数据库的设计目标是每张表易于理解:打开看一下表结构,就能大致了解存储的数据内容及其原因
- 千万不要采用复杂的表结构(也就是数据结构) ,会给代码带来极大的复杂性和性能约束
-
数据库往往是系统瓶颈,因为每个页面请求可能要调用数十次、数百次数据库,而且是按顺序调用
- 为了避免瓶颈,数据库可以做成一个 写入节点和多个只读副本 。数据查询都发往只读副本,数据写入发往写入节点
- 写入节点与只读副本之间存在数据复制延迟。如果更新一条记录后,你需要立即读取它,那么可以将数据放入内存,写入数据库成功后从内存读取
-
耗时的操作要拆分出来,放在后台作业(即系统外部的单独服务),排队完成
- 后台作业主要分成两个组件:一个队列服务,一个作业运行器(从队列中获取任务并执行)
- 队列任务可以用 Redis(需要尽快执行的任务),也可以用数据库(不着急的任务)
-
如果数据的生成速度和读取速度不匹配,经典解决方案就是缓存
- 缓存的最简单做法,就是把数据保存在内存,否则就使用专门的 key-value 数据库(比如 Redis 或 Memcached),后者的好处是多个服务器可以共享缓存
- 初级工程师希望缓存所有内容,而高级工程师希望尽量少用缓存。因为 缓存是状态的来源 ,不可避免需要校验状态和处理状态过期
-
除了缓存和后台作业,大型系统通常还有事件中心,一般用的是 Kafka
- 事件中心也是一个队列,存放的是“某件事发生了”的消息。比如,用户注册触发了“新帐户创建”事件,该事件就放入事件中心,然后由事件中心去通知订阅该事件的多个服务——发送欢迎电子邮件、设置个人空间等等
- 事件中心适用于:发送事件的代码不关心其他服务如何处理事件,或者事件量很大且对响应时间不太敏感
- 不要过度使用事件,很多时候更简单的做法是让一个服务请求另一个服务的 API
- 为了便于 debug,所有日志最好都放在一起,你可以立即看到另一个服务的响应
-
如果数据需要传送到多处,有拉取( pull )和推送( push )两种选择
- 一般来说,拉取比较简单(比如大多数网站采用的轮询),推送更节省资源,不需要用户主动请求数据,一旦后端数据发生变化,服务器主动将数据推送给每个客户端