当今的数据处理大致可分为两大类

联机事务处理 OLTP(on-line transaction processing)

联机分析处理 OLAP(On-Line Analytical Processing)

OLTP 是传统关系型数据库的主要应用用来执行一些基本的、日常的事务处理,比如数据库记录的增、删、改、查等等
而 OLAP 则是分布式数据库的主要应用,它对实时性要求不高,但处理的数据量大,通常应用于复杂的动态报表系统上

image.png

通常来说,OLTP适合使用行式存储实现,OLAP适合使用列式存储实现

行式存储和列式存储

  • 行式存储 (Row-based)
  • 列式存储(Column-based)

在基于行式存储的数据库中, 数据是按照行数据为基础逻辑存储单元进行存储的, 一行中的数据在存储介质中以连续存储形式存在。传统的关系型数据库都是行式存储,如 Oracle、DB2、MySQL、SQL SERVER 等

列式存储(Column-based)是相对于行式存储来说的。在基于列式存储的数据库中, 数据是按照列为基础逻辑存储单元进行存储的,一列中的数据在存储介质中以连续存储形式存在。
新兴的 Hbase、HP Vertica、EMC Greenplum 等分布式数据库均采用列式存储。

image.png
在传统的行式数据库系统中,数据按如下顺序存储:
image.png
处于同一行中的数据总是被物理的存储在一起。
常见的行式数据库系统有:MySQL、Postgres和MS SQL Server。
在列式数据库系统中,数据按如下的顺序存储:
image.png
这些示例只显示了数据的排列顺序。来自不同列的值被单独存储,来自同一列的数据被存储在一起。
常见的列式数据库有: Vertica、 Paraccel (Actian Matrix,Amazon Redshift)、 Sybase IQ、 Exasol、 Infobright、 InfiniDB、 MonetDB (VectorWise, Actian Vector)、 LucidDB、 SAP HANA、 Google Dremel、 Google PowerDrill、 Druid、 kdb+。

不同的数据存储方式适用不同的业务场景,数据访问的场景包括:进行了何种查询、多久查询一次以及各类查询的比例;每种类型的查询(行、列和字节)读取多少数据;读取数据和更新之间的关系;使用的数据集大小以及如何使用本地的数据集;是否使用事务,以及它们是如何进行隔离的;数据的复制机制与数据的完整性要求;每种类型的查询要求的延迟与吞吐量等等。 系统负载越高,依据使用场景进行定制化就越重要,并且定制将会变的越精细。没有一个系统能够同时适用所有不同的业务场景。如果系统适用于广泛的场景,在负载高的情况下,要兼顾所有的场景,那么将不得不做出选择。是要平衡还是要效率?

OLAP场景的关键特征

  • 绝大多数是读请求
  • 数据以相当大的批次(> 1000行)更新,而不是单行更新;或者根本没有更新。
  • 已添加到数据库的数据不能修改。
  • 对于读取,从数据库中提取相当多的行,但只提取列的一小部分。
  • 宽表,即每个表包含着大量的列
  • 查询相对较少(通常每台服务器每秒查询数百次或更少)
  • 对于简单查询,允许延迟大约50毫秒
  • 列中的数据相对较小:数字和短字符串(例如,每个URL 60个字节)
  • 处理单个查询时需要高吞吐量(每台服务器每秒可达数十亿行)
  • 事务不是必须的
  • 对数据一致性要求低
  • 每个查询有一个大表。除了他以外,其他的都很小。
  • 查询结果明显小于源数据。换句话说,数据经过过滤或聚合,因此结果适合于单个服务器的RAM中

很容易可以看出,OLAP场景与其他通常业务场景(例如,OLTP或K/V)有很大的不同, 因此想要使用OLTP或Key-Value数据库去高效的处理分析查询场景,并不是非常完美的适用方案。例如,使用OLAP数据库去处理分析请求通常要优于使用MongoDB或Redis去处理分析请求。

列式数据库更适合OLAP场景的原因

列式数据库更适合于OLAP场景(对于大多数查询而言,处理速度至少提高了100倍),下面详细解释了原因(通过图片更有利于直观理解):
行式:
image.png
列式:
image.png

为什么会发生这种情况。

输入/输出

  1. 针对分析类查询,通常只需要读取表的一小部分列。在列式数据库中你可以只读取你需要的数据。例如,如果只需要读取100列中的5列,这将帮助你最少减少20倍的I/O消耗。
  2. 由于数据总是打包成批量读取的,所以压缩是非常容易的。同时数据按列分别存储这也更容易压缩。这进一步降低了I/O的体积。
  3. 由于I/O的降低,这将帮助更多的数据被系统缓存。

例如,查询«统计每个广告平台的记录数量»需要读取«广告平台ID»这一列,它在未压缩的情况下需要1个字节进行存储。如果大部分流量不是来自广告平台,那么这一列至少可以以十倍的压缩率被压缩。当采用快速压缩算法,它的解压速度最少在十亿字节(未压缩数据)每秒。换句话说,这个查询可以在单个服务器上以每秒大约几十亿行的速度进行处理。这实际上是当前实现的速度。

