Hive的WITH table_name AS ..语句优化性能

本文介绍Hive中WITH AS语句的功能和使用方法,它能有效提升含有重复子查询的SQL语句的执行效率。特别适用于UNION ALL操作中避免多次执行相同的子查询,从而减少计算成本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Hive的WITH语句会将表预先加载入内存,在以提高代码运行效率。

一.WITH AS的含义
WITH AS短语,也叫做子查询部分(subquery factoring),可以让你做很多事情,定义一个SQL片断,该SQL片断会被整个SQL语句所用到。有的时候,是为了让SQL语句的可读性更高些,也有可能是在UNION ALL的不同部分,作为提供数据的部分。
特别对于UNION ALL比较有用。因为UNION ALL的每个部分可能相同,但是如果每个部分都去执行一遍的话,则成本太高,所以可以使用WITH AS短语,则只要执行一遍即可。如果WITH AS短语所定义的表名被调用两次以上,则优化器会自动将WITH AS短语所获取的数据放入一个TEMP表里,如果只是被调用一次,则不会。而提示materialize则是强制将WITH AS短语里的数据放入一个全局临时表里。很多查询通过这种方法都可以提高速度。

二.使用方法
先看下面一个嵌套的查询语句:

select * from person.StateProvince where CountryRegionCode in
(select CountryRegionCode from person.CountryRegion where Name like 'C%')

再看用with改写之后的语句

with
cr as
(
select CountryRegionCode from person.CountryRegion where Name like 'C%'
)
select * from person.StateProvince where CountryRegionCode in (select * from cr)

