架构词典:缓存
这篇想讨论的缓存不是[[redis]]的全部功能,而仅仅是其缓存部分。redis 在架构上其实承担着两个角色,一个是缓存,另一个是内存计算服务。比如实现 HyperLogLog 算法的 PFADD 等指令就属于典型的内存计算服务。
共享的缓存(而不是直接在业务代码中缓存)其实出现得比较早,广泛使用的 [[memcached]] 诞生于 2003 年,是 [[LiveJournal]] 公司为了解决自己的业务问题发明的。
缓存以及后面出现的[[ES]]等全文搜索引擎很大程度缓解了[[数据库]]的压力,甚至改变了数据库在架构中的一些使用方式。比如之前对数据库使用推崇读写分离,但缓存引入之后,数据库的读写量差异已经不大,所以不少设计又回归到在线业务全部使用主库的设计,这个设计更加简单,还能规避掉很多因为数据库主从不一致导致的幻读等问题。ES 则大幅度解放了对数据库索引的需求,引入 ES 后,数据索引一般只用于必要的主键、外键、唯一性约束等场景。
缓存的使用其实需要比较谨慎,因为每引入一处缓存就需要解决一处同步问题。我个人比较喜欢在两端加缓存:API 出口以及紧邻数据库的区域,这两种之外一般还会有第三方调用缓存、计算缓存等场景。
对于 API 出口,一般情况下会设计成对于单个实体的缓存周期略长,但在使用缓存时会先校验关键元素(实体本身以及被引用的其他身体)版本是否变更;对于列表则会设计成较低缓存周期(比如 1-5 分钟),但查询时不做任何检查。
对于紧邻数据库的场景,则需要严肃考虑缓存淘汰机制。一般我缓存的目标会是一个领域对象,而不是简单的数据库的一行。更新领域对象时,会先更新数据库,然后清理缓存。但这种处理方式仍然没法严格保证最终一致性,一般解决方式有三种,a. 设置缓存有效期 b. 用一个延时队列再清理一次缓存 c. 使用分布式事务锁。考虑到 b/c 两种方式成本高,普通场景下用 a 的比较多。这种方式对多中心的设计也比较友好,当多中心数据库同步时,只需要对同步的每一个 Entity 都清理对应的缓存的即可。对于紧邻数据库的列表型缓存,一般也有两种处理手法,一种是短缓存周期存储全部内容;另一种是中缓存周期,但只保存 ID。
版权声明: 本文为 InfoQ 作者【lidaobing】的原创文章。
原文链接:【http://xie.infoq.cn/article/1ce8eb04222dc00d6dcca76d8】。文章转载请联系作者。
评论