memcache@facebook
memcache@facebook演讲者是 Marc,FB资深架构师,这个PPT讨论了FB如何通过memcache来进行scale来承载高流量。这篇PPT相当有水准,很多思路值得学习借鉴。 由于未参加Qconf,只是从ppt中分析揣摩,难免有不准确的地方还望指正。
Facebook的规模是400m活跃用户
- 每天有超过60million的statusupdates,平均每秒694次。
- 每月有超过3billion张照片的上传,平均每秒上传23张。
- 每周有超过5billion的内容碎片信息(weblinks,newsstories,blogposts,notes,photoalbums,etc.)的分享,平均每秒8k次
- 网站上平均每个用户有130个好友
- 50billion的好友关系图谱数据
Infrastructure
- 成千上万的服务器分别部署于美国东西海岸的多个机房(包括Web servers、DB servers、Memcache Servers、Other services)
Memcache的状况
所有memcached服务器每秒承担400m次gets请求和28m次sets请求
cache了超过2T的items,超过200Tbytes
网路I/O:receive60GB/stransmit120GB/s
单台memcached服务器每秒承担80kgets和2ksets
存放200M的items
网络I/O:receive9.7MB/s,transmit19MB/s。
MemcacheRulesoftheGame
- 从memcache获取对象
- 如果miss,就直接从数据库获取并且将对象set到memcache中
- 更新数据库中的一条记录,直接从缓存中删除这个对象
- 在memcache中存放的对象都是比较简单的对象,没有派生继承之类的对象
- 每一个memcache对象直接与数据库中的数据进行映射。也即是说,FB他们绝大多数场景下都是把数据库当做Key/ValueStore在使用。
Pools and Threads
不同的对象有着不同大小和不同的访问模式。facebook建立了memcache池(本人认为用region这种说法可能更为贴切),以此来分隔不同类型的对象以提升缓存性能和内存利用率。
PhattyPhattyMultiget(notes)
- PHPruntime是单线程和同步的。
- 要想性能变得更好,得要让数据支持并行读取,这是必须得要让memcache以并行的方式获取请求。
- 当初facebook只是在PHP上使用pollingI/O的方式。
- 之后他们写了一个PHP的C扩展,使其支持真正的异步I/O。
- 上面的这两个做法所带来的结果是通过并行机制来降低延迟。
Pools and Threads (notes)
- 隐私对象虽小,但命中率很差
- 用户 个人信息 较大,而且具有良好的命中率
- facebook 他们将不同的类的对象分散到不同的 memcache 服务器的不同的池中。
- memcache 原本就是一个经典的单线程 的UNIX 守护进程
- 为了 让memcache更好的榨取服务器 的效能 ,在 memcache 服务器上开了 4 个 memcache 实例 ,每个实例占用 1/4 的内存。
- 但也带来了另外的麻烦,比如每个连接数增长了 4 倍
- 4 倍的元数据开销
- 采用 多线程服务
Connections and Congestion (notes)
- 当增加 web 服务器 ,与memcache box的连接也会增长。
- 每个网络服务器运行50-100 PHP 进程。
- 每个 memcache box 有 超过 100 万 + TCP 连接。
- UDP 可以减少连接的数量。
- 当添加用户和功能, 也就意味着每一次的 memcache 的 multiget 操作所传入的 keys 也在不断的增加。
- 受欢迎的名人和 团体。
- 开放 平台和FBML 。
- 上面这一系列的场景都会导致 拥塞。
Serialization and Compression
- 1 千个 PHP 序列化的对象
- 修改为他们自己设计的序列化
- 基于 thrift 的格式来进行序列化
- 性能提升 3 倍
- 与先前的内存占用相比,变小了 30%
- 采用 gzcompress 来序列化字符串
Multiple Datacenters (notes)
- 早些时候FB是两个数据中心。其中一个中心提供对外服务,另外一个中心并不对外服务,只做灾备。说白了还是一个单数据中心的架构
- 事实上,随着FB的快速成长, 单数据中心无法再满足业务的需求。
- 但即便是这样 FB仍然还是采用了单Master的数据库层次结构。更多是借助于缓存以及对数据库的Sharding来分摊压力。
- 对于memcache还是记住那个游戏规则,数据一旦更新,删除所有层级与之相关的缓存。
- 同时FB通过自行研发的mcproxy来复制多套缓存,以此来抵挡更多的读操作。
Multiple Regions (notes)
- 早期系统只部署在美国西海岸, 由于FB是面向全球的, 这就导致了美国东海岸和欧洲用户的延迟非常严重
- 因此,FB在 Ashburn VA 部署了slave数据库
- slave 库通过追踪 master 库的 binlog 来进行同步
- 数据同步的过程中肯定会有延迟,同时也会引入一些资源竞争的情况
- 为解决这个问题,FB仍然是通过mcproxy来解决这个问题
- 解决的思路是,在进行 mysql 更新和插入的操作中加入了 对memcache进行 删除的程序逻辑
- 在东海岸也通过 mcproxy 与 slave mysqld进行集成,在其中 加入一个线程来进行同步后的缓存删除清理工作
Replicated Keys (notes)
- 随着像病毒式的groups和applications的不断演进, 从而使得一部分的数据访问频率极高(ppt中称之为Hot key)。
- 对于这种热门数据,他需要比通常的单台memcache服务器处理更多的gets操作。
- 但是前面提到的memcache的游戏规则,一旦更新,删除所有相关的缓存。
- 意味着有更多的请求会打到数据库。同时也意味着这台数据库会执行更多的查询。
- 最终会导致数据库被打死,从而使得与之相关的 groups和applications完全当掉。
- 解决的思路是创建Key别名,并将此分发到不同的服务器上 ,用更多的服务器来分摊请求的压力。
- 再将热门的 keys 分发到所有的 web 服务器上,并存为localcache。
- 每一个 web 服务器 也都 匹配一个 gets 的别名
- get key:xxx => get key:xxx#N .(没看太懂,以我自己的理解,是不是 key:xxx#N就是key:xxx的副本,key:xxx#N表示本地缓存,key:xxx表示在memcache中的缓存,数据也都是一致的,我估计FB有一个缓存层在统管localcache、memcache等各种类型的cache)
- 遇到更新操作的时候,每个web服务器删除所有的别名缓存以及原有memcache中的缓存。
(最后这两条纯属YY,可能有不正确的地方。)
NewRule
- 如果键是热门数据,那么就采用别名的机制来进行读取
- 在更新的时候删除所有的别名缓存
Mirrored Pools (notes)
- 随着FB的memcache层的成长,keys/packet的比率也在逐渐下降。
- 先前从1个服务器获取100个keys,只需要发送一个 packet ( 100 keys/ 1 server = 1 packet )
- 随着memcache层的增大,100个服务器,那么获取100个keys最糟糕的情况是需要发送100个 packet ( 100 keys/ 100 server = 100 packets )
- 这网络开销多了去了
- memcache 服务器每次请求的内核中断也多了去了
(看不懂了,直接上原文了%>_<%)
- Confirmed Info - critical account meta-data
- Have you confirmed your account?
- Are you a minor?
- Pulled from large user-profile objects
- Since we just need a few bytes of data for many users
前面有个动画,看不了,省略号.......
HotMisses(notes)
- 再回忆一下缓存的游戏规则
- 更新就删除
- miss了就查库,然后再set到缓存
- 当这个对象是非常poplar的,查询的也会变得频繁,甚至会打死数据库
- 流量控制!
可以通过下面这个Rule来做到流量控制
MemcacheRulesoftheGame(再给MemcacheRuleoftheGame添点料)
- 针对于那些热门数据,一旦miss在进行db查询前抢先加入一个互斥锁。
- 在memcache上针对于这个对象通过add协议加入一个互斥标记。
- 比如这个对象通过key:xxx来获取,如果miss,那么就add一个key:xxx#mutex的缓存(key:xxx=>key:xxx#mutex)
- 如果add成功就执行query操作
- 如果add失败(因为mutex缓存已经存在)回退并重试
- query操作执行成功后删除mutex缓存
Hot Deletes (notes)
- FB也尚未走出困境
- 在频繁更新对象的场景下,Cache mutex这招也不管用了。
- 就像viral groups and applications中的membership列表、walls
- 每个进程刚刚才创立一个互斥对象,发现马上这个对象又立即被删除了。
- ...又搞了一次
- ...又搞了一次
- ...虚脱了!
Rules of the Game: Caching Intent
翻译出来总觉得别扭,直接看英文吧!
- Eachmemcacheserverisintheperfectpositiontodetectandmitigatecontention
- Recordmisses
- Recorddeletes
- Servestaledata
- Servelease-ids
- Don’tallowupdateswithoutavalidleaseid
Shaping Memcache Traffic
- mcproxy 好比是路由器
- 通过mcproxy进行接入控制
- 通过mcproxy来解决 跨数据中心的传输互联问题
Cache Hierarchies
- 通过mcproxy 来对冷集群进行预热以便让缓存充满后再切换到这堆集群上。
- 对没有缓存的集群进行代理(Proxies for Cacheless Clusters)
对mcproxy感兴趣的童鞋可以去看看moxi。与memcache proxy特性比较类似。
moxi=>memcacheproxy
BigLowLatencyClusters
- BiggerClustersareBetter
- LowLatencyisBetter
- L2.5
- UDP
- ProxyFacebookArchitecture
Why Memcache Works
- 一句话,通过拆分降低延迟,也会带来更好的用户体验
- 充分利用memcache中提供的一些健壮的原语
- 做好key与server的映射规划,不同的类型的对象分别存放到不同的缓存服务器上(key-to-server mapping)
- 尽可能用并行的方式来获取数据(parallel I/O)
- 流量控制(flow-control)
- 传输设施的构筑,引入mcproxy类似的解决方案来解决一系列的跨机房跨地域的问题。(traffic shaping)
- scaling问题所涵盖的领域太广,具体场景具体分析,不排除会使用一些特设或临时的(Ad-hoc)解决方案