MyTopling 索引:双数组 DoubleArray Trie Cache

(一)背景

MyTopling 是基于 ToplingDB 的 MySQL,分叉自 MyRocks,ToplingDB 则分叉自 RocksDB,兼容 RocksDB 接口,从而 MyTopling 可以复用 MyRocks 的大部分成果。

ToplingDB 早已开源,MyTopling 也会于近期开源。

(二)MyTopling 索引结构

MyTopling 索引的逻辑结构继承自 MyRocks,跟一般的方案不同,没有使用自定义 Comparator,而是对 Key 进行编码,使得编码后的格式,使用(默认的) BytewiseComparator ,等效于使用自定义 Comparator 对原始 Key 进行比较。

逻辑上,编码后的 Key 大致是这样的:


其中索引ID用来区分不同的 Table(primary index)及次级索引,是个 32 位整数,所有整数都按 BigEndian 方式编码(从而整数的大小比较等效于 memcmp)。

(三)大部分索引 Key 都是定长的

DB 中的各种索引一般都是整数类型(各种 ID,主键、外键……),映射到存储引擎中,那就是:相同的 索引 ID,落到 ToplingDB 中,其 Key 的长度都是相同的。例如,在 sysbench 中:

CREATETABLEsbtest1(

idintNOTNULLAUTO_INCREMENT,

kintNOTNULLDEFAULT'0',

cchar(120)NOTNULLDEFAULT'',

padchar(60)NOTNULLDEFAULT'',

PRIMARYKEY(id),

KEY(k)

);

sbtest1 的存储和索引结构,套用(二)中的表格,就是:


显而易见,主键索引 Key 落到引擎中,是固定的 8 字节,次级索引落到引擎中,是固定的 12 字节。

在这里,主键索引会使用 ToplingDB 的 UintIndex,在大多数情况下,这个索引的空间占用是 O(1),即不管有多少条 KV,空间占用就是那么几个字节,在 ToplingDB 的可视化 WebView 中:


这个 168 个字节中,主要是元信息(Header,类名……),这个基本上已经不可能再优化了。

次级索引则会命中 NLT(NestLoudsTrie)索引,因为 4 个字节的前缀完全相同,所以其有效 Key 只有 8 字节(k + id),NLT 可以将这 8 字节压缩到(平均)大约 5 字节。这对 NLT 其实是一个 Bad Case,甚至算得上是 Worst Case,这样的数据,不光压缩率差,而且访问速度慢,特别是遍历(Iteration)的速度。

在 ToplingDB 中,原本有另一种选择:ToplingDB 的 CompositeUintIndex,但是在 sysbench 这个场景中,索引选择时其评分(score)太低——因为 k 的 distinct 值太多。所以最终回到了默认的 NLT。

(四)新的 FixedLenKeyIndex

在使用 NLT 对索引数据进行压缩之前,是难以预判最终的压缩率的,所以,我们最近做了一个改进:在 Key 长度固定的情况下,如果创建出来的 NLT 压缩率太差,我们就使用新的索引:FixedLenKeyIndex。

FixedLenKeyIndex 非常简单,创建时首先移除公共前缀,剩余的部分,放到一个固定长度的数组中,这个固定长度,可能是任何值,例如,3,5,7,9。

搜索时使用二分搜索,当然这个二分搜索我们也做了类似于 ToplingDB 的去虚拟化(devirtualization) 这样的优化。

但是,作为数据库,索引可能是非常大的,比如100亿条,在这么大规模的数组上使用二分搜索,是有性能瓶颈的:内存访问延迟。因为其(精确的)时间复杂度是 O(log2(n)),100亿条,就需要平均 34 次内存访问,当然,因为 CPU Cache 的存在,真正的内存访问可能是 20 多次,在实测中,这比 NLT 的搜索还要慢得多(大约 3 倍),相比 NLT,其优势就只有遍历(iteration)。

(五)DoubleArray Cache

一般的思路,就是创建类似 BTree 这样的数据结构,在这里如果也用 BTree,就相当于是静态 BTree 了。但是 就算使用 BTree,也需要在(包含多个分叉的)结点中(使用二分法)搜索,其实相当于把全局的二分搜索,变成对每层节点内的二分搜索。

有没有更快的办法呢?

在 ToplingZip 的全局压缩 PA-Zip 算法中,对全局字典的搜索使用 DoubleArray Trie 进行加速,使得该算法有了现实可行性。

关于 DoubleArray Trie 的科普介绍,我这里就不多讲了,关键是灵活运用:我们怎样把这个 Double Array 创建出来,又怎么使用。

5.1 创建

我们把 FixedLenKeyVector 看成一个矩阵,每个 Key 是一行,不同 Key 中相同位置是一列,顺着列的方向,执行宽度优先搜索,每拿到一个子矩阵,如果该子矩阵的行数大于指定的阈值,就将其放入搜索队列,否则剪掉对这个子矩阵及其子孙矩阵的搜索。以这样的方式,我们创建了一个临时 Trie 树,然后在此基础上创建 Double Array Trie。

通过改变阈值,我们可以控制 Trie 树的大小,因为只是一个 Cache,所以,这个 DoubleArray Trie 的内存占用一般只有(去除了公共前缀的) KeyVector 的 3% 左右。

5.2 搜索

搜索就是经典的 Double Array 搜索算法,循环中只需要一个判断语句,但是相当于做了一个 256 分叉的搜索:

MatchStatusda_match(constbyte*input,size_tlen)constnoexcept{minimize(len,m_fixlen);size_tlo=0,hi=m_size,pos=0,state=0;constautoda=m_da_data;for(;pos<len;pos++){size_tchild=da[state].m_child0+input[pos];if(da[child].m_parent==state){state=child;lo=da[child].m_lo;hi=da[child].m_hi;}elsebreak;}return{lo,hi,pos};}

从这里拿到 {lo,hi} 范围后,继续在 KeyVector 中二分搜索,此时这个范围已经很小了,可以理解成是在 BTree 的叶子结点中搜索,只是我们搜索定位这个 BTree 叶子结点的算法,比传统算法要快得多!

5.3 实测效果

使用简单的二分搜索,FixLenKeyIndex 比 NLT 慢大约 3 倍,加上 Double Array Trie Cache,速度一下提了 5、6 倍,形势就逆转了,现在,FixLenKeyIndex 的搜索性能和遍历性能都得到了保证。在火焰图中:


FixedLenKeyIndex::Iter::Seek 的耗时占比只有 0.83%! 再细化一下:


da_match 和 lower_bound_prefix 的耗时对比大约是 1:2,如果 cache 大一点,da_match 占比会更多一些。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,923评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,154评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,775评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,960评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,976评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,972评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,893评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,709评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,159评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,400评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,552评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,265评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,876评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,528评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,701评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,552评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,451评论 2 352

推荐阅读更多精彩内容