OLAP Indexes
对于传统的分析数据库结构大致如下
OLTP数据库保持相互独立,然后将需要的数据传输到OLAP数据库。
OLAP Schemas
决策支持系统(DECISION SUPPORT SYSTEMS)
为组织的管理,运营和计划层面提供帮助的人通过分析历史数据来帮助人们做出有关未来问题和问题的决策的应用程序。
大数据的起源就是这儿。
STAR SCHEMA
一种数据库模式。
以沃尔玛的购物系统设计为例。中间的表是Fact表,每次购物刷一次单就会产生一个这样的记录。但是它自己本身的属性只有价格和数量,其余都是外键。
外键约束来自于其他表,这些表被称为Mentioned表。
这种模式设计非常类似一棵树。
SNOWFLAKE SCHEMA
和上一个模式是同样场景的另一种模式。
在上一个模式中,mentioned表只有一层,这个模式用了更严格的规范化(在这儿是BC范式),相当于将mentioned表变成了多层的表。
两个模式的比较
这两种模式的优缺点可以参考范式的规范程度深浅的优缺点。
- 规范化
- Snowflake schema占用较少的存储空间
- 非规范化数据模型可能会破坏完整性和一致性(integrity and
consistency violations)。
- 查询复杂度
- Snowflake schema需要更多的Join次数
- Star schema的查询速度一般来说会更快
索引
在Fact表上使用B+树索引会导致大量浪费的存储空间,如果这些值是重复的而且数量很低的话。
对于如下的两个表的声明
CREATE TABLE location_dim (
id INT PRIMARY KEY,
⋮
zip_code INT
);
CREATE TABLE sales_fact (
id INT PRIMARY KEY,
⋮
location_fk INT
REFERENCES location_dim (id)
);
我们需要进行这样的查询。
SELECT COUNT(*)
FROM sales_fact AS S
JOIN location_dim AS L
ON S.location_fk = L.id
WHERE L.zip_code = 15217
假设我们在location_dim的zip_code有一个UNclustering索引,使得我们可以用
O(log N)
的复杂度得到L.zip_code=15217
的数据,但是为了进行JOIN操作,我们必须扫描sales_face这个表,时间复杂度是
O(N)
,而且通常情况下,这种表有上百亿的数据。
Projection/Columnar Indexes (MSSQL)
MSSQL就是微软的SQL Server。微软在2010年发表了一篇论文,讲述了将行分解为单个属性的压缩列段的思想。论文SQL Server Column Store Indexes:
- 原始数据仍保留在行存储中,但是列索引单独存储。
- 无法将列索引中的条目映射回行存储中相应的条目(最初的设计会导致一张表变为只读模式,例如,你告诉SQL Server我要创建一个columnar indexes,那么它就会将这个表冰冻,变为只读模式)。
- 用到了非常多SQL Server已经拥有的技术。
将数据表分割成几个部分,每个部分对其进行统计、编码、压缩,得到了右边这个索引。
比如,我们需要查Row Group 3的Column C的信息,就可以直接去查找。
分段目录(Segment Directory)
记录每个分组的每个分段的统计信息。
- Group的大小
- Group的行数
- 最小和最大键值
- 编码元数据
数据字典(Data Dictionary)
对于每个元组,将其值的32位id存储在数据库中,并在字典中id映射到其原始值,一种数据压缩的方式。
值编码(Vaue Encoding)
将数值列段的域转换为整数较小域中的一组不同的值。
允许DBMS使用较小的数据类型来存储较大的值。
有时也称为增量编码(delta encoding)。
RUN-LENGTH ENCODING
将单一的相同值进行压缩一个的编码方式
将一些列的值压缩成一个tuple:
- 属性的值。
- 列段中的起始位置。
- 运行中的元素个数
需要将列进行智能排序从而最大化压缩率。
我们假设一个sex是1 Bytes,一个tuple是3 Bytes。
那么可以发现,左表需要9 Bytes,右表需要15 Btypes,经过压缩它的所需要的空间反而增加了。
但是排序后的数据进行压缩,则压缩后仅仅需要6 Bytes。所以需要将压缩前的数据进行排序。
Bitmap Indexes
改进列索引
上文提到了MSSQL在2010年的局限性,他们在2012年进行了改进,论文Enhancements to SQL server column stores。
- 聚集列索引。
- 更多的数据类型。
- 支持INSERT,UPDATE和DELETE:
- 使用delta store进行修改和更新。 DBMS将来自列索引和delta store的结果无缝地结合在一起。
- 删除的元组被标记在一个bitmap中,进行逻辑删除
Bitmap Indexes
为特定属性的每个唯一值创建单独的bitmap,其中矢量中的偏移量与元组相对应:bitmap中的第i个位置对应于表中的第i个元组。
通常分成块,以避免分配大块连续的内存。
那么对于id=1的数据,它的M-bitmap是1,而F-bitmap是0,所以它的值是M。
它具有一定局限性:
CREATE TABLE customer_dim (
id INT PRIMARY KEY,
name VARCHAR(32),
email VARCHAR(64),
address VARCHAR(64),
zip_code INT
);
对于上表的zip_code
我们创建了一个bitmap,我们假设有
107
条数据,共有
43000
种zip codes。
占用了一大堆无用的空间,同时每次插入一条数据,我们都要创建一个 43000 的一个bitmap,这也将花费大量的时间。
编码策略
- Equality Encoding: 每个唯一值具有一个位图的基本方案,上面两种例子都是这样。
- Range Encoding:每个区间使用一个位图而不是每个值一个。
- Hierarchical Encoding:分级的策略,使用树来识别空的键范围。
- Bit-sliced Encoding:在所有值上使用每位位置的位图。
Hierarchical Encoding
对于根节点而言,1010表示,根节点的第一个儿子至少有一个1,第二个儿子没有1,第三个儿子至少有一个1,第四个儿子没有1。
同理,第二层的最左边节点,1011,1表示第一个儿子至少有一个1,0表示第二个儿子没有1,剩下的两个同理。
我们只需要存储有1的数据,但是尽管这个结构在2003年就提出了,但是工业界没有人真正实现这种想法。似乎是因为缓存的问题。
Bit-sliced Encoding
将这个扩大到每一个数据
SELECT * FROM customer_dim
WHERE zipcode < 15217
对于此查询,我们按列进行处理
将前三列含有1的列剔除,也就是说,我们接下来只需要扫描(2,3,6)行。但是这样处理需要有能够处理Bit块的数据结构(有一个论文讲到了这个数据结构,使得这些操作非常快)。
Bit-slices也可以用于高效地运行一些聚合函数,比如SUM,只需要按位处理即可。
Bit-Map数据压缩
通用压缩
- 使用标准的压缩算法(例如,LZ4,Snappy)。
- 必须先解压才能使用它来处理查询。对于in-memory DBMS没有什么用。
Byte-aligned Bitmap Codes
- 结构化的run-length encoding压缩。
Roaring Bitmaps
- run-length encoding和value list的混合。
Byte-aligned Bitmap Codes
因为对于Bit map而言,大部分的位都是0,所以我们将将位图分成不同的块,每个块含有不同的字节类别:
- Gap Bytes:所有位都是0。
- Tail Bytes:有些位是1。
对每个块,如果它是由一些Gap Bytes在前,一些Tail Bytes在后组成的,则对其进行编码:
- Gap Bytes用RLE压缩。
- Tail Bytes未压缩存储,除非它只包含1个字节或只有1个非零位。
如下图所示
我们将这个Bit Map分成两个块。
第一块,我们将它压缩为(010)(1)(0100)
。
- 1-3位表示Gap Bytes的数量,这里是2个
- 4位表示Tail Bytes是不是特殊的,比如,只含有一个1。这的1表示是特殊的。
- 5-7表示,若是特殊的,则特殊的位置是哪里,这里是第4位是1。
- 由于gap长度<7,没有用于标识gap长度的长度的字节。
- 由于Tail是特殊的,所以没有不经过压缩的Tail Bytes。
第二块比较复杂,我们将它压缩为(111)(0)(0010) 00001101 01000000 00100010
- 1-3位表示Gap Bytes的数量,这里有13个,所以用的是111,并附带了00001101表示长度。
- 4位表示是否特殊,0表示不特殊。
- 5-7表示,若不是特殊,有几个Tail Bytes,这儿有两个,所以附带了两个没有压缩过的01000000 00100010。
原本是18 bytes,现在是5 bytes。
这种压缩方式的效果,可以极大地减少所需要的空间,但是他在压缩和解压的时候,有着过多的分治,所以会比近期的其他方法更慢。
Word-Aligned Hybrid (WAH) 是从BBC中发展出的一种新的技术,能够提供更好的结果。
但是这两种都不能支持随机访问,如果你需要确定是否存在一个数据,你需要将整个数据从头开始解压。
ROARING BITMAPS
将32位整数存储在紧凑的两级索引数据结构中。
- 密集块使用bit map存储
- 稀疏块使用16位整数的数组。
对于每一个k,我们记录它除4096的值和mod 4096的值。如果k值小于4096,则直接按照数组存储;如果k值大于4096,则用bit map标记。
COLUMN IMPRINTS
这个技术是和Bit Map一起用的。
用一个单独的Bit Map表示那些列里面是有值的。