如果数据不适合内存。 最简单的方法


土豚女性与幼崽。 照片: Scotto Bear ,CC BY-SA 2.0

您编写了一个用于数据处理的程序,它完美地通过了一个小文件的测试,但是在实际负载下崩溃了。

问题是内存不足。 如果您有16 GB的RAM,则将无法在其中下载100 GB的文件。 在某些时候,操作系统将耗尽内存,它将无法分配新的操作系统,并且程序将崩溃。

怎么办

好了,您可以部署一个大数据集群,只需:

  • 查找计算机集群。
  • 一周内设置好。
  • 了解新的API并重写您的代码。

它昂贵且令人不快。 幸运的是,通常没有必要。

我们需要一个简单的解决方案:以最小的设置和最大程度地利用已连接的库,在一台计算机上处​​理数据。 借助于简单的方法(有时称为“核外计算”),几乎总是可以做到这一点。

在本文中,我们讨论:

  • 为什么我们完全需要RAM。
  • 处理不适合内存的数据的最简单方法是花一点钱。
  • 用于处理大量数据的三种主要软件方法:压缩,分块和索引。

未来的文章将在实践中展示如何在特定的库(例如NumPy和Pandas)中应用这些方法。 但首先是理论。

为什么根本需要RAM?


在讨论解决方案之前,让我们弄清楚为什么这个问题根本存在。 您可以将数据写入随机存取存储器(RAM),也可以写入硬盘,那么为什么需要RAM? 光盘比较便宜,通常没有空间不足的问题,为什么不将自己局限于光盘的读写?

从理论上讲,这可能会起作用。 但是,即使是现代快速固态硬盘,其运行速度也远比RAM 得多:

  • 从SSD读取:〜16,000纳秒
  • 从RAM读取:〜100纳秒

为了进行快速计算,我们别无选择:必须将数据写入RAM,否则代码将减慢150倍的速度。

最简单的解决方案:更多RAM


解决RAM不足问题的最简单方法是花一些钱。 您可以购买功能强大的计算机,服务器或租用具有大量内存的虚拟机。 在2019年11月,快速搜索和非常简短的价格比较提供了以下选择:

  • 以1074美元的价格购买具有6核和64 GB RAM的Thinkpad M720 Tower
  • 以3.62美元/小时的价格在云中租用具有64个内核和432 GB RAM的虚拟机

这些只是快速搜索后的数字。 经过出色的研究,您一定会找到更好的交易。

在硬件上花一些钱以将数据装入RAM通常是最便宜的解决方案。 毕竟,我们的时间很昂贵。 但是有时候这还不够。

例如,如果您在一段时间内执行许多数据处理任务,那么云计算可能是很自然的解决方案,但代价也很昂贵。 在我们的一个项目中,这样的计算成本会消耗掉产品的所有预计收入,包括支付我的薪水所需的最重要的收入。

如果购买/租用大量RAM无法解决问题或无法解决问题,则下一步是优化应用程序本身,以减少内存消耗。

技术编号1.压缩


压缩允许您将相同的数据放入更少的内存中。 压缩有两种形式:

  • 无损 :压缩后,将保存与原始数据完全相同的信息。
  • 有损 :存储的数据丢失了一些细节,但是理想情况下,这不会对计算结果造成很大的影响。

只是为了清楚起见,当磁盘上压缩数据时,它与zip或gzip文件无关。 要处理ZIP文件中的数据,通常需要将其解压缩,然后将文件加载到内存中。 因此,这无济于事。

我们需要的是压缩内存中数据的表示形式。

假设您的数据仅存储两个可能的值,而没有其他任何值: "AVAILABLE""UNAVAILABLE" 。 您可以将它们另存为仅用一个字节编码的布尔值TrueFalse ,而不是将每个记录存储的字符串存储为10个字节以上。 您甚至可以将信息压缩到一位,从而将内存消耗再降低八倍。

方法2:拆分为块,一次加载一个块的数据


在不必同时将数据加载到内存中的情况下,分段很有用。 取而代之的是,我们可以将它们分段加载,一次处理一个片段(或者,正如我们在下一篇文章中讨论的,可以并行处理多个片段)。

假设您想在书中找到最大的单词。 您可以一次将所有数据加载到内存中:

 largest_word = "" for word in book.get_text().split(): if len(word) > len(largest_word): largest_word = word 

但是,如果该书无法容纳在内存中,则可以逐页加载:

 largest_word = "" for page in book.iterpages(): for word in page.get_text().split(): if len(word) > len(largest_word): largest_word = word 

由于一次只加载一本书的一页,因此大大减少了内存消耗。 在这种情况下,结果将是相同的答案。

技术之三:仅需要数据子集时建立索引


如果您只想使用数据的一个子集,并且要在不同的时间加载不同的子集,则索引编制很有用。

原则上,在这种情况下,您可以过滤掉必要的部分并丢弃不必要的部分。 但是过滤是缓慢的并且不是最佳的,因为在丢弃数据之前必须先将大量额外数据加载到内存中。

如果您只需要一部分数据,而不是碎片,那么最好使用索引-指示其实际位置的数据压缩

想象一下,您只想读一本关于土豚的文章的片段(土狼在本文开头是一只可爱的哺乳动物)。 如果依次检查所有页面,整本书将逐页装入,以寻找土豚-这将花费大量时间。

或者,您可以立即在书末打开字母索引-并找到“ aardvark”一词。 它指出在第7、19和120-123页提到了该词。 现在,您可以阅读这些页面,而且只能阅读它们,这要快得多。

这是一种有效的方法,因为索引比整本书要小得多,因此仅将索引加载到内存中以查找相关数据要容易得多。

最简单的索引方法


最简单,最常见的索引方法是在目录中命名文件:

 mydata/ 2019-Jan.csv 2019-Feb.csv 2019-Mar.csv 2019-Apr.csv ... 

如果您需要2019年3月的数据,则只需上传文件2019-Mar.csv无需下载2月,7月或任何其他月份的数据。

下一步:应用这些方法


购买RAM后,借助金钱最容易解决RAM不足的问题。 但是,如果这不可能或不够,则无论如何都要使用压缩,分段或索引。

各种软件包和工具中使用相同的方法 。 甚至高性能的大数据系统都建立在它们之上:例如,并行处理单个数据片段。

在以下文章中,我们将研究如何在特定的库和工具(包括NumPy和Pandas)中应用这些方法。

Source: https://habr.com/ru/post/zh-CN479282/


All Articles