#键值存储 #rocksdb #跟踪 #历史 #数据库 #观测 #范围

hkvdb

一个简单的键值存储,用于跟踪历史信息

4个版本 (2个重大更改)

0.3.0 2023年2月6日
0.2.1 2022年2月20日
0.2.0 2022年2月17日
0.1.0 2022年1月31日

数据库实现中排名#110

自定义许可证

34KB
781

hkvdb

Rust build status Coverage status

请注意,此软件不是“开源”的,但源代码可供个人、非营利组织和工作合作社使用和修改(有关详细信息,请参阅下面的许可证部分)。

关于

这是一个小型项目,其中包含我在多个地方使用的某些代码。动机是在最近几个项目中,我需要一种方式来存储将某些实体与特定时间点的字段值相关联的历史观测数据。例如,我们可能想收集一组Twitter账户的配置文件图像URL,同时跟踪每个配置文件图像首次和最后已知使用日期。

我们可以使用关系型数据库来完成此类任务,但此类数据通常只是其他流程中的中间步骤,结构非常简单,因此使用Postgres(甚至SQLite)来设置数据库似乎是过度杀鸡用牛刀。使用RocksDB存储提供了轻量级高效处理此类数据的方法,而无需太多设置成本。

例如,从2021年一个月的Twitter Stream Grab数据中加载1800万个用户配置文件快照的URL大约需要35分钟,而包含38,937,009个URL和31,393,631个账户的结果存储(仅2.2 GB)。查找数据值非常快,启动时间非常短。例如,在我的机器上搜索1000个用户的配置文件图像URL和使用日期仅需数十毫秒。

$ time target/release/demo data/profile-image-demo-2021-08/ < users-ids-1k.txt > out.txt

real	0m0.053s
user	0m0.049s
sys     0m0.034s

此实现版本目前仅支持跟踪观测日期范围或实例(使用纪元秒)。它还仅支持通过键进行查找(例如,上面的示例中的Twitter用户ID),尽管我有一些用于索引值的代码,我将在某个时候集成 [我草草地复制了一些,但尚未进行测试]。

用法

API除了putget之外没有太多内容。例如,假设我们有以下数据:


struct UserSnapshot {
    user_id: u64,
    timestamp_s: u32,
    screen_name: String,
}

let snapshots = vec![
    UserSnapshot {
        user_id: 770781940341288960,
        timestamp_s: 1577933499,
        screen_name: "RudyGiuliani".to_string(),
    },
    UserSnapshot {
        user_id: 770781940341288960,
        timestamp_s: 1479920042,
        screen_name: "xxxxxxx37583982".to_string(),
    },
    UserSnapshot {
        user_id: 6510972,
        timestamp_s: 1643648042,
        screen_name: "travisbrown".to_string(),
    },
    // Millions of other user profile snapshots?
];

我们可以使用put创建数据库并插入这些用户数据

use hkvdb::Hkvdb;

let db: Hkvdb<Range32> = Hkvdb::new("profile-image-urls")?;

for snapshot in snapshots {
    db.put(
        snapshot.user_id,
        &snapshot.screen_name,
        snapshot.timestamp_s,
    )?;
}

然后可以使用get查找与ID相关联的所有数据值

let values = db.get(770781940341288960)?;

let mut expected = HashMap::new();
expected.insert("xxxxxxx37583982".to_string(), 1479920042.into());
expected.insert("RudyGiuliani".to_string(), 1577933499.into());

assert_eq!(values, expected);

我们还可以选择性地创建索引,并通过数据值搜索数据库

db.make_index(CaseSensitivity::Insensitive)?;

let user_ids = db.search_ci("RuDYgiuLianI")?;

assert_eq!(user_ids, vec![770781940341288960]);

目前这就是你可以用它做的全部了!

详细信息

格式非常简单,应该可以从任何RocksDB客户端或库中轻松读取。例如,对于包含日期范围的版本

+----------------------------+---------+
| key                        | value   |
+-+--------+---------········+----+----+
|0| id     | data            |1st |last|
+-+--------+---------········+----+----+

存储所有观测时间戳的版本在值部分有更多的内容

+----------------------------+--------------+
| key                        | value        |
+-+--------+---------········+----+----+····|
|0| id     | data            |1st |2nd |etc.|
+-+--------+---------········+----+----+····|

如果你生成了索引,额外的行看起来像这样

+-------------------+--------------------------+
| key               | value                    |
+-+---------········+--------+--------+········|
|1| data            | id 1   | id 2   | etc.   |
+-+---------········+--------+--------+········|

所有整数都使用大端字节序存储。

许可

本软件在反资本主义软件许可协议(v. 1.4)下发布。

依赖项

~26MB
~536K SLoC