蒙古语网站建设,男女做那个全面视频网站,wordpress中点击图片_图片显示出来后的底色,甘肃制作网站原文链接#xff1a;OceanBase 社区 对于分析类查询#xff0c;列存可以极大地提升查询性能#xff0c;也是 OceanBase 做好 HTAP 和 OLAP 的一项不可缺少的特性。本文介绍 OceanBase 列存的实现特色。
OceanBase从诞生起就一直坚持LSM-Tree架构#xff0c;不断打磨功能支… 原文链接OceanBase 社区 对于分析类查询列存可以极大地提升查询性能也是 OceanBase 做好 HTAP 和 OLAP 的一项不可缺少的特性。本文介绍 OceanBase 列存的实现特色。
OceanBase从诞生起就一直坚持LSM-Tree架构不断打磨功能支持了各类典型的 TP 类型业务持续优化性能满足各种极限负载压力积累了大量工程实践经验打造出一套纯自主研发且有充分特色的业界领先LSM-tree 存储引擎。常见的 OLAP 场景往往是批量写入不会有大量随机更新尽量保证列存组织数据是静态的这种场景天然适合 LSM-Tree 架构。
在4.3版本基于原有技术积累OceanBase 存储引擎继续扩展实现对列存的支持实现存储一体化一套代码一个架构一个OBServer列存数据和行存数据完美共存这样真正实现了对TP类和AP类查询的性能的兼顾。
整体架构
OceanBase作为原生分布式数据库用户数据默认会多副本存储为了利用多副本的优势为用户提供数据强校验以及迁移数据重用等进一步的增强体验自研的LSM-Tree存储引擎也做了较多的针对性设计首先每个用户数据整体可以分成两个大部分基线数据和增量数据。
基线数据。不同于其它主流LSM-Tree数据库OceanBase利用分布式多副本的基础提出每日合并的概念租户会定期或者根据用户操作选择一个全局版本号租户数据的所有副本均以这个版本完成一轮Major Compaction最后生成这个版本的基线数据所有副本同一个版本的基线数据物理完全一致。增量数据。相对基线数据而言用户数据在最新版本的基线数据之后所有写入数据均属于增量数据具体来说增量数据可以是用户刚写入Memtable的内存数据也可以是已经转储为SSTable的磁盘数据。 对于用户数据的所有副本来说增量数据各个副本独立维护不保证一致并且不同于基线数据基于指定版本生成增量数据包含所有多版本数据。
基于列存应用场景随机更新量可控的背景OceanBase结合自身基线数据和增量数据的特质提出了一套对上层透明的列存实现方式
基线数据存储为列存模式增量数据保持行存用户所有 DML 操作不受影响上下游同步无缝接入列存表数据仍然可以像行存表一样进行所有事务操作。列存模式下每列数据存储为一个独立SSTable所有列的SSTable组合成为一个虚拟SSTable作为用户的列存基线数据如下图所示。根据用户建表指定设置基线数据可以有行存列存行存列存冗余三种模式。 我们不仅在存储引擎中实现了列存模式为了让用户能够更容易从其它 OLAP 数据库迁移过来以及帮助之前有 OLAP 需求的 OceanBase 老客户升级到列存从优化器到执行器以及存储其它相关模块都针对列存进行了适配以及优化让用户迁移到列存后基本对业务无感能够像使用行存一样享受到列存带来的性能优势。 也让OceanBase真正实现了TP/AP一体化实现一套引擎一套代码支持不同类型业务的目标打造完善的HTAP引擎。 SQL一体化 为列存设计实现了新的代价模型并增加列存相关统计信息优化器根据数据表存储模式根据代价自动选择计划。实现新的向量化引擎完成关键算子的新引擎重构不同类型计划根据代价自适应选择向量化以及批大小。存储一体化 用户数据根据表模式指定可以根据业务负载类型灵活设置为列存行存或者行列冗余模式用户查询/备份恢复等操作完全透明。列存表完整支持所有在线及离线DDL操作完整支持所有数据类型及二级索引创建保证用户使用方法和行存别无二致。事务一体化 增量数据全部为行存事务内修改、日志内容以及多版本控制等和行存完全共享逻辑。 核心特性
特性1自适应Compaction
引入新的列存存储模式之后数据合并行为和原有行存数据有较大变化由于增量数据全部是行存需要和基线数据合并后拆分到每个列的独立SSTable中合并时间和资源占用相对行存会有较大增长.
为了加速列存表合并速度Compaction流程进行大幅增强对于列存表除了能够像行存表一样进行水平拆分并行合并加速之外还增加了垂直拆分加速列存表会降多个列的合并动作放在一个合并任务内进行并且一个任务内的列数能够根据系统资源自主选择升降保证整体在合并速度以及内存开销达到更好的平衡。
特性2列式编码算法
OceanBase一直以来存储数据会经过两级压缩第一级是OceanBase自研的行列混合编码压缩第二级是通用压缩其中行列混合编码由于是数据库内置算法因此可以支持不解压直接查询同时可以利用编码信息进行查询过滤加速尤其对AP类查询会有极大的加速。
但是原有行列混合编码算法仍然偏向行组织因此针对列存表实现了全新的列式编码算法相比原有编码算法新算法支持查询的全面向量化执行支持兼容不同指令集的SIMD优化同时针对数值类型大幅提高压缩比实现对原有算法在性能和压缩比上的全面提升。
特性3Skip Index
常见列存数据库一般均会对每列数据按照一定的粒度进行预聚合计算聚合的结果随数据一起持久化当用户查询请求访问列数据时数据库能够通过预聚合数据过滤数据大幅减少数据访问开销减少不必要的IO消耗。
在列存引擎中我们同样增加了skip index的支持针对每列数据会按照微块粒度进行最大值、最小值、和以及null总量等多个维度的聚合计算并逐层向上聚合累加获得宏块、SSTable等更大粒度的聚合值用户查询能够根据扫描范围不断下钻选取合适粒度聚合值进行过滤以及聚合输出。
特性4查询下压
OceanBase 在3.2版本开始初步支持简单的查询下压从4.x版本开始存储全面支持了向量化以及更多的下压支持在列存引擎中下压功能进一步得到增强和扩展具体包括:
所有查询filter下压同时根据filter类型能够进一步利用skip index以及编码信息加速。常用聚合函数的下压非group by场景下目前count/max/min/sum/avg等聚合函数已能下压到存储引擎.group by下压在NDV较少的列上支持group by下压存储计算利用微块内字典信息进行大幅加速。
使用列存
默认创建列存表
对于 OLAP 业务我们推荐默认创建列存表。如何让租户创建出来的表默认就是列存表通过下面的配置项即可实现
alter system set default_table_store_format column;
随后我们创建的表格没有指定 column group 时默认就是列存表。
OceanBase(roottest)create table t1 (c1 int primary key, c2 int ,c3 int);
Query OK,0 rows affected (0.301 sec)OceanBase(roottest)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(roottest)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(roottest)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(roottest)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(roottest)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), rowset16 |
| access([tt_column_store.c1], [tt_column_store.c2], [tt_column_store.c3]), partitions(p0) |
| is_index_backfalse, is_glOceanBaseal_indexfalse, |
| range_key([tt_column_store.c1]), range(MIN ; MAX)always true |
-------------------------------------------------------------------------------------------------------- 计划展示上新增COLUMN TABLE GET描述列存表上的指定主键的get操作
OceanBase(roottest)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), rowset16 |
| access([tt_column_store.c1], [tt_column_store.c2], [tt_column_store.c3]), partitions(p0) |
| is_index_backfalse, is_global_indexfalse, |
| 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(roottest)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), rowset16 |
| access([tt_column_row.c1], [tt_column_row.c2], [tt_column_row.c3]), partitions(p0) |
| is_index_backfalse, is_global_indexfalse, |
| range_key([tt_column_row.c1]), range(MIN ; MAX)always true |
-------------------------------------------------------------------------------------------------- 此时如果用户还是希望手动调优走列存扫描可以通过hint USE_COLUMN_TABLE来强制tt_column_row 表走列存扫描
OceanBase(roottest)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), rowset16 |
| access([tt_column_row.c1], [tt_column_row.c2], [tt_column_row.c3]), partitions(p0) |
| is_index_backfalse, is_global_indexfalse, |
| range_key([tt_column_row.c1]), range(MIN ; MAX)always true |
-------------------------------------------------------------------------------------------------- 类似的通过hint NO_USE_COLUMN_TABLE可以强制表不走列存扫描
OceanBase(roottest)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), rowset16 |
| access([tt_column_row.c2]), partitions(p0) |
| is_index_backfalse, is_global_indexfalse, |
| range_key([tt_column_row.c1]), range(MIN ; MAX)always true |
------------------------------------------------------------------
11 rows in set (0.053 sec)