Doris 2.1.0 Variant 新特性验证

Doris 2.1.0 Variant 新特性验证


Variant 数据类型

过去 Apache Doris 在应对复杂半结构化数据的存储和分析处理时,一般有两种方式:

  1. 一种方式是用户提前预定好表结构,加工成宽表,在数据进入前将数据解析好,这种方案的优点是写入性能好,查询也不需要解析,但是使用不够灵活、对表结构发起变更增加运维、研发的成本。

  2. 使用 Doris 中的 JSON 类型、或是存成 JSON String,将原始 JSON 数据不经过加工直接入库, 查询的时候,用解析函数处理。优点是不需要额外的数据加工、预定义表结构拍平嵌套结构,运维、研发方便,但存在解析性能以及数据读取效率低下的问题。

为了解决上述半结构化数据的挑战,Apache Doris 在2.1 版本中引入全新的数据类型VARIANT支持存储半结构化数据、允许存储包含不同数据类型(如整数、字符串、布尔值等)的复杂数据结构,无需在表结构中提前定义具体的列,其存储和查询与传统的 String、JSONB 等行存类型发生了本质的改变,期望其作为半结构化数据首选数据类型,给用户带来更加高效的数据处理机制。

Variant 类型特别适用于处理结构可能随时会发生变化 的复杂嵌套结构。在写入过程中,Variant 类型可以自动根据列的结构和类型推断列信息,并将其合并到现有表的 Schema 中,将 JSON 键及其对应的值灵活存储为动态子列。同时,一个表可以同时包含灵活的 Variant 对象列和预先定义类型的更严格的静态列,从而在数据存储、查询上提供了更大的灵活性。除此之外,Variant 类型能够与 Doris 核心特性融合,利用列式存储、向量化引擎、优化器等技术,为用户带来极高性价比的查询性能及存储性能。

可行性分析

ods_test_log 表 schema+--------------+---------------+------+-------+---------+-------+
| Field        | Type          | Null | Key   | Default | Extra |
+--------------+---------------+------+-------+---------+-------+
| id           | VARCHAR(256)  | No   | true  | NULL    |       |
| time         | BIGINT        | No   | true  | NULL    |       |
| event        | VARCHAR(256)  | Yes  | true  | NULL    |       |
| properties   | TEXT          | Yes  | false | NULL    | NONE  |
| dt           | DATE          | Yes  | false | NULL    | NONE  |
+--------------+---------------+------+-------+---------+-------+

  1. 根据不同的事件类型,properties 中嵌套的嵌套的kv也是不同的,即符合 Variant 类型功能(随时会发生变化的复杂嵌套结构)
  2. 采用传统 string 类型存储半结构化数据,随着数据日益增多,现存储占用也日渐增大,Variant 类型优秀的存储性能对此问题具有可行性。
  3. 需求是查询dwd层的明细表,但针对特殊需求,需要直接查询 ods_test_log 表,时间跨度大点,数据量多时,一个查询通常需要数秒才能出结果,Variant 类型优秀的查询性能对此问题具有可行性。

分析验证

-- 原 ods_test_log 建表
CREATE TABLE ods_test_log (
    `id` varchar(256) NOT NULL COMMENT '用户id',
    `time`        BIGINT NOT NULL COMMENT '时间戳',
     `event`      varchar(256)  COMMENT '事件类型',
    `properties`  string COMMENT '具备的属性',
    `dt`          DATE COMMENT '事件时间'
)ENGINE=OLAP
DUPLICATE KEY(`id`, `time`,`event`)
PARTITION BY RANGE (`dt`)
(
 FROM ("2024-01-01") TO ("2024-04-07") INTERVAL 1 DAY
)
DISTRIBUTED BY HASH (`event`) BUCKETS 1
PROPERTIES (
	"replication_allocation" = "tag.location.default: 1",
	"compression"="LZ4",
	"dynamic_partition.enable" = "true",
	"dynamic_partition.time_unit" = "DAY",
	"dynamic_partition.end" = "3",
	"dynamic_partition.prefix" = "p",
	"dynamic_partition.buckets" = "1"
);

-- 采用 variant 类型建表
CREATE TABLE ods_test_variant (
    `id` varchar(256) NOT NULL COMMENT '用户id',
    `time`        BIGINT NOT NULL COMMENT '时间戳',
     `event`      varchar(256)  COMMENT '事件类型',
    `properties`  VARIANT NULL COMMENT '具备的属性',
    `dt`          DATE COMMENT '事件时间'
)ENGINE=OLAP
DUPLICATE KEY(`id`, `time`,`event`)
PARTITION BY RANGE (`dt`)
(
 FROM ("2024-01-01") TO ("2024-04-07") INTERVAL 1 DAY
)
DISTRIBUTED BY HASH (`event`) BUCKETS 1
PROPERTIES (
	"replication_allocation" = "tag.location.default: 1",
	"compression"="LZ4",
	"dynamic_partition.enable" = "true",
	"dynamic_partition.time_unit" = "DAY",
	"dynamic_partition.end" = "3",
	"dynamic_partition.prefix" = "p",
	"dynamic_partition.buckets" = "1"
);

之后导入测试的数据

mysql> select count(1) from ods_test_log;
+----------+
| count(1) |
+----------+
|  8637519 |
+----------+
1 row in set (0.02 sec)



