整篇文章在今天看来没有太新的东西,不过在当时,能用不高的代价,来解决遇到的问题,就很棒。
简单记录一些要点。
背景是facebook请求太多,数据库处理不过来,所以需要用缓存来解决。但是集群太大,使用单机的memcache搞不定,facebook借助开源的memcache,实现了缓存集群。
- 使用了缓存删除策略,即修改数据库数据时,将缓存中的对应key删除,不使用更新策略的原因是容易出现并发问题,导致缓存和数据库最终不一致。
- 降低延迟,使用udp来实现读请求(不怕丢),使用tcp来实现数据修改请求。
- 客户端方面进行了改进提高效率,batch和parallel,都是最有效的策略。parallel是分析了用户请求的所有数据之间的依赖,不存在依赖关系的数据可以并行请求。
- 通过lease token机制解决数据不一致和缓存击穿问题。每次cache miss时,memcache给该请求分配一个token,仅有带有token的请求可以在miss之后去查数据库。没有token的等待一小段时间后再请求cache(这时候多半不会miss了)。仅带有token的请求可以修改cache(查询数据库完成后回填),memcach会比较token是否是有效的,如果是无效的不让写入,token是有lease的,过期会自动删除,如果一个请求修改了数据,那就会delete掉缓存,然后删除该key对应的所有lease。
- 缓存池。不同的数据特性不同,有的数据更新快,但是cache miss代价低,有的更新慢,miss代价高,分出不同的pool来存储不同特性的数据,防止缓存污染。
- 错误处理。节点故障后,没有使用一致性哈希这样的策略,理由是怕引起级连故障。所以隔离一个gutter pool,大约占整个集群的1%资源,平时不用,承担所有故障机器的key。即用户请求memcache失败后,就请求gutter pool,再失败就请求数据库。数据库中的key应该有ttl,很快会过期,不然gutter可能会存放所有的key,如果每个key更新都会delete一次gutter,开销太大了。
- 某些pool内部,某些memcache无法承载压力,可以通过多副本复制来解决,这种复制选择全量复制,而不是partition,理由是对于一个memcache请求1个key和请求100个key的压力差不多,如果partition,那就是两个50 key的请求,压力不会有效降低。
- 一个region内集群会非常大,所以会拆分成多个集群,降低网络的复杂度,多个集群之间replication,共享一个数据库集群,集群内部应该是partition,集群的淘汰策略靠订阅mysql的commit log。memcache太多了会导致扇出太大,可以通过多级扇出来降低压力。
思考:分布式中常见的两种手段,partition和replication,他们的场景?很多产品中都将二者结合使用,如spanner,为了解决什么问题?