文章目录

  • 存储方式
  • 简单对比
  • 列式存储和行式存储它们真正的区别
    • 概述
    • 四大优化策略详解
      • 块遍历
      • 压缩
      • 延迟物化
      • 隐式链接
    • 总结

存储方式


行式存储就是每一行的所有数据存在一个block中,各个block之间连续存储;
列式存储就是每一列的所有数据存在一起,不同列之间可以分开存储。

简单对比

Row-Store Column-Store
写入 每一行的所有字段都存在一起,优点:对数据进行插入和修改操作很方便 当一条新数据到来,每一列单独存储,缺点:插入和修改操作麻烦
查询 查询时即使只涉及某几列,所有数据也都会被读取;优点:适合随机查询;在整行的读取上,要优于列式存储;缺点:行式存储不适合扫描,这意味着要查询一个范围的数据 查询时只有涉及到的列会被读取;缺点:查询完成时,被查询的列要重新进行组装
寻道范围 读取数据的时候硬盘寻址范围很大 由于仅对需要的列进行查找,因此硬盘寻道范围小
索引 缺点:要加速查询的话需要建立索引,建立索引需要花费很多时间。 优点:任何列都能作为索引(每一列单独存储,查询个别列的时候,可以仅读取需要的那几个列,相当于为每一列都建立了索引)
压缩 缺点:不利于压缩 把一列数据保存在一起,而一列的数据类型相同 ;优点:利于压缩
空间 按行存储,不利于压缩,压缩比较差,占空间大 列式存储的时候可以为每一列创建一个字典,存储的时候就仅存储数字编码即可,降低了存储空间需求
聚合 不利于聚合操作 按列存储,利于数据聚合操作
应用 MySQL中的iInnoDB和MyISAM存储引擎是行式存储 MySQL中的infobright存储引擎是列式存储
使用场景 OLTP(存储关系型数据,用于使用数据的时候需要经常用到数据之间的依赖关系的场景,即读取的时候需要整行数据或者整行中大部分列的数据,需要经常用到插入、修改操作) OLAP(分布式数据库和数据仓库,适合于对大量数据进行统计分析,列与列之间关联性不强,仅进行插入和读取操作的场景)
  1. 列式存储是非关系型数据库中的一种,非关系型数据库的目的在于去掉关系数据库的关系型特性,使得数据之间无关系,使得扩展性高。非关系型数据库一般具有大数据量、高性能的特点,典型的有Key-Value键值存储数据库等。
  2. 行存储、列存储,最终都需要把数据存到磁盘块。行存储优点很明显,更新快、单条记录的数据集中,适合事务。但缺点也很明显,查询慢。列存储的优势其实是在查询和聚合运算。之所以说行存储适合事务,其实是相对的,是因为列存储非常不适合事务。试想一下,你更新一个表的若干个数据,如果要在不同块中更新,就可能产生多次更新操作。更新次数越多,保证一致性越麻烦。在单机环境我们可以上锁,可以用阻塞队列,可以用屏障……但是分布式场景中保证一致性(特别是强一致性)开销很大。因此我们说行存储适合事务,而列存储不适合。

列式存储和行式存储它们真正的区别

参考文献:《Column-Stores vs. Row-Stores: How Different Are They Really?》

概述

对于分析类的查询为什么Column-Store比Row-Store好那么多?好在哪里?一般认为原因是:

分析类查询往往只查询一个表里面很少的几个字段,Column-Store只需要从磁盘读取用户查询的Column,而Row-Store读取每一条记录的时候你会把所有Column的数据读出来,在IO上Column-Store比Row-Store效率高很多,因此性能更好。

通过《Column-Stores vs. Row-Stores: How Different Are They Really?》这篇论文,可以知道Column-Store在存储格式优势只是一方面,如果没有查询引擎上其它几个优化措施的配合,性能也不会太好的,这篇论文认为Column-Store在查询引擎层有以下几种大的优化手段:
面向列的系统中优化:

  • 延迟物化(Late Materialization )
  • 块遍历(Block Iteration )
  • 压缩(Compression )
  • 隐式连接(Invisible Joins)

四大优化策略详解

块遍历

块遍历(Block Iteration)是相对于单记录遍历(per-tuple iteration)而言的,其实说白了就是一种批量化的操作。单记录遍历的问题在于对于每个条数据,我们都要从Row数据里面抽取出我们需要的column(针对Row-Store来说),然后调用相应的函数去处理,函数调用的次数跟数据的条数成是 1:1的,在大数据量的情况下这个开销非常可观。而块遍历,因为是一次性处理多条数据,函数调用次数被降下来,当然可以提高性能。

