密钥:Python中的值存储可能有100 GB的数据,没有客户端/服务器

Bas*_*asj 8 python serialization dictionary key-value key-value-store

解决方法有很多连载小词典:json.loads/ json.dumps,pickle,shelve,ujson,甚至使用sqlite.

但是当处理可能100 GB的数据时,再也不可能使用在关闭/序列化时可能重写整个数据的模块.

redis 实际上不是一个选项,因为它使用客户端/服务器方案.

问题:哪些密钥:值存储,无服务器,能够使用100多GB数据,在Python中经常使用?

我正在寻找具有标准"Pythonic" d[key] = value语法的解决方案:

import mydb
d = mydb.mydb('myfile.db')
d['hello'] = 17          # able to use string or int or float as key
d[183] = [12, 14, 24]    # able to store lists as values (will probably internally jsonify it?)
d.flush()                # easy to flush on disk 
Run Code Online (Sandbox Code Playgroud)

注意:BsdDB(BerkeleyDB)似乎已被弃用.似乎有一个用于PythonLevelDB,但它似乎并不为人所知 - 我还没有找到一个可以在Windows上使用的版本.哪些是最常见的?

saa*_*aaj 14

您可以使用sqlitedict为SQLite数据库提供键值接口.

SQLite 限制页面表示理论最大值为140 TB,具体取决于page_sizemax_page_count.但是,Python 3.5.2-2ubuntu0~16.04.4(sqlite32.6.0)的默认值是page_size=1024max_page_count=1073741823.这提供了大约1100 GB的最大数据库大小,符合您的要求.

您可以使用如下包:

from sqlitedict import SqliteDict

mydict = SqliteDict('./my_db.sqlite', autocommit=True)
mydict['some_key'] = any_picklable_object
print(mydict['some_key'])
for key, value in mydict.items():
    print(key, value)
print(len(mydict))
mydict.close()
Run Code Online (Sandbox Code Playgroud)

更新

关于内存使用情况 SQLite不需要您的数据集适合RAM.默认情况下,它最多可以缓存cache_size页面,这几乎不是2MiB(与上面相同的Python).这是您可以用来检查数据的脚本.运行前:

pip install lipsum psutil matplotlib psrecord sqlitedict
Run Code Online (Sandbox Code Playgroud)

sqlitedct.py

#!/usr/bin/env python3

import os
import random
from contextlib import closing

import lipsum
from sqlitedict import SqliteDict

def main():
    with closing(SqliteDict('./my_db.sqlite', autocommit=True)) as d:
        for _ in range(100000):
            v = lipsum.generate_paragraphs(2)[0:random.randint(200, 1000)]
            d[os.urandom(10)] = v

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

像那样运行它./sqlitedct.py & psrecord --plot=plot.png --interval=0.1 $!.在我的情况下,它生成此图表: 图表

和数据库文件:

$ du -h my_db.sqlite 
84M my_db.sqlite
Run Code Online (Sandbox Code Playgroud)


C. *_*oli 10

LMDB(闪电内存映射数据库)是一种非常快速的键值存储,它具有Python 绑定,可以轻松处理巨大的数据库文件。

还有提供 Pythonic语法的lmdbmd[key] = value包装器。

默认情况下,它仅支持字节值,但可以轻松扩展以使用序列化程序(json、msgpack、pickle)来处理其他类型的值。

import json
from lmdbm import Lmdb

class JsonLmdb(Lmdb):
  def _pre_key(self, value):
    return value.encode("utf-8")
  def _post_key(self, value):
    return value.decode("utf-8")
  def _pre_value(self, value):
    return json.dumps(value).encode("utf-8")
  def _post_value(self, value):
    return json.loads(value.decode("utf-8"))

with JsonLmdb.open("test.db", "c") as db:
  db["key"] = {"some": "object"}
  obj = db["key"]
  print(obj["some"])  # prints "object"
Run Code Online (Sandbox Code Playgroud)

一些基准。批量插入(每个 1000 个项目)用于 lmdbm 和 sqlitedict。对于这些非批量插入,写入性能会受到很大影响,因为默认情况下每次插入都会打开一个新事务。dbm 指的是 stdlib dbm.dumb。在 Win 7、Python 3.8、SSD 上测试。

以秒为单位连续写入

| items | lmdbm | pysos |sqlitedict|   dbm   |
|------:|------:|------:|---------:|--------:|
|     10| 0.0000| 0.0000|   0.01600|  0.01600|
|    100| 0.0000| 0.0000|   0.01600|  0.09300|
|   1000| 0.0320| 0.0460|   0.21900|  0.84200|
|  10000| 0.1560| 2.6210|   2.09100|  8.42400|
| 100000| 1.5130| 4.9140|  20.71700| 86.86200|
|1000000|18.1430|48.0950| 208.88600|878.16000|
Run Code Online (Sandbox Code Playgroud)

几秒内随机读取

