OceanBase 4.3 特性解析:列存技术

news2024/11/17 7:49:37

在涉及大规模数据的复杂分析或即时查询时,列式存储是支撑业务负载的关键技术之一。相较于传统的行式存储,列式存储采用了不同的数据文件组织方式,它将表中的数据以列为单位进行物理排列。这种存储模式允许在分析过程中,查询计算仅需针对所需的列数据进行扫描,从而避免了不必要的整行扫描,显著降低了IO和内存等资源的消耗,进而提升了计算效率。此外,列式存储天然具备更佳的数据压缩优势,能够实现较高的压缩比,有效节约了存储空间,并降低了网络传输带宽的占用。

常见的列存存储引擎在实现上往往假设不会有大量随机更新, 尽量保证列存组织数据是静态的。当真正伴随大量数据随机更新时,也会不可避免的存在系统性能问题。OceanBase LSM-Tree 架构可以将基线数据和增量数据分别处理,正好可以解决这一场景问题。因此 OceanBase 4.3 版本基于当前架构基础进行扩展,正式推出列存引擎,在一个架构、一个数据库上,实现了列存和行存数据存储一体化,兼顾 TP 和 AP 查询性能。

为了让有分析诉求的用户顺畅使用新版本,围绕列存引擎,从优化器到执行器、从 DDL 到事务处理等多模块都进行了适配优化。包括基于列存的新的代价模型和向量化引擎,查询下压功能的扩展和增强,Skip Index,新的列式编码算法,自适应 Compaction 等。本文将深入探讨 OceanBase 4.3 版本带来的列存能力、应用场景,以及用户关心的未来发展规划

一、列存整体架构

OceanBase 作为原生分布式数据库,默认情况下会为用户数据创建多个副本。为了充分利用多副本的优势,为用户提供数据强校验和数据迁移重用等增强体验,OceanBase 自研的 LSM-Tree 存储引擎做了深度优化:

○  基线数据:相较于业内常见的 LSM-Tree 实现逻辑,OceanBase 提出了"每日合并"的概念。用户可定期或根据操作选择一个全局版本号,所有副本的租户数据将在这个版本上进行一轮 Major Compaction,生成这个版本的基线数据。所有副本在同一版本下的基线数据完全一致,物理上保持一致。

○  增量数据:相对于基线数据,增量数据是指在最新版本的基线数据之后写入的数据。增量数据可以是刚写入Memtable的内存数据,也可以是已经转储为SSTable 的磁盘数据。增量数据在每个副本中独立维护,不保证一致性,并且包含了所有多版本的数据。

基于列存应用场景随机更新量可控的背景,OceanBase 4.3 结合自身基线数据和增量数据的特质,提出了一套对上层透明的列存实现方式:基线数据存储为列存模式,增量数据保持行存,确保用户所有 DML 操作不受影响,上下游同步无缝接入,列存表数据仍然可以像行存表一样进行所有事务操作。列存模式下每列数据存储为一个独立 SSTable,所有列的 SSTable 组合成为一个虚拟 SSTable 作为用户的列存基线数据。同时,用户可根据实际业务诉求在建表环节指定设置,基线数据可以支持行存、列存、行存列存冗余三种模式,提供更好的灵活性。

1716796000

OceanBase 4.3 版本中不仅在存储引擎中实现了列存模式,更从优化器、执行器以等多维度进行列存的适配优化。用户在迁移到列存模式后基本上不会感受到业务变化,能够像使用行存一样享受到列存带来的性能优势。列存引擎的全面优化,也使得 OceanBase 真正实现了 TP & AP 一体化,实现了一套引擎、一套代码支持不同类型业务的目标,打造更加完善的 HTAP 混合负载实时分析能力。

二、OceanBase 实现列存,有哪些天然优势

(一)成熟的 LSM-Tree 引擎

与传统数据库相比,OceanBase 拥有天然的 Delta Store,非常适合实现列存。基于 LSM-Tree 存储引擎的支持,OceanBase 列存不仅支持完整的事务,而且基础算子的性能不弱于传统的 TP 数据库。在列存上,完整的事务支持使得 OceanBase 在更新方面具有天然优势,所有事物语义和多样事物的管理对用户来说完全透明的,用户可以轻松切换到列存模式,将列存数据库当成行存数据库使用,对业务完全透明,不需要做任何改动。

(二)完善的执行引擎

OceanBase 不仅拥有完整的执行引擎,还具备通用的优化器是通用的。在行存模式下,OceanBase 已经实现向量化存储引擎的无缝对接,无需任何修改即可支持向量化执行。此外,OceanBase 实现一套优化器的代码在上层对行存和列存进行不同代价的估算,使得用户的 SQL 可以自动选择行存或列存。

