梳理Python中几个不同类的大型数据结构;如何在减少内存使用的同时合并和存储所需的数据? 发生什么事?

梳理Python中几个不同类的大型数据结构;如何在减少内存使用的同时合并和存储所需的数据? 发生什么事?,python,memory,net-snmp,overhead-minimization,Python,Memory,Net Snmp,Overhead Minimization,在Python2.7.8中,我每隔几分钟通过包netsnmp从几千个网络设备收集数据。我还使用了fastsnmpy,这样我就可以访问(更高效的)netsnmp命令snmpbulkwalk 我试图减少脚本使用的内存量。我正在运行同一脚本的三个实例,该脚本在重新查询所有设备以获取所需数据之前会休眠两分钟。当我在bash中创建原始脚本时,如果同时激活,它们将使用少于500MB的内存。但是,当我将其转换为Python时,每个实例占用4GB的内存,这表明(对我来说)我的数据结构需要更有效地管理。即使在空闲

在Python2.7.8中,我每隔几分钟通过包
netsnmp
从几千个网络设备收集数据。我还使用了
fastsnmpy
,这样我就可以访问(更高效的)netsnmp命令
snmpbulkwalk

我试图减少脚本使用的内存量。我正在运行同一脚本的三个实例,该脚本在重新查询所有设备以获取所需数据之前会休眠两分钟。当我在
bash
中创建原始脚本时,如果同时激活,它们将使用少于500MB的内存。但是,当我将其转换为Python时,每个实例占用4GB的内存,这表明(对我来说)我的数据结构需要更有效地管理。即使在空闲时,它们也总共消耗4GB


代码活动 我的脚本首先创建一个列表,在其中打开一个文件并将目标设备的主机名附加为单独的值。这些名称通常包含80到1200个名称

expand = []
f = open(self.deviceList, 'r')
for line in f:
    line = line.strip()
    expand.append(line)
从那里我设置SNMP会话并执行请求

expandsession = SnmpSession ( timeout = 1000000 ,
    retries = 1,            # I slightly modified the original fastsnmpy
    verbose = debug,        # to reduce verbose messages and limit
    oidlist = var,          # the number of attempts to reach devices
    targets = expand,
    community = 'expand'
)
expandresults = expandsession.multiwalk(mode = 'bulkwalk')
由于这两个SNMP包的行为,设备响应被解析为列表并存储到一个巨大的数据结构中。比如说,

for output in expandresults:
    print ouput.hostname, output.iid, output.val
#
host1 1 1
host1 2 2
host1 3 3
host2 1 4
host2 2 5
host2 3 6
# Object 'output' itself cannot be printed directly; the value returned from this is obscure
...
host1,1,2,3
host2,4,5,6
host3,7,8,9,10,11,12
host4,13,14
host5,15,16,17,18
...
我必须迭代每个响应,组合相关数据,然后输出每个设备的完整响应。这有点难比如说

for output in expandresults:
    print ouput.hostname, output.iid, output.val
#
host1 1 1
host1 2 2
host1 3 3
host2 1 4
host2 2 5
host2 3 6
# Object 'output' itself cannot be printed directly; the value returned from this is obscure
...
host1,1,2,3
host2,4,5,6
host3,7,8,9,10,11,12
host4,13,14
host5,15,16,17,18
...
每个设备都有不同数量的响应。我不能期望每个设备都有统一的任意数量的值组合成一个字符串写入CSV


我是如何处理数据的 我相信这是我消耗大量内存的地方,但我无法解决如何在删除访问数据的同时简化过程

expandarrays = dict()
for output in expandresults:
    if output.val is not None:
        if output.hostname in expandarrays:
            expandarrays[output.hostname] += ',' + output.val
        else:
            expandarrays[output.hostname] = ',' + output.val

for key in expandarrays:
    self.WriteOut(key,expandarrays[key])
目前,我正在创建一个新的字典,检查设备响应是否为null,然后将响应值附加到一个字符串,该字符串将用于写入CSV文件

问题是我实际上是在克隆现有的字典,这意味着我使用了两倍的系统内存。我想在将
expandresults
中访问的值移动到
expandarrays
时删除这些值,这样我就不会占用太多内存。有没有一种有效的方法来做到这一点?还有没有更好的方法来降低代码的复杂性,以便更容易理解


罪魁祸首
感谢那些回答的人。对于那些将来由于遇到类似问题而偶然发现此线程的用户来说:
fastsnmpy
包是大量使用系统内存的罪魁祸首。
multiwalk()
函数为每个主机创建一个线程,但一次创建所有线程,而不是设置某种上限。因为我的脚本的每个实例将处理多达1200个设备,这意味着1200个线程在几秒钟内被实例化并排队。使用
bulkwalk()
函数速度较慢,但仍然足够快,可以满足我的需要。两者之间的区别是4GB与250MB(系统内存使用量)。

如果设备响应有序且按主机分组,则您不需要字典,只需要三个列表:

last_host = None
hosts = []                # the list of hosts
host_responses = []       # the list of responses for each host
responses = []
for output in expandresults:
    if output.val is not None:
        if output.hostname != last_host:    # new host
            if last_host:    # only append host_responses after a new host
                host_responses.append(responses)
            hosts.append(output.hostname)
            responses = [output.val]        # start the new list of responses
            last_host = output.hostname
        else:                               # same host, append the response
            responses.append(output.val)
host_responses.append(responses)

for host, responses in zip(hosts, host_responses):
    self.WriteOut(host, ','.join(responses))

通过使用探查器,您可能更容易确定内存的去向:

此外,如果您已经在调整fastsnmpy类,那么只需更改实现即可为您进行基于词典的结果合并,而不是让它先构建一个庞大的列表


你会坚持多久?如果重复使用,结果列表将无限增长

内存消耗是由于以未绑定的方式实例化了几个worker

我已经更新了fastsnmpy(最新版本是1.2.1)并上传到 皮皮。您可以从PyPi搜索“fastsnmpy”,也可以抓取它 直接从我的PyPi页面

刚刚完成文档更新,并将其发布到

我在这里所做的基本上是用多处理中的进程池来替换早期的未绑定工人模型。这可以作为参数传入,也可以默认为1

为了简单起见,现在只有两种方法。 snmpwalk(进程=n)和snmpbulkwalk(进程=n)


你不应该再看到内存问题了。如果需要,请在github上ping我。

我不知道我可以发布会话。Python文档和包含的测试文件没有提到从API中执行此操作的方法。我猜在将响应存储在results变量中后,我需要
del
每个会话?我的意思是,您是否为每次扫描创建新会话对象或重新启动脚本等。因为查看库代码,如果您继续使用相同的会话对象,我使用内存分析器并实现了Brent提供的循环。奇怪的是,我的数据使用的内存总量实际上不到33MB。这让我相信,要么
netsnmp
要么
fastsnmy
,要么两者都是罪魁祸首。NetSNMP C语言项目本身以前也有一些内存泄漏的错误报告。也许这就是我下一步需要深入研究的内容。(编辑)我在一个无限的全局循环中完成了所有这些,每次都创建了一个新会话。我现在设置在结果传递后删除会话,希望它释放列表并释放内存。循环始终依赖于数据的排序,这似乎不是一个好的假设,但我会让您解决这个问题。解决内存问题的最简单方法是每隔几分钟从头开始运行python脚本,因为实际上您似乎没有内存问题。这样你就不用担心泄露了。如果每次扫描都要创建一个新会话,gc会为您清理旧会话,您应该