Python 高效(时间和空间)字典数据库(uniq id的唯一单词和唯一单词)

Python 高效(时间和空间)字典数据库(uniq id的唯一单词和唯一单词),python,c,database,encoding,dictionary,Python,C,Database,Encoding,Dictionary,我正在寻找一种解决方案,它能够: 存储任意大小的唯一字及其唯一的64位无符号整数标识符和32或64位无符号整数引用计数 使用以下模式快速访问数据: 要查找单词,请返回其uint64标识符 要查找标识符,请返回单词 插入新记录,最好具有自动递增的标识符和原子递增的引用计数,最好在批提交中插入(不是指逐字插入,每个记录在单独的事务中,而是在一个提交的事务中插入多个字) 原子地删除引用计数为零的记录(即使在速率受限的全表扫描中也可以这样做,方法是迭代所有记录并删除事务中引用计数为0的记录) 在传

我正在寻找一种解决方案,它能够:

  • 存储任意大小的唯一字及其唯一的64位无符号整数标识符和32或64位无符号整数引用计数
  • 使用以下模式快速访问数据:
    • 要查找单词,请返回其uint64标识符
    • 要查找标识符,请返回单词
  • 插入新记录,最好具有自动递增的标识符和原子递增的引用计数,最好在批提交中插入(不是指逐字插入,每个记录在单独的事务中,而是在一个提交的事务中插入多个字)
  • 原子地删除引用计数为零的记录(即使在速率受限的全表扫描中也可以这样做,方法是迭代所有记录并删除事务中引用计数为0的记录)
  • 在传统的旋转铁锈(硬盘)上存储大量记录,记录数量大约在1亿到1万亿(1000*10^9)之间
  • 平均字大小在25-80字节之间
  • 最好有一个python(用于原型设计)和C接口,主要是可嵌入的,或者一个高效的“远程”(仅在本地主机上)API
例如,MySQL模式如下所示:

CREATE TABLE words (
    id SERIAL,
    word MEDIUMTEXT,
    refcnt INT UNSIGNED,
    INDEX(word(12)),
    PRIMARY KEY (id)
)
这当然是可行的,但MySQL不能胜任这项任务,而且由于单词搜索所需的索引,它不必要地存储冗余信息

在寻找最有效的解决方案的过程中,我发现了以下几点: -因为这些单词有很多共同点(大多数是各种语言和字符集中的普通字典单词),所以这样做很好 -到目前为止,我能找到的最好的是东京内阁的TDB:packages.python.org/tokyocatent-python/TDB.html,但我必须评估它的性能和可能的设置(在哪里存储什么,在哪里使用什么类型的索引,以获得最佳的时间和空间效率)

有什么想法、算法、甚至更好的、随时可用的产品和设置吗

谢谢,

您可能会想。它涵盖了大部分(如果不是全部的话) 你的要求。它对于您的用例具有良好的性能,具有原子性 用于创建引用计数和唯一标识符的增量/减量, 客户端存在于,您可以包装 事务中的命令序列。它还支持列表、集合和排序 设置和其他一些可能有用的功能

如果可以对工作进行分区,则可以加载/处理来自多个站点的数据 主机并行。考虑到redis的速度,您可能不需要批量处理 但这是可能的(
MSET
命令)

另一个好的方面是,您可以使用
redis cli
命令。通过这种方式,您可以尝试/调试 在尝试编写任何代码之前,请执行以下命令。假设redis正在运行 使用默认端口的localhost,键入以下内容:

% redis-cli
redis>
我编写了一组支持您的用例的快速命令

此代码段创建一个名为
next.words.id
的整数键,并将其递增 原子地返回新值。为了便于说明,我在
123455
开始了这个序列。
(整数)123456
是 返回给您的客户:

redis> SET next.words.id 123455
OK
redis> INCR next.words.id
(integer) 123456
然后,我们将这个单词映射到它的id
“chaos”->123456
,然后创建一个反向 从
id:123456->“混沌”
映射,最后创建一个引用计数键 设置为
0
。前缀
id:
ref:
next.words.id
只是 我选择的惯例--你可以使用任何你喜欢的命名

redis> SET chaos 123456
OK
redis> SET id:123456 chaos
OK
redis> SET ref:chaos 0
OK
要增加“混沌”一词的引用计数:

要减少引用计数,请使用DECR:

redis> DECR ref:chaos
(integer) 1
redis> DECR ref:chaos
(integer) 0
此时,您的代码可能会检测到“混沌”的refcount已被删除
0
并在单个事务中执行以下命令:删除 word及其
id:
ref:
键。我使用了
WATCH
命令来避免竞争条件:如果任何其他客户端在提交事务之前更改
ref:chaos
键,它将被中止

redis> WATCH ref:chaos
OK
redis> GET chaos
(integer) 123456
redis> MULTI
redis> DEL chaos
QUEUED
redis> DEL id:123456
QUEUED
redis> DEL ref:chaos
QUEUED
redis> EXEC
1) (integer) 1
2) (integer) 1
3) (integer) 1

希望这能有所帮助。

删除记录后是否允许重复使用ID?奇怪的是,为什么您认为MySQL无法完成此任务?你试过了吗?如果你不喜欢MySQL,可以试试MongoDB…@caf:是的,它可以重复使用删除的记录IDs@MattBillenstein:是的,我用上面的模式尝试过MySQL,它在数百万条记录之后使用了太多的CPU(固定为100%),当然速度也慢了很多。MongoDB是另一个参与者,我以前使用过它,但是它产生了太大的数据库,这里的空间效率也很重要。顺便说一句,单词的双重存储(一个用于实数,一个用于索引)似乎也是一个主要问题,我不确定它在可伸缩性方面是否会比MySQL好。就您计划存储的密钥数量而言,我对Redis的建议可能行不通,因为它的虚拟内存方案仍然要求所有密钥都适合内存,但我会留下答案供参考。正如你所说,东京内阁可能值得调查。还可以为您提供所需的高性能和空间效率。
redis> WATCH ref:chaos
OK
redis> GET chaos
(integer) 123456
redis> MULTI
redis> DEL chaos
QUEUED
redis> DEL id:123456
QUEUED
redis> DEL ref:chaos
QUEUED
redis> EXEC
1) (integer) 1
2) (integer) 1
3) (integer) 1