这种提高性能的方法在Row-Store里面是case-by-case实现的(不是一种共识), 而对于Column-Store来说已经形成共识,大家都是这么做的。而如果column的值是字节意义上等宽的,比如数字类型,Column-Store可以进一步提高性能,因为查询引擎要从一个Block里取出其中一个值进行处理的时候直接用数组下标就可以获取数据,进一步提升性能。而且以数组的方式对数据进行访问使得我们可以利用现代CPU的一些优化措施比如SIMD(Single Instruction Multiple Data)来实现并行化执行,进一步提高性能。

压缩

压缩,就是将相似度很高、信息熵很低的数据放在一起,用更小的空间表达相同的信息量。列存的数据往往类型相同,相同的特征更多,更容易被压缩,所以列存系统的压缩优化比行存系统更加有效。

但是为什么压缩就能带来查询的高效呢?压缩首先带来的硬盘上存储空间的降低,但是硬盘又不值钱。它的真正意义在于:数据占用的硬盘空间越小,查询引擎花在IO上的时间就越少(不管是从硬盘里面把数据读入内存,还是从内存里面把数据读入CPU)。同时要记住的是数据压缩之后,要进行处理很多时候要需要解压缩(不管是Column-Store还是Row-Store), 因此压缩比不是我们追求的唯一,因为后面解压也需要花时间,因此一般会在压缩比和解压速度之间做一个权衡。

高压缩比的典型如Lempel-Ziv, Huffman, 解压快的典型如: Snappy, Lz2
前面提到解压缩,有的场景下解压缩这个步骤可以彻底避免掉,比如对于采用Run-Length编码方式进行压缩的数据,我们可以直接在数据压缩的格式上进行一些计算:

Run-Length的大概意思是这样的, 对于一个数字序列: 1 1 1 1 2 2 2, 它可以表达成 1x4, 2x3
这样不管进行 count (4 + 3), sum (1 x 4 + 2 x 3) 等等都可以不对数据进行解压直接计算,而且因为扫描的数据比未压缩的要少,从而可以进一步的提升性能。另外,
对于Column-Store应用压缩这种优化最好的场景是配合 sort 使用,如果数据是经过排序的,则更容易找到相邻数据的同质化特征,获得更好的压缩效果。

延迟物化

物化Materialization 是指为了把底层面向列的存储格式跟要求的行式格式对上,需要在查询的某个阶段转换一下。

理解物化的概念后,延迟物化就很好理解了,意思就是把物化的时机尽量地拖延到整个查询声明的后期。延迟物化意味着在查询执行的前一段时间内,查询执行的模型不是关系代数,而是基于 Column 的。下面看个例子, 比如下面的查询:

SELECT name
FROM person
WHERE id > 10and age > 20

一般的做法是从文件系统读出三列的数据,马上物化成一行行的person数据,然后应用两个过滤条件: id > 10 和 age > 20 , 过滤完了之后从数据里面抽出 name 字段,作为最后的结果,大致转换过程如下图:

而延迟物化的做法则会先不拼出行式数据,直接在 Column 数据上分别应用两个过滤条件,从而得到两个满足过滤条件的 bitmap,然后再把两个 bitmap 做位与的操作得到同时满足两个条件的所有的 bitmap,因为最后用户需要的只是 name 字段而已,因此下一步我们拿着这些 position 对 name 字段的数据进行过滤就得到了最终的结果。如下图:

发现没有?整个过程中我们压根没有进行物化操作,从而可以大大的提高效率。

总结起来延迟物化有四个方面的好处:

  • 关系代数里面的 selection 和 aggregation 都会产生一些不必要的物化操作,从一种形式的tuple, 变成另外一种形式的tuple。如果对物化进行延迟的话,可以减少物化的开销(因为要物化的字段少了),甚至直接不需要物化了;
  • 如果Column数据是以面向Column的压缩方式进行压缩的话,如果要进行物化那么就必须先解压,而这就使得我们之前提到的可以直接在压缩数据上进行查询的优势荡然无存了;
  • 列式的内存组织形式对 CPU Cache 非常友好,从而提高计算效率,相反行式的组织形式因为非必要的列占用了 Cache Line 的空间,Cache 效率低;
  • 块遍历的优化手段对 Column 类型的数据效果更好,因为数据以 Column 形式保存在一起,数据是定长的可能性更大,而如果 Row 形式保存在一起数据是定长的可能性非常小(因为你一行数据里面只要有一个是非定长的,比如 VARCHAR,那么整行数据都是非定长的);

