列式存储的另一面

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

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

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

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

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

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

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

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

相关文章
|
6月前
|
存储 运维 物联网
TDengine 助力中电启明星处理高复杂性和大数据量时序数据
智能电网和可再生能源推动电力系统数据爆炸式增长,TDengine 因其高效处理时序数据的能力,成为中电启明星信息系统深化应用项目的关键技术。该合作彰显 TDengine 在电力行业的影响力,已应用于多家电力企业,涉及能源大数据、风电运维、智慧能源等多个领域。中电启明星,作为国网信息通信股份的子公司,利用 TDengine 实现了数据管理和业务优化。TDengine,一款高性能时序数据库,为物联网和工业互联网提供一站式解决方案,助力行业数字化转型。
70 0
|
存储 算法 搜索推荐
海量数据处理
海量数据处理
164 0
|
存储 关系型数据库 MySQL
MySQL索引探秘:加速数据检索的必备利器
MySQL索引探秘:加速数据检索的必备利器
274 0
|
存储 缓存 自然语言处理
阿里二面:MySQL索引是怎么支撑千万级表的快速查找?
在 MySQL 官方提到,改善操作性能的最佳方法 SELECT 在查询中测试的一个或多个列上创建索引。索引条目的作用类似于指向表行的指针,从而使查询可以快速确定哪些行与WHERE子句中的条件匹配,并检索这些行的其他列值。所有MySQL数据类型都可以建立索引。
280 0
|
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字节。
110 0
|
消息中间件 存储 SQL
四款面向高并发、海量级分布式存储的分布式架构对比
四款面向高并发、海量级分布式存储的分布式架构对比
四款面向高并发、海量级分布式存储的分布式架构对比
|
存储 SQL 关系型数据库
选型宝访谈:当业务炸裂式增长 ,如何让关系型数据库平滑扩展?
当业务炸裂式增长,如何让关系型数据库平滑扩展? 爱奇艺、饿了么、摩拜单车…这些国民级应用的疯狂增长背后,是怎样一款国产的分布式NewSQL数据库,在做平滑支撑? 对话内容 选型宝:您怎么理解数据库技术的发展历程,分几个阶段?黄东旭:其实整个大的背景大概是这样,上世纪六七十年代就有关系数据库概念了,一直发展到90年代,基本都是以IBM DB2、 Oracle,微软的SQL Server等单机关系型数据库为主的行业现状。
1612 0