Python DBM真的快吗?

6 投票
2 回答
13119 浏览
提问于 2025-04-17 04:09

我在想,Python自带的数据库管理工具(DBM)应该比一些NoSQL数据库,比如东京柜(Tokyo Cabinet)、MongoDB等要快很多,因为Python的DBM功能和选项比较少,也就是系统比较简单。我用一个非常简单的读写例子进行了测试,结果如下:

#!/usr/bin/python
import time
t = time.time()
import anydbm
count = 0
while (count < 1000):
 db = anydbm.open("dbm2", "c")
 db["1"] = "something"
 db.close()
 db = anydbm.open("dbm", "r")
 print "dict['Name']: ", db['1'];
 print "%.3f" % (time.time()-t)
 db.close()
 count = count + 1

读/写:1.3秒
读取:0.3秒
写入:1.0秒

而MongoDB的这些值至少快了5倍。这真的是Python DBM的性能吗?

2 个回答

4

在Python3中,嵌入式密钥存储的速度已经足够快。我们可以把它和原生的字典作为比较,比如说:

for k in random.choices(auList,k=100000000):
    a=auDict[k]
CPU times: user 1min 6s, sys: 1.07 s, total: **1min 7s**

GDBM的表现和这个相比也不差。

%%time
with db.open("AuDictJson.gdbm",'r') as d:
    for k in random.choices(auList,k=100000000):   
        a=d[str(k)]   
CPU times: user 2min 44s, sys: 1.31 s, total: **2min 45s**

而且,即使是专门为JSON序列化列表准备的预编译表,比如keyvy,性能也差不多。

%%time
d = keyvi.Dictionary("AuDictJson.keyvi")
for k in random.choices(auList,k=100000000):   
    a=d[str(k)].GetValue()
CPU times: user 7min 45s, sys: 1.48 s, total: 7min 47s

一般来说,嵌入式数据库,特别是当它是只读的并且只有一个用户时,通常会比外部数据库更快。这是因为访问资源时需要处理的网络连接和信号量的开销。而如果你的程序是一个服务,比如说你在写一个网络服务,那么访问资源的开销可能就不那么重要了。

不过,如果外部数据库提供了额外的服务,你会发现使用它们有一些好处。比如Redis,它可以处理集合的并集。

%%time
for j in range(1000):
    k=r.sunion(('s'+str(k) for k in random.choices(auList,k=10000)))  
CPU times: user 2min 24s, sys: 758 ms, total: 2min 25s

用GDBM做同样的任务,性能差不多。虽然Redis的速度还是慢了五倍,但也不是慢到完全不能用。

%%time 
with db.open("AuDictPSV.gdbm",'r') as d:
    for j in range(1000):
        a=set()
        for k in random.choices(auList,k=10000):
            a.update(d[str(k)].split(b'|'))
CPU times: user 33.6 s, sys: 3.5 ms, total: 33.6 s

在这种情况下,使用Redis可以让你享受到数据库的完整功能,而不仅仅是一个简单的数据存储。当然,如果有很多客户端同时访问,或者有很多单独的获取请求,它的表现会比嵌入式资源差。

至于GDBM的竞争,Charles Leifer在2014年的基准测试中显示,GDBM在读取方面的表现超过了KyotoCabinet,而在写入方面则差不多。此外,LevelDB和RocksDB可以被视为更高级的替代方案。

15

Python没有内置的DBM实现,它的DBM功能是基于一些第三方库,比如AnyDBM、Berkeley DBM和GNU DBM。

Python的字典实现对于存储键值对来说非常快,但它不具备持久性。如果你需要快速查找键值对,字典可能更合适——你可以用cpickle或shelve来管理数据的持久性。如果你更看重启动时间(比如在修改数据时的结束时间),而不是运行时的访问速度,那么使用DBM会更好。

在你的评估中,你在主循环里同时包含了DBM的打开调用和数组查找。其实这样使用DBM并不太现实:为了存储一个值而打开DBM,然后再关闭再重新打开去查找,这样做会导致性能很慢(这样管理持久数据存储是很低效的)。

根据你的需求,如果你需要快速查找而不太在意启动时间,DBM可能是个解决方案——但在进行性能测试时,只需在循环中包含写入和读取的操作!下面的代码可能适合你:

import anydbm
from random import random
import time

# open DBM outside of the timed loops
db = anydbm.open("dbm2", "c")

max_records = 100000

# only time read and write operations
t = time.time()

# create some records
for i in range(max_records):
  db[str(i)] = 'x'

# do a some random reads
for i in range(max_records):
  x = db[str(int(random() * max_records))]

time_taken = time.time() - t
print "Took %0.3f seconds, %0.5f microseconds / record" % (time_taken, (time_taken * 1000000) / max_records)

db.close()

撰写回答