如何在亿级数据中判断一个元素是否存在?

184 阅读3分钟

背景

有一亿条数字,怎么判断一个数字是否在这一亿条数字当中?时间复杂度、空间复杂度越低越好

想一想如果是你,你会怎么解决?以及你的解决方案的成本是多少?

实现方式

使用map存储这一亿条数字,然后判断是否在map中

package main

import (
	"fmt"
	"runtime"
)

func PrintMemory() {
	var memStats runtime.MemStats
	runtime.ReadMemStats(&memStats)

	// 将字节转换为兆字节(MB)
	toMB := func(bytes uint64) uint64 {
		return bytes / 1024 / 1024
	}

	fmt.Printf("Allocated Memory: %d MB\n", toMB(memStats.Alloc))
	fmt.Printf("Total Allocated Memory: %d MB\n", toMB(memStats.TotalAlloc))
	fmt.Printf("Heap Memory: %d MB\n", toMB(memStats.HeapAlloc))
	fmt.Printf("Heap In-use Memory: %d MB\n", toMB(memStats.HeapInuse))
	fmt.Printf("Stack Memory: %d MB\n", toMB(memStats.StackInuse))
	fmt.Println("========")
}

func main() {
	fmt.Println("初始化内存")
	PrintMemory()
	var intMap = make(map[int]struct{})
	for i := 0; i < 10000*10000; i++ {
		intMap[i] = struct{}{}
	}

	if _, ok := intMap[10]; ok {
		fmt.Println("key existed")
	}

	fmt.Println("map 创建完成后的内存")
	PrintMemory()

	runtime.GC()
	fmt.Println("gc之后的内存")
	PrintMemory()

}

初始化内存
Allocated Memory: 0 MB
Total Allocated Memory: 0 MB
Heap Memory: 0 MB
Heap In-use Memory: 0 MB
Stack Memory: 0 MB
========
key existed
map 创建完成后的内存
Allocated Memory: 2357 MB
Total Allocated Memory: 3271 MB
Heap Memory: 2357 MB
Heap In-use Memory: 2359 MB
Stack Memory: 0 MB
========
gc之后的内存
Allocated Memory: 0 MB
Total Allocated Memory: 3271 MB
Heap Memory: 0 MB
Heap In-use Memory: 0 MB
Stack Memory: 0 MB
========

数据库存储这一亿条数字,然后使用select查询

create table data_map
(
  id int(11) unsigned auto_increment comment '自增ID'
  primary key,
  data_id int(11) unsigned not null comment '数据ID',
  index data_id_index (data_id)
)

每行包含一个 id 字段(4字节),一个 data_id 字段(4字节) 8字节 * 100000000 大概 800M(没统计索引等占用的存储空间)

image.png

redis set 存储这一亿条数字,然后使用SISMEMBER判断是否存在

image.png

其他的分布式存储等等

布隆过滤器

布隆过滤器

当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。

原理简述

布隆过滤器是一个 bit 向量或者说 bit 数组,长这样:

image.png 输入baidu,经过K个hash函数计算

image.png 输入tencent,经过K个hash函数计算

image.png

优缺点分析

优点

  1. 占用存储空间低,只使用一个bitmap存储
  2. 查找效率和新增元素效率高,布隆过滤器存储空间和插入/查询时间都是常数 O(K)
  3. 不存储元素本身,对保密要求非常严格的场合有优势

缺点

  1. **误算率!!! **
  2. 不支持删除 可以了解下布谷鸟过滤器

如何选择哈希函数个数和布隆过滤器长度

  1. bitmap长度不能太短,不然查询值大部分都是“可能存在”
  2. hash函数个数不能太少也不能太多,太多影响性能,太少误报率会增高

使用在线工具去计算出满足业务场景的值

image.png

image.png

应用场景

脱离业务场景的讲架构都是耍流氓!!!

URL 去重

需求

一个爬虫程序抓取上亿条url,url可能存在重复,已经抓取过的url就不要再次抓取,减少请求量

功能分析
  1. 可以使用布隆过滤器存储抓取过的url,误判导致的影响很低,再抓取一次也可以
  2. 爬虫程序可能部署在多个节点,布隆过滤器要考虑分布式环境,可以采用redis实现布隆过滤器

总结

  1. 大数据量判断数据是否存在的各种方式
  2. 布隆过滤器的原理及应用场景