mysql> select count(1) from ods_test_variant;
+----------+
| count(1) |
+----------+
|  8638462 |
+----------+
1 row in set (0.02 sec)


-- 数据量基本一致

存储占用

mysql> show data from ods_test_log  ;
+---------------+---------------+----------+--------------+----------+------------+
| TableName     | IndexName     | Size     | ReplicaCount | RowCount | RemoteSize |
+---------------+---------------+----------+--------------+----------+------------+
| ods_test_log  | ods_test_log  | 1.770 GB | 103          | 8650954  | 0.000      |
|               | Total         | 1.770 GB | 103          |          | 0.000      |
+---------------+---------------+----------+--------------+----------+------------+
2 rows in set (0.00 sec)



mysql> show data from ods_test_variant  ;
+-------------------+-------------------+------------+--------------+----------+------------+
| TableName         | IndexName         | Size       | ReplicaCount | RowCount | RemoteSize |
+-------------------+-------------------+------------+--------------+----------+------------+
| ods_test_variant  | ods_test_variant  | 455.069 MB | 103          | 8648850  | 0.000      |
|                   | Total             | 455.069 MB | 103          |          | 0.000      |
+-------------------+-------------------+------------+--------------+----------+------------+
2 rows in set (0.01 sec)

**采用 variant 类型,由优化前的 1.77G (1.77 * 1024MB)下降至 455.1MB ,存储占用下降约 75% **

查询速度

-- variant 查询访问默认是关闭的
-- 查询前设置会话变量
set enable_variant_access_in_original_planner = true;

需求1、各用户的PC和APP访问次数
-- ods_test_log  
SELECT
    id,
    sum(xxx)PC,
    sum(xxx)APP
FROM(
        SELECT
            id,
            if(get_json_string(xxx, '$.xx')='xx','xx', 'xx')
        FROM
            ods_test_log  
        WHERE dt = xxx
        ) t
GROUP BY id; 

在这里插入图片描述

-- ods_test_variant
SELECT
    id,
    sum(xxx)PC,
    sum(xxx)APP
FROM(
        SELECT
            id,
            if(xxx['xxx']='xxx','xx', 'xx')
        FROM
            ods_test_variant
        WHERE dt = xxx
        ) t
GROUP BY id;

在这里插入图片描述

查询速度提升近4倍

需求2、求点击xxxx次数
-- ods_test_log  
SELECT 
	flag,
	count(*)
FROM
	(
	SELECT
		get_json_string(properties,'$.xxx')flag
	FROM
		ods_test_log  
	WHERE 
		event = 'xxx'
		AND dt = xx)t1
WHERE
	flag IN ('xxxx', 'xxxx')
GROUP BY 
	flag;

在这里插入图片描述

-- ods_test_variant

SELECT 
	flag,
	count(*)
FROM
	(
	SELECT
		cast(properties['$xxx'] as text) as flag
	FROM
		ods_test_variant
	WHERE 
		event = 'xxx'
		AND dt = xxx)t1
WHERE
	flag in ('xxxx', 'xxxx')
GROUP BY 
	flag;

注意:如使用过滤和聚合等功能来查询子列, 需要对子列进行额外的 cast 操作(因为存储类型不一定是固定的,需要有一个 SQL 统一的类型)。

**例如 SELECT * FROM tbl where CAST(var[‘titile’] as text) MATCH “hello world” **

在这里插入图片描述

查询速度提升48.5倍

需求3、访问用户地区统计
-- ods_test_log  
SELECT
	DISTINCT id,
	get_json_string(xxx,'$.xxx')xx,
	get_json_string(xxx,'$.xxx')xx
FROM
	ods_test_log  
WHERE
	dt = xxx
	AND get_json_string(properties,'$.xxx') LIKE 'xxxx';

在这里插入图片描述

-- ods_test_variant
SELECT
	DISTINCT id,
	cast(xxx['xxx'] as text) as xx,
	cast(xxx['xxx'] as text) as xx
FROM
	ods_test_variant
WHERE
	dt = xxx
	AND cast(properties['xxx'] as text) LIKE 'xxxx';

在这里插入图片描述

查询速度提升133倍!

总结

在存储占用方面,采用 Variant 数据类型后,存储占用下降约74%(测试数据较少),与官方宣传测试相符;
在这里插入图片描述

在查询速度方面,采用 Variant 数据类型后,查询速度提升倍数不一(测试样例少),但可发现,在需要过滤、聚合、子查询中使用 get_json_string 解析时,Variant 数据类型查询速度提升极其可观;
在这里插入图片描述

注意事项:

  • 目前 Variant 暂不支持 Aggregate 模型,也不支持将 Variant 类型作为 Unique 或 Duplicate 模型的主键及排序键;
  • 推荐使用 RANDOM 模式或者开启 Group Commit 导入,写入性能更高效;
  • 日期、Decimal 等非标准 JSON 类型尽可能提取出来作为静态字段,性能更好;
  • 二维及其以上的数组以及数组嵌套对象,列存化会被存成 JSONB 编码,性能不如原生数组;
  • 查询过滤、聚合需要带 Cast,存储层会根据存储类型和 Cast 目标类型来提示(hint)存储引擎谓词下推,加速查询。
  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值