前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊一聊数据库的行存与列存

聊一聊数据库的行存与列存

作者头像
芋道源码
发布2022-07-26 17:47:15
1.4K0
发布2022-07-26 17:47:15
举报
文章被收录于专栏:芋道源码1024

点击上方“芋道源码”,选择“设为星标

管她前浪,还是后浪?

能浪的浪,才是好浪!

每天 10:33 更新文章,每天掉亿点点头发...

源码精品专栏

来源:my.oschina.net/gaussdb

/blog/5544252


好多人最开始学习数据库的时候,是关系数据库,数据以表格形式存储,一行表示一条记录。其实这种就是典型的行存储(Row-based store),将表按行存储到磁盘分区上。

而一些数据库还支持列存储(Column-based store),它将表按列存储到磁盘分区上。

存储方式比较

这两者的差异如下图:

从图上可以看出,行存的时候,一行记录的属性值存储在临近的空间,然后接着是下一条记录的属性值。

而列存的时候,单个属性所有的值存储在临近的的空间,即一列的所有数据连续存储的,每个属性有不同的空间。

这里,大家可以自行思考一下这两种那种更适合查询,那种更适合修改?

在数据写入上的对比:

1)行存储的写入是一次完成。写入建立在操作系统的文件系统上,可以保证写入过程的成功或者失败,数据的完整性因此可以确定。

2)列存储由于需要把一行记录拆分成单列保存,写入次数明显比行存储多,再加上磁头需要在盘片上移动和定位花费的时间,实际时间消耗会更大。所以,行存储在写入上占有很大的优势。

3)还有数据修改,这实际也是一次写入过程。所以,数据修改也是以行存储占优。

在数据读取上的对比:

1)行存储通常将一行数据完全取出,如果只需要其中几列数据的情况,就会存在冗余列,出于缩短处理时间的考量,消除冗余列的过程通常是在内存中进行的。

2)列存储每次读取的数据是集合的一段或者全部,不存在冗余性问题,查找内容连续存储,特别适合投影。

3) 两种存储的数据分布。由于列存储的每一列数据类型是同质的,不存在二义性问题。比如说某列数据类型为整型 (int),那么它的数据集合一定是整型数据。这种情况使数据解析变得十分容易。相比之下,行存储则要复杂得多,因为在一行记录中保存了多种类型的数据,数据解析需要在多种数据类型之间频繁转换,这个操作很消耗 CPU,增加了解析的时间。所以,列存储的解析过程更有利于分析大数据。

4)从数据的压缩以及更性能的读取来对比。同一列的数据,数据类型一致,列存的模式下就适合数据压缩,不同的列可以采用不同的压缩算法,压缩存储就会带来 IO 性能的提升。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

优缺点比较

表的存储类型是表定义设计的第一步,客户业务类型是决定表的存储类型的主要因素。行、列存储模型各有优劣,建议根据实际情况选择。

行、列存优缺点及适用场景比较见下表:

行存

列存

优点

数据被保存在一起。INSERT/UPDATE 容易。

查询时只有涉及到的列会被读取。投影 (Projection) 很高效。任何列都能作为索引。

缺点

选择 (Selection) 时即使只涉及某几列,所有数据也都会被读取。

选择完成时,被选择的列要重新组装。INSERT/UPDATE 比较麻烦。点查询不适合。

适用场景

点查询 (返回记录少,基于索引的简单查询)。增、删、改操作较多的场景。

统计分析类查询 (OLAP,比如数据仓库业务,此类型的表上会做大量的汇聚计算,且涉及的列操作较少,关联、分组操作较多)。即时查询(查询条件不确定,行存表扫描难以使用索引)。

行存与列存实验

openGauss 支持行列混合存储,可以在建表的时候指定存储方式。下面我们进行一下实验。

实验环境 :华为云服务器 + openGauss 企业版 3.0.0 + openEuler20.03 创建行存表 custom1 和列存表 custom2 ,插入 50 万条记录。

代码语言:javascript
复制
openGauss=# create table custom1 (id integer,name varchar2(20)); 
CREATE TABLE 
openGauss=# create table custom2 (id integer,name varchar2(20)) with (orientation = column); 
CREATE TABLE 
openGauss=# insert into custom1 select n,'testtt'||n from generate_series(1,500000) n; 
INSERT 0 500000 
openGauss=# insert into custom2 select * from custom1; 
INSERT 0 500000

我们看下两个表的存储空间,比较 Size 列,可以看出列存表比行存表占用存储空间小的非常多,差不多是行存表空间的 1/7。

代码语言:javascript
复制
openGauss=# \d+ 
                                           List of relations 
 Schema |    Name    | Type  | Owner |    Size    |               Storage                | Description 
--------+------------+-------+-------+------------+--------------------------------------+------------- 
 public | custom1    | table | omm   | 24 MB      | {orientation=row,compression=no}     | 
 public | custom2    | table | omm   | 3104 kB    | {orientation=column,compression=low} |

比较下插入一条新记录的时间,列存表要稍微慢一点。

代码语言:javascript
复制
openGauss=# explain analyze insert into custom1 values(1,'zhang3'); 
                                          QUERY PLAN 
----------------------------------------------------------------------------------------------- 
 [Bypass] 
 Insert on custom1  (cost=0.00..0.01 rows=1 width=0) (actual time=0.059..0.060 rows=1 loops=1) 
   ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1) 
 Total runtime: 0.135 ms 
(4 rows) 
 
openGauss=# explain analyze insert into custom2 values(1,'zhang3'); 
                                          QUERY PLAN 
----------------------------------------------------------------------------------------------- 
 Insert on custom2  (cost=0.00..0.01 rows=1 width=0) (actual time=0.119..0.120 rows=1 loops=1) 
   ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.002 rows=1 loops=1) 
 Total runtime: 0.207 ms 
(3 rows)

最后删除测试表。

代码语言:javascript
复制
openGauss=# drop table custom1; 
DROP TABLE 
openGauss=#drop table custom2; 
DROP TABLE

感兴趣的同学可以自己测试更多的的场景,比如创建大宽表、update 表等场景测试下。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

选择建议

  • 更新频繁程度:数据如果频繁更新,选择行存表。
  • 插入频繁程度:频繁的少量插入,选择行存表。一次插入大批量数据,选择列存表。
  • 表的列数:一般情况下,如果表的字段比较多即列数多(大宽表),查询中涉及到的列不多的情况下,适合列存储。如果表的字段个数比较少,查询大部分字段,那么选择行存储比较好。
  • 查询的列数:如果每次查询时,只涉及了表的少数(<50% 总列数)几个列,选择列存表。(不要问剩下的列干啥用,甲方说有用就是有用。)
  • 压缩率:列存表比行存表压缩率高。但高压缩率会消耗更多的 CPU 资源。

注意事项

列存由于特殊的存储方式,使用时约束比较多。比如,列存表不支持数组、不支持生成列、不支持创建全局临时表、不支持外键,支持的数据类型也会比行存要少。使用时需要查看对应的数据库文档。



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

已在知识星球更新源码解析如下:

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

代码语言:javascript
复制
文章有帮助的话,在看,转发吧。谢谢支持哟 (*^__^*)
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-07-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 芋道源码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 存储方式比较
  • 优缺点比较
  • 选择建议
  • 注意事项
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档