隐式链接

最后本文提出了一个具有创新性的性能优化措施: Invisible Join。Invisible Join 针对的场景是数仓里面的星型模型(Star Schema), 如果用户查询符合下面的模式就可以应用Invisible Join:

利用事实表的里面的外键跟维度表的主键进行JOIN的查询的, 最后select出一些column返回给用户。
所谓的星型模型指的是一个事实表(fact table), 周围通过外键关联一堆维度表(dimension table)的这么一种模型


为了介绍Invisible Join的思路,我们要先介绍一下两种传统的方案,通过对比我们才能看Invisible Join方案的优点。

传统方案一: 按Selectivity依次JOIN
传统方案一最简单,按照 Selectivity 从大到小对表进行JOIN:

传统方案二: 延迟物化
方案二比较有意思, 它应用了延迟物化的策略,它先不进行JOIN,而是先在维度表上对数据进行过滤,拿到对应表的 POSITION, 然后把表的主键跟事实表的外键进行JOIN,这样我们就可以拿到两类POSITION: 事实表的POSITION和维度表的POSITION, 然后我们通过这些POSITION把数据提取出来就完成了一次JOIN, 重复以上的操作我们就可以完成整个查询。

Invisible Join详解
上面两种方案都各有各的缺点:

  • 传统方案一因为一开始就做了JOIN,享受不了延迟物化的各种优化。
  • 传统方案二在提取最终值的时候对很多Column的提取是乱序的操作,而乱序的提取性能是很差的(随机IO)。
    下面正式介绍一下我们的主角: Invisible JOIN

Invisible JOIN其实是对传统方案二的一种优化,传统方案二的精髓在于延迟物化,但是受制于大量的值的提取还是乱序的,性能还是不是最好。Invisible JOIN把能这种乱序的值提取进一步的减少, 它的具体思路如下:

  1. 把所有过滤条件应用到每个维度表上,得到符合条件的维度表的主键(同时也是事实表的外键)。
  2. 遍历事实表,并且查询第一步得到的所有外键的值,得到符合条件的bitmap(s), 这里会有多个bitmap,因为维度表可能有多个。
  3. 对第二步的多个bitmap做AND操作,得到最终事实表里面符合过滤条件的bitmap。
  4. 根据第三步的事实表的bitmap以及第一步的符合条件的维度表的主键值,组装出最终的返回值。

如果只是这样的话Invisible JOIN可能比上面的第二种方案好不了多少,论文认为在很多时间维度表里面符合过滤条件的数据往往是连续的,连续的好处在于,它能把lookup join变成一个值的范围检查,范围检查比lookup join要快,原因很简单,范围检查只需要所算数运算就好了,不需要做lookup,因此可能大幅度的提高性能。

性能对比
从论文提供的性能对比数字来看,这几大优化策略里面延迟物化的效果最好,能够提升性能3倍以上;压缩的优化效果次之: 两倍以上;Invisible JOIN 再次之:50% 到 70%;块遍历则能性能 5% 到 50%

而如果把这些优化手段都去掉,Column-Store的性能跟一个普通的Row-Store就没什么区别了。

总结

读这篇论文最大的收获是首先知道了到底哪些因素促使Column-Store对分析类查询性能可以大幅优于Row-Store: 延迟物化、压缩、Invisible Join以及块遍历。

特别佩服这篇论文的是很多人稍微想一下就会觉得Column-Store在分析类场景下性能优于Row-Store是天经地义的: 更少的IO,但是这篇论文详细的对各种场景进行了测试、论证,同时对Row-Store应用类似的性能优化的手段再进行测试、对比,掰开了揉碎了的分析。最终告诉我们:

Column-Store的优点不止在于它的存储格式,查询引擎层的各种优化也同样关键,而由于Row-Store本身存储格式的限制,即使在Row-Store上使用这些优化,效果也不好。

参考自:
https://developer.aliyun.com/article/792679
https://zhuanlan.zhihu.com/p/54433448
http://www.cs.umd.edu/~abadi/papers/abadi-sigmod08.pdf?spm=a2c6h.12873639.article-detail.4.560c6ba5jxcuAH&file=abadi-sigmod08.pdf

