Hologres的数据存储分布
文章目录一、表存储格式列存、行存、行列共存1、数据库选型2、列存3、行存4、行列共存二、数据分布原理1、Hologres数据分布特性2、SQL优化一、表存储格式列存、行存、行列共存1、数据库选型Hologres支持行存、列存、行列共存三种存储格式首先请参考下图所示流程确定您表的存储格式。如果您的业务场景尚未完全明确请优先选择行列共存以兼顾更多可能出现的场景。Hologres支持三种表存储格式分别为行存、列存和行列共存不同的存储格式适用于不同的查询场景您需要根据表的使用场景设置表的存储格式合适的存储格式可以显著提高数据处理和查询速度同时也可以节省存储空间。表的存储格式使用建议如下。存储格式适用场景列限制使用说明列存适用于OLAP场景适合各种复杂查询、数据关联、扫描、过滤和统计。建议不超过300列。列存会默认创建更多的索引包括对字符串类型创建bitmap索引这些索引可以显著加速查询过滤和统计。行存适合基于Primary Key点查的场景即查询语句如下所示。SELECT * FROM tablename WHERE pk xxx;建议不超过3000列。行存默认仅对主键创建索引仅支持主键的快速查询使用场景也受到限制。行列共存支持行存和列存的所有场景以及非主键点查的场景。建议不超过300列。行列共存适用的场景更广但会带来更多的存储开销以及内部数据状态同步的开销。2、列存如果表是列存那么数据将会按照列的形式存储。列存默认使用ORC格式采用各种类型的Encoding算法如RLE、字典编码等对数据进行编码并且对编码后的数据应用主流压缩算法如Snappy、 Zlib、 Zstd、 Lz4等对数据进一步进行压缩并结合Bitmap index、延迟物化等机制提升数据的存储和查询效率。系统会为每张表在底层存储一个主键索引文件详情请参见主键Primary Key。列存表如果设置了主键PK系统会自动生成一个Row IdentifierRID用于快速定位整行数据同时如果为查询的列设置合适的索引如Distribution Key、Clustering Key等那么就可以通过索引快速定位到数据所在的分片和文件从而提升查询性能因此列存的适用范围更广通常用于OLAP查询的场景。示例如下。V2.1版本起支持的建表语法CREATETABLEpublic.tbl_col(idTEXTNOTNULL,nameTEXTNOTNULL,classTEXTNOTNULL,in_time TIMESTAMPTZNOTNULL,PRIMARYKEY(id))WITH(orientationcolumn,clustering_keyclass,bitmap_columnsname,event_time_columnin_time);SELECT*FROMpublic.tbl_colWHEREid3333;SELECTid,class,nameFROMpublic.tbl_colWHEREid3333ORDERBYid;所有版本支持的建表语法BEGIN;CREATETABLEpublic.tbl_col(idTEXTNOTNULL,nameTEXTNOTNULL,classTEXTNOTNULL,in_time TIMESTAMPTZNOTNULL,PRIMARYKEY(id));CALLset_table_property(public.tbl_col,orientation,column);CALLset_table_property(public.tbl_col,clustering_key,class);CALLset_table_property(public.tbl_col,bitmap_columns,name);CALLset_table_property(public.tbl_col,event_time_column,in_time);COMMIT;SELECT*FROMpublic.tbl_colWHEREid3333;SELECTid,class,nameFROMpublic.tbl_colWHEREid3333ORDERBYid;3、行存如果Hologres的表设置的是行存那么数据将会按照行存储。行存默认使用SST格式数据按照Key有序分块压缩存储并且通过Block Index、Bloom Filter等索引以及后台Compaction机制对文件进行整理优化点查查询效率。推荐设置主键Primary Key系统会为每张表在底层存储一个主键索引文件详情请参见主键Primary Key。行存表设置了Primary KeyPK的场景系统会自动生成一个Row IdentifierRIDRID用于定位整行数据同时系统也会将PK设置为Distribution Key和Clustering Key这样就能快速定位到数据所在的Shard和文件在基于主键查询的场景上只需要扫描一个主键就能快速拿到所有列的全行数据提升查询效率SQL示例如下。V2.1版本起支持的建表语法CREATETABLEpublic.tbl_row(idTEXTNOTNULL,nameTEXTNOTNULL,classTEXT,PRIMARYKEY(id))WITH(orientationrow,clustering_keyid,distribution_keyid);--基于PK的点查示例SELECT*FROMpublic.tbl_rowWHEREid1111;--查询多个keySELECT*FROMpublic.tbl_rowWHEREidIN(1111,2222,3333);所有版本支持的建表语法BEGIN;CREATETABLEpublic.tbl_row(idTEXTNOTNULL,nameTEXTNOTNULL,classTEXT,PRIMARYKEY(id));CALLset_table_property(public.tbl_row,orientation,row);CALLset_table_property(public.tbl_row,clustering_key,id);CALLset_table_property(public.tbl_row,distribution_key,id);COMMIT;--基于PK的点查示例SELECT*FROMpublic.tbl_rowWHEREid1111;--查询多个keySELECT*FROMpublic.tbl_rowWHEREidIN(1111,2222,3333);不建议使用设置的PK和Clustering Key不一致但如果在建表时设置表为行存表且将PK和Clustering Key设置为不同的字段查询时系统会根据PK定位到Clustering Key和RID再通过Clustering Key和RID快速定位到全行数据相当于扫描了两次有一定的性能牺牲SQL示例如下。V2.1版本起支持的建表语法设置行存表PK和Clustering Key不一致CREATETABLEpublic.tbl_row(idTEXTNOTNULL,nameTEXTNOTNULL,classTEXT,PRIMARYKEY(id))WITH(orientationrow,clustering_keyname,distribution_keyid);所有版本支持的建表语法设置行存表PK和Clustering Key不一致BEGIN;CREATETABLEpublic.tbl_row(idTEXTNOTNULL,nameTEXTNOTNULL,classTEXT,PRIMARYKEY(id));CALLset_table_property(public.tbl_row,orientation,row);CALLset_table_property(public.tbl_row,clustering_key,name);CALLset_table_property(public.tbl_row,distribution_key,id);COMMIT;综上行存表非常适用于基于PK的点查场景能够实现高QPS的点查。同时建表时建议只设置PK系统会自动将PK设置为Distribution Key和Clustering Key以提升查询性能。不建议将PK和Clustering Key设置为不同的字段设置为不同的字段会有一定的性能牺牲。4、行列共存在实际应用场景中一张表可能用于主键点查又用于OLAP查询因此Hologres在V1.1版本支持了行列共存的存储格式。行列共存同时拥有行存和列存的能力既支持高性能的基于PK点查又支持OLAP分析。数据在底层存储时会存储两份一份按照行存格式存储一份按照列存格式存储因此会带来更多的存储开销。数据写入时会同时写一份行存格式和写一份列存格式只有两份数据都写完了才会返回成功保证数据的原子性。数据查询时优化器会根据SQL解析出对应的执行计划执行引擎会根据执行计划判断走行存还是列存的查询效率更高要求行列共存的表必须设置主键对于主键点查场景如select * from tbl where pkxxx语句以及Fixed Plan加速SQL执行场景优化器会默认走行存主键点查的路径。对于非主键点查场景如select * from tbl where col1xx and col2yyy语句尤其是表的列很多且查询结果需要展示很多列行列共存针对该场景优化器在生成执行计划时会先读取列存表的数据读取完成后根据列存键值Key查询行存表的数据避免全表扫描提升非主键查询性能。该场景能充分发挥行列共存的优势提高数据的快速检索性能。对于其他的普通查询则会默认走列存。因此行列共存表在通常查询场景尤其是非主键点查场景查询效率更好示例如下。V2.1版本起支持的建表语法CREATETABLEpublic.tbl_row_col(idTEXTNOTNULL,nameTEXTNOTNULL,classTEXTNOTNULL,PRIMARYKEY(id))WITH(orientationrow,column,distribution_keyid,clustering_keyclass,bitmap_columnsname);SELECT*FROMpublic.tbl_row_colWHEREid‘2222’;--基于主键的点查SELECT*FROMpublic.tbl_row_colWHEREclass‘二班’;--非主键点查SELECT*FROMpublic.tbl_row_colWHEREid‘2222’ANDclass‘二班’;--普通OLAP查所有版本支持的建表语法BEGIN;CREATETABLEpublic.tbl_row_col(idTEXTNOTNULL,nameTEXTNOTNULL,classTEXT,PRIMARYKEY(id));CALLset_table_property(public.tbl_row_col,orientation,row,column);CALLset_table_property(public.tbl_row_col,distribution_key,id);CALLset_table_property(public.tbl_row_col,clustering_key,class);CALLset_table_property(public.tbl_row_col,bitmap_columns,name);COMMIT;SELECT*FROMpublic.tbl_row_colWHEREid‘2222’;--基于主键的点查SELECT*FROMpublic.tbl_row_colWHEREclass‘二班’;--非主键点查SELECT*FROMpublic.tbl_row_colWHEREid‘2222’ANDclass‘二班’;--普通OLAP查二、数据分布原理本文为您介绍Hologres中的关键索引如Distribution Key、Event Time ColumnSegment Key和Clustering Key1、Hologres数据分布特性Hologres是一个分布式数据仓库采用并行计算和向量计算技术实现秒级查询响应因此数据的分布特征对性能有关键影响包括数据在多个分布式节点间的分布均衡性distribution_key以及单个节点内文件之间的分布有序性event_time_column/segment_key。同时Hologres在OLAP场景默认使用列存储格式因此数据在文件内的有序性clustering_key也至关重要。由于数据分布特征是在数据写入时确定调整成本高因此建议在建表时设计与数据布局相关的三个属性。同时Hologres的元数据采用三级结构DatabaseSchemaTable建议逻辑相关的表内聚在Schema下避免跨库查询。Database是元数据隔离的基本单位不是资源隔离的单位。分类维度Distribution KeyEvent Time ColumnSegment KeyClustering Key中文名称分布键分段键、事件时间列聚簇索引、聚簇键作用层级节点Shard之间单个Shard内的文件之间单个文件内部核心作用决定一行数据存入哪个Shard减少不同文件之间数据范围重叠对文件内部数据进行排序数据组织方式Hash分布按字段值范围组织、合并文件按指定字段顺序排序主要优化目标分布均衡、并行计算、Local Join、减少Shuffle文件裁剪、范围查询、主键更新定位文件内数据裁剪、点查和范围过滤典型字段用户ID、门店ID、订单ID、关联键业务时间、事件时间、更新时间高频过滤维度、时间、状态、类别是否适合时间字段不一定非常适合适合但取决于查询条件是否要求数据单调有序不要求但要求分布均匀最好单调递增或递减不要求写入本身有序系统在文件内排序推荐字段数量一般不超过2个一般不超过2个一般不超过2个修改方式通常需要重新建表导入需要重新建表需要重新建表2、SQL优化建表时设计合适的数据分布能够使SQL在执行时快速命中数据减少IO消耗以更少的计算资源实现更高的查询性能同时均衡的数据分布也使得并发资源可以充分发挥避免单点瓶颈。下图是一个SQL从发起到获取数据的执行流程可以通过下图理解减少IO的流程。分区剪枝Partition PruningSQL执行时对于目标分区表会通过分区裁剪定位到所在分区。如果查询条件和分区不匹配需要遍历所有分区会引起过多的IO扫描通常分区选择日粒度比较合适。对于非分区表直接略过不进行分区裁剪。分片剪枝Shard Pruning通过分布键distribution_key快速定位到数据所在的数据分片可以减少单个SQL执行时的资源消耗对于并发SQL满足更高的吞吐能力如果无法定位到某个分片会通过分布式框架调度所有的分片参与计算单个SQL的并行度更高资源使用更多但并发能力会降低部分需要集中化执行的算子会带来额外的Shuffle开销。通常分布键选择订单ID、用户ID、事件ID等分布比较均衡的字段多个需要JOIN的表使用相同的分布键可以使相关的数据分片到同一个Shard通过Local JOIN实现更高的JOIN效率。文件剪枝Segment Key Pruning通过分段键event_time_column/segment_key快速定位到单个节点内部多个文件中的数据所在文件位置避免打开不需要访问的文件。如果无法过滤则需要遍历所有的文件。聚簇剪枝Clustering Key Pruning通过聚簇键clustering_key快速定位单个文件内部的数据段提高范围查询和字段排序的效率。