数据库索引对于数据查询的重要性不可言喻, 因此作者在存储层实现了二级索引, 以及利用索引进行扫描的功能. 目前仅实现了分区表与非分区表的本地索引(数据与索引共用一个 Raft 组管理), 全局索引及反向索引待以后再实现.
一, 存储结构:
在介绍索引前先了解一下数据与索引是以何种结构存储于 RocksDB 内的, 每个节点的 RocksDB 实例都包含以下两个 ColumnFamily, 每个列簇的存储结构如下:
1. TableCF: 存储实体数据
1.1 Key 存储编码:
TableId(with OrderFlag) | EntityId |
---|---|
32bit | 128bit |
OrderFlag 用于 EntityId 按升序还是降序排列
EntityId 包含实体创建时间戳, 全局惟一
1.2 Value 存储编码:
Versions | GC Flag | [VersionTS + DataPtr] | [Data [FieldId + Value]] |
---|---|---|---|
15bit | 1 bit | Versions * (64+32bit) | Versions * nbit |
Versions 表示该条记录有多少个 Mvcc 版本
GC Flag 表示第一个版本前是否被清理掉了
[VersionTS + DataPtr]其中 VersionTS 是混合逻辑时间戳, DataPtr 指向此版本的数据位置, 另外 DataPtr=0xFFFFFFFF 表示记录删除标记
数据部分每 100 个 (暂定) 保存一个 FullVersion 的记录数据, 后跟差异部分
1.3 dbscan 工具输出示例:
2. IndexCF: 存储索引数据
2.1 Key 存储编码:
TableId | IndexId | IndexKey Values | None unique index's EntityId |
---|---|---|---|
32bit | 8bit | [FieldId + Value] | 128bit |
IndexKey Values 中的 FieldId 有一位是排序标志位
None unique index's EntityID 表示非惟一索引指向的目标, 惟一索引存在于 Value 内
2.2 Value 存储编码:
与 TableCF 的 Value 编码相同.
2.3 dbscan 工具输出示例:
二, 索引管理:
在新建实体模型及修改实体模型时均可添加与删除索引(如下图所示), 需要注意的是修改模型时添删索引会启用异步任务变更表结构并重建索引数据(请参考之前的文章: 异步结构变更), 如果是重建惟一索引可能失败, 在实体模型设计器内可查看索引重建状态. 在索引重建过程中或重建失败后利用索引扫描数据会直接报错, 告知索引尚未准备好.
三, 索引扫描:
不同于传统 Sql 数据库解析 Sql 后利用索引扫描, 服务模型的代码必须明确指定用哪个索引来查询数据, 具体参考以下示例代码如何利用索引扫描数据:
- public async Task<object> IndexScan()
- {
- // 新建索引扫描, 范型参数 1 为实体类型, 参数 2 为索引类型
- var q = new IndexScan<Entities.VehicleState, Entities.VehicleState.IX_VID_Speed>();
- // 如果是分区表可通过分区谓词指定分区扫描, 非分区表无效
- q.Partitions.Equal(t => t.VID, 3);
- // 可指定索引谓词确定扫描范围
- q.Keys.Equal(t => t.VID, 3);
- // 如果是复合索引可指定其他索引谓词
- q.Keys.Equal(t => t.Speed, 100);
- return await q.Take(10).ToListAsync();
- }
索引谓词目前仅实现了相等性判断, 其他如大于, 小于等稍后实现
索引扫描的附加过滤条件尚未实现
另如果需要插入一批测试数据可参考以下示例代码:
- public async Task<object> FillData()
- {
- // 第一个参数 128 表示并行任务数, 第二个参数表示每个任务执行次数
- // 作者虚拟机 (I74C8G) 执行以下代码约每秒插入 13000 条记录
- return await SimplePerfTest.Run(128, 500, async (i, j) =>
- {
- var obj = new Entities.VehicleState(i);
- obj.Speed = j;
- await EntityStore.SaveAsync(obj);
- });
- }
四, 本篇小结:
本篇介绍数据及索引的存储结构以及利用索引扫描 API 来查询数据, 下一步作者将实现其他谓词条件, 另外实现聚合扫描并优化单分区事务递交. GitHub 上的运行时已更新 (包括 dbscan 工具) 可供测试. 如果您有问题或 Bug 报告, 请留言或提交 Issue, 另外您的关注与点赞将是作者最大的动力.
来源: https://www.cnblogs.com/BaiCai/p/11239172.html