Duplicate elimination and SimHash
Duplicate URL elimination, Bloom filters, and 64-bit SimHash fingerprints compared by Hamming distance.
What you will learn
- Tell duplicate URLs from duplicate content.
- Normalize URLs, and size a Bloom filter for a billion of them.
- Detect near-duplicate pages with SimHash, and find fingerprints within bits quickly.
Two kinds of duplicate
- Duplicate URLs: the same link found on many pages. Download it once.
- Duplicate content: different URLs serving the same or nearly the same page, such as mirrors, syndicated articles, or pages that differ only in an advert. Manku, Jain and Das Sarma open their paper with "Near-duplicate web documents are abundant", and note that such differences "are irrelevant for web search".
The primer covers both. Remove duplicate URLs, with sort | unique for small lists or MapReduce at 1 billion links. Detect duplicate content by comparing page signatures for similarity, for example with the Jaccard index or cosine similarity.
Duplicate URLs
Mercator's duplicate URL eliminator checks each extracted URL against every URL "in the URL frontier or [that] has already been downloaded". Only new ones go to the frontier. It stores fixed-size fingerprints of URLs, not the URLs themselves, mostly on disk with recent entries buffered in memory.
Normalize before comparing, so trivially different spellings count as one URL: lower-case the scheme and host, drop default ports and #fragments, and resolve relative links. Stripping known tracking parameters is a policy choice, because some query parameters do change the page.
A Bloom filter (chapter 6) can answer "seen before?" in memory. Its size for items and false-positive rate is:
For the primer's links and : bits, about 1.8 GB, with hash functions. A Bloom filter never says "new" for a URL it has seen. But it wrongly says "seen" for about 0.1% of new URLs, and those pages are skipped. Whether that is acceptable is a product decision.
Near-duplicate content: SimHash
A cryptographic hash such as MD5 or SHA-1 cannot detect near duplicates. In Manku et al.'s words, two documents that differ in a single byte hash "into two completely different hash-values".
SimHash (Charikar) is built so that similar documents get similar fingerprints. As Manku et al. apply it to web pages:
- Turn the page into weighted features (tokens after case folding, stop-word removal, stemming and so on).
- Keep a vector of counters, all zero.
- Hash each feature to bits. Where a bit is 1, add the feature's weight to that counter; where it is 0, subtract it.
- The fingerprint's bit is 1 if , otherwise 0.
Near-duplicates' fingerprints differ in only a few bit positions. Manku et al. "experimentally validate that for a repository of 8B webpages, 64-bit simhash fingerprints and are reasonable." Here is the largest Hamming distance still treated as a near-duplicate.
Finding fingerprints within bits
Comparing a new fingerprint with billions of stored ones one by one is too slow. Manku et al. use the pigeonhole principle. Split the 64 bits into blocks: if two fingerprints differ in at most 3 bits, then with 4 blocks of 16 bits, at least one block is identical. Keep one table per block, sorted by that block, and look the new fingerprint up in each.
Each lookup still returns a group of candidates to check. A table keyed on bits over fingerprints returns about per probe. With the paper's (8 billion) fingerprints, 4 tables keyed on 16 bits return about 256K candidates per probe. 16 tables keyed on 28 bits return about 64. More tables mean less work per query but more memory, and the paper works through this trade-off. For the primer's (1 billion) pages, 4 tables keyed on 16 bits still return about 16,000 candidates per table.
64-bit SimHash
Words are the features, weighted by count. Similar pages get fingerprints that differ in few bits; Manku et al. found k = 3 bits reasonable for 8 billion pages.
- Raw HTML Stream: 4 × App Compute Server
- DOM Text Tokenizer & Weighter: 4 × App Compute Server
- 64-bit SimHash Calculator: 4 × App Compute Server
- Redis 64-bit Fingerprint Table: 3 × Redis In-Memory Cache
- S3 Unique Documents: 2 × Object Storage (S3)
- Raw HTML Stream sends requests to DOM Text Tokenizer & Weighter
- DOM Text Tokenizer & Weighter sends requests to 64-bit SimHash Calculator
- 64-bit SimHash Calculator sends requests to Redis 64-bit Fingerprint Table
- 64-bit SimHash Calculator sends requests to S3 Unique Documents
Key takeaways
- Normalize URLs before comparing them, then check each against every URL already queued or downloaded.
- A Bloom filter for URLs at a 0.1% false-positive rate takes about 1.8 GB; it never misses a seen URL but occasionally skips a new one.
- A cryptographic hash changes completely for a one-byte difference; SimHash gives similar pages fingerprints a few bits apart (64 bits, for 8 billion pages).
- Split the fingerprint into blocks: two within 3 bits must agree exactly on at least one block, so a few tables keyed on blocks find the candidates.
Sources
- System Design Primer: Design a web crawler, handling duplicates
- Manku, Jain and Das Sarma, “Detecting Near-Duplicates for Web Crawling”, WWW 2007
- Najork and Heydon, “High-Performance Web Crawling”, section 3.2: duplicate URL eliminators
Adapted from The System Design Primer by Donne Martin, licensed under CC BY 4.0. Changes were made.