充满活力的位图

什么是位图?
这里的位图是一种索引。如果您是因为图形学而进来的,请直接点击返回即可。
基本概念
位图是数据库索引技术的一种形式。它将某一列的值表示为位数组或向量中的一个元素或形式,每个比特表示该列中是否存在某个特定值。因此,最基本的形式是为每一行分配一个比特索引,并用0和1表示是否存在。
例如,假设有一个包含性别列的表。该表中有5行数据,性别列的值如下:
- 男性
- 女性
- 男性
- 女性
- 其他
在这种情况下,性别列的位图索引可以表示如下:
- 男性: 10100
- 女性: 01010
- 其他: 00001
那么它究竟用在哪里呢
大家可能会认为,它不只是用来表示这些内容的。为了真正地使用位图索引,我们来考虑以下情况。
1CREATE TABLE IF NOT EXISTS Users (
2 id INT PRIMARY KEY,
3 name VARCHAR(100),
4 active BOOLEAN,
5 sex BOOLEAN,
6 age INT
7);
假设该表中有1,000,000行数据。然后执行如下查询。
1SELECT * FROM Users WHERE active = TRUE AND SEX = TRUE;
该查询用于查找active和sex列都为TRUE的行。那么,要最快地找到两列都满足TRUE条件的行,可以利用位与(bit and)运算。
- active列的位图索引已预先建立为
1100110011...(1,000,000位的比特数组), - sex列的位图索引已预先建立为
1010101010...(1,000,000位的比特数组)。
这样,对两个位图索引执行位与运算,就能快速找到两列都为TRUE的行。由于这只是简单的位运算,因此可以比其他索引扫描更快地得到结果。
权衡取舍
位图索引虽然可以非常高效,但也存在一些权衡取舍。其缺点有很多,这里只谈其中一点。
即位图索引更适合低基数(即列中可能取值较少的情况)。若对高基数的列使用位图索引,位图可能会变得非常大。
对高基数的列使用位图索引时,由于每个唯一值都需要单独生成一个位图,因此可能需要大量存储空间。例如,如果某列有1,000,000个唯一值,就需要生成1,000,000个位图,这可能会非常低效。
因此我们
为解决高基数带来的位图问题,可以使用名为Roaring Bitmap的结构。Roaring Bitmap具有多种特性,但其最核心的设计理念和特征是:根据数据的密度动态选择最合适的存储方式,从而同时最大化压缩率和运算速度。
Roaring Bitmap的基本概念
两级索引
Roaring Bitmap为存储一个整数,首先要经过以下两个步骤。虽然称为两级索引,但您可以把它直接理解为一个二维数组。
- 提取高16位:提取整数的高16位,并将其用作索引。它是一个长度为65536的二维数组中外层数组的索引。该数组保存后文所述容器的地址。
- 提取低16位:提取整数的低16位,并将其用作容器内的位置。
容器
如刚才的比喻所示,容器对应二维数组中的内层数组。但与高16位的那一层不同,容器的内部结构会根据其中存储的数据量而变化。Roaring Bitmap支持以下3种容器。
- Array Container:当容器内存储的数据较少时使用。具体而言,当存储的元素不超过4096个时使用。该容器简单地以有序整数数组实现,可以通过二分查找来定位元素。
- Bitmap Container:当容器内存储的数据较多时使用。具体而言,当存储的元素超过4096个时使用。该容器以65536位(8192字节)的位图实现,每个比特表示对应位置的整数是否存在。
- Run Container:该容器用于存储连续的整数范围。例如,对于1, 2, 3, 4, 5这样的连续整数,存储效率很高。该容器以(起始值, 长度)对的列表实现。如果全部都存在呢?那就意味着从头到尾都存在,因此只需存储(0, 65536)一项即可。
这种方式使Roaring Bitmap能够非常高效地使用内存,并在各种数据密度下提供最优的性能。
Roaring Bitmap的功能
基本功能
Roaring Bitmap提供以下基本功能。
- 插入:向位图中添加新的整数。在此过程中,可以选择合适的容器,或根据需要转换容器。
- 删除:从位图中移除已有的整数。此过程同样会根据容器的状态进行适当处理。
- 查找:检查特定整数是否存在于位图中。此过程执行速度非常快。
集合运算
Roaring Bitmap还支持以下集合运算。
- 并集(Union):生成包含两个位图所有元素的新位图。
- 交集(Intersection):生成仅包含两个位图中都存在的元素的新位图。
- 对称差集(Symmetric Difference):生成仅包含两个位图中其中一个存在的元素的新位图。
这些运算会根据位图的容器类型进行优化,因此执行速度非常快。
在Go语言中
在Go语言中,可以使用github.com/RoaringBitmap/roaring包轻松地使用Roaring Bitmap。该包提供了Roaring Bitmap的全部功能,并针对Go语言的特性进行了优化。
安装
1go get github.com/RoaringBitmap/roaring/v2
使用示例
1package main
2
3import (
4 "log"
5
6 "github.com/RoaringBitmap/roaring/v2"
7)
8
9func main() {
10 b1 := roaring.BitmapOf(1, 2, 3, 4) // 用1, 2, 3, 4初始化
11 b2 := roaring.BitmapOf(2, 4, 6, 8, 12) // 用2, 4, 6, 8, 12初始化
12
13 b1.Add(5) // 添加5
14 b2.Add(10) // 添加10
15
16 b2.Remove(12) // 移除12
17
18 and := roaring.FastAnd(b1, b2)
19 or := roaring.FastOr(b1, b2)
20
21 // bitmap运算会修改原始对象,因此使用副本
22 xor := b1.Clone()
23 xor.Xor(b2)
24
25 log.Printf("and result: %v", and.ToArray())
26 log.Printf("or result: %v", or.ToArray())
27 log.Printf("xor result: %v", xor.ToArray())
28}
12025/09/08 22:16:27 and result: [2 4]
22025/09/08 22:16:27 or result: [1 2 3 4 5 6 8 10]
32025/09/08 22:16:27 xor result: [1 3 5 6 8 10]
除上述示例代码中未出现的主要功能外,还包括:
Contains(x uint32) bool:检查特定值是否存在于位图中GetCardinality() uint64:返回位图中包含的元素个数Rank(x uint32) uint64:返回小于或等于特定值的元素个数ToBytes() ([]byte, error):将位图序列化为字节切片FromBytes(data []byte) error:从字节切片恢复位图Clone() *Bitmap:创建位图的副本Clear():清空位图NextValue(x uint32) int64:返回大于或等于x的下一个元素PreviousValue(x uint32) int64:返回小于或等于x的前一个元素Maximum() uint32:返回位图中最大的元素Minimum() uint32:返回位图中最小的元素
以上就是这些功能。详细内容请参考官方文档。
另外,roaring包内部还包含支持uint64的包,而不仅限于uint32。它提供几乎相同的接口,因此可以直接替换使用。
1package main
2
3import (
4 "log"
5
6 "github.com/RoaringBitmap/roaring/v2/roaring64"
7)
8
9func main() {
10 b1 := roaring64.BitmapOf(1, 2, 3, 4) // 用1, 2, 3, 4初始化
11 b2 := roaring64.BitmapOf(2, 4, 6, 8, 12) // 用2, 4, 6, 8, 12初始化
12
13 b1.Add(5) // 添加5
14 b2.Add(10) // 添加10
15
16 b2.Remove(12) // 移除12
17
18 and := roaring64.FastAnd(b1, b2)
19 or := roaring64.FastOr(b1, b2)
20
21 // bitmap运算会修改原始对象,因此使用副本
22 xor := b1.Clone()
23 xor.Xor(b2)
24
25 log.Printf("and result: %v", and.ToArray())
26 log.Printf("or result: %v", or.ToArray())
27 log.Printf("xor result: %v", xor.ToArray())
28}
结语
Roaring Bitmap是一种强大的工具,能够高效地存储和操作高基数的整数集合。在Go语言中,使用github.com/RoaringBitmap/roaring包即可轻松利用Roaring Bitmap的全部功能。该包通过多种容器类型和优化的运算,最大化内存使用效率和性能。在数据库索引、日志分析、统计计算等多个领域,都可以利用Roaring Bitmap实现高效的数据处理。
Roaring Bitmap在大规模数据集上尤其表现出色,可广泛应用于各种应用程序中。结合Go语言简洁高效的语法,Roaring Bitmap为开发者提供了强大的工具。随着Roaring Bitmap的发展,预计将会有更多功能和优化出现。
我为什么要做这些呢?是为了在时序数据库中对每个标签进行索引,并实现快速的并集运算。