缓存架构的理论分析

sorra 发表于 2016 07/10 11:08 修改于 01/11 21:47 阅读数1124

问题背景

略谈服务端缓存设计 一文说到缓存不是必须的,因为数据库本身就利用了内存。但实际情况是缓存是大型网站的标配。

虽然经验显示RDBMS最快时只需0~1ms就能响应,不逊于专门的缓存,但是当压力增大时,性能的下降也是飞快的。随着业务的逐渐复杂、开发团队的逐渐扩大,难以全面优化所有的SQL,数据库内存的命中率难免下降。

数据库的连接数是有限的、磁盘的并发能力也很差,因此当访问量增大或数据库内存命中率下降,平均响应速度会陡然下降;更糟的是,某些查询导致大量的冷数据换入并占用数据库内存(例如全表扫描),真正最需要的热数据暂时被挡在内存外,等到1~100ms(或更久)之后才能重新被读入数据库内存,到这种时候,压力可能爆棚了。

  • MySQL的buffer pool hit rate指标统计了命中率。
  • MySQL有类似于分代LRU的机制,按这个链接调优能有所缓解。

以上分析告诉我们:缓存架构要满足冷热分离的特征——RDBMS不满足,因为冷数据可能挤走热数据。

另外,众所周知,缓存架构还要满足读写分离的特征——RDBMS也不满足,因为写操作会争抢读操作的资源。

鉴于这些局限性,RDBMS终归还是顶不住,缓存成为大型网站的标配就是顺理成章的了。

方案选择

略谈服务端缓存设计 一文还比较了本地缓存和分布式缓存,首推分布式缓存。那么就要选择一款缓存系统。而且强烈建议预先考虑水平扩展,如果先用了单机方案,之后很难不关机就在线迁移到基于sharding的集群方案。

缓存系统的选择,我随手列一些典型方案(没有每个都用过),分成三系:

  • Memcached系:Twemproxy(手动rebalance),Couchbase

  • Redis系:Twemproxy(手动rebalance),Codis,官方的Redis Cluster

  • Java系:Apache Ignite(支持多种语言,兼容Memcached API),基础性能低于前两系,但支持SQL查询、事务和丰富的数据结构,还能远程执行代码、推送事件通知、对接本地缓存/数据库/HDFS等。同类产品有Hazelcast、Gemfire。

应用代码怎么写?通常是:读操作先get缓存,若没有,查数据库,并且set缓存;写操作直达数据库,并且delete缓存。这种风格称为直写式缓存。
为避免重复代码,可以利用AOP:

  • Java可用Spring Cache,另外Hibernate可自动管理缓存。
  • 支持高阶函数的语言自带AOP,把数据操作变成闭包,外层包一个处理缓存的函数。

还有一种风格称为写回式缓存,读写操作都走缓存,由缓存来负责与数据库同步。这种风格需要缓存系统的支持。

本地缓存就不赘述了,前篇文章讲得还可以。

结语

以上对问题背景和方案选择都做了分析,尤其触及一些知识盲点,然而这只是理论。
是的!鉴于实际环境的复杂性(即使是本篇文章也无法反映真实情况),最推荐的做法仍然是实测!根据真实的应用场景来设计你的缓存架构!(并不是只能线上实测,可以在测试环境尽量模拟。)

最后再推荐一些相关文章:

Pinterest谈实战经验:如何在两年内实现零到数百亿的月访问 http://www.qingjingjie.com/blogs/16

微博“异地多活”部署经验谈 http://www.infoq.com/cn/articles/weibo-multi-datacenter-deployments

The Log: What every software engineer should know about real-time data's unifying abstraction https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying