SlimTrie:战胜Btree单机百亿文件的极致索引-实现篇

最近,知名博主 @drdrxp (张炎泼) 一条关于SlimTrie介绍的微博引发了热议

640?wx_fmt=png

高可用架构联系了 XP 老师,通过本文首次介绍了 SlimTrie 的详细实现。本文作者李文博,吴义谱、张炎泼对本文亦有贡献。


640?wx_fmt=png李文博,目前就职于白山云科技有限公司,从事云存储研发工程师。在白山主要有 s2 分布式对象存储系统的日常建设和 ec 冷数据存储集群开发的实战经历,在分布式存储服务方向有一些积累和经验。

640?wx_fmt=png吴义谱,目前就职于白山CWN云存储部门,2016年加入白山,主要负责分布式对象存储的研发工作,熟悉了解行业内主流的分布式存储系统,积累了丰富的云存储相关技术,并运用这些技术攻克了实际中遇到的难点,运用EC(Erasure Code)技术解决了冷数据在保证可靠性的同时降低33%成本,运用haystack技术解决百亿级别小文件的访问IO瓶颈问题,运用paxos技术解决几十亿集群管理和leader选举问题。

640?wx_fmt=png张炎泼 (xp),30 年软件开发经验,物理系背叛者,设计师眼中的美工,bug maker,vim 死饭,悬疑片脑残粉。曾就职新浪, 美团。现在白山云,不是白云山 。

上一篇 SlimTrie 设计篇 [1] 中,我们介绍了单机百亿文件的索引设计思路,今天我们来具体介绍下它代码级别的实现。文中我们要解决的问题是: 在一台通用的100TB的存储服务器的内存中, 索引100亿个小文件。

而最终我们通过SlimTrie对存储系统中静态文件的索引, 内存开销只占 Btree的 13%, 查询速度却是 Btree 的 2.6倍!

索引的一点背景知识


索引可以被认为是业务数据(用户文件)之外的一些"额外"的数据, 在这些额外的数据帮助下, 可以在大量的数据中快速找到自己想要的内容. 就像一本数学课本的2个"索引": 一个是目录, 一个是关键词索引.现实系统中,存储系统中的索引需要做到:

  • 足够小 : 一般实现为将索引信息全部存储在内存中可以达到比较好的性能。访问索引的过程中不能访问磁盘, 否则延迟变得不可控(这也是为什么leveldb或其他db在我们的设计中没有作为索引的实现来考虑).

  • 足够准确 : 对较小的文件, 访问一个文件开销为1次磁盘IO操作。


分析下已有的2种索引类型, hash-map类型的和tree类型的,Hash map类索引利用hash函数的计算来定位一个文件:

  • 优势 :快速,一次检索定位数据。非常适合用来做 单条 数据的定位。

  • 劣势 :无序。不支持范围查询。必须是等值匹配的,不支持“>”、“<”的操作。

  • 内存开销: O(k * n)。

  • 查询效率: O(k)。


而基于Tree 的索引中代表性的有: B+tree, RBTree, SkipList, LSM Tree, 排序数组 :

  • 优势 : 它对保存的key是排序的;

  • 劣势 : 跟Hash map一样, 用Tree做索引的时候, map.set(key = key, value = (offset, size)) 内存中必须保存完整的key, 内存开销也很大: O(k * n);

  • 内存开销: O(k * n);

  • 查询效率: O(k * log(n));


以上是两种经典的索引都存在一个无法避免的问题: key的数量很大时,它们对内存空间的需求会变的非常巨大:O(k * n) 。

如果100亿个key(文件名)长度为1KB的文件。那么仅这些key的索引就是 1KB * 100亿 = 10,000GB。导致以上的经典数据结构无法将索引缓存在内存中。而索引又必须避免访问磁盘IO,基于这些原因我们实现了一套专为存储系统设计的SlimTrie索引.

索引数据大小的理论极限


如果要索引n个key, 那每条索引至少需要 log 2 (n) 个bit的信息, 才能区分出n个不同的key. 因此理论上所需的内存空间最低是log 2 (n) * n * n, 这个就是我们空间优化的目标. 在这里,  空间开销仅仅依赖于key的数量,而不会受key的长度的影响!

我们在实现时将所有要索引的key拆分成多组,来限制了单个索引数据结构中 n的大小, 这样有2个好处:

  • n 和  log 2 (n)  都比较确定, 容易进行优化;

  • 占用空间更小, 因为: a * log(a) + b * log(b) <(a+b) * log(a+b);


SlimTrie索引结构的设计


我们最终达到每个文件的索引平均内存开销与key的长度无关, 每条索引一共10 byte, 其中:

  • 6 byte是key的信息;

  • 4 byte是value: (offset, size); // value的这个设定是参考通常的存储实现举的一个例子,不一定是真实生产环境的配置。


实现思路:从Trie开始


在研究Trie索引的时候, 我们发现它的空间复杂度(的量级)、顺序性、查询效率(的量级)都可以满足预期, 虽然实现的存储空间开销仍然很大,但有很大的优化空间。

Trie 就是一个前缀树, 例如,保存了8个key("A", "to", "tea", "ted", "ten", "i", "in", and "inn")的Trie的结构如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值