设计一个 kv 缓存系统
设计一个 kv 内存缓存系统,保存最近 web 的查询
1. 准备和计算
面试官一般会提供一些假设和约束,比如用户量,数据量等,我们需要粗略做个计算。
假设这题有以下假设和约束:
- 用户数 1000w
- 每月查询量 100 亿
- 内存有限
- 要求低延时
- 服务高可用
我们可以做出如下粗略盘点:
假设每个查询 50 个 bytes,根据查询解析出的 key 算它 20 bytes,value 是一个快照 snippet 算它 200 bytes,那么一次查询共 270 bytes.
每条查询 270 byte ,每月 100 亿查询,如果每次查询都是唯一的话,需要存储 2.7T 数据。
每月 100 亿查询,每月 250万 秒,平均每秒 4000 次
1 | 1G ≈ 1000M ≈ 1000000K(100万k) ≈ 1000000000b(10亿byte) |
2. 抽象设计
3. 核心组件设计
常见的缓存查询做法,通常使用 redis 或者 memcached 来降低延时。
从内存顺序读取1 MB 大约需要 250 微秒,从 SSD 读取数据需要 4 倍时间,而从磁盘读取需要 80 倍时间
因为内存有限,这里使用 LRU 缓存,过滤到常使用的缓存。
client 端发送一个请求到 web server,通常是一个反向代理服务。
web server 将请求转发到 query api server。
query api server 执行以下操作
- 解析请求
- 检查内存里是否有内容匹配此查询
- 如果有
- 更新缓存到 LRU 头部
- 返回该值
- 如果没有
- 使用反向索引服务,查询匹配查询的文档
- 反向索引服务会对查询结果排序,并返回最前面的结果
- 使用文档服务返回查询标题和快照内容
- 更新内存,并将内容放到 LRU 头部
- 使用反向索引服务,查询匹配查询的文档
- 如果有
缓存实现:使用一个双向链表,新的内容会添加到头部,老的会被移除,同时使用一个 hash table 来快速查找内容。
具体代码可以参考之前面试对象设计之设计一个LruCache.
4. 扩展服务设计
服务扩展性有很多放面可以做,选取一个基线,做假设,再讨论。
以下一些 topic 几乎每个系统设计都会提到,这里不做扩展,可以自行研究。
- DNS (域名解析)
- Load Balancer (负载均衡)
- Horizontal scaling (水平扩展)
- Web Server (反向代理)
- Api Server (应该服务)
- cache (缓存)
- Consistency patterns (一致性原则)
- Availability patterns (可用性原则)
为了应对大量请求负载,以及大量的内存,需要水平扩展机器,有三种主要的方法。
- 每台机器在集群里保存各自的缓存。(简单,但查找会延时)
- 每台机器在集群里都保存同一份缓存副本。 (简单,但浪费机器内存)
- 缓存分片,集群里每台机器保存一定hash范围内的缓存。(复杂,理论上最好)
5. 进一步讨论
取决于问题领域和剩余时间。
SQL (读写分离,分库分表,分片,sql 调优)
NoSql (kv 存储,document 存储,宽表存储,图形数据库)
缓存 (client,cdn,web server,database)
消息队列,微服务,服务发现
其他方面的权衡取舍
6. 其他
持续对系统进行基准测试和监控,发现瓶颈,并解决它,服务扩展时一个持续的过程。
Title: 设计一个 kv 缓存系统
Author: mjd507
Date: 2019-12-21
Last Update: 2024-01-27
Blog Link: https://mjd507.github.io/2019/12/21/system-design-2/
Copyright Declaration: This station is mainly used to sort out incomprehensible knowledge. I have not fully mastered most of the content. Please refer carefully.