# Vite 存储层设计详解之 indexDB 索引系统设计

indexDB 主要通过 k-v 数据库 levelDB 来存储账本的索引,索引对象包括账户 (Account)、交易 (Transaction/AccountBlock)、快照 (SnapshotBlock)、SendBlock/ReceiveBlock 映射,在 levelDB 之外还添加了 hot cache、memory cache 两种类型的缓存提升读取性能,系统整体架构如下。

# 1. 读写流程

数据写入时,数据会先写入 memory cache,然后周期性 (当 snapshot block 插入时) 地将部分数据从 memory cache 拷贝到 pending area 中,pending area 存储的是待写入 levelDB 的数据集合,在下次” 异步批量 Flush“时,会将数据从 pending area 写入 levelDB。 在数据读取时,数据可能存在于 hot cache、memory cache、levelDB 中,数据读取的效率是 hot cache > memory cache > levelDB。

# 2.levelDb 索引

levelDB 作为一个字节序的 kv 存储结构,简洁的实现对于固定类型的存储数据来说非常便于优化。通过预定义的 key 字节结构,有效的将同一类型的数据进行物理的排序,能够充分发挥底层硬件性能。 具体实现是通过定义不同的字节前缀将不同的数据类型进行物理的分段,需要的话每个类型的数据还可以在这个基础上定义自己的子前缀,从而实现不同子类型数据结构的有序聚簇存储。

# 3.hot cache 与 memory cache 缓存

hot cache 是一个高速读缓存,经常读取到的热数据会缓存于 hot cache 中,以提高整体的读性能。memory cache 是一个写暂存区,会暂存最近写入的数据,因而在 memory cache 中的部分数据可能还未写入 levelDB,而通过一定的设计,使得无论数据存储在 memory cache 还是 levelDB 中,插入、删除、读取、更新、迭代等操作都是一致的,因而对于上层模块而言,写入 memory cache 数据即是可读的。

# 4.pending area 与 redo log

pending area 暂存准备写入 levelDB 的数据。” 异步批量 Flush“触发时,数据会从 pending area 写入 levelDB,先写 redo log 再写入 levelDB,redo log 在逻辑上记录的是数据操作顺序,如 "put k1 v1; put k2 v2; put k1 v2; delete k3...",多次执行同一个 redo log 是幂等的。

# 5. 索引维度

通过分析每种类型的数据读写场景,并有针性的定义最适合的维度,在数据写入时多个维度的索引会分别写入,这样每个场景的的读取效率都会因为有了最优的索引而得到保证。