持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情
本系列主要是《数据密集型应用系统设计》阅读笔记,本文记录列式存储的数据结构主题的笔记心得。
几乎所有的大型企业都有数据仓库,但是在小型公司中几乎没有。大多数小公司只拥有少量的数据,完全可以在传统的SQL数据库中进行查询分析,对于大公司,需要做大量繁重的工作来完成在小公司看似很简单的一些事情。 使用单独的数据仓库而不是直接查询OLTP系统进行分析,很大的优势在于数据仓库可以针对分析访问模式进行优化。
前面讨论的索引算法适合OLTP,但不擅长应对分析查询。 现在将重点讨论针对分析型而优化的存储引擎。
数据仓库
数据仓库的数据模型最常见的是关系型,因为SQL通常适合分析查询。有许多图形化数据分析工具,他们可以生成SQL查询、可视化结果并支持分析师探索数据,例如通过向下钻取、切片等操作。
虽然看起来和OLTP比较像但是其实不一样。
- 商业数据仓库系统比如Teradata、Vertica
- 开源的有Apache Hive/Spark SQL等,其中一些系统是通过Google Dremel来构建的。
分析型业务的建模
数据仓库里面表的概念可以分为两类:
- 事实表是存储有事实记录的表,如果有万亿、PB大小的数据,则高效的存储和查询这些数据将成为一个具有挑战性的问题。
- 维度表一般在数百万行级别 维度表是与事实表相对应的一种表;它保存了维度的属性值,可以跟事实表做关联;相当于将事实表上经常重复出现的属性抽取、规范出来用一张表进行管理。常见的维度表有:日期表(存储与日期对应的周、月、季度等的属性)、地点表(包含国家、省/州、城市等属性)等。维度是维度建模的基础和灵魂,使用维度表有诸多好处,具体如下:
- 缩小了事实表的大小。
- 便于维度的管理和维护,增加、删除和修改维度的属性,不必对事实表的大量记录进行改动。
星型模式和雪花
星型模式也称为维度建模,如下图所示的维度仓库,模式的中心是一个事实表。
通常,事实被捕获为单独的事件,这样之后的分析具有最大的灵活性。像苹果、沃尔玛、或者ebay这样的大企业,其数据仓库可能有数十PB的交易历史。维度通常代表事件的对象、什么、地点、时间、方法以及原因。
比如产品表就是一个维度,一般用外键来表示。
维度可以进一步细分为子空间,这样的模型就是雪花模型。
列式存储
事实表有数万亿行,则高效的存储和查询这些数据将成为一个具有挑战性的问题。维度表通常小的多。
所以事实表这种大表该怎么存储呢?
虽然事实表通常超过100列,但典型的数据仓库查询往往一次只访问其中的4、5个。
那么是否可以不要将一行中的所有值存储在一起,而是将每列中的所有值存储在一起?这就是面向列的存储。
面向列的存储依赖一组列文件,如果需要组装一行数据,则需要从单独的列文件中获取第23个条目,并将他们放在一起构成表的第23行。
- 列文件
-
每个列存储在一个单独的文件中,查询只需要读取和解析在该查询中使用的列
-
每个文件以相同顺序保存着数据行,如下图
-
面向列的适合压缩
- 位图,如下图所示
- 游程编码,如下图
-
注意Cassandra和Hbase有一个列族的概念,但是并不是列。列族,将一行中的所有列和行主键一起保存,然而这还是面向行的
列存储的关键:排序
在列存储中,某列的第k项和另一列的第k项必须属于同一行。
行的顺序不重要,如果要按照某一列排序,要怎么做呢?单独排序某列是没有意义的,需要一次排序整行。 这样看来排序的代价是很高的。但排序的效果也是显而易见的。
如果查询经常以日期范围为目标,那么明智的做法通常是将date_key设置为第一个排序键。查询优化器之扫描上个月的行,这比扫描所有行快的多。
date_key也就是行存储模型中的索引机制,还可以继续指定第二个排序键...
帮助压缩
排序能帮助压缩,如果主排序列上没有很多不同的值,那么在排序之后,将出现一个非常长的序列。其中相同的值在一行中重复多次。一个简单的游程编码,可以将拥有万亿行的表压缩到几千字节。
列存储的难点:写入
面向列的排序和压缩让写入变得困难。如果在排序表的中间插入一行,那么很可能不得不修改所有的列文件。
幸运的是,可以利用前面提到的LSM tree的思想。所有的写入首先进入内存存储区,将其添加到排序的结构中,接着再写入磁盘,内存中的是面向列或者行都无所谓。当累计了足够的写入时,将和磁盘上的列文件合并,并批量写入新文件。本质上这是Vertica的原理。
执行查询的时候,需要检查磁盘上的数据和内存中最近的写入,并结合两者的结果。
参考文献
- 《数据密集型应用系统设计》