一、需求场景
每天大量数据入库,但是90%以上的数据都是主键冲突的,如果主键冲突则跳过,如果不冲突则写入。
比较简单的逻辑是,每来一条数据就在表里查询一下主键是否已经存在,如果存在跳过,如果不存在写入。但是这样的情况会导致大量的数据库查询操作,因此采用在内存中构建一个bitmap,通过bitmap判断数据是否需要写入,从而避免大量的低价值的查询操作。
二、BitMap代码
class BitMap(object):
def __init__(self,max_value):
self._bitmap_max_value = max_value
self.bitmap = [0 for i in range(int((max_value + 31 - 1) / 31))]
def set(self,num):
element_index = self.__get_element_index(num)
bit_index = self.__get_bit_index(num)
self.bitmap[element_index] = self.bitmap[element_index] | (1 << bit_index)
def find(self,num):
element_index = self.__get_element_index(num)
bit_index = self.__get_bit_index(num)
if self.bitmap[element_index] & (1 << bit_index):
return True
return False
def __get_element_index(self,value):
"""
获取该数即将储存的字节在数组中下标
"""
return value // 31
def __get_bit_index(self,value):
"""
获取该数在元素中的位下标
"""
return value % 31
三、BitMap原理
bitmap的思想就是将所有的int类型的key转化成一个对应的bit位,并由一个int类型的列表来存储。从而大大降低内存占用并提高查询、写入的性能。
一个int类型的数据占32个bit。也就是说int类型的列表中的每一个值都可以表示32个key。
因此n个key只需要 n // 31 + 1 长度的int类型列表即可标识。但是由于int类型需要一个标识位来表示正负,因此需要 (n - 1) // 31 + 1 长度的int类型列表。
同理对于一个key,其可以通过int类型列表的第m个int,的第n位来表示。
m = key // 31
n = key % 31
四、BitMap的读取和写入
1、读取
我们已经知道了,对于一个key,其可以通过int类型列表的第m个int,的第n位来表示。
m = key // 31
n = key % 31
因此我们需要先计算出,这个key存储的位置,即m、n的值。获取int类型列表m位置的值,之后将1无符号左移n位,将对应的值进行与运算即可。
2、写入
我们已经知道了,对于一个key,其可以通过int类型列表的第m个int,的第n位来表示。
m = key // 31
n = key % 31
因此我们需要先计算出,这个key存储的位置,即m、n的值。获取int类型列表m位置的值,之后将1无符号左移n位,将对应的值进行或运算即可。