论文笔记:Scaling Memcache at Facebook
论文介绍了Facebook如何使用memcache,以及相关魔改和维护memcache集群。web
欢迎访问个人博客数据库
背景
facebook使用一致性哈希来构建memcahce集群。后端
We provision hundreds of memcached servers in a cluster to reduce load on databases and other services.
Items are distributed across the memcached servers through consistent hashing [22].
Thus web servers have to routinely communicate with many memcached servers to satisfy a user request
使用
cache aside模式
facebook根据他们的场景,也就是读多写少使用了cache aside模式。原文叫demand-filled look-aside cache。具体使用方式以下图。

facebook 使用delete而不是update缓存的缘由是由于delete是幂等的,而update不是(we choose to delete cached data instead of updating it because deletes are idempotent).若是直接更新可能出现先更新的后放入缓存。缓存
在facebook也是用memcache做为大数据平台计算的中间结果存储平台,用来存放一些中间结果服务器
魔改
魔改背景
一个web请求会请求平均521个独立的缓存项.(A single user web request can often result in hundreds of individual memcache get requests. For example, loading one of our popular pages results in an average of 521 distinct items fetched from memcache)网络
魔改目的
魔改的目的是为了下降延迟,下降负载。数据结构
魔改方法
延迟与负载:1.网络延迟 2.因为cache miss带来的延迟和后端的负载。解决方案以下:ide
- 代码批量化访问缓存,减小来回的网络开销
- 对于网络延迟的下降,facebook对于GET使用UDP传输,SET和DELETE使用TCP传输。由于UDP是无链接的,能够下降TCP维护连接的开销。使用UDP客户端本身记录请求序号,丢包和乱序被视为客户端错误,在实际生产环境中,有0.25%的get请求被扔掉。其中的80%是由于回包延迟,其余的是由于乱序。对于上面的错误,客户端直接认为是cache miss。直接取后端,可是不会放入缓存是为了不额外的网络开销
效果以下图:

- 使用相似TCP拥塞控制的方法来防止缓存集群负载太高。客户端维护一个滑动窗口,当服务端正常响应,就加大窗口,当服务端没响应或者超时就减少窗口。
后端负载下降
1.引入“租约Leases”,租约用来解决两个方面的问题 1. 过时的设置 2.惊群效应(stale sets and thundering herds)。memcached
- 过时的设置主要来源于放入缓存的顺序错乱(后来先到)。
- 惊群效应发生在某个key被频繁的读写。因为写是invalid cache,因此大量读请求就会没法命中缓存,致使进入了后端的数据库。
- 租约就是一个对应于请求的key的64bit的token。(The lease is a 64-bit token bound to the specific key the client originally requested)。当set cache的时候,客户端须要把这个key带上一块儿给到缓存服务器,当服务端发现token不匹配的时候,就说明这是一个过时的写请求。
- 租约同时能够下降惊群效应。缓存服务器控制发token的频率,默认是10秒一次,缓存服务器发过一次token后,后续再来的请求就直接等一会(等前面那个拿到token的返回以后,缓存就有数据了)
Without leases, all of the cache misses resulted in a peak database query rate of 17K/s. With leases, the peak database query rate was 1.3K/s
2.使用过时值fetch
- 经过返回slightly out-of-date data,当一个key被删除的时候,在真正flush以前,会被放入一个特殊的数据结构中。get请求能够返回一个租约或者这个被标记为过时的值。
3.处理失败,这里的失败有两种
- 有一小部分机器因为网络缘由没法访问(facebook依赖一个自动化的组件去发现这些机器,但须要时间 few minutes,在这一段时间内可能会带来一些连锁失败)
解决办法:有一个叫gutter的小池子,大概是cache集群的1%大小,用来替代那些没法访问的机器。当client没法收到缓存服务器的响应,他们就去访问gutter池子里面的机器。注意这根通常一致性哈希的玩法不同,缘由是有可能有些key很是的热,若是把它hash到下一台机器,就会增长那台机器的负载,致使又爆炸。把它导到空闲的gutter机器上就能够避免这个问题。一旦出现一台缓存机器没法访问,gutter一般能在4分钟内,缓存命中率能超过35%,有时能达到50%
- 集群中的大部分机器都没法访问
解决办法:切换流量到其余可用region
维护memcache集群
待续