返回上级
io - multiplexing - event - loop
Redis 单线程为什么快? NGINX 为什么几个 worker 能扛几万连接? Netty 的 EventLoop 是什么? 背到第三遍我才反应过来,这三个组件讲的是同一件事: 连接很多,但大部分连接大部分时间都在等数据。 让一个线程盯住所有连接,谁就绪处理谁。 这个机制在 Linux 上叫 epoll,架在它上面的编程模式叫事件循环。
page - and - block
学 MySQL 的时候我记住了"InnoDB 以 16 KB 的页为单位管理数据",学操作系统的时候又记住了"内存以 4 KB 的页为单位管理"。当时当成两个孤立知识点背,后来才看清这是同一个决定在不同尺度上的重复:
cache - layers
CPU 有 L1/L2/L3,TCP 有收发缓冲区,操作系统有 page cache,MySQL 有 buffer pool,MyBatis 有一二级缓存,应用前面摆着 Redis,浏览器和 CDN 还各有一层。 这些东西散在五门课和四个组件的文档里,本章把它们全部收进一个字典:每一站是什么结构、多大、怎么淘汰、怎么保一致、坏了是什么现象,逐个讲透。
write - ahead - log
MySQL 有 redo log、undo log、binlog,Redis 有 AOF 和 RDB,Kafka 干脆整个就是日志,文件系统还有 journal。我背这些的时候一度觉得存储系统怎么都在写日志,写这么多日志不慢吗。本章把这一族机制归到一个根上,然后把每个成员单独讲透:
replication - log - replay
MySQL 主从复制、Redis 主从复制、Kafka 副本机制,我最初是当三个独立专题学的,每个都有一堆专有名词:binlog、relay log、PSYNC、replbacklog、ISR、HW。本章先给出统一它们的那句话,再把三家逐个拆到零件级:
heartbeat - quorum - election
上一章搭好了主从:主写从读,日志复制。下一个问题自然冒出来:主挂了怎么办。人肉切换太慢,自动切换就要回答三个连环问题:
sharding - routing
复制解决了"机器挂了",但每个副本都存全量数据,数据涨到一台装不下(或单机写入吞吐到顶)时,复制帮不上忙,只能把数据切开分到多台。这就是分片。所有分片方案的核心是同一个函数:
indirection - layer
计算机科学有句流传很广的老话(出自 David Wheeler):
pooling
创建一个线程、建立一条数据库连接、分配一块堆外内存,这三件事有个共同点:贵。贵的资源用完就扔,等于每次都重新付一遍创建费。池化的思路一句话:
cow - mvcc
很多场景需要一份"不动的数据":备份要一个时间点的快照,读操作想不加锁也不被写打扰,共享数据想安全地发给别人。老实拷贝一份当然不动了,但拷贝太贵。这一族机制的思路:
batching
单次操作往往有一笔和数据量无关的固定成本:一次系统调用的进出内核、一次网络往返、一次 fsync。操作一多,固定成本累加起来比正事还贵。批处理的思路一句话:
laziness - amortization
有些操作单次代价巨大:搬迁一张百万桶的哈希表、删除百万个过期 key、把整个程序载入内存。硬着头皮一次做完,就是一次长停顿。这一族机制的思路:
unified - map
前十二章每章追一个思想。本章做收拢:把十二个思想钉进一张总图,把它们之间的咬合线画出来,再用三个没讲过的新技术做落格练习,验证这张图真的能把"学一个新中间件"的成本从全量压到增量。
evolution - chains
前十三章是横截面:一个思想在各个组件里的复现。本章换成时间轴:一个需求下的技术怎么一代代迭代。读演进链的固定三问,每一环都拿它过一遍:
decoupling
先给耦合下一个可操作的定义,不然解耦就是一句口号:
double - buffer
空间换时间是个大家族,系列里其实到处是它的成员:缓存拿内存换访问延迟(第 3 章)、索引拿磁盘换查找次数(第 2 章)、副本拿三倍存储换可用和读扩展(第 5 章)、池拿闲置资源换创建开销(第 9 章)、冗余字段拿双写换跨片查询(第 7 章)。本章讲这个家族里最精巧的成员,它在 JVM 的 survivor 区和 Redis 的双 dict 里长得几乎一样:
README · universal-patterns
这个模块研究的是:同一批底层思想,如何在 MySQL、Redis、Kafka、NGINX、Netty 这些看似无关的组件里反复复现。