(三)灵活的原生分布式

OceanBase 天然支持分布式并行查询引擎,未来还可以轻松扩展到列存异构副本。列存异构副本的优势体现在用户需要完全硬隔离的应用场景中,未来的OceanBase 版本将新增这一功能。

综上所述,OceanBase 凭借其天然优势推动了 4.3 版本中列存功能的实现。引入列存储引擎后,OceanBase 整体架构在外部表现上完全不变,并且从架构层面支持了列存相关的三种模式:

○  基线列存 +增量行存:基线数据采用列存方式存储,增量数据采用行存方式存储。

○  灵活的行存/列存索引:可以对行存表建立列存索引,也可以对列存表建立行存索引,还可以对两者进行任意组合。由于所有列存表和索引的底层存储结构是统一的,因此 OceanBase 可以自动支持列存和行存的索引。

○  列存副本:OceanBase 正在研发的列存副本功能。得益于原生分布式能力,只需对模式或表做部分修改,即可以通过 Compaction 将新增的只读副本转换为列存存储模式。

三、列存使用方法

(一)默认创建列存表

对于 OLAP 业务需求,我们推荐默认创建列存表。如何确保租户创建出来的表默认为列存表?只通过下面的配置项即可实现:

alter system set default_table_store_format = "column";

随后我们创建的表格没有指定 column group 时,默认创建为列存表。

OceanBase(root@test)>create table  t1 (c1 int primary key, c2 int ,c3 int);
Query OK,0 rows affected (0.301 sec)

OceanBase(root@test)>show create table t1;

