Kyuubi Zorder 调研测试

Kyuubi Z-order 调研测试

Z-order 是一种将多维数据映射到一维上的排序算法,排序后使得多维数据彼此临近,更有利于数据压缩和查询时 Data Skip。

所谓的 Z-order 优化实际上是在数据写入时添加 Z-order 排序,将数据进行聚类,很好的提升了数据的压缩率和查询性能。

Kyuubi 通过 Spark 插件的形式,实现了 Z-order 优化,具体使用参考:Z-Ordering Support

下面我参考了 Kyuubi 官方文档对 Z-order 功能进行调研测试:Z-order Benchmark

数据准备

使用 Beeline 连接 Kyuubi,准备测试数据。

1、创建原始数据表:

关闭 AQE ,便于控制输出文件数。

1
2
3
// 关闭 AQE
set spark.sql.adaptive.enabled=false;
set spark.sql.shuffle.partitions=200;

创建原始表:

1
2
3
4
5
create table sample.conn_random (
src_ip string,
src_port int,
dst_ip string,
dst_port int) stored as parquet;

使用 Scala 执行模式生成原始表数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 切换成 Scala 执行模式
set kyuubi.operation.language=scala;

import scala.util.Random;
def randomIPv4(r: Random) = Seq.fill(4)(r.nextInt(256)).mkString(".");
def randomPort(r: Random) = r.nextInt(65536);

case class ConnRecord(src_ip: String, src_port: Int, dst_ip: String, dst_port: Int);
def randomConnRecord(r: Random) = ConnRecord(src_ip = randomIPv4(r), src_port = randomPort(r), dst_ip = randomIPv4(r), dst_port = randomPort(r));

val numRecords = 100 * 1000 * 1000L;
val numFiles = 200;

val df = spark.range(0, numFiles, 1, numFiles).mapPartitions { it =>
val partitionID = it.toStream.head
val r = new Random(seed = partitionID)
Iterator.fill((numRecords / numFiles).toInt)(randomConnRecord(r))
};

// 创建原始测试数据表 sample.conn_random
df.write.mode("overwrite").format("parquet").insertInto("sample.conn_random");

// 切换成 SQL 执行模式
spark.sql("set kyuubi.operation.language=sql");

2、生成 Order by 排序的表:

1
2
3
4
5
6
7
// conn_order_only_ip
create table sample.conn_order_only_ip like sample.conn_random;
INSERT overwrite sample.conn_order_only_ip select * from sample.conn_random order by src_ip, dst_ip;

// conn_order
create table sample.conn_order like sample.conn_random;
INSERT overwrite sample.conn_order select * from sample.conn_random order by src_ip, src_port, dst_ip, dst_port;

2、生成 Z-order 优化的表:

1
2
3
4
5
6
7
8
9
// conn_zorder_only_ip
create table sample.conn_zorder_only_ip like sample.conn_random;
insert overwrite sample.conn_zorder_only_ip select * from sample.conn_random;
OPTIMIZE sample.conn_zorder_only_ip ZORDER BY src_ip, dst_ip;

// conn_zorder
create table sample.conn_zorder like sample.conn_random;
insert overwrite sample.conn_zorder select * from sample.conn_random;
OPTIMIZE sample.conn_zorder ZORDER BY src_ip, src_port, dst_ip, dst_port;

Zorder 优化效果对比分析

1、存储分析

表名 大小 生成时间
conn_random 2.7 G 1.1 min
conn_order_only_ip 2.2 G 1.3 min
conn_order 2.2 G 52 s
conn_zorder_only_ip 1510.3 MiB 1.9 min
conn_zorder 1510.4 MiB 1.9 min

2、查询分析

1
2
3
4
5
6
// query
select count(*) from sample.conn_random where src_ip like '157%' and dst_ip like '216.%';
select count(*) from sample.conn_order_only_ip where src_ip like '157%' and dst_ip like '216.%';
select count(*) from sample.conn_order where src_ip like '157%' and dst_ip like '216.%';
select count(*) from sample.conn_zorder_only_ip where src_ip like '157%' and dst_ip like '216.%';
select count(*) from sample.conn_zorder where src_ip like '157%' and dst_ip like '216.%';
表名 扫描数据量
conn_random 100,000,000
conn_order_only_ip 400,000
conn_order 400,648
conn_zorder_only_ip 120,000
conn_zorder 120,000

3、总结

从上面的测试结果中可以很直观的看到 Z-order 优化的一些特点:

  • 排序消耗:由于需要对数据进行排序,所以对于数据生产任务有少许额为的排序消耗。
  • 减少空间:数据进行了聚类,使得相邻数据有很高的相似性,大幅提高数据压缩率,减少存储空间。
  • 提高查询性能:数据临近也让 RowGroup 数据范围比较小,加大了查询时 Data skip 效果,减少扫描的数据量,提升查询性能。