Chinaunix首页 | 论坛 | 博客
  • 博客访问: 364143
  • 博文数量: 78
  • 博客积分: 2222
  • 博客等级: 大尉
  • 技术积分: 745
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-24 10:48
文章分类

全部博文(78)

文章存档

2012年(7)

2011年(33)

2010年(38)

分类: 系统运维

2010-10-14 16:34:13

这是 url 队列一环的要害之一。

爬虫必然要考虑的问题之一就是url的去重问题,很容易想到的方法是 hashmap/hashtable(md5(url)):程序退出时序列化并写入持久介质,启动时重新读入,反序列化载入内存。或者考虑如Berkeley DB等key-value结构的持久存储方案,可以屏蔽了很多如持久化、高并发、随机/顺序存储等操作。忽略md5的重复几率,在数据量不是太大的情况下不失为一个办法,md5之后Hash映射碰撞几率很小,或者足以容忍,而且实现简单,也比较准确。但hashtable的内存利用率常常只有50%(googleblog),当需要消重的数据量数以亿计的时候这个问题就异常突出了,幸好早在1970年巴顿.布隆就给我们准备好了解决方案:布隆过滤器(Bloom Filter)。

布隆过滤器(Bloom Filter)实际上是一个很长的二进制向量和一系列随机映射函数,可以用于检索一个元素是否在一个集合中。由一组hash函数和一个给定长度的位向量组成,位向量的长度、hash函数的数量依赖于数据量和我们能容忍的假命中率高低;其优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。使用高质量的hash函数相当重要,它保证输出等分在所有可能值上,hash函数里的“热点”会大幅增加假命中率。

package isabella;

import java.util.BitSet;

public class BloomFilter {
    private int setLen = 2 << 32 - 1;
    private BitSet bits;

    public BloomFilter() {
        bits = new BitSet(setLen);
    }

    /**
     * @param url
     * @return contains or not
     */

    public boolean contains(String url) {
        if (url == null) {
            return true;
        }

        int pos0 = hash1(url);
        int pos1 = hash1(url);
        int pos2 = hash2(url);
        int pos3 = hash3(url);
        if (bits.get(pos0) && bits.get(pos1) && bits.get(pos2) && bits.get(pos3)) {
            return true;
        }
        return false;
    }

    public void put(String url) {
        if (url == null) {
            return;
        }
        int pos0 = hash0(url);
        int pos1 = hash1(url);
        int pos2 = hash2(url);
        int pos3 = hash3(url);

        bits.set(pos0);
        bits.set(pos1);
        bits.set(pos2);
        bits.set(pos3);
    }

    /**
     * K Hash
     * @param line
     * @return
     */

    private int hash3(String line) {
        int h = 0;
        for (int i = 0; i < line.length(); i++) {
            h = 37 * h + line.charAt(i);
        }
        return check(h);
    }

    private int hash2(String line) {
        int h = 0;
        for (int i = 0; i < line.length(); i++) {
            h = 33 * h + line.charAt(i);
        }
        return check(h);
    }

    private int hash1(String line) {
        int h = 0;
        for (int i = 0; i < line.length(); i++) {
            h = 31 * h + line.charAt(i);
        }
        return check(h);
    }

    private int hash0(String line) {
        int h = 0;
        for (int i = 0; i < line.length(); i++) {
            h = 30 * h + line.charAt(i);
        }
        return check(h);
    }

    private int check(int h) {
        return setLen & h;
    }
}


阅读(1277) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~