CREATE TABLE `t1` (
  `c1` int(11) NOT NULL,
  `c2` int(11) DEFAULT NULL,
  `c3` int(11) DEFAULT NULL,
  PRIMARY KEY (`c1`)
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 1 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0
WITH COLUMN GROUP(each column)

1 row in set (0.101 sec)

(二)指定创建列存表

为了方便用户创建列存表,列存引入新的语法 with column group,当用户建表时最后指定 `with column group(each column)` ,即表示创建列存表。

OceanBase(root@test)>create table  tt_column_store (c1 int primary key, c2 int ,c3 int) with column group (each column);
Query OK,0 rows affected (0.308 sec)

OceanBase(root@test)>show create table tt_column_store;

CREATE TABLE `tt_column_store` (
  `c1` int(11) NOT NULL,
  `c2` int(11) DEFAULT NULL,
  `c3` int(11) DEFAULT NULL,
  PRIMARY KEY (`c1`)
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 1 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 WITH COLUMN GROUP(each column)

1 row in set (0.108 sec)

(三)指定创建列存行存冗余表

在某些场景下,用户可以容忍一定程度的数据冗余,以满足 AP/TP 业务场景的双重需求。此时,可以增加行存数据的冗余,通过 `with column group` 语法增加指定 `all columns` 即可实现。

create table  tt_column_row (c1 int primary key, c2 int , c3 int) with column group (all columns, each column);
Query OK, 0 rows affected (0.252 sec)

OceanBase(root@test)>show create table tt_column_row;
CREATE TABLE `tt_column_row` (
  `c1` int(11) NOT NULL, 
  `c2` int(11) DEFAULT NULL, 
  `c3` int(11) DEFAULT NULL, 
  PRIMARY KEY (`c1`)
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 1 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 WITH COLUMN GROUP(all columns, each column)

1 row in set (0.075 sec)

(四)列存扫描

如何查看是否列存扫描计划?计划展示上新增 COLUMN TABLE FULL SCAN,描述列存表的范围扫描。

OceanBase(root@test)>explain select * from tt_column_store;
+--------------------------------------------------------------------------------------------------------+
| Query Plan                                                                                             |
+--------------------------------------------------------------------------------------------------------+
| =================================================================                                      |
| |ID|OPERATOR              |NAME           |EST.ROWS|EST.TIME(us)|                                      |
| -----------------------------------------------------------------                                      |
| |0 |COLUMN TABLE FULL SCAN|tt_column_store|1       |7           |                                      |
| =================================================================                                      |
| Outputs & filters:                                                                                     |
| -------------------------------------                                                                  |
|   0 - output([tt_column_store.c1], [tt_column_store.c2], [tt_column_store.c3]), filter(nil), rowset=16 |
|       access([tt_column_store.c1], [tt_column_store.c2], [tt_column_store.c3]), partitions(p0)         |
|       is_index_back=false, is_glOceanBaseal_index=false,                                                      |
|       range_key([tt_column_store.c1]), range(MIN ; MAX)always true                                     |
+--------------------------------------------------------------------------------------------------------+

计划展示上新增 COLUMN TABLE GET,描述列存表上的指定主键的 get 操作。

OceanBase(root@test)>explain select * from tt_column_store where c1 = 1;
+--------------------------------------------------------------------------------------------------------+
| Query Plan                                                                                             |
+--------------------------------------------------------------------------------------------------------+
| ===========================================================                                            |
| |ID|OPERATOR        |NAME           |EST.ROWS|EST.TIME(us)|                                            |
| -----------------------------------------------------------                                            |
| |0 |COLUMN TABLE GET|tt_column_store|1       |14          |                                            |
| ===========================================================                                            |
| Outputs & filters:                                                                                     |
| -------------------------------------                                                                  |
|   0 - output([tt_column_store.c1], [tt_column_store.c2], [tt_column_store.c3]), filter(nil), rowset=16 |
|       access([tt_column_store.c1], [tt_column_store.c2], [tt_column_store.c3]), partitions(p0)         |
|       is_index_back=false, is_global_index=false,                                                      |
|       range_key([tt_column_store.c1]), range[1 ; 1],                                                   |
|       range_cond([tt_column_store.c1 = 1])                                                             |
+--------------------------------------------------------------------------------------------------------+
12 rows in set (0.051 sec)

如何通过 Hint 指定列存行存冗余表走列存扫描?对于列存行存冗余表,优化器会根据代价选择走行存或者列存扫描,如简单场景做全表扫描,会默认使用行存生成计划。

OceanBase(root@test)>explain select * from tt_column_row;
+--------------------------------------------------------------------------------------------------+
| Query Plan                                                                                       |
+--------------------------------------------------------------------------------------------------+
| ========================================================                                         |
| |ID|OPERATOR       |NAME         |EST.ROWS|EST.TIME(us)|                                         |
| --------------------------------------------------------                                         |
| |0 |TABLE FULL SCAN|tt_column_row|1       |3           |                                         |
| ========================================================                                         |
| Outputs & filters:                                                                               |
| -------------------------------------                                                            |
|   0 - output([tt_column_row.c1], [tt_column_row.c2], [tt_column_row.c3]), filter(nil), rowset=16 |
|       access([tt_column_row.c1], [tt_column_row.c2], [tt_column_row.c3]), partitions(p0)         |
|       is_index_back=false, is_global_index=false,                                                |
|       range_key([tt_column_row.c1]), range(MIN ; MAX)always true                                 |
+--------------------------------------------------------------------------------------------------+

如果用户希望通过手动调优走列存扫描,可以通过 hint USE_COLUMN_TABLE 来强制 tt_column_row 表走列存扫描。

OceanBase(root@test)>explain select /*+ USE_COLUMN_TABLE(tt_column_row) */ * from tt_column_row;
+--------------------------------------------------------------------------------------------------+
| Query Plan                                                                                       |
+--------------------------------------------------------------------------------------------------+
| ===============================================================                                  |
| |ID|OPERATOR              |NAME         |EST.ROWS|EST.TIME(us)|                                  |
| ---------------------------------------------------------------                                  |
| |0 |COLUMN TABLE FULL SCAN|tt_column_row|1       |7           |                                  |
| ===============================================================                                  |
| Outputs & filters:                                                                               |
| -------------------------------------                                                            |
|   0 - output([tt_column_row.c1], [tt_column_row.c2], [tt_column_row.c3]), filter(nil), rowset=16 |
|       access([tt_column_row.c1], [tt_column_row.c2], [tt_column_row.c3]), partitions(p0)         |
|       is_index_back=false, is_global_index=false,                                                |
|       range_key([tt_column_row.c1]), range(MIN ; MAX)always true                                 |
+--------------------------------------------------------------------------------------------------+

类似的方式,通过 Hint NO_USE_COLUMN_TABLE 可以强制表不进行列存扫描。

OceanBase(root@test)>explain select  /*+ NO_USE_COLUMN_TABLE(tt_column_row) */ c2 from tt_column_row;
+------------------------------------------------------------------+
| Query Plan                                                       |
+------------------------------------------------------------------+
| ========================================================         |
| |ID|OPERATOR       |NAME         |EST.ROWS|EST.TIME(us)|         |
| --------------------------------------------------------         |
| |0 |TABLE FULL SCAN|tt_column_row|1       |3           |         |
| ========================================================         |
| Outputs & filters:                                               |
| -------------------------------------                            |
|   0 - output([tt_column_row.c2]), filter(nil), rowset=16         |
|       access([tt_column_row.c2]), partitions(p0)                 |
|       is_index_back=false, is_global_index=false,                |
|       range_key([tt_column_row.c1]), range(MIN ; MAX)always true |
+------------------------------------------------------------------+
11 rows in set (0.053 sec)

四、未来展望

OceanBase 4.3 列存的引入,为用户的数据分析以及实时分析场景提供了新的选择。未来,OceanBase 列存将持续演进,为用户带来更加丰富的 feature、更强劲的性能以及更灵活的部署模式。

第一,更丰富的功能。目前,我们支持纯列存储引擎,未来将实现可自定义的灵活列组组织支持,满足不同场景的分析需求。此外,我们计划将增量旁路导入功能进一步增强,帮助用户实现高效的数据导入,缩短数据分析准备时间。

第二,更好的性能。增强 Skip Index 的支持,使其能够更好地满足用户的查询需求。此外,我们计划实现格式一体化,目前存储的格式多样化,未来将实现存储格式与 SQL 向量化引擎的紧密结合,使得在执行 SQL 计算时,系统能够识别不同的存储格式,从而帮助用户节省更多的数据转换开销。

第三,更灵活的部署模式。在未来的版本中,我们将支持 OLAP 所需的异构副本,以满足用户对强依赖异构副本的需求。此外,未来还将支持存算分离模式,使得所有用户的 AP 数据库都能够以更低的成本享受存储与计算的分离。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1799992.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt之QGraphicsView —— 笔记3:矩形图元连接(附完整源码)

效果 完整源码 注意:在ui文件中拖入一个QGraphicsView类窗口控件,然后用MyGraphicsView提升该类。 main.cpp #include "widget.h" #include <QApplication>int main(

SpringCloud Consul基础入门与使用实践总结

【1】Consul简介 官网地址&#xff1a;https://www.consul.io/intro/index.html 下载地址&#xff1a;https://www.consul.io/downloads.html 中文文档&#xff1a;https://www.springcloud.cc/spring-cloud-consul.html ① 基础概念 Consul 是一套开源的分布式服务发现和…

Apache安装教程

目录 一、Apache知识点 Apache服务简介 Apache下载网址 Apache的主要特点 二、Apache服务的搭建 1. 关闭防火墙 2. 安装依赖环境以及编译工具 3. 将apache安装包拖入xshell内 4. 解压压缩包 5. 进入httpd主包 6. 指定安装路径&#xff0c;启用字符集支持等 7. 优化执…

工厂模式——工厂方法模式+注册表

工厂方法模式的瑕疵 在前一篇笔记中我们介绍了工厂方法模式&#xff0c;示例的类图如下&#xff1a; 考虑一种情况&#xff1a;现在要在程序运行时&#xff0c;根据外部资源&#xff0c;动态的实例化对象。也就是说在编译期我们无法知道要实例化的对象的类型。因此在实例化的过…

TP8 定时任务yzh52521/http-crontab 教程

官方文档&#xff1a; https://www.thinkphp.cn/ext/36 源码仓&#xff1a; http-crontab: 接口化秒级定时任务管理 GitHub - yuanzhihai/http-crontab: 接口化秒级定时任务管理 一、composer 安装 composer require yzh52521/http-crontab 二、启动yzh52521/http-cronta…

树的遍历详解

目录 树的静态写法 树的先根遍历 树的层次遍历 从树的遍历看DFS和BFS DFS与先根遍历 BFS与层次遍历 树的静态写法 这里讨论的树是一般意义上的树&#xff0c;即子结点个数不限且子节点没有先后次序的树。 建议使用静态写法进行结点的定义 struct node{typename data;i…

UI框架与MVC模式详解(1)——逻辑与数据分离

【效率最高的耦合方式】 以实际的例子来说明&#xff0c;更容易理解些。 这里从上到下&#xff0c;从左到右共有8个显示项&#xff0c;如果只需要显示这8个&#xff0c;不会做任何改变&#xff0c;数据固定&#xff0c;那么我们只需要最常规的思路去写就好&#xff0c;这是最…

antdv 穿梭框

antd的穿梭框的数据貌似只接收key和title&#xff0c;而且必须是字符串&#xff08;我测试不是字符串的不行&#xff09;&#xff0c; 所以要把后端返回的数据再处理一下得到我们想要的数据 除了实现简单的穿梭框功能&#xff0c;还想要重写搜索事件&#xff0c;想达到的效果是…

时间序列新范式!多尺度+时间序列,刷爆多项SOTA

当我们面对复杂模式和多变周期的应用场景&#xff08;比如金融市场分析&#xff09;时&#xff0c;采用多尺度时间序列来做分析和预测是个更好的选择。 这是因为&#xff1a;传统时序方法通常只用固定时间窗口来提取信息&#xff0c;难以适应不同时间尺度上的模式变化。但多尺…

opencv实战小结-银行卡号识别

实战1-银行卡号识别 项目来源&#xff1a;opencv入门 项目目的&#xff1a;识别传入的银行卡照片中的卡号 难点&#xff1a;银行卡上会有一些干扰项&#xff0c;如何排除这些干扰项&#xff0c;并且打印正确的号码是一个问题 最终效果如上图 实现这样的功能需要以下几个步骤…

蓝桥云课第12届强者挑战赛

第一题&#xff1a;字符串加法 其实本质上就是一个高精度问题&#xff0c;可以使用同余定理的推论 &#xff08;ab&#xff09;%n((a%n)(b%n))%n; #include <iostream> using namespace std; const int mod1e97; int main() {string a,b;cin>>a>>b;ab;int …

GAN网络理论和实验(二)

文章目录 一、说明二、什么是生成对抗网络&#xff1f;三、判别模型与生成模型四、生成对抗网络的架构五、你的第一个 GAN六、准备训练数据七、实现鉴别器八、实现生成器九、训练模型十、检查 GAN 生成的样本十一、使用 GAN 生成手写数字十二、准备训练数据十三、实现鉴别器和生…

LabVIEW减压阀和温控阀综合测试系统

在使用LabVIEW开发阀门测试软件时&#xff0c;特别是针对减压阀和温控阀&#xff0c;测试内容和注意事项包括以下方面&#xff1a; 测试内容 压力测试&#xff1a; 入口压力&#xff1a;测量阀门在不同入口压力下的表现。 出口压力&#xff1a;确保减压阀能够将出口压力控制在…

【TIPs】 Visual Stadio 2019 中本地误使用“git的重置 - 删除更改 -- hard”后,如何恢复?

环境&#xff1a; VS 2019Windows10本地版本管理&#xff08;非远程&#xff09; 前言&#xff1a; git 在Visual Stadio 2019中集成了git的版本管理&#xff0c;在本地用来做版本管理&#xff0c;本来比较好用。 不过有一次&#xff0c;由于拿最初始的版本的时候&#xf…

代驾公司在市场竞争中如何保持优势?

在竞争激烈的市场中&#xff0c;代驾公司可以通过多种策略保持其竞争优势&#xff0c;包括利用市场潜力、创新服务模式、提高服务效率以及加强品牌建设等。以下是具体的策略&#xff1a; 利用市场潜力 汽车产业空间巨大&#xff1a;随着汽车保有量的增加&#xff0c;代驾行业…

centos官方yum源不可用 解决方案(随手记)

昨天用yum安装软件的时候&#xff0c;就报错了 [rootop01 ~]# yum install -y net-tools CentOS Stream 8 - AppStream 73 B/s | 38 B 00:00 Error: Failed to download metadata for repo appstream: Cannot prepare internal mirrorlis…

Intel VT-x怎么开启?如何解决VMware打开虚拟机报错问题?

许多小伙伴在安装完VMware不能打开虚拟机&#xff0c;每次打开都会出现一个“此主机支持 Intel VT-x&#xff0c;但 Intel VT-x 处于禁用状态”的报错&#xff0c;然后因此启动不了虚拟机。今天小编就带来如何解决这个报错的方法。 什么是Intel VT-x&#xff1f; 这是英特尔cp…

从零开始手把手Vue3+TypeScript+ElementPlus管理后台项目实战六(引入pinia,注册成功返回的信息在主页显示)

简介 pinia是多页面之间共享数据的一个组件&#xff0c;用法比较简单&#xff0c;具体参照以下。 安装Pinia pnpm install pinia引入Pinia main.ts 新增store src目录下新建stores目录&#xff0c;stores目录下新增user.ts文件 import { ref } from vue; import { define…

论文代码解读STPGNN

1.前言 本次代码文章来自于《2024-AAAI-Spatio-Temporal Pivotal Graph Neural Networks for Traffic Flow Forecasting》&#xff0c;基本模型结构如下图所示&#xff1a; 文章讲解视频链接 代码开源链接 接下来就开始代码解读了。 2.代码解读 class nconv(nn.Module):de…

离散数学答疑 3

&#xff5e;A&#xff1a;A的补集 有时候空集是元素&#xff0c;有时候就是纯粹的空集 A-B的定义&#xff1a; 笛卡尔积&#xff1a; 求等价关系&#xff1a;先求划分再一一列举 不同划分&#xff1a;分几块。一块&#xff1a;两块&#xff1a;三块&#xff1a;分别计算 Ix是…