再谈大量数据去重:字典树(Trie)和外排序(External Sorting)

面对数亿级别的URL去重问题,本文探讨了在内存有限的情况下,如何利用位图、布隆过滤器、字典树、空间压缩编码以及外部排序算法(如外排序、二路归并、多路归并)来高效解决。重点介绍了字典树在结构化数据处理中的优势以及外部排序在外存数据操作中的应用,展示了如何在资源受限的环境下,通过算法优化平衡时间和空间效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

时隔多年又再次见到这个问题,不得不说是一个经久不衰的问题。与友人讨论时,感叹这个问题反复在不同场合被不同人提起,但每次探索答案时,又总不能获得一个“标准答案”。最早我第一次遇到这个问题时,我给出的令我自己可以满意的答案是:大量数据去重:Bitmap和布隆过滤器(Bloom Filter)。在一定程度上,它确实大大缓解了“有限空间”对hash实现的限制。但是回头一看,布隆过滤器作为工程实现(带有一定错误率),它不应该是一道算法题的解决思路——即最优解。所以,今次再次谈及此题,又有了一些别的思考。时移事易,今日的题设有了一些变化:

有A、B两份大文件,按行分隔,放置了数以亿记的URL。请你给出一个算法,找出所有两份文件同时出现的URL。内存空间有限,你只能适用512MB内存。

换汤不换药,单纯的逐行读取文件并不需要一次性加载整份文件。因此我们以B文件为逐行读取的对象,那么每一个URL,我们需要判断它是否存在于A中,这个操作越快越好。如果空间不限,我们可以把A加载到内存,创建一个哈希表——单纯的位图并不适用与这种场景,因为URL即便映射到某个位上,为了解决碰撞问题,我们仍然需要存储URL本身,以便碰撞发生时进行开放地址(Open addressing)或链地址(Separate chaining)1。这种做法,平均上仍然可以在O(1)时间内查到一个URL存在与否。但是,空间上所有字符都需要进入内存,空间复杂度为 O ( N L ) O(NL) O(NL) N N N为A文件的行数, L L L为URL的平均长度。RFC72302中提及实践中建议客户端和服务器支持8000字节的URL,而大多数浏览器支持2000字节的URL3
URL Syntax

空间压缩

和位图类似,遇到这类空间有限的问题,我们总是会先想到如何压缩空间到极致、而不是优先考虑牺牲一部分东西——布隆过滤器牺牲了正确率,以及我们即将提到的外排序牺牲了时间。位图相当于为每个整型值找了一个单独的比特来表示其存在性,而对于URL,我们能做些什么呢?

编码

如上图所示,URL是一种高度结构化的字符串4,从某种角度上可以进行编码压缩。

  • 对于scheme部分,有限的几种协议如http、https、ftp等,可以用一个字节进行表示;
  • 对于host部分,以ip形式出现的host实际是“点分十进制”表示的四个字节,“127.0.0.1”可以直接被压缩成十六进制数0x0F000001;
  • 对于port部分,可能的端口号为0~65535,可以用两个字节表示;
  • 从定长码的角度,URL可能字符集为84个字符5
    {   c h   ∣   c h   i n   A   t o   Z , a   t o   z , 0   t o   9   a n d   − .   : / ? # [ ] @ ! $ & ′ ( ) ∗ + , ; = } \{\ ch\ |\ ch\ in\ A\ to\ Z,a\ to\ z,0\ to\ 9\ and\ -._~:/?\#[]@!\$\&'()*+,;=\} {  ch  ch in A to Z,a to z,0 to 9 and . :/?#[]@!$&()+,;=}
    严格上来说,并不需要一个字节来表示一个字符,可以将每个字符映射到一个7比特码本(码空间大小为128),这样可以将空间消耗缩小为原来的7/8;
  • 从变长码的角度,URL也是一种类字词拼接的文本形式,霍夫曼编码可以对URL进行无损压缩。

压缩之后的字节流可以成倍缩短URL的长度,但是仍然需要成百字节来构成一个URL。如果我们用一个很不保守的长度50字节来估算一个URL,1亿条URL(假设各不相同、没有重复)需要空间:
50 B y t e s ∗ 100000000 ≈ 5 ∗ 2 30 B y t e s = 5 G B 50 Bytes * 100000000 ≈ 5 * 2^{30} Bytes = 5 GB 50Bytes1000000005230Bytes=5GB
这看起来并不是很大,但是仍然没法在512MB如此严酷的内存条件下完成任务。

字典树(Trie)

字典树(Trie)也叫前缀树,它是我这次看到这题时,第一个想到的解决办法。首先,URL这种高度结构化的、自带前缀叠合属性的文本串,用字典树这种前缀结构时再合适不过了。本身来说,每个网站在有限的URI内,实际上是把自身有限的资源按目录结构有序放置在一个多叉树下,就如文件系统一样,所以经常会出现前缀相同的资源:如www.baidu.com/abc/1.txt和www.baidu.com/abc/2.txt,他们实际的差异就是最后的资源文件名,前缀则完全相同。当然,这个假设是建立在输入URL都是有效的URL的前提下——如来源于爬虫爬取等,当然这也是最常见的实际应用。如果是随机暴力拼装的URL,则不会具备任何前缀特性,访问之后对端服务器大概率会丢给你一个404。假定,A文件有大量来自于http://www.google.com/mail、http://www.google.com/document以及http://www.facebook.com/的链接,以字典树的形式会被压缩为:
Compressed Trie Format
Typical Trie Format
这种形式的压缩就不再局限于URL本身了,而是一种

先让我们看看原题的三个任务介绍: Task 1: Sorting the LINEITEM table by External Merge Sort Consider two cases: 1) using 5 buffer pages in memory for the external merge sort; 2) using 129 buffer pages in memory for the external merge sort. In the implementation, each buffer page occupies 8K bytes. The ORDERKEY attribute of the LINEITEM table is assumed to be the sort key in the external merge sort. Please report the number of passes and also the running time of the external merge sort in each case. Task 2: Organizing the sorted LINEITEM table into disk pages Please use the page format for storing variable-length records to organize the LINEITEM table sorted in Task 1. In the implementation, each disk page occupies 1K bytes. For each page we maintain a directory of slots, with a pair per slot. Both “record offset” and “record length” are 4 bytes wide. Task 3: Building a B-Tree over LINEITEM disk pages by Bulk Loading. Please use bulk loading to build a B-Tree over the disk pages of the LINEITEM table, which are generated in Task 2. The ORDERKEY attribute of the LINEITEM table is used as the (search) key for building the B-Tree. In the B-Tree, each internal node corresponds to a page of 1K bytes, both key and pointer are 4 bytes wide. Please report the running time of the bulk loading. A query interface is required for checking the B-Tree. For a reasonable ORDERKEY value, please print out all the pages visited along the path to find the corresponding record. Please also report the running time of the search.
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值