memcache@facebook演讲者是 Marc,FB资深架构师,这个PPT讨论了FB如何通过memcache来进行scale来承载高流量。这篇PPT相当有水准,很多思路值得学习借鉴。 由于未参加Qconf,只是从ppt中分析揣摩,难免有不准确的地方还望指正。
Facebook的规模是400m活跃用户
*每天有超过60million的status updates,平均每秒694次。
*每月有超过3billion张照片的上传,平均每秒上传23张。
*每周有超过5billion的内容碎片信息(web links, news stories,blog posts, notes, photo albums, etc.)的分享,平均每秒8k次
*网站上平均每个用户有130个好友
*50billion的好友关系图谱数据
Infrastructure
*成千上万的服务器分别部署于美国东西海岸的多个机房(包括Web servers、DB servers、Memcache Servers、Other services)
Memcache的状况
所有memcached服务器每秒承担400m次gets请求和28m次sets请求
cache了超过2T的items,超过200T bytes
网路I/O:receive 60GB/s transmit 120GB/s
单台memcached服务器每秒承担80k gets和2k sets
存放200M的items
网络I/O:receive 9.7MB/s, transmit 19MB/s。
Memcache Rules of the Game
*从 memcache 获取对象
*如果 miss ,就直接从数据库获取并且将对象 set 到 memcache 中
*更新数据库中的一条记录,直接从缓存中删除这个对象
*在 memcache 中存放的对象都是比较简单的对象,没有派生继承之类的对象
*每一个 memcache 对象直接与数据库中的数据进行映射。也即是说,FB他们绝大多数场景下都是把数据库当做Key/Value Store在使用。
Pools and Threads
不同的对象有着不同大小和不同的访问模式。 facebook 建立了 memcache 池 ( 本人认为用 region 这种说法可能更为贴切 ) ,以此来分隔不同类型的对象以提升缓存性能和内存利用率。
Phatty Phatty Multiget (notes)
*PHP runtime 是单线程和同步的。
*要想性能变得更好,得要让数据支持并行读取,这是必须得要让 memcache 以并行的方式获取请求。
*当初 facebook 只是在 PHP 上使用 polling I/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,可能有不正确的地方。)
New Rule
*如果键是热门数据,那么就采用别名的机制来进行读取
*在更新的时候删除所有的别名缓存
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
前面有个动画,看不了,省略号.......
Hot Misses (notes)
*再回忆一下缓存的游戏规则
*更新就删除
*miss了就查库,然后再set到缓存
*当这个对象是非常poplar的,查询的也会变得频繁,甚至会打死数据库
*流量控制!
可以通过下面这个Rule来做到流量控制
Memcache Rules of the Game (再给Memcache Rule of the Game添点料)
*针对于那些热门数据, 一旦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
翻译出来总觉得别扭 ,直接看英文吧!
*Each memcache server is in the perfect position to detect and mitigate contention
*Record misses
*Record deletes
*Serve stale data
*Serve lease-ids
*Don’t allow updates without a valid lease id
Shaping Memcache Traffic
*mcproxy 好比是路由器
*通过mcproxy进行接入控制
*通过mcproxy来解决 跨数据中心的传输互联问题
Cache Hierarchies
*通过mcproxy 来对冷集群进行预热以便让缓存充满后再切换到这堆集群上。
*对没有缓存的集群进行代理(Proxies for Cacheless Clusters)
对mcproxy感兴趣的童鞋可以去看看moxi。与memcache proxy特性比较类似。
moxi => memcache proxy
Big Low Latency Clusters
*Bigger Clusters are Better
*Low Latency is Better
*L 2.5
*UDP
*Proxy Facebook Architecture
Why Memcache Works
*一句话,通过拆分降低延迟,也会带来更好的用户体验
*充分利用memcache中提供的一些健壮的原语
*做好key与server的映射规划,不同的类型的对象分别存放到不同的缓存服务器上(key-to-server mapping)
*尽可能用并行的方式来获取数据(parallel I/O)
*流量控制(flow-control)
*传输设施的构筑,引入mcproxy类似的解决方案来解决一系列的跨机房跨地域的问题。(traffic shaping)
*scaling问题所涵盖的领域太广,具体场景具体分析,不排除会使用一些特设或临时的(Ad-hoc)解决方案