eBay PB 级日志系统的存储方案实践( 二 )


eBay PB 级日志系统的存储方案实践

文章插图
 
图 2(点击可查看大图)
1)在根目录下建立 1024 个 Bucket, Client (CAL 的 Client,其实是业务 APP 服务器) 通过哈希映射到其中一个 bucket 下  。由于哈希的(统计意义)均衡性,每个 Bucket 里的 Client 文件夹数量是均衡的 。而 Client 文件夹的总数,和业务 APP 服务器的总数对应 。
虽然虚拟化、容器化使得 Client 越来越小,越来越多,但因为 Bucket 数量足够多, 每个 Bucket 下的文件夹数依旧是可控的  。而对于每个 Client 文件夹里的文件数,根据配置,每小时产生 5-20 个 不等的文件,文件总数 = 5~20 /hour * rotation_hours 。 可见,其文件数也是可控的  。
2)Bucket 通过 auth_pin , 绑定到 MDS_rank 上,简单来说:
auth_pin = bucket_id % NUM_MDSs
基于之前讨论的 Bucket 均衡性,以及所有 Bucket 被均衡地分布到各个 MDS 。 可见,MDS 的负载也是均衡的  。
除此之外,我们也预留了拓展性 。当前我们用了 33 个 MDS 做了 AA,其中 32 个 MDS 分别绑定了 32 个 Bucket,rank 为 0 的 MDS 绑定到了根目录 。日后,只需要增加 MDS 的数量,例如从 33(32+1) 增加到 65(64+1) ,修改 auth_pin 绑定关系,就能在客户不感知的情况下实现无缝扩容 。
2. Cache TierCeph Cache Tier 是一个在诞生之初被抱以巨大期望,而在现实中让不少部署踩坑的技术,尤其是在 RBD 和 CephFS 上 。在社区邮件组和 IRC 里最经常出现的问题就是应用了 Cache Tier,性能反而下降了 。
其实,那些场景多数是误读了 Cache Tier 的应用领域 。Cache Tier 的管理粒度是 Object(默认 4MB) ,而在 RBD/CephFS 上的用户 IO 通常明显 小于 4MB  。当用户访问未命中,Cache Tier 决定将这个 Object 从 Base Tier 缓存到 Cache Tier 时,付出了 OBJ_SIZE 大小的 慢速读 (Base Tier)加上 OBJ_SIZE 大小的 快速写 (Cache Tier) 。
只有后续用户对同一个 Object 的重复访问达到足够多的次数,才能体现 Cache Tier 的性能和成本优势 。例如,业务负载是随机 IO,平均请求大小 4KB ,则命中率必须达到 **99.9%** 以上 。
现实是许多业务负载并没有这样的特性 。
在 CAL 的业务模型里,典型的 IO 负载是应用日志每隔一分钟或者 128KB 触发一次写入 。同时,读取端不断地追踪日志,以同样的间隔把数据读走 。针对这个特点,我们决定将 Cache Tier 配置成为一个 Writeback Cache(回写式缓存),这样就能实现多个目的 :
1)写入缓冲与合并 。Cache Tier 的设计容量足够缓存一小时的写入量,不论业务的写入请求大小是多少,都会被 Cache Tier 吸收,以 OBJ_SIZE(4M)为单位刷回 Base Tier 。4M 的大块写,是对 HDD 最友好的访问行为,可以最大化 Base Tier 的吞吐量 。
2)读取缓存 。CAL 的读取行为也大多集中在对最近一个小时写入数据的读取 。这部分数据在 Cache Tier 里,所以读请求也能基本全部命中 Cache Tier 。
3)故障隔离 。机械硬盘的故障率比 SSD 高几个数量级,并且更容易因坏道导致响应时间增长,从而使得 Ceph 集群出现 慢请求(slow request)  。表现在服务质量上,就是延迟相对不稳定且 尾延迟(tail latency) 特别长 。借助 Cache Tier 的缓冲,业务并不会直接感受到由全机械硬盘构成的 Base Tier 的性能抖动,从而达到更好的性能一致性 。
值得一提的一个优化点是:我们通过禁用 Hitset 关闭了 Proxy Write 这个功能 。Ceph Cache Tier 的默认行为是 Proxy Write——即若待写入数据在 Cache Tier 不命中,Cache Tier 会直接把请求写入 Base Tier,并在写入完成后才将结果返回给应用 。
这是一个正确的设计,主要是为了避免写请求不命中时 promote 带来的额外延迟,并解耦数据写入逻辑和 Cache Promote 逻辑 。
但在 eBay 日志的应用场景中,因为写入行为的顺序性和日志不可变更的特性,可以明确知道 写入不命中一定是因为写入了一个新的 Ceph Object,而非对老 Object 的改写,因此 proxy write 的逻辑就带来了额外的开销。
例如,对 obj x 的第一次写入,write(obj_x, 128kb), 在默认的 Proxy Write 行为下 ,这个写被 proxy 到了 Base Tier  。写入成功后,在 Base Tier 留下 128KB 的 object,然后 Cache Tier 再把 obj_x 从 Base_tier promote 上来 。


推荐阅读