memcache@facebook

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)解决方案

你可能感兴趣的:(Facebook,游戏,PHP,memcached,数据结构)