Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/354.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用python维护大型列表_Python_Database_Zodb - Fatal编程技术网

用python维护大型列表

用python维护大型列表,python,database,zodb,Python,Database,Zodb,我需要维护一个python可pickle对象的大列表。列表太大,无法全部存储在RAM中,因此需要一些数据库\分页机制。我需要该机制将支持列表中靠近(附近)区域的快速访问 列表应该实现所有python列表功能,但大多数时候我会按顺序工作:扫描列表中的某个范围,并在扫描时决定是否要在扫描点插入\pop一些节点 该列表可能非常大(2-3 GB),不应一次全部包含在RAM中。 节点很小(100-200字节),但可以包含各种类型的数据 一个很好的解决方案是使用BTree,其中只有最后访问的bucket加载

我需要维护一个python可pickle对象的大列表。列表太大,无法全部存储在RAM中,因此需要一些数据库\分页机制。我需要该机制将支持列表中靠近(附近)区域的快速访问

列表应该实现所有python列表功能,但大多数时候我会按顺序工作:扫描列表中的某个范围,并在扫描时决定是否要在扫描点插入\pop一些节点

该列表可能非常大(2-3 GB),不应一次全部包含在RAM中。 节点很小(100-200字节),但可以包含各种类型的数据

一个很好的解决方案是使用BTree,其中只有最后访问的bucket加载到RAM中

使用SQL表并不好,因为我需要实现一个复杂的索引键机制。 我的数据不是一个表,它是一个简单的python列表,具有在特定索引中添加元素和从特定位置弹出元素的特性

我尝试了和,它实现了一个基于BTree的列表,可以存储在ZODB数据库文件中,但我不知道如何配置它,以便在合理的时间内运行上述功能。 我不需要所有多线程\事务处理功能。除了我的单线程程序之外,没有其他人会接触数据库文件

有谁能解释一下如何配置ZODB\zc.blist,使上述功能快速运行,或者向我展示一个不同的大列表实现

我尝试了一些快速而肮脏的代码:

import time
import random

NODE_JUMP = 50000
NODE_ACCESS = 10000

print 'STARTING'


random_bytes = open('/dev/urandom', 'rb')

my_list = list()

nodes_no = 0

while True:
    nodes_no += NODE_JUMP
    start = time.time()
    my_list.extend(random_bytes.read(100) for i in xrange(NODE_JUMP))
    print 'extending to %s nodes took %.2f seconds' % (nodes_no, time.time() - start)

    section_start = random.randint(0, nodes_no -NODE_ACCESS -1)
    start = time.time()
    for index in xrange(section_start, section_start + NODE_ACCESS):
        # rotate the string
        my_list[index] = my_list[index][1:] + my_list[index][0]

    print 'access to %s nodes took %.2f seconds' % (NODE_ACCESS, time.time() - start,)
打印以以下内容结束:

extending to 5000000 nodes took 3.49 seconds access to 10000 nodes took 0.02 seconds extending to 5050000 nodes took 3.98 seconds access to 10000 nodes took 0.01 seconds extending to 5100000 nodes took 2.54 seconds access to 10000 nodes took 0.01 seconds extending to 5150000 nodes took 2.19 seconds access to 10000 nodes took 0.11 seconds extending to 5200000 nodes took 2.49 seconds access to 10000 nodes took 0.01 seconds extending to 5250000 nodes took 3.13 seconds access to 10000 nodes took 0.05 seconds Killed (not by me) 扩展到5000000个节点需要3.49秒 访问10000个节点需要0.02秒 扩展到5050000个节点需要3.98秒 访问10000个节点需要0.01秒 扩展到5100000个节点需要2.54秒 访问10000个节点需要0.01秒 扩展到5150000个节点需要2.19秒 访问10000个节点需要0.11秒 扩展到520万个节点需要2.49秒 访问10000个节点需要0.01秒 扩展到5250000个节点需要3.13秒 访问10000个节点需要0.05秒 被杀(不是被我杀的)
我认为ZODB是可以使用的工具。它将存储大量任意项目,它处理内存问题

这里是一个工作示例,在本例中,我包括了相互引用的对象以及按编号存储在BTree中的对象

import random
from collections import deque

import ZODB
from ZODB.FileStorage import FileStorage
from ZODB.DB import DB
import transaction
import persistent
import BTrees

def random_string(n=100):
    return ''.join([chr(random.randint(0,95)+32) for i in xrange(n)]) 


class Node(persistent.Persistent):
   def __init__(self, value=None):
       if not value:
           self.value =  random_string()

   def setNeighbors(self, refs):
       self.p1 = refs[0]
       self.p2 = refs[1]
       self.p3 = refs[2]
       self.p4 = refs[3]


def getTree():
    storage = FileStorage('c:\\test.zdb')
    db = DB(storage)
    connection = db.open()
    root = connection.root()
    if root.has_key('tree'):
        tree = root['tree']
    else:
        tree = BTrees.OOBTree.OOBTree()
        root['tree'] = tree
        transaction.commit()
    return tree


