梳理Python中不同类的几种大数据结构;如何在减少内存使用的同时合并和存储我需要的数据?

Combing through several large data structures of different classes in Python; How can I combine and store data I need while reducing memory usage?

怎么回事

我在 Python 2.7.8 中每隔几分钟通过包 netsnmp 从几千个网络设备收集数据。我也在使用 fastsnmpy 以便我可以访问(更有效的)Net-SNMP 命令 snmpbulkwalk.

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


代码Activity

我的脚本从创建一个列表开始,我在其中打开一个文件并将目标设备的主机名附加为单独的值。这些通常包含 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
...

每个设备都有不同数量的响应。我无法循环期望每个设备都具有统一的任意数量的值以组合成一个字符串以写入 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])

目前我正在创建一个新字典,检查设备响应不为空,然后将响应值附加到将用于写入 CSV 文件的字符串。

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


罪魁祸首

感谢回答的人。对于那些在未来因遇到类似问题而偶然发现此线程的人: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))

使用分析器可能更容易找出内存的去向:

https://pypi.python.org/pypi/memory_profiler

此外,如果您已经在调整 fastsnmpy 类,您可以只更改实现来为您进行基于字典的结果合并,而不是让它先构建一个巨大的列表。

您要坚持多长时间?如果重复使用,结果列表将无限增长。

内存消耗是由于以未绑定的方式实例化多个工作程序造成的。

I've updated fastsnmpy (latest is version 1.2.1 ) and uploaded it to PyPi. You can do a search from PyPi for 'fastsnmpy', or grab it directly from my PyPi page here at FastSNMPy

刚刚更新完文档,并将它们发布到 fastSNMPy DOCS

的项目页面

我在这里所做的基本上是用多处理中的 process-pool 替换早期的 unbound-workers 模型。这可以作为参数传入,或者默认为 1.

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

您应该不会再看到内存问题了。如果你这样做,请在 github.

上 ping 我