<think>我们之前使用的是COW表,现在需要转换为MOR表并使用Bucket索引。注意,Hudi表类型在创建后无法直接转换,需要重新写入数据。以下是详细步骤: ### 1. 修改表类型并启用Bucket索引 ```scala import org.apache.spark.sql.functions import org.apache.hudi.DataSourceWriteOptions._ import org.apache.hudi.config.HoodieWriteConfig._ // 从Hudi表读取现有数据(或从原始数据源重新读取) val base_data = spark.read.format("hudi").load("/tmp/tpcds_hudi_cow_1000/store_sales") // 配置MOR表参数 val morOptions = Map( TABLE_TYPE.key -> "MERGE_ON_READ", // 改为MOR表 TABLE_NAME.key -> "store_sales_mor", // 新表名(或相同表名但不同路径) OPERATION.key -> "bulk_insert", // 批量插入 RECORDKEY_FIELD.key -> "id", // 主键 PRECOMBINE_FIELD.key -> "id", // 预合并字段 PARTITIONPATH_FIELD.key -> "ss_sold_date_sk", // 分区字段 // 索引配置 INDEX_TYPE.key -> "BUCKET", // Bucket索引 "hoodie.bucket.index.hash.field" -> "id", // 分桶主键(与记录键一致) "hoodie.bucket.index.num.buckets" -> "8", // 分桶数量(根据数据量调整) // Hive同步配置 HIVE_SYNC_ENABLED.key -> "true", HIVE_DATABASE.key -> "tpcds_hudi_mor_1000", HIVE_TABLE.key -> "store_sales_mor", HIVE_PARTITION_FIELDS.key -> "ss_sold_date_sk", HIVE_PARTITION_EXTRACTOR_CLASS.key -> "org.apache.hudi.hive.MultiPartKeysValueExtractor" ) // 写入新路径(避免覆盖原COW表) base_data.write.format("org.apache.hudi") .options(morOptions) .mode("overwrite") .save("/tmp/tpcds_hudi_mor_1000/store_sales_mor") ``` ### 2. Bucket索引关键参数说明 | **参数** | **作用** | **推荐值** | |----------|----------|------------| | `hoodie.index.type` | 索引类型 | `BUCKET` | | `hoodie.bucket.index.hash.field` | 分桶字段 | 通常与`RECORDKEY_FIELD`一致 | | `hoodie.bucket.index.num.buckets` | 分桶数量 | 每个分区文件数,建议设为`spark.default.parallelism`的倍数 | ### 3. MOR表查询优化 ```scala // 实时视图(读取最新快照) spark.read.format("hudi") .option(QUERY_TYPE.key, QUERY_TYPE_SNAPSHOT_OPT_VAL) .load("/tmp/tpcds_hudi_mor_1000/store_sales_mor") // 增量查询(读取指定提交后的数据) spark.read.format("hudi") .option(QUERY_TYPE.key, QUERY_TYPE_INCREMENTAL_OPT_VAL) .option(BEGIN_INSTANTTIME.key, "20230901000000") .load("/tmp/tpcds_hudi_mor_1000/store_sales_mor") ``` ### 4. 性能优化对比 ```mermaid graph LR A[COW表] -->|更新时重写整个文件| B(高写放大) C[MOR表] -->|更新写入日志文件| D(低延迟写入) E[Bucket索引] -->|O(1)查找| F(避免全分区扫描) ``` ### 5. 验证MOR表 ```scala // 检查表类型 spark.sql(""" describe formatted tpcds_hudi_mor_1000.store_sales_mor """).filter($"col_name".contains("Type")).show() // 应输出: MERGE_ON_READ // 检查索引类型 spark.sql(""" show tblproperties tpcds_hudi_mor_1000.store_sales_mor """).filter($"key".contains("index")).show() // 应输出: BUCKET ``` ### 6. 注意事项 1. **数据迁移**: 重新写入全量数据,非原地转换 2. **小文件处理**: 启用`hoodie.cleaner.commits.retained`和`hoodie.compact.inline`控制日志文件合并 3. **查询性能**: MOR表读时合并可能增加CPU开销,需权衡读写性能 §§相关问题§§ 1. 如何监控Hudi MOR表的实时摄取延迟? 2. Bucket索引与Bloom索引的性能差异? 3. 如何优化MOR表的压缩策略?</think>### 将 Hudi 表从 COW 迁移到 MOR 并使用 Bucket 索引的完整方案 #### 1. 迁移流程(需重新写入数据) ```scala import org.apache.hudi.DataSourceWriteOptions._ import org.apache.hudi.config.HoodieWriteConfig._ // 从原COW表读取数据(或从原始数据源读取) val df = spark.read.format("hudi").load("/tmp/tpcds_hudi_cow_1000/store_sales") // 配置MOR表参数 val morOptions = Map( TABLE_TYPE.key -> "MERGE_ON_READ", // 改为MOR表 TABLE_NAME.key -> "store_sales_mor", // 新表名 OPERATION.key -> "bulk_insert", RECORDKEY_FIELD.key -> "id", PRECOMBINE_FIELD.key -> "id", PARTITIONPATH_FIELD.key -> "ss_sold_date_sk", // Bucket索引配置 INDEX_TYPE.key -> "BUCKET", // 启用Bucket索引 "hoodie.bucket.index.hash.field" -> "id,ss_item_sk", // 分桶字段 "hoodie.bucket.index.num.buckets" -> "16", // 桶数量 // Hive同步 HIVE_SYNC_ENABLED.key -> "true", HIVE_DATABASE.key -> "tpcds_hudi_mor_1000", HIVE_TABLE.key -> "store_sales_mor", HIVE_PARTITION_FIELDS.key -> "ss_sold_date_sk" ) // 写入MOR表(新路径) df.write.format("org.apache.hudi") .options(morOptions) .mode("overwrite") .save("/tmp/tpcds_hudi_mor_1000/store_sales") ``` #### 2. Bucket 索引关键配置说明 | **参数** | **作用** | **推荐值** | |----------|----------|------------| | `hoodie.index.type=BUCKET` | 启用桶索引 | 必须 | | `hoodie.bucket.index.hash.field` | 分桶字段 | 主键+常用查询字段 | | `hoodie.bucket.index.num.buckets` | 桶数量 | 建议 $2^n$ 且 $\geq$ 计算核心数 | | `hoodie.bucket.index.engine` | 计算引擎 | `SPARK`(默认) | #### 3. MOR 表优化配置 ```properties # 日志文件压缩策略(在hudi.properties中) hoodie.compact.inline=true hoodie.compact.inline.max.delta.commits=5 # 查询优化 hoodie.payload.ordering.field=id hoodie.datasource.query.type=snapshot ``` #### 4. 性能对比分析 ```mermaid graph LR A[COW表] -->|更新时重写文件| B(高写放大) C[MOR表] -->|更新写入日志文件| D(低延迟写入) E[Bucket索引] -->|O(1)查找| F(避免全分区扫描) ``` #### 5. 验证步骤 ```scala // 检查表类型 spark.sql(""" show tblproperties tpcds_hudi_mor_1000.store_sales_mor """).filter($"key" === "hoodie.table.type").show() // 应输出: MERGE_ON_READ // 检查索引类型 spark.sql(""" show tblproperties tpcds_hudi_mor_1000.store_sales_mor """).filter($"key" === "hoodie.index.type").show() // 应输出: BUCKET ``` #### 6. 注意事项 1. **数据迁移**: Hudi 表类型无法直接转换,必须重新写入数据 2. **桶数量优化**: 根据数据量计算最优值:$$ \text{bucket\_count} = \frac{\text{总记录数}}{\text{目标文件大小}} $$ 3. **查询模式**: MOR 表提供两种查询视图: - `snapshot`:实时合并视图(默认) - `read_optimized`:仅列存文件(更快但非实时)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值