列式存储的另一面

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: 列存是常见的数据存储技术,说到列存常常就意味着高性能,现代分析型数据库基本都会把列存作为标配,列存的基本原理是减少硬盘的读取量。一个数据表有多个列,但运算可能只会用到其中少数几列,采用列存时,用不着的列就不必读出来了,而采用行式存储时,则要把所有列都扫描一遍。当取用列只占总列数的小部分时,列存的 IO 时间优势会非常大,就会显得计算速度快了很多。不过,列存也有另一面,并不是在任何场景下都有优势。

列存是常见的数据存储技术,说到列存常常就意味着高性能,现代分析型数据库基本都会把列存作为标配,
列存的基本原理是减少硬盘的读取量。一个数据表有多个列,但运算可能只会用到其中少数几列,采用列存时,用不着的列就不必读出来了,而采用行式存储时,则要把所有列都扫描一遍。当取用列只占总列数的小部分时,列存的 IO 时间优势会非常大,就会显得计算速度快了很多。
不过,列存也有另一面,并不是在任何场景下都有优势。

列存的实现远比行存复杂,因为数据表的列数可以事先确定,但行数却在不断增长。行存时按记录顺序写出,有追加时继续写即可,使用单个文件也很容易实现。列存则不行,考虑到数据有追加的情况,事先就无法确定行数,不可能把一列全部写出去再写下一列。一般会分块处理,一个数据块写入固定的 N 行数据,写满后启用下一个数据块;读取时以块为单位,块内列存,块间可以理解成是行存。这需要有个专门的管理模块用目录表记着这些不断增加的数据块及其中每个列的信息,麻烦很多。所以列存很难在单个数据文件中实现,一般只会出现在专业的数据仓库产品中。

这种分块机制在数据量不太大的时候对并行计算不友好。并行时要能把数据分段。这有两个要求:每段数据量基本相同(每线程处理能力相当),可以较灵活的分段(事先不能预测并行数)。行存可以按数据行分段,很小数据量就可以并行了。列存就只能按块分段,块内数据不能再分。但是每块的记录数(就是那个 N)不能太小,否则还是会由于硬盘的最小读取单位而造成较大的浪费,极端情况 N=1 就相当于行存,而且 N 太小也会导致总数据量很大时的目录表很大,目录管理的负担过重。所以通常这个 N 会取到 100 万或更大的数,而总块数要在数百以上才能保证灵活分段,也就是说,总数据量达到几亿条以上时,列存的并行计算才会比较顺畅。
esProc SPL 有个倍增分段算法,可以让这个 N 随着数据量增加而变大,而总块数则维持固定。这样目录表规模也是固定的,在单个文件中也能方便地实现列存,较小数据量也能灵活分段并行。

从列存的原理上看,只有当运算涉及的列较少时,列存的优势才会明显。很多用于性能测试的案例(比如作为国际标准的 TPCH)也确实是这样,容易测出列存数据库的优势。但实际场景却不全是这样,像金融业务中,上百列的表且其中大部分都要用到的情况并不罕见,这时列存的效果就会大打折扣。虽然因为列存压缩比更高,读取量仍然会比行存更小,但涉及列很多时,优势并没那么明显,毕竟列存的读取过程也要比行存复杂很多。
所以,在实际场景中发现跑不出测试案例的性能时,也不要觉得很奇怪,也不表示测试是有假。

列存还会造成硬盘的随机读取。每列是连续存储的,但不同的列就不连续了。读的列越多造成的随机程度就越重,即使单线程单任务也会这样。对于固态硬盘,这个问题不算很严重,每一列都连续且上面那个 N 够大时,读取浪费占比很小,而固态硬盘没有寻道时间。
但对于机械硬盘,因为有寻道时间的存在,这将是灾难性的。在访问列数较多时,很可能发生还不如行存的性能好的现象。并发或并行还会进一步恶化这个问题。而通过加大缓冲区来缓解又会占用大量内存。
机械硬盘要慎用列存。