def fillDB():
    tree = getTree()

    # start with some initial objects.
    nodes = deque([Node(), Node(), Node(), Node()])
    node = Node()

    for n in xrange(20000):
        tree[n] = node           # store the node based on a numeric ID
        node.setNeighbors(nodes) # Make the node refer to more nodes.
        node = nodes.popleft()   # maintain out list of 4 upcoming nodes.
        nodes.append(Node())
        if n % 1000 == 0:
            transaction.commit() # Must commit for data to make it to disk.
            print n
    transaction.commit()
    return tree
此时,
变量基本上像字典一样工作,可以通过键访问。您还可以使用树键(最小值、最大值)获取某个范围内的键,如所述

您可以将10个列表存储在ZODB返回的
根对象的不同键下。
root
对象充当ZODB对象存储的“网关”

得益于ZODB,您还可以使用对象间引用以及Btree索引。例如:

tree = getTree()

node1 = tree[1]
print node1.p1.p1.p1.p1.p1.p1.p1.p1.p1.value

用东京橱柜怎么样?非常快速和简单,就像列表一样,但它是为你想要的东西而构建的。

毕竟,使用zc.blist可以带来好的结果,并且在创建DB时设置“cache_size”选项可以控制将保留在RAM中的数据的大小。如果您不经常执行“transaction.commit”,使用的RAM的大小可能会变大。通过定义较大的缓存大小并经常执行transaction.commit,blist最后访问的存储桶将留在RAM中,让您能够快速访问它们,并且使用的RAM数量不会增长太多

虽然打包很贵,但是如果你有一个大硬盘,你也不必经常打包

这里有一些代码供您自己尝试。在后台运行“top”并更改cache_大小,以查看它如何影响已用RAM的数量

import time
import os
import glob
from ZODB import DB
from ZODB.FileStorage import FileStorage
import transaction
from zc.blist import BList

print('STARTING')

random = open('/dev/urandom', 'rb')


def test_list(my_list, loops = 1000, element_size = 100):
    print('testing list')
    start = time.time()
    for loop in xrange(loops):
        my_list.append(random.read(element_size))
    print('appending %s elements took %.4f seconds' % (loops, time.time() - start))

    start = time.time()
    length = len(my_list)
    print('length calculated in %.4f seconds' % (time.time() - start,))

    start = time.time()
    for loop in xrange(loops):
        my_list.insert(length / 2, random.read(element_size))
    print('inserting %s elements took %.4f seconds' % (loops, time.time() - start))

    start = time.time()
    for loop in xrange(loops):
        my_list[loop] = my_list[loop][1:] + my_list[loop][0]
    print('modifying %s elements took %.4f seconds' % (loops, time.time() - start))

    start = time.time()
    for loop in xrange(loops):
        del my_list[0]
    print('removing %s elements took %.4f seconds' % (loops, time.time() - start))

    start = time.time()
    transaction.commit()
    print('committing all above took %.4f seconds' % (time.time() - start,))

    del my_list[:loops]
    transaction.commit()

    start = time.time()
    pack()
    print('packing after removing %s elements took %.4f seconds' % (loops, time.time() - start))

for filename in glob.glob('database.db*'):    
    try:
        os.unlink(filename)
    except OSError:
        pass

db = DB(FileStorage('database.db'),
        cache_size = 2000)

def pack():
    db.pack()

root = db.open().root()

root['my_list'] = BList()

print('inserting initial data to blist')

for loop in xrange(10):
    root['my_list'].extend(random.read(100) for x in xrange(100000))
    transaction.commit()

transaction.commit()

test_list(root['my_list'])

400MB有多大?你的计算机有多少内存?假设它可以达到2GB。我不想让它浪费所有的内存资源,我第一次尝试将4000000个100字节的对象放入一个字典,得到了一个消耗900MB的python进程。花费的时间只有几十秒,访问字典的时间基本上是即时的。我的程序中需要大约10个这样的列表。我承认这是一个非常低级的描述。我会把问题弄清楚的。我这里没有任何示例代码。我现在试着写点东西,这完全不是我的意思。每个节点没有4个指针。列表外有4个“访问节点”(总共4个,而不是每个节点4个)。对我需要什么的新解释更好。有没有跳过“提交”的方法?您知道ZODB树索引节点(除树按钮处的bucket之外的任何树节点)是否总是存储在RAM中?如果他们这样做了,那么我可以使用上面的“deque”技巧来确保快速顺序访问。例如,当我想要节点[314]时,我可以将节点[315:(315+100)/100*100]添加到deque。然后,对节点[315]、节点[316]、节点[317]的所有访问。。。将非常快。但是当您插入315和316之间的节点时,索引将是什么?315.5? (也许可以,但如果有足够的项目,您将遇到FP精度限制)。很容易得到一个双链接的节点列表,这些节点只是相互引用、遍历和插入/删除,但是有些列表操作会花费很多钱(比如len(),除非更新count属性)。。。或者通过索引进行随机访问。可能有:我认为他会遇到与SQL相同的“索引键”问题。