假设我有一个如下数据集:
Screen ID User ID
1 24
2 50
2 80
3 23
5 50
3 60
6 64
. .
. .
. .
400,000 200,000
我希望跟踪每个用户访问过的屏幕。我的第一种方法是创建一个哈希映射,其中键将是用户ID,值将是屏幕ID。但是,使用Java时出现OutofMemory错误。是否有可以处理这一数据量的高效数据结构?将有大约3,000,000个密钥,每个密钥大约有1000个值。 Spark(Python)会成为可行的方法吗?原始数据集有大约300,000,000行和2列。
答案 0 :(得分:3)
为什么要将如此大的数据存储在内存中最好将其存储在数据库中并仅使用所需的数据。使用任何语言的任何数据结构都会消耗几乎相等的内存。
答案 1 :(得分:1)
HashMap不适用于您所描述的内容,因为密钥必须是唯一的。您的方案是重复键。
如果您希望提高内存效率并且无法访问关系数据库或外部文件,请考虑使用数组进行设计。
数组的优点是能够存储使用比对象少的数据的基元。集合将始终隐式地将原语转换为存储时的包装类型。
您可以让您的数组索引代表屏幕ID,并且索引中存储的值可以是存储相关用户ID的另一个数组或集合。
答案 2 :(得分:1)
您使用的是哪种数据类型?让我们说你正在使用..
Map<Integer,Integer>
。然后每个条目需要8个字节(32位)或16个字节(64位)..让我们计算你的内存消耗:
8 * 400000 = 3200000字节/ 1024 = 3125千字节/ 1024 = 3.05MB
对于64位数据类型(如Long)或6.1MB
说它很短.. 3.05 MB或6 MB对你的硬件来说没什么。
即使我们计算了300万个条目,我们也会得到22 MB的内存使用量(如果是整数条目集)。我不认为OutofMemory异常是由数据大小引起的。检查您的数据类型或 切换到MapDB以获得快速原型(支持堆外内存,见下文)。
是处理3 000 000 000条记录越来越严重。我们最终的内存使用量为22.8 gig。在这种情况下,你应该考虑 一个可以有效处理这一数据量的数据存储。我不认为Java Map(或另一种语言的矢量)是这种数据量的一个很好的用例 (正如Brain所写,有了这么多数据,你必须增加JVM堆空间或使用MapDB)。还要考虑一下你的部署;你的产品需要22 gig的内存 意味着高硬件成本。然后问题成本与内存中的表现必须平衡......我会选择以下其中一种方法:
使用上述解决方案之一,您还可以持久化并查询数据(无需编写索引,B树和内容)。这就是你想要做的,我想, 处理和操作您的数据。最后,只有测试可以显示哪种技术可以满足您的需求。
OutofMemory异常与java或python无关。您的用例可以在java中实现,没有任何问题。
答案 3 :(得分:0)
只看数据结构。你有一个二维矩阵,由user-id和screen-id索引,包含一个布尔值,无论是否被该用户访问:visited[screen-id, user-id]
在每个用户几乎访问每个屏幕的情况下,最佳表示将是一组比特。这意味着您需要400k x 200k
位,大约为10G字节。在Java中,我将使用BitSet并线性化访问,例如BitSet.get(screen-id + 400000 * user-id)
如果每个用户只访问几个屏幕,那么位集中会有很多重复的错误值。这就是所谓的sparse matrix。实际上,这是计算机科学中一个研究得很好的问题,你会发现很多不同的解决方案。
这回答了您原来的问题,但可能无法解决您的问题。在评论中,您声明要查找访问特定屏幕的用户。现在,这是一个不同的问题领域,我们正在从高效的数据表示和存储转向高效的数据访问。
查找访问一组屏幕的用户,与查找包含一组单词的文档基本上是完全相同的问题。这是一个基本的信息检索问题。对于此问题,您需要一个所谓的反向索引数据结构。一个流行的库是Apache Lucene。
您可以自己阅读访问并构建数据结构。本质上,它是一个地图,由screen-id寻址,返回一组受影响的用户,即:Map<Integer, Set<Integer>>
。对于整数集,第一选择是HashSet,它的内存效率不高。我建议使用针对整数值的高性能集库,例如IntOpenHashSet。但是,这可能不适合内存,但是,如果使用Spark,您可以在切片中拆分处理并稍后加入处理结果。