1. 缓存的读写模式和分类
1)缓存的读写模式
(1) 缓存有3种读写模式
- Cache Aside(旁路缓存)
- Read/Write(读写穿透)
- Write Behind Caching(异步缓存写入)
(2) Cache Aside
- 写过程:更新DB => 删除cache => DB驱动缓存数据更新(cache lazy更新,eg: 异步探测DB变更日志,重新计算后进行缓存更新);
- 读过程:读cache => (cache未使中)读DB => DB数据更新cache(异步);
- 特点:业务应用方关注cache与DB的读写全过程,写过程cache更新使用lazy机制由DB实时变更更新,高一致性;
- 适合场景:高一致性需求,缓存数据更新比较复杂(需复杂计算)等;
(2) Read/Write Through
- 写过程:Cache更新(Cache存在的情况先更新) => DB更新 (两者同步更新);
- 读过程:读cache => (cache未使中)读DB => 回种cache(非异步,同步);
- 特点:存储服务封装了所有的数据处理细节(cache + DB),业务应用端代码只用关注业务逻辑本身,系统的隔离性更佳, 另外 cache没有数据则不更新,有缓存才更新,内存效率高;
- 适合场景:数据有明显冷热区分,并对实时性有一定要求。如weibo大V和僵尸的feed列表更新;
(3) Write Behind Caching
- 写过程:Cache更新(Cache存在的情况先更新) => DB更新 (异步批量更新DB);
- 读过程:读cache => (cache未使中)读DB => 回种cache(非异步,同步);
- 特点:
- 优势:1)存储服务封装了所有的数据处理细节(cache + DB),业务应用端代码只用关注业务逻辑本身,系统的隔离性更佳。2)数据存储的写性能高
- 缺点:1)数据一致性差,极端场景会存在数据丢失问题(eg:比如系统 Crash、机器宕机时,如果有数据还没保存到 DB,则会存在丢失的风险);
- 适合场景:1) 非常适合一些变更特别频繁的业务,特别是可以合并写请求的业务(eg:一些计数业务,一条 Feed 被点赞 1万 次,如果更新 1万 次 DB 代价很大,而合并成一次请求直接加 1万,则是一个非常轻量的操作)
总结:三种模式各有优劣,不存在最佳模式。实际上,我们也不可能设计出一个最佳的完美模式出来,如同前面讲到的空间换时间、访问延迟换低成本一样,高性能和强一致性从来都是有冲突的,系统设计从来就是取舍,随处需要 trade-off。
2)缓存的分类
(1) 按宿主层次分类
- 本地Cache: 进程内Cache,读写性能高且无任何网络开销,但容易丢失(进程或应用重启);
- Ehcache
- Guava Cache
- Self Build(自建缓存,如map, set, array等)
- 进程间Cache: 本机Cache,读写性能较高,可以大幅度减少网络开销,但运维复杂肯与应用程序有资源竞争,也易造成数据丢失且适合无状态应用(机器重启);
- Memcached
- Redis
- Fatcache
- Pika
- Others
- 远程Cache:独立部署,容量大易扩展,但需网络跨机访问,带宽是瓶颈;
- Memcached
- Redis
- Fatcache
- Pika
- Others
(2) 按储存介质分类
- 内存型缓存:数据存储在内存,读写性能好,数据易丢失(但系统重启或是crash后);
- Memcached
- Redis
- 持久化型缓存:数据存储在高速硬盘(SSD/Fusion-IO), 容量比内存型大1个数量级,但读写情能慢1~2个数量级,数据不易丢失(持久化到存储介质了)
- Pika
- 其他基于RocksDB开发的缓存组件
2)缓存的架构设计
未完待续。。。