上千万数据的IP取前100个出现次数最多的

一、思路:

首先上千万级别的数据量的文件,大概有几十G的大小(考虑到里面有重复出现的IP),内存肯定是不够用的。所以要拆分处理。大概分为如下三个步骤

1、大文件拆分小文件

所以要先把文件分为小文件,例如分为100个文件。然后要有顺序的把IP分类存入小文件,这里用到了hash,IP对100取余,并且保证相同IP在同一个文件中,分成了100份,先对数据经过hash计算,然后让相同的值放入100个文件中。

2、在拆分的小文件中统计

在小文件(一般小于1G)中,对每个相同的IP进行统计,在这100个文件中用HashMap结构进行统计每个IP出现的次数。具体细节,每次读取一个,如果该字串不在Table中,那么加入该值,并且将Value值设为1;如果该字串在Table中,那么将该字串的计数加一,最终我们在O(N)的时间复杂度内用Hash表完成了统计;

下面是来自其他地方的对IP进行hash计算的算法,有兴趣的可以研究一下。

    for ( ;; ) {
 
         for (i = 0; i < 3; i++) {  
            hash = (hash * 113 + iphp->addr[i]) % 6271; //iphp->addr[i]为ip的点分十进制法的第i段
        }
 
        p = hash % iphp->rrp.peers->number;
 
        n = p / (8 * sizeof(uintptr_t));
        m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
 
        if (!(iphp->rrp.tried[n] & m)) {
 
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
                            "get ip hash peer, hash: %ui %04XA", p, m);
 
            peer = &iphp->rrp.peers->peer[p];
 
            /* ngx_lock_mutex(iphp->rrp.peers->mutex); */
 
            if (!peer->down) {
 
                if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
                    break;
                }
 
                if (now - peer->accessed > peer->fail_timeout) {
                    peer->fails = 0;
                    break;
                }
            }
 
            iphp->rrp.tried[n] |= m;
 
            /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
 
            pc->tries--;
        }
 
        if (++iphp->tries >= 20) {
            return iphp->get_rr_peer(pc, &iphp->rrp);
        }
    }

3、堆排序

借助堆这个数据结构,找出Top K,时间复杂度为N‘logK。即借助堆结构,我们可以在log量级的时间内查找和调整/移动。因此,维护一个K(该题目中是10)大小的小根堆,然后遍历300万的Query,分别和根元素进行对比。所以,我们最终的时间复杂度是:O(N) + N' * O(logK),(N为1000万,N’为300万)。

总结:

要解决该问题首先要进行分类,把重复出现的IP都放到一个文件里面,一共分成100份,这可以通过把IP对100取模得到,具体方法如把IP中的点转化为整型long型变量,这样取模为0,1,2...99的IP都分到一个文件了,但是要考虑一个问题,如果某个文件的IP取余之后还是特别多无法放入内存中,可以再对这一类IP做一次取模,直到每个小文件足够载入内存为止。这个分类很关键,如果是随便分成100份,相同的IP被分在了不同的文件中,接下来再对每个文件统计次数并做归并,这个思路就没有意义了,起不到“大而化小,各个击破,缩小规模,逐个解决”的效果了。

接下来把每个小文件载入内存,建立哈希表将每个IP作为关键字映射为出现次数,这个哈希表建好之后也得先写入硬盘,因为内存就那么多,一共要统计100个文件。

在统计完100个文件之后,我再建立一个小顶堆,大小为100,把建立好并存在硬盘哈希表载入内存,逐个对出现次数排序,挑出出现次数最多的100个,由于次数直接和IP是对应的,找出最多的次数也就找出了相应的IP。

猜你喜欢

转载自blog.csdn.net/Iloveyougirls/article/details/81814524