How to compress small strings
我有一个包含大量URL的sqlite数据库,它占用了大量的磁盘空间,访问它会导致许多磁盘查找,而且速度很慢。URL路径的平均长度是97字节(主机名重复很多,所以我将它们移到了一个外键表中)。有没有压缩它们的好方法?大多数压缩算法都能很好地处理大文档,而不是平均小于100字节的"文档",但即使减少20%也非常有用。有什么有效的压缩算法吗?不需要任何标准。
使用压缩算法,但使用共享字典。
我以前做过类似的事情,在这里我使用了lzc/lzw算法,正如unix compress命令所使用的那样。
使用短字符串进行良好压缩的诀窍是使用由正在压缩的URL的标准示例组成的字典。
你应该很容易得到20%。
编辑:LZC是LZW的变体。您只需要lzw,因为您只需要一个静态字典。LZC增加了对字典/表满后重置的支持。
我用下面的策略尝试了这个方法。它使用的是一个共享字典,但是围绕着python的zlib不允许您访问字典本身的方式工作。
首先,通过运行一组训练字符串初始化预先训练的压缩器和解压器。丢弃输出字符串。
然后,使用经过训练的压缩器的副本来压缩每个小字符串,并使用解压器的副本来解压它们。
这里是我的python代码(为丑陋的测试道歉):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import zlib class Trained_short_string_compressor(object): def __init__(self, training_set, bits = -zlib.MAX_WBITS, compression = zlib.Z_DEFAULT_COMPRESSION, scheme = zlib.DEFLATED): # Use a negative number of bits, so the checksum is not included. compressor = zlib.compressobj(compression,scheme,bits) decompressor = zlib.decompressobj(bits) junk_offset = 0 for line in training_set: junk_offset += len(line) # run the training line through the compressor and decompressor junk_offset -= len(decompressor.decompress(compressor.compress(line))) # use Z_SYNC_FLUSH. A full flush seems to detrain the compressor, and # not flushing wastes space. junk_offset -= len(decompressor.decompress(compressor.flush(zlib.Z_SYNC_FLUSH))) self.junk_offset = junk_offset self.compressor = compressor self.decompressor = decompressor def compress(self,s): compressor = self.compressor.copy() return compressor.compress(s)+compressor.flush() def decompress(self,s): decompressor = self.decompressor.copy() return (decompressor.decompress(s)+decompressor.flush())[self.junk_offset:] |
通过测试,我在一组10000个短(50->300个字符)的Unicode字符串上节省了30%以上。压缩和解压缩也需要大约6秒(相比之下,使用简单的zlib压缩/解压缩大约需要2秒)。另一方面,简单的zlib压缩节省了大约5%,而不是30%。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | def test_compress_small_strings(): lines =[l for l in gzip.open(fname)] compressor=Trained_short_string_compressor(lines[:500]) import time t = time.time() s = 0.0 sc = 0. for i in range(10000): line = lines[1000+i] # use an offset, so you don't cheat and compress the training set cl = compressor.compress(line) ucl = compressor.decompress(cl) s += len(line) sc+=len(cl) assert line == ucl print 'compressed',i,'small strings in',time.time()-t,'with a ratio of',s0/s print 'now, compare it ot a naive compression ' t = time.time() for i in range(10000): line = lines[1000+i] cr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,zlib.DEFLATED,-zlib.MAX_WBITS) cl=cr.compress(line)+cr.flush() ucl = zlib.decompress(cl,-zlib.MAX_WBITS) sc += len(cl) assert line == ucl print 'naive zlib compressed',i,'small strings in',time.time()-t, 'with a ratio of ',sc/s |
注意,如果删除它,它就不会持久。如果你想要坚持,你就必须记住训练集。
你考虑过使用静态哈夫曼编码吗?
您可以使用现有的URL体,根据其频率计算URL中出现的所有字节的哈夫曼代码。然后您可以将这组代码存储一次,并使用它对所有URL进行编码。我觉得它应该能给你很好的压缩。
你的网址是什么格式?
如果任何一个URL共享一个或多个域,并且您拥有大约20亿个域名,那么您可以为域名创建一个池。如果您共享了相对路径,那么可以将它们汇集到一起。
对于数据库中的每个URL,将每个URL分为三部分。方案和域,例如http://my domain.com、realtive url/my/path/和rest mypage.html?id=4(如果有查询字符串参数)
这样,您应该将每个域和相对路径的开销减少到8字节左右。如果你想查找URL的一部分,那就应该更好、更快。
注意:只有"http"方案字符串本身是4个字节,您将在每个域条目上保存超出该值的任何内容。如果每个URL都以"http://www."开头,则每次保存":/www."7个字节。尝试一下如何拆分和构造URL,我敢打赌这是您将找到压缩的地方。现在,剩下的字符串不是公共域或相对路径,您可以用它做什么?
压缩URL一般用途的压缩,这种方法是从算术编码派生出来的。信息理论之父香农(Shannon)在60年代写了一篇关于这一点的论文。我从事压缩工作已有一段时间了,我一直发现,通用压缩永远无法解决实际问题。
您很幸运,因为URL具有结构,您应该使用该结构来更好地存储您的URL。
如果要应用压缩算法(我认为应该更改主题以反映URL压缩,因为它是特定于域的),则必须检查数据的熵。因为它会告诉你一些关于存储量的信息。URL是ASCII字符,任何不在ASCII范围0x20-0x7e内的字符都不会发生,并且会丢弃区分大小写的能力,您只剩下63个不同的状态。!"%&;'()*+,-/0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz包括空格。
您可以创建剩余字符的频率表并执行算术编码。您知道您最多需要6位,这意味着对于URL数据库中的每一个字符,您现在都在浪费2位,如果您只是将内容转移到适当的位置并使用查找表,您将得到20%的压缩。就像那样;)
因为数据非常具体,所以只使用通用方法进行压缩并不是一个好主意。最好是对信息进行结构化,并将其拆分为可以更有效地存储的数据片段。你对这个领域了解很多,用这些知识来压缩你的数据。
文摘:
大型搜索引擎和网络蜘蛛的一个常见问题是如何处理大量遇到的URL。传统的搜索引擎和网络蜘蛛使用硬盘来存储URL而不进行任何压缩。这会导致性能降低和空间需求增加。本文描述了一个简单的URL压缩算法,允许有效的压缩和解压缩。压缩算法基于增量编码方案,提取共享公共前缀的URL,并利用AVL树实现高效的搜索速度。实验结果表明,该方法可使尺寸减小50%。1。
--Kasom Koht Arsa计算机工程系。
资源
如何使用URL表?
您通常只进行"范围扫描"或唯一ID查找吗?
如果你不做像
是97字节,还是97 8位ASCII字符,还是97 16位Unicode字符?
假设您的所有URL都是符合http://www.w3.org/addressing/url/url-spec.txt的合法URL,那么您应该只有ASCII字符。
如果仅存储每个字符的低位字节的97个16位Unicode字符将自动为您节省50%。
如果是97个8位字符,请注意您只需要7位。您可以简单地将7位一次传入您的位流,并将该位流存储到数据库中;使用一些旧的7位传输协议;或者使用您自己的特殊方法将每个8位字符的位存储在前7个字符的高位。