CPU

由于执行一个查询需要处理大量的行,因此在整个向量上执行所有操作将比在每一行上执行所有操作更加高效。同时这将有助于实现一个几乎没有调用成本的查询引擎。如果你不这样做,使用任何一个机械硬盘,查询引擎都不可避免的停止CPU进行等待。所以,在数据按列存储并且按列执行是很有意义的。
有两种方法可以做到这一点:

  1. 向量引擎:所有的操作都是为向量而不是为单个值编写的。这意味着多个操作之间的不再需要频繁的调用,并且调用的成本基本可以忽略不计。操作代码包含一个优化的内部循环。
  2. 代码生成:生成一段代码,包含查询中的所有操作。

这是不应该在一个通用数据库中实现的,因为这在运行简单查询时是没有意义的。但是也有例外,例如,MemSQL使用代码生成来减少处理SQL查询的延迟(只是为了比较,分析型数据库通常需要优化的是吞吐而不是延迟)。
请注意,为了提高CPU效率,查询语言必须是声明型的(SQL或MDX), 或者至少一个向量(J,K)。 查询应该只包含隐式循环,允许进行优化。


行式存储 列式存储
优点 Ø 数据被保存在一起
Ø INSERT/UPDATE容易
Ø 查询时只有涉及到的列会被读取
Ø 投影(projection)很高效
Ø 任何列都能作为索引
缺点 Ø 选择(Selection)时即使只涉及某几列,所有数据也都会被读取 Ø 选择完成时,被选择的列要重新组装
Ø INSERT/UPDATE比较麻烦

行式存储的适用场景包括:

1、适合随机的增删改查操作;
2、需要在行中选取所有属性的查询操作;
3、需要频繁插入或更新的操作,其操作与索引和行的大小更为相关。
实操中我们会发现
行式数据库在读取数据的时候
会存在一个固有的“缺陷”
比如,所选择查询的目标即使只涉及少数几项属性
但由于这些目标数据埋藏在各行数据单元中
而行单元往往又特别大
应用程序必须读取每一条完整的行记录
从而使得读取效率大大降低
对此,行式数据库给出的优化方案是加“索引”
在OLTP类型的应用中
通过索引机制或给表分区等手段
可以简化查询操作步骤,并提升查询效率

但针对海量数据背景的OLAP应用
(例如分布式数据库、数据仓库等等)
行式存储的数据库就有些“力不从心”了
行式数据库建立索引和物化视图
需要花费大量时间和资源
因此还是得不偿失
无法从根本上解决查询性能和维护成本等问题
也不适用于数据仓库等应用场景
所以后来出现了基于列式存储的数据库

对于数据仓库和分布式数据库来说
大部分情况下它会从各个数据源汇总数据
然后进行分析和反馈
其操作大多是围绕同一列属性的数据进行的
而当查询某属性的数据记录时
列式数据库只需返回与列属性相关的值
在大数据量查询场景中
列式数据库可在内存中高效组装各列的值
最终形成关系记录集
因此可以显著减少IO消耗
并降低查询响应时间
非常适合数据仓库和分布式的应用

列式存储引擎的适用场景包括:

1、查询过程中,可针对各列的运算并发执行(SMP),最后在内存中聚合完整记录集,最大可能降低查询响应时间;
2、可在数据列中高效查找数据,无需维护索引(任何列都能作为索引),查询过程中能够尽量减少无关IO,避免全表扫描;
3、因为各列独立存储,且数据类型已知,可以针对该列的数据类型、数据量大小等因素动态选择压缩算法,以提高物理存储利用率;如果某一行的某一列没有数据,那在列存储时,就可以不存储该列的值,这将比行式存储更节省空间。
当然,跟行数据库一样
列式存储也有不太适用的场景
主要包括:
数据需要频繁更新的交易场景
表中列属性较少的小量数据库场景
不适合做含有删除和更新的实时操作


最后总结如下:
传统行式数据库的特性如下:

  • ①数据是按行存储的。
  • ②没有索引的查询使用大量I/O。比如一般的数据库表都会建立索引,通过索引加快查询效率。
  • ③建立索引和物化视图需要花费大量的时间和资源。
  • ④面对查询需求,数据库必须被大量膨胀才能满足需求。

列式数据库的特性如下:

  • ①数据按列存储,即每一列单独存放。
  • ②数据即索引。
  • ③只访问查询涉及的列,可以大量降低系统I/O。
  • ④每一列由一个线程来处理,即查询的并发处理性能高。
  • ⑤数据类型一致,数据特征相似,可以高效压缩。比如有增量压缩、前缀压缩算法都是基于列存储的类型定制的,所以可以大幅度提高压缩比,有利于存储和网络输出数据带宽的消耗。