设计一个缓存系统
Posted Daily Problem
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计一个缓存系统相关的知识,希望对你有一定的参考价值。
设计一个 kv 缓存系统,保存最近 web 的查询
1. 准备和计算
面试官一般会提供一些假设和约束,比如用户量,数据量等,我们需要粗略做个计算。
假设这题有以下假设和约束:
用户数 1000w
每月查询量 100 亿
内存有限
要求低延时
服务高可用
我们可以做出如下粗略盘点:
假设每个查询 50 个 bytes,根据查询解析出的 key 算它 20 bytes,value 是一个快照 snippet 算它 200 bytes,那么一次查询共 270 bytes.
每条查询 270 byte ,每月 100 亿查询,如果每次查询都是唯一的话,需要存储 2.7T 数据。
每月 100 亿查询,每月 250万 秒,那么平均每秒 4000 次请求。
1G ≈ 1000M ≈ 1000000K(100万k) ≈ 1000000000b(10亿byte)
1T ≈ 10000亿byte
30 天 = 24 * 30 h = 24 * 30 * 3600 s ≈ 250万 s
2. 抽象设计
3. 核心组件设计
常见的缓存查询做法,通常使用 redis 或者 memcached 来降低延时。从内存顺序读取1 MB 大约需要 250 微秒,从 SSD 读取数据需要 4 倍时间,而从磁盘读取需要 80 倍时间
因为内存有限,这里使用 LRU 缓存,过滤到不常使用的缓存。整个流程大致如下:
client 端发送一个请求到 web server,通常是一个反向代理服务。
web server 将请求转发到 query api server。
query api server 执行以下操作
解析请求
检查内存里是否有内容匹配此查询
如果有 (更新缓存到 LRU 头部,返回该值)
如果没有
使用反向索引服务,查询匹配查询的文档
使用文档服务返回查询标题和快照内容
更新内存,并将内容放到 LRU 头部
具体的LRU缓存实现,之前文章提到过:使用一个双向链表,新的内容添加到头部,超过限制,老的会被移除,同时使用一个 hash table 来提高查找速度。
具体代码可以参考
4. 扩展设计
服务扩展性有很多方面可以做,选取一个基线,做假设,再讨论。
比如内存系统为了应对大量请求负载,需要水平扩展机器,有三种主要的方法。
每台机器在集群里保存各自的缓存。(简单,但查找会延时)
每台机器在集群里都保存同一份缓存副本。(简单,但浪费机器内存)
缓存分片,集群里每台机器保存一定hash范围内的缓存。(复杂,理论上最好)
以下一些 topic 几乎每个系统设计都会提到,这里不做扩展,可以自行研究。
DNS (域名解析)
Load Balancer (负载均衡)
Horizontal scaling (水平扩展)
Web Server (反向代理)
Api Server (应该服务)
cache (缓存)
Consistency patterns (一致性原则)
Availability patterns (可用性原则)
5. 进一步
取决于问题领域和剩余时间。
SQL (读写分离,分库分表,分片,sql 调优)
NoSql (kv 存储,document 存储,宽表存储,图形数据库)
缓存 (client,cdn,web server,database)
消息队列,微服务,服务发现
其他方面的权衡取舍
6. 其他
持续对系统进行基准测试和监控,发现瓶颈,并解决它,服务扩展是一个持续的过程。
以上。
以上是关于设计一个缓存系统的主要内容,如果未能解决你的问题,请参考以下文章
Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题