基本概念
基于MMP架构得事实分析型数据库
MPP ( Massively Parallel Processing ),即大规模并行处理,
MPP 是将任务并行的分散到多个服务器和节点上,在每个节点上计算完成后,
将各自部分的结果汇总在一起得到最终的结果 ( 与 Hadoop 相似 )。
使用场景
多作为数仓查询层使用
项目架构
Doris 的架构很简洁,只设 FE(Frontend)、BE(Backend)两种角色、两个进程。
- Frontend(FE),主要负责用户请求的接入、查询解析规划、元数据的管理、节点管理相关工作。
- Backend(BE),主要负责数据存储、查询计划的执行
- 类比hive, FE=matestore BE=HDFS
架构图
FE和BE均支持横向扩展,两个进程通过一致性协议保证高可用,数据高可靠
FE: 主要有三个角色:Leader、follower、Observer
- follower为从节点,一般为奇数个,因为元数据写入需要多数 Follower 节点写入成功,才算成功
- Leader为主节点,是特殊得follower节点,众多follower节点选举出来得
- Observer为观察者节点,不会参与选举,同步已经成功写入的元数据日志,并且提供元数据读服务,不会参与写得逻辑
- 通常使用 1 follower + 2 observer ,可减少多follower一致性协议出错,高并发查询则observer
** 查询引擎**
- 查询基于MPP,节点间和节点内都并行执行,也支持多个大表的分布式 Shuffle Join
- 向量化查询,所有数据均可按列式部署
使用接口
Doris 采用 MySQL 协议,高度兼容 MySQL 语法,支持标准 SQL,用户可以通过各类客户端工具来访问 Doris,并支持与 BI 工具的无缝对接。Doris 当前支持多种主流的 BI 产品,包括不限于 SmartBI、DataEase、FineBI、Tableau、Power BI、SuperSet 等,只要支持 MySQL 协议的 BI 工具,Doris 就可以作为数据源提供查询支持
建表默认语句
CREATE TABLE IF NOT EXISTS table_name
(
`user_id` LARGEINT NOT NULL COMMENT "用户id",
`date` DATE NOT NULL COMMENT "数据灌入日期时间",
`timestamp` DATETIME NOT NULL COMMENT "数据灌入的时间戳",
`city` VARCHAR(20) NOT NULL COMMENT "用户所在城市",
`age` SMALLINT COMMENT "用户年龄",
`sex` TINYINT COMMENT "用户性别",
`last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "用户最后一次访问时间",
`cost` BIGINT SUM DEFAULT "0" COMMENT "用户总消费",
`max_dwell_time` INT MAX DEFAULT "0" COMMENT "用户最大停留时间",
`min_dwell_time` INT MIN DEFAULT "99999" COMMENT "用户最小停留时间"
)
--引擎,默认olap,也可以为 mysql、broker、es
ENGINE=olap
--数据模型,当前含义为聚合模式,且key为 `user_id`, `date`, `timestamp`, `city`, `age`, `sex`结合
AGGREGATE KEY(`user_id`, `date`, `timestamp`, `city`, `age`, `sex`)
--数据分区,当前含义为根据城市分区保存
PARTITION BY LIST(`city`)
(
PARTITION `p_cn` VALUES IN ("Beijing", "Shanghai", "Hong Kong"),
PARTITION `p_usa` VALUES IN ("New York", "San Francisco"),
PARTITION `p_jp` VALUES IN ("Tokyo")
)
--数据分桶,当前含义为根据城市分区后根据user_id分桶保存
DISTRIBUTED BY HASH(`user_id`) BUCKETS 16
-- 设置表属性
-- replication_num为分片数量,默认3个,应该满足replication_num<=BE的个数
-- storage_medium数据存储介质 ssd盘
-- storage_cooldown_time 设定到期时间
-- 当前含义为 相同数据会备份到3个BE里,且存储在ssd盘,2018-01-01 12:00:00会自动迁移到 HDD 存储上
PROPERTIES
(
"replication_num" = "3",
"storage_medium" = "SSD",
"storage_cooldown_time" = "2018-01-01 12:00:00"
);
数据模型
数据以表(Table)得形式存储数据,一张表包含行(Row)与列(Column)
Row即用户的一行数据
Column 用于描述一行数据中不同的字段。其中分为两大类key和value
- key : 建表语句中的关键字’unique key’或’aggregate key’或’duplicate key’定义得
- value : 没有关键字修饰得
Doris 的数据模型主要分为3类:
Aggregate
被Aggregate修饰得字段集合聚合成一条(类似mysql得groupBy),value集合会根据字段得聚合方式聚合
- SUM:求和,多行的 Value 进行累加。
- REPLACE:替代,下一批数据中的 Value 会替换之前导入过的行中的 Value。
- MAX:保留最大值。
- MIN:保留最小值。
- REPLACE_IF_NOT_NULL:非空值替换。和 REPLACE 的区别在于对于null值,不做替换。
- HLL_UNION:HLL 类型的列的聚合方式,通过 HyperLogLog 算法聚合。
- BITMAP_UNION:BIMTAP 类型的列的聚合方式,进行位图的并集聚合。
例:
-- 创建表
-- key : `user_id`, `date`, `city`, `age`, `sex` (设置语句:AGGREGATE KEY(`user_id`, `date`, `city`, `age`, `sex`) )
-- value : last_visit_date(REPLACE),cost (SUM ),max_dwell_time(MAX),min_dwell_time(MIN)
CREATE TABLE IF NOT EXISTS table_name
(
`user_id` LARGEINT NOT NULL COMMENT "用户id",
`date` DATE NOT NULL COMMENT "数据灌入日期时间",
`city` VARCHAR(20) COMMENT "用户所在城市",
`age` SMALLINT COMMENT "用户年龄",
`sex` TINYINT COMMENT "用户性别",
`last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "用户最后一次访问时间",
`cost` BIGINT SUM DEFAULT "0" COMMENT "用户总消费",
`max_dwell_time` INT MAX DEFAULT "0" COMMENT "用户最大停留时间",
`min_dwell_time` INT MIN DEFAULT "99999" COMMENT "用户最小停留时间"
)
AGGREGATE KEY(`user_id`, `date`, `city`, `age`, `sex`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
-- 插入语句
insert into table_name values
(10000,"2017-10-01","北京",20,0,"2017-10-01 06:00:00",20,10,10),
(10000,"2017-10-01","北京",20,0,"2017-10-01 07:00:00",15,2,2),
(10001,"2017-10-01","北京",30,1,"2017-10-01 17:05:45",2,22,22),
(10002,"2017-10-02","上海",20,1,"2017-10-02 12:59:12",200,5,5),
(10003,"2017-10-02","广州",32,0,"2017-10-02 11:20:00",30,11,11),
(10004,"2017-10-01","深圳",35,0,"2017-10-01 10:00:15",100,3,3),
(10004,"2017-10-03","深圳",35,0,"2017-10-03 10:20:22",11,6,6);
-- 相同key
(10000,"2017-10-01","北京",20,0,"2017-10-01 06:00:00",20,10,10)
(10000,"2017-10-01","北京",20,0,"2017-10-01 07:00:00",15,2,2)
-->(10000,"2017-10-01","北京",20,0,"2017-10-01 07:00:00",35,10,2)
-- 不同key
(10000,"2017-10-01","北京",20,0,"2017-10-01 06:00:00",20,10,10)
(10001,"2017-10-01","北京",30,1,"2017-10-01 17:05:45",2,22,22)
--> (10000,"2017-10-01","北京",20,0,"2017-10-01 06:00:00",20,10,10)
(10001,"2017-10-01","北京",30,1,"2017-10-01 17:05:45",2,22,22)
Unique
对数据唯一性要求,采取写时合并(merge on write),通过在写入时做一些额外的工作,实现了最优的查询性能。 写时合并将在未来替换读时合并成为Unique模型的默认实现方式
与Aggregate差别在:
- 查询速度变快,在开启了写时合并选项的Unique表上,数据在导入阶段就会去将被覆盖和被更新的数据进行标记删除,同时将新的数据写入新的文件。在查询的时候, 所有被标记删除的数据都会在文件级别被过滤掉,读取出来的数据就都是最新的数据,消除掉了读时合并中的数据聚合过程,并且能够在很多情况下支持多种谓词的下推。因此在许多场景都能带来比较大的性能提升,尤其是在有聚合查询的情况下
- Aggregate只聚合不含有排序,unique带有排序也带有聚合
语法:
-- 添加唯一键:UNIQUE KEY(`user_id`, `username`)
CREATE TABLE IF NOT EXISTS table_name
(
`user_id` LARGEINT NOT NULL COMMENT "用户id",
`username` VARCHAR(50) NOT NULL COMMENT "用户昵称",
`city` VARCHAR(20) COMMENT "用户所在城市",
`age` SMALLINT COMMENT "用户年龄",
`sex` TINYINT COMMENT "用户性别",
`phone` LARGEINT COMMENT "用户电话",
`address` VARCHAR(500) COMMENT "用户地址",
`register_time` DATETIME COMMENT "用户注册时间"
)
UNIQUE KEY(`user_id`, `username`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1",
"enable_unique_key_merge_on_write" = "true"
);
Duplicate
在某些多维分析场景下,数据既没有主键,也没有聚合需求。类似mysql得不设唯一键得数据存储,自带排序,且符合前缀索引建立,一般四个以内
语法
-- 添加唯一键:UNIQUE KEY(`user_id`, `username`)
CREATE TABLE IF NOT EXISTS table_name
(
`user_id` LARGEINT NOT NULL COMMENT "用户id",
`username` VARCHAR(50) NOT NULL COMMENT "用户昵称",
`city` VARCHAR(20) COMMENT "用户所在城市",
`age` SMALLINT COMMENT "用户年龄",
`sex` TINYINT COMMENT "用户性别",
`phone` LARGEINT COMMENT "用户电话",
`address` VARCHAR(500) COMMENT "用户地址",
`register_time` DATETIME COMMENT "用户注册时间"
)
DUPLICATE KEY(`user_id`, `username`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1",
"enable_unique_key_merge_on_write" = "true"
);
数据表物理结构
table:数据表
Partition:分区 (逻辑上最小的管理单元。数据的导入与删除,仅能针对一个 Partition 进行)
Tablet:数据分桶,物理上独立存储
Row:数据行
Column:列 (描述行数据的不同字段)
关系:
table->多个Partition
Partition->多个Tablet
Tablet->若干row
数据划分
类似于MySql分库分表概念
Doris支持两层的数据划分。第一层是 Partition,支持 Range 和 List 的划分方式。第二层是 Bucket(Tablet),支持 Hash 和 Random 的划分方式。
如果已划分区且导入的数据不存在分区内,则无法导入
Partition(分区)
基本概念:
- Partition 列可以指定一列或多列,分区列必须为 KEY 列。
- 不论分区列是什么类型,在写分区值时,都需要加双引号。
- 分区数量理论上没有上限。
- 当不使用 Partition 建表时,系统会自动生成一个和表名同名的,全值范围的 Partition。该 Partition 对用户不可见,并且不可删改。
- 创建分区时不可添加范围重叠的分区。
- 如果导入的数据不存在分区内,则无法导入
- 是否分区这个属性在建表时确定,之后不可更改。即对于分区表,可以在之后的使用过程中对分区进行增删操作,而对于无分区的表,之后不能再进行增加分区等操作。
Range(范围,限定好列的范围,符合条件的进入对应partition)
- 分区列通常为时间列,以方便的管理新旧数据。
- Range 分区支持的列类型:[DATE,DATETIME,TINYINT,SMALLINT,INT,BIGINT,LARGEINT]
- Partition 支持通过
VALUES LESS THAN (...)
仅指定上界,系统会将前一个分区的上界作为该分区的下界,生成一个左闭右开的区间。也支持通过VALUES [...)
指定上下界,生成一个左闭右开的区 - 批量新增分区:FROM (“2022-01-03”) TO (“2022-01-06”) INTERVAL 1 DAY ->每隔一天生成一个分区,且左开右毕
- 可使用动态分区 详情请至PROPERTIES
示例:
-- 单列
-- date<2017-02-01 进入p201701分区
-- 2017-02-01<date<2017-03-01进入p201702分区
-- 2017-03-01<date<2017-04-01进入p201703分区
PARTITION BY RANGE(`date`)
(
PARTITION `p201701` VALUES LESS THAN ("2017-02-01"),
PARTITION `p201702` VALUES LESS THAN ("2017-03-01"),
PARTITION `p201703` VALUES LESS THAN ("2017-04-01")
)
-- 多列
-- date<2017-02-01 & money<1000 进入p201701分区
-- 2017-02-01<date<2017-03-01 & 1000<money<2000 进入p201702分区
-- 2017-03-01<date<2017-04-01 & 2000<money<3000 进入p201703分区
-- 2017-04-01<date<2017-05-01 & money>3000 进入p201704分区
PARTITION BY RANGE(`date`,`money`)
(
PARTITION `p201701` VALUES LESS THAN ("2017-02-01",1000),
PARTITION `p201702` VALUES LESS THAN ("2017-03-01",2000),
PARTITION `p201703` VALUES LESS THAN ("2017-04-01",3000),
PARTITION `p201704` VALUES LESS THAN ("2017-05-01")
)
List分区(类似于枚举类型,等于该类型的进入对应partition)
- 分区列支持
BOOLEAN, TINYINT, SMALLINT, INT, BIGINT, LARGEINT, DATE, DATETIME, CHAR, VARCHAR
数据类型,分区值为枚举值。只有当数据为目标分区枚举值其中之一时,才可以命中分区。 - Partition 支持通过
VALUES IN (...)
来指定每个分区包含的枚举值
示例:
-- 单列
-- sex 为 "男"或"男性"都进入p1_man分区
PARTITION BY List(`sex`)
(
PARTITION `p1_man` VALUES IN ("男","男性"),
PARTITION `p1_woman` VALUES IN ('女',"女性")
)
-- 多列
-- p1_man 存储
--①男+18 ②男性+18 ③男+18岁 ④男性+18岁
PARTITION BY List(`sex`,`age`)
(
PARTITION `p1_man` VALUES IN (("男","男性"),("18","18岁")),
PARTITION `p1_woman` VALUES IN (('女',"女性",("18","18岁"))
)
Bucket(分桶)
数据划分规则,类似于按用户id分表,
-
如果存在Partition,则对每个Partition进行数据划分
-
如果不存在,则对整个Table的数据划分
-
分桶列可以是多列,Aggregate 和 Unique 模型必须为 Key 列,Duplicate 模型可以是 key 列和 value 列
-
桶列可以和 Partition 列相同或不同。
-
分桶列的选择,是在 查询吞吐 和 查询并发 之间的一种权衡:
- 如果选择多个分桶列,则数据分布更均匀。如果一个查询条件不包含所有分桶列的等值条件,那么该查询会触发所有分桶同时扫描,这样查询的吞吐会增加,单个查询的延迟随之降低。这个方式适合大吞吐低并发的查询场景。
- 如果仅选择一个或少数分桶列,则对应的点查询可以仅触发一个分桶扫描。此时,当多个点查询并发时,这些查询有较大的概率分别触发不同的分桶扫描,各个查询之间的IO影响较小(尤其当不同桶分布在不同磁盘上时),所以这种方式适合高并发的点查询场景
-
AutoBucket: 根据数据量,计算分桶数。 对于分区表,可以根据历史分区的数据量、机器数、盘数,确定一个分桶。
可在PROPERTIES设置”estimate_partition_size" = “100G”
-
分桶的数量理论上没有上限
分桶语句;
--不加分区
DISTRIBUTED BY HASH(`user_id`) BUCKETS 16 --根据userid分16个桶
-- 分区
-- 根据城市分区,然后分区内的row根据userid分成16个桶
PARTITION BY LIST(`city`)
(
PARTITION `p_cn` VALUES IN ("Beijing", "Shanghai", "Hong Kong"),
PARTITION `p_usa` VALUES IN ("New York", "San Francisco"),
PARTITION `p_jp` VALUES IN ("Tokyo")
)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 16
Partition 和 Bucket 的数量和数据量
- 一个表的 Tablet 总数量等于 (Partition num * Bucket num)。
- 一个表的 Tablet 数量,在不考虑扩容的情况下,推荐略多于整个集群的磁盘数量。
- 单个 Tablet 的数据量理论上没有上下界,但建议在 1G - 10G 的范围内。如果单个 Tablet 数据量过小,则数据的聚合效果不佳,且元数据管理压力大。如果数据量过大,则不利于副本的迁移、补齐,且会增加 Schema Change 或者 Rollup 操作失败重试的代价(这些操作失败重试的粒度是 Tablet)。
- 当 Tablet 的数据量原则和数量原则冲突时,建议优先考虑数据量原则。
- 在建表时,每个分区的 Bucket 数量统一指定。但是在动态增加分区时(
ADD PARTITION
),可以单独指定新分区的 Bucket 数量。可以利用这个功能方便的应对数据缩小或膨胀。 - 一个 Partition 的 Bucket 数量一旦指定,不可更改。所以在确定 Bucket 数量时,需要预先考虑集群扩容的情况。比如当前只有 3 台 host,每台 host 有 1 块盘。如果 Bucket 的数量只设置为 3 或更小,那么后期即使再增加机器,也不能提高并发度。
- 举一些例子:假设在有10台BE,每台BE一块磁盘的情况下。如果一个表总大小为 500MB,则可以考虑4-8个分片。5GB:8-16个分片。50GB:32个分片。500GB:建议分区,每个分区大小在 50GB 左右,每个分区16-32个分片。5TB:建议分区,每个分区大小在 50GB 左右,每个分区16-32个分片。
注:表的数据量可以通过
SHOW DATA
命令查看,结果除以副本数,即表的数据量。
PROPERTIES
--PROPERTIES属性设置
PROPERTIES
(
-- 动态分区
"dynamic_partition.enable" = "true", --是否开启 Doris 的动态分区功能
"dynamic_partition.time_unit" = "DAY",-- 动态分区调度的单位 HOUR、DAY、WEEK、MONTH、YEAR
"dynamic_partition.time_zone" ="Asia/Shanghai" -- 时区 默认为当前系统时区
"dynamic_partition.start" = "-7", --动态分区的起始偏移为负数。
"dynamic_partition.end" = "3", --动态分区的结束偏移,为正数
"dynamic_partition.prefix" = "p", --动态创建的分区名前缀
"dynamic_partition.buckets" = "32" --动态创建的分区所对应的分桶数量。
"dynamic_partition.replication_num"="3" --副本数量,
"dynamic_partition.start_day_of_week"="1" --每周起始点(start_day_of_week) 每月起始点(start_day_of_month)
"dynamic_partition.create_history_partition"="false",--: 是否创建历史分区。
"dynamic_partition.history_partition_num"="1",--: 指定创建历史分区的数量。
"dynamic_partition.reserved_history_periods"="1",--dynamic_partition.reserved_history_periods: 用于指定保留的历史分区的时间段
-- 基本配置
"replication_num"="3" , --副本数量 默认为3
"storage_medium" = "SSD",-- 数据存储介质
"storage_cooldown_time" = "2020-11-20 00:00:00" --到期时间 到期自动转到HDD存储上
"bloom_filter_columns"= "k1, k2, k3",--Bloom Filter 索引的列名称列表。各个列的 Bloom Filter 索引是独立的,并不是组合索引
"compression"="zstd",--压缩方式
"disable_auto_compaction" = "false",--自动compaction。compaction进程会跳过这个表的所有tablet
--自动分桶
"estimate_partition_size" = "100G"
)
数据缓存
缓存策略
-
SQLCache
sql签名+记录查询的分区id+分区最新版本 作为缓存key,数据/sql/分区变更均可能无法名字,适用于记录不变更,且查询重复,类似大屏 -
PartitionCache
缓存原理:
a. sql拆分,每一个查询条件对应一个结果集
b. 区域拆分为只读分区与可更新分区,对于变动的结果集放入可更新分区,对于数据不变动的结果放入只读分区缓存起来 -
缓存设置
--SQLCache开启
cache_enable_sql_mode=true
--PartitionCache开启
cache_enable_partition_mode=true
--缓存时间
-- 两种缓存开启,查询语句均能命中缓存, SQLCache优先,间隔版本时间超过失效时间,则使用PartitionCache
cache_last_version_interval_second=900
- 监控
query_table //Query中有表的数量
query_olap_table //Query中有Olap表的数量
cache_mode_sql //识别缓存模式为sql的Query数量
cache_hit_sql //模式为sql的Query命中Cache的数量
query_mode_partition //识别缓存模式为Partition的Query数量
cache_hit_partition //通过Partition命中的Query数量
partition_all //Query中扫描的所有分区
partition_hit //通过Cache命中的分区数量
Cache命中率 = (cache_hit_sql + cache_hit_partition) / query_olap_table
Partition命中率 = partition_hit / partition_all
query_cache_memory_total_byte //Cache内存大小
query_query_cache_sql_total_count //Cache的SQL的数量
query_cache_partition_total_count //Cache分区数量
SQL平均数据大小 = cache_memory_total / cache_sql_total
Partition平均数据大小 = cache_memory_total / cache_partition_total
- 优化
FE:合理的设置每次缓存的大小(cache_result_max_data_size),避免单次查询大批数据进入缓存,占用内存
BE:
a. 合理配置单sql的最大分区数量(cache_max_partition_count),默认时间分区可缓存2年,如果需要缓存更长时间,则需要调增参数
b. 缓存内存设置,有两个参数query_cache_max_size和query_cache_elasticity_size两部分组成(单位MB),内存超过query_cache_max_size + cache_elasticity_size会开始清理,并把内存控制到query_cache_max_size以下。可以根据BE节点数量,节点内存大小,和缓存命中率来设置这两个参数
BROKER
用于支持 Doris 读写远端存储上的文件和目录,是一个数据通道,正常存在多个,汇成一个组
架构图
冷热分层
长期不变动Partition的数据设置freeze time,表示多久这个Partition会被freeze,并且定义freeze之后存储的remote storage的位置。在be上daemon线程会周期性的判断表是否需要freeze,若freeze后会将数据上传到s3和hdfs上
- 冷数据存储对象存储,查询时从远程缓存到本地
- 冷数据删除则会删除远程数据
- 缓存优化保证冷热查询性能,be线程池优化,区分查询时冷热数据,减少延迟
Storage policy
存储策略是使用冷热分层功能的入口,用户只需要在建表或使用doris过程中,给表或分区关联上storage policy,即可以使用冷热分层的功能
-- S3
--创建远程资源
CREATE RESOURCE "remote_s3"
PROPERTIES
(
"type" = "s3",
"s3.endpoint" = "bj.s3.com",
"s3.region" = "bj",
"s3.bucket" = "test-bucket",
"s3.root.path" = "path/to/root",
"s3.access_key" = "bbb",
"s3.secret_key" = "aaaa",
"s3.connection.maximum" = "50",
"s3.connection.request.timeout" = "3000",
"s3.connection.timeout" = "1000"
);
CREATE STORAGE POLICY test_policy
PROPERTIES(
"storage_resource" = "remote_s3",
"cooldown_ttl" = "1d"
);
-- 使用远程资源
CREATE TABLE IF NOT EXISTS create_table_use_created_policy (
k1 BIGINT,
k2 LARGEINT,
v1 VARCHAR(2048)
)
UNIQUE KEY(k1)
DISTRIBUTED BY HASH (k1) BUCKETS 3
PROPERTIES(
--指定该表的缓存资源为S3
"storage_policy" = "test_policy"
);
-- hdfs
CREATE RESOURCE "remote_hdfs" PROPERTIES (
"type"="hdfs",
"fs.defaultFS"="fs_host:default_fs_port",
"hadoop.username"="hive",
"hadoop.password"="hive",
"dfs.nameservices" = "my_ha",
"dfs.ha.namenodes.my_ha" = "my_namenode1, my_namenode2",
"dfs.namenode.rpc-address.my_ha.my_namenode1" = "nn1_host:rpc_port",
"dfs.namenode.rpc-address.my_ha.my_namenode2" = "nn2_host:rpc_port",
"dfs.client.failover.proxy.provider" = "org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider"
);
CREATE STORAGE POLICY test_policy PROPERTIES (
"storage_resource" = "remote_hdfs",
"cooldown_ttl" = "300"
)
CREATE TABLE IF NOT EXISTS create_table_use_created_policy (
k1 BIGINT,
k2 LARGEINT,
v1 VARCHAR(2048)
)
UNIQUE KEY(k1)
DISTRIBUTED BY HASH (k1) BUCKETS 3
PROPERTIES(
"storage_policy" = "test_policy"
);