行式存储和列式存储的区别相关推荐

  1. 行式数据和列式数据对比 存储压缩性能

    一.行式数据库和列式数据库的对比 1.存储比较 行式数据库存储在hdfs上式按行进行存储的,一个block存储一或多行数据.而列式数据库在hdfs上则是按照列进行存储,一个block可能有一列或多列数 ...

  2. 数据库—行式存储和列式存储

    1. 数据库的存储模式 1.1 关系型数据库 关系型数据库RDS(Relational Database Service),大部分为行式存储数据. 行式存储: 据以行相关的存储体系架构进行空间分配,主 ...

  3. 行式存储和列式存储的数据库

    定义 关系数据库采用的数据存储有两种方式:行式存储和列式存储(也被称为columnar或C-store) 行式存储 是按记录组织数据的数据库,将与记录相关联的所有数据彼此相邻地保存在内存中.面向行的数 ...

  4. Mysql框架<二> 行式存储与列式存储

    文章目录 数据处理场景分类 OLTP 联机事务处理 OLAP 联机分析处理 行式存储和列式存储 概念 特点 对比 数据处理场景分类 OLTP 联机事务处理 OLTP : on-line transac ...

  5. 行式存储和列式存储优缺点和paruqet文件结构

    参考文章:行式存储和列式存储优缺点和paruqet文件结构 优缺点分析 列存储优点: 单列数据保存在一起,不同列分开存储,导致存下同样一个表需要更多的Block文件,看起来是更复杂了,但是基于列和列分 ...

  6. “行式存储”和“列式存储”的区别

    https://www.jianshu.com/p/3d3950c9fb06 我们知道 当今的数据处理大致可分为两大类 联机事务处理 OLTP (on-line transaction process ...

  7. 五大存储模型关系模型、键值存储、文档存储、列式存储、图形数据

    五大存储模型关系模型.键值存储.文档存储.列式存储.图形数据 时间:2014-06-12 16:15来源:知行网www.zhixing123.cn 编辑:麦田守望者 昨天跟一同事讨论Sybase是不是 ...

  8. 行式数据库 VS 列式数据库

    行式数据库 VS 列式数据库 一   基本概念 1  行式数据库    http://zhusihu.blog.163.com/blog/static/178880235201102381753490 ...

  9. 行式数据库与列式数据库的对比

    导语:随着大数据的发展,现在出现的列式存储和列式数据库.它与传统的行式数据库有很大区别的. 正文: 行式数据库是按照行存储的,行式数据库擅长随机读操作不适合用于大数据.像SQL server,Orac ...

最新文章

  1. python获取mac、计算机id_python 获取本机IP、mac地址、计算机名的简单示例
  2. Spyder更改默认工作路径已经文件路径
  3. html javascript 引号
  4. 记一次小的51CTO聚会
  5. golang中的http conn实现分析
  6. MongoDB入门教程(1)
  7. 大厂面试官最喜欢问的面试难点
  8. 【BZOJ3328】PYXFIB【矩阵快速幂】【单位根反演】【二项式定理】
  9. 张小二:做一个幸福的胖子
  10. 未捕获的异常 'NSInternalInconsistencyException'
  11. [php] in_array 判断问题(坑)
  12. 深度css:关于浮动(float,clear)的图形化理解
  13. aboboo 上一句 快捷键_电脑软件推荐|这几个快捷键你一定能用得上
  14. Redis数据结构详解之Zset(五)
  15. 找最长的字符串 (15 分)
  16. netbean使用有感
  17. Kali Rolling更换登录界面的背景
  18. GVS与唯康教育达成战略合作,共建智能家居人才培养高地
  19. Ubuntu18.04安装GoldenDict词典【词库】
  20. IPC 38模组板图分析-雄迈IPG-50H10PE-SL-Hi3518E+OV9712

热门文章

  1. 大学里计算机专业很忙吗,大学里“特累”的10大专业,忙到没时间恋爱!
  2. 厨神之路十--减脂餐
  3. java闪屏_Java Tomcat 启动闪屏-原因之一---配置问题
  4. Unity学习_我终于终于把unity音乐这块用单例控制得死死的了(1)!!!!
  5. 线程的几种状态极其转换
  6. 计算机科学与技术领域主要免费网络资源数据库网址
  7. PS 学习笔记 22-矩形工具
  8. 为什么多肉不能多浇水
  9. Actifio Sky软件保护Oracle12cRAC,备份和还原的准备和相关工作
  10. 私域社群运营—朋友圈运营策略