| items | lmdbm | pysos |sqlitedict|  dbm   |
|------:|------:|------:|---------:|-------:|
|     10| 0.0000|  0.000|    0.0000|  0.0000|
|    100| 0.0000|  0.000|    0.0630|  0.0150|
|   1000| 0.0150|  0.016|    0.4990|  0.1720|
|  10000| 0.1720|  0.250|    4.2430|  1.7470|
| 100000| 1.7470|  3.588|   49.3120| 18.4240|
|1000000|17.8150| 38.454|  516.3170|196.8730|
Run Code Online (Sandbox Code Playgroud)

有关基准测试脚本,请参阅https://github.com/Dobatymo/lmdb-python-dbm/blob/master/benchmark.py


Joh*_*nck 7

我会为此考虑HDF5。它有几个优点:

  • 可用于多种编程语言。
  • 可以通过优秀的h5py包从 Python 中使用。
  • 实战测试,包括大数据集。
  • 支持可变长度的字符串值。
  • 值可以通过类似文件系统的“路径”(/foo/bar)寻址。
  • 值可以是数组(通常是),但不一定是。
  • 可选的内置压缩。
  • 可选的“分块”以允许增量写入块。
  • 不需要一次将整个数据集加载到内存中。

它也有一些缺点:

  • 非常灵活,以至于很难定义单一方法。
  • 复杂的格式,没有官方的 HDF5 C 库就无法使用(但有很多包装器,例如h5py)。
  • Baroque C/C++ API(Python 不是这样)。
  • 对并发作者(或作者 + 读者)的支持很少。写入可能需要以粗粒度锁定。

您可以将 HDF5 视为一种将值(标量或 N 维数组)存储在单个文件(或多个此类文件)内的层次结构中的方法。仅将您的值存储在单个磁盘文件中的最大问题是您会淹没某些文件系统;您可以将 HDF5 视为文件中的文件系统,当您将一百万个值放入一个“目录”时,该文件系统不会崩溃。

  • @amirouche:显然 HDF5 不是数据库。该问题没有要求数据库。HDF5 不需要将所有内容加载到内存中——您可以加载切片、“超级实验室”、单个数组或分层文件中的属性等。它绝对不需要将任何超出您想要的内容加载到内存中。无论如何,OP 的数据大约为 100 GB,而如今在商品服务器甚至某些台式机上很容易找到 100 GB 的主内存。 (7认同)
  • HDF5 不是数据库,而是序列化格式。它需要将整个加载到内存中。 (4认同)

Mat*_*eld 6

标准库中的shelve模块就是这样做的:

import shelve
with shelve.open('myfile.db') as d:
    d['hello'] = 17  # Auto serializes any Python object with pickle
    d[str(183)] = [12, 14, 24]  # Keys, however, must be strings
    d.sync()  # Explicitly write to disc (automatically performed on close)
Run Code Online (Sandbox Code Playgroud)

这使用 python dbm模块从磁盘保存和加载数据,而不加载整个数据。

dbm 示例:

import dbm, json
with dbm.open('myfile2.db', 'c') as d:
    d['hello'] = str(17)
    d[str(183)] = json.dumps([12, 14, 24])
    d.sync()
Run Code Online (Sandbox Code Playgroud)

但是,使用shelf时有两个注意事项:

  • 它用于pickle序列化。这意味着数据与 Python 以及可能用于保存数据的 Python 版本耦合。如果这是一个问题,dbm可以直接使用该模块(相同的接口,但只能使用字符串作为键/值)。
  • Windows 实现似乎性能不佳

因此,从此处复制的以下第三方选项将是不错的选择:


dag*_*ies 5

我知道这是一个老问题,但我很久以前写过这样的东西:

https://github.com/dagnelies/pysos

它的工作原理与普通的 python 类似dict,但优点是比shelveWindows 更高效,而且是跨平台的,这shelve与数据存储因操作系统而异的情况不同。

安装:

pip install pysos
Run Code Online (Sandbox Code Playgroud)

用法:

import pysos
db = pysos.Dict('somefile')
db['hello'] = 'persistence!'
Run Code Online (Sandbox Code Playgroud)

编辑:性能

只是为了给出一个大概的数字,这里是一个迷你基准(在我的 Windows 笔记本电脑上):

pip install pysos
Run Code Online (Sandbox Code Playgroud)

生成的文件大约有 3.5 Mb 大。...所以,非常粗略地说,您每秒可以插入 1 mb 的数据。

编辑:它是如何工作的

每次设置值时它都会写入,但仅写入键/值对。因此,添加/更新/删除项目的成本始终相同,尽管仅添加“更好”,因为大量更新/删除会导致文件中的数据碎片(浪费垃圾字节)。内存中保存的是映射(键 -> 文件中的位置),因此您只需确保有足够的 RAM 用于所有这些键。SSD也强烈推荐。100 MB 既简单又快速。最初发布的 100 GB 会很多,但也是可行的。即使原始读/写 100 GB 也需要相当长的时间。