在Java中搜索对象的压缩通常不是很好...不太好。
首先,您需要了解Java对象有许多不需要的附加信息。如果你拥有数百万个物体,你就会有数百万次的物质损失。
作为一个例子,让我们一个双链表。每个元素都有一个前一个和一个下一个指针+为这种交互存储一个long值(timestamp)+字节,为用户id存储两个整数。由于我们使用指针压缩,所以我们是6Bytes * 2 + 8 + 4 * 2 = 28Bytes。 Java为填充添加了8字节+ 12字节。这使得每个元素有48Bytes。
现在我们创建千万列出了20元每个(过去三年(我们要找出模式)的时间系列的用户点击事件)。
所以我们有200万* 48字节的元素= 10GB的内存(好的不多)。
好了旁边的垃圾收集杀死我们和JDK skyrocks内的开销,我们最终与10GB的内存。
现在让我们使用我们自己的内存/对象存储。我们将它作为列明智的数据表存储,其中每个对象实际上是单个行。所以我们在时间戳,上一个,下一个,userIdA和userIdB集合中有2亿行。
一个和下一个正在指向行ID,并成为4个字节(或5bytes如果我们超过40亿个条目(不太可能))。所以我们有8 + 4 + 4 + 4 + 4 => 24 * 200兆欧= 4.8GB +没有GC问题。
由于时间戳列以最小最大时间存储时间戳,我们的时间戳全部在三年之内,所以我们只需要5个字节来存储每个时间戳。由于指针现在存储为相对(+和 - ),并且由于点击系列适时关系密切,因此我们只需要前后两位用户的平均值为2字节,而用户ID则使用字典,因为点击系列对于大约500,000个用户我们只需要三个字节。所以我们现在有5 + 2 + 2 + 3 + 3 => 15 * 200Mio => 3GB + 4 * 500k * 4 = 8MB = 3GB + 8MB的字典。听起来不同于10GB的权利?
但是我们还没有完成。由于我们现在没有对象,只有行和数据,所以我们将每个系列存储为一个表行,并使用特殊的列作为数组的集合,这些列实际上存储了5个值和一个指向下5个值+指针前一个的指针。我们每个列表都有20 *(5 + 3 + 3)+ 4 * 6(允许添加部分填充元素的部分开销)=> 20(因为我们有开销),所以我们有10个每个列表20个列表* 11 + 5 * 6 => 250 * 10Mio => 2,5GB +我们可以比步行元素更快地访问阵列。
但是,嘿,它还没有结束......时间戳现在相对存储,只需要每个条目3个字节+第一个条目中的5个。 - >所以我们可以节省更多20 * 9 + 2 + 5 * 6 => 212 * 10Mio => 2,12 GB。现在使用gzip将它全部存储到内存中,因为我们可以将所有的lineary存储起来,所以它首先存储数组的长度,所有的时间戳,所有的用户标识都非常高,因此可以压缩比特中的图案。由于我们使用的是字典,我们只需根据每个用户id的可行性将其排序即可成为系列的一部分。
由于所有东西都是表格,所以你可以以几乎读取的速度反序列化所有东西,所以现代固态硬盘上的1GB成本需要2秒的时间才能加载。尝试使用序列化/反序列化,你可以听到内心的用户哭泣。
因此,在您压缩序列化数据之前,将其存储在表中,检查每个列/属性是否可以逻辑压缩。最后玩得开心。
还记得今天1TB(ECC)的价格是10k。没什么。和1TB SSD 340欧元。所以不要浪费你的时间在这个问题上,除非你真的必须这样做。