列存还有个较大的问题是它的索引性能要远比行存低。我们之前讲过索引表会存储有序的键值和对应记录在原表中的位置。对于行存来说,记录位置就是一个数;但列存就不一样了,一条记录的每个列各有各的位置,原则上需要都记录下来,这样一来,索引表几乎和原表一样大,空间占用大,读取成本高,这和复制原表后排序区别并不大了。
实际上当然不会这么做了,通常用的手段还是上述的分块机制,索引中只保存记录的序号。查找时从索引中读出序号,再定位到相应的块,然后从块起始点“数”到相应序号后读出列值。这个“数”的动作会针对每个列都执行,最好情况也要读出列数个硬盘单位,运气不好整个块都要扫描一遍,而行存索引一般只要读一两个硬盘单位就够了(视记录占的空间)。列存比行存的读取量会大出几十上百倍,碰到机械硬盘更是还有要命的寻道时间。所以列存基本上无法应对高并发查找的需求。

遍历用列存,查找用行存。遍历和查找都有高性能需求的数据,甚至有必要冗余两份存储。数据平台应该允许程序员根据实际情况决定采用的存储方式,而不是一刀切地替用户选择。
嗯,esProc SPL 就可以自由选择行存还是列存,还有带值索引方案可以为遍历用列存数据提供一套查找用的行存副本。

相关文章
|
13天前
|
SQL 存储 Cloud Native
揭秘TDengine:这个数据库如何以光速处理时间序列数据,颠覆你的世界观!
【8月更文挑战第7天】随着物联网的发展,数据生成呈爆炸式增长,催生了如TDengine这样的高性能时序数据库。TDengine采用优化的列式存储和标签索引,实现高速写入与高效压缩,减少存储空间的同时保持高性能。内置丰富的分析函数支持复杂的数据聚合操作,并通过数据复制保障高可靠性。其SQL接口易于使用,分布式架构便于扩展,且支持多种云环境部署,成为处理物联网、车联网等场景下时间序列数据的理想选择。
30 0
|
2月前
|
存储 运维 物联网
TDengine 助力中电启明星处理高复杂性和大数据量时序数据
智能电网和可再生能源推动电力系统数据爆炸式增长,TDengine 因其高效处理时序数据的能力,成为中电启明星信息系统深化应用项目的关键技术。该合作彰显 TDengine 在电力行业的影响力,已应用于多家电力企业,涉及能源大数据、风电运维、智慧能源等多个领域。中电启明星,作为国网信息通信股份的子公司,利用 TDengine 实现了数据管理和业务优化。TDengine,一款高性能时序数据库,为物联网和工业互联网提供一站式解决方案,助力行业数字化转型。
23 0
|
存储 算法 搜索推荐
海量数据处理
海量数据处理
144 0
|
12月前
|
SQL 存储 分布式数据库
如何同时兼顾多维分析和快速查询的需求?Kudu来帮忙!彭文华
如何同时兼顾多维分析和快速查询的需求?Kudu来帮忙!彭文华
|
存储 小程序 编译器
”神仙修炼“之C的数据存储
char //字符型,所占存储空间为1字节int //基本整形,所占存储空间为4字节unsigned int //无符号整型,所占存储空间为4字节long (int) //长整型,所占存储空间为4/8字节unsigned long //无符号长整型,所占存储空间为4/8字节long long (int) //长长整型,所占存储空间为8字节short (int) //短整型,所占存储空间为2字节unsigned short //无符号短整型,所占存储空间为2字节。
79 0
|
SQL 存储 前端开发
宽表为什么横行?
宽表为什么横行?
41412 8
宽表为什么横行?
来,一起“八卦”一下数据湖
经过了几年的发展,在大数据时代红极一时的数据湖也在理想与现实之间慢慢发生着蜕变。现在,数据湖已经成为海量数据存储与分析的重要承载方式。
989 0
来,一起“八卦”一下数据湖
|
SQL 负载均衡 算法
炸!业界难题,跨库分页的几种常见方案
为什么需要研究跨库分页?
1554 0
炸!业界难题,跨库分页的几种常见方案
|
存储 调度 开发者
奈学:数据湖有哪些缺点?
数据湖本身是一个中心化的存储,能够存储任意规模的结构化与非结构化数据。数据湖相比数据仓库有很多的优势,但大多是在理想状态下的,一旦执行起来还有很多技术挑战。
|
存储 监控 数据库
时序数据库连载系列: RISElab的大杀器Confluo
挑战 随着越来越多的应用达到每秒千万级的数据点采集能力,比如终端IoT网络监控,智能家居,数据中心等等。 并且这些数据被应用于在线查询展示,监控,离线根因分析和系统优化。 这些场景要求系统具备高速写入,低延迟的在线查询以及低开销的离线查询的能力。
2651 0