官宣!AWS Athena正式可查詢Apache Hudi資料集
阿新 • • 發佈:2020-07-27
## 1. 引入
Apache Hudi是一個開源的增量資料處理框架,提供了行級insert、update、upsert、delete的細粒度處理能力(`Upsert`表示如果資料集中存在記錄就更新;否則插入)。
Hudi處理資料插入和更新,不會建立太多的小檔案(小檔案會導致查詢端效能降低),Apache Hudi自動管理及合併小檔案,讓其保持指定大小,這避免了自建解決方案來監控和重寫小檔案為大檔案。
Hudi資料集在如下場景下非常適用
* 使用GDPR和CCPA法規來刪除使用者個人資訊或修改個人資訊用途。
* 處理感測器或IoT裝置的流式資料,涉及資料插入和更新。
* 實現CDC系統
Hudi使用開放的資料格式管理S3的資料集。現在Athena可以查詢Hudi資料集,但暫還不支援寫入,Athena使用Apache Hudi 0.5.2-incubating版本,0.5.2-incubating版本資訊可參考[這裡](https://github.com/apache/hudi/tree/release-0.5.2)
## 2. Hudi資料集型別
Hudi資料集有如下型別
* **Copy on Write (CoW)** – 使用Parquet列式儲存,每次更新將會建立一個新版本。
- **Merge on Read (MoR)** – 使用Parquet列式 + Avro行式儲存,更新將會寫入`delta`日誌檔案,後面將會和Parquet列式檔案進行壓縮生成新版本列式檔案。
對於CoW資料集,對記錄更新時,包含記錄的檔案將會被重寫;對於MoR資料集,對記錄更新時,Hudi僅僅只會寫更新的值。因此MoR更適合重寫的場景,CoW更適合重讀場景(資料很少變更)。
Hudi提供了三種邏輯檢視來訪問資料:
- **Read-optimized 檢視** – 提供CoW表最新提交的資料集和MoR表最新壓縮的資料集,均讀取Parquet檔案。
- **Incremental 檢視** – 提供CoW表中兩次提交的變更流,便於下游ETL作業。
- **Real-time 檢視** – 提供MoR表最新提交的資料,在查詢時合併列式和行式檔案。
現在Athena只支援Read-optimized檢視,這提供了更好的查詢效能但未包含最新的delta提交。關於資料集型別做的tradeoff,可以參考Hudi文件[Storage Types & Views](https://hudi.apache.org/docs/0.5.0-concepts.html#storage-types--views) 。
## 3. 考慮及限制
- Athena對Hudi資料集僅支援查詢Read-optimized檢視
- 對於CoW型別,Athena支援快照查詢;
- 對於MoR型別,Athena支援讀優化查詢;
- Athena對Hudi資料集不支援[CTAS](https://docs.aws.amazon.com/athena/latest/ug/ctas.html) 或 [INSERT INTO](https://docs.aws.amazon.com/athena/latest/ug/insert-into.html),更多關於如何寫入Hudi資料集,可參考
- [Amazon EMR 釋出指南](https://docs.aws.amazon.com/emr/latest/ReleaseGuide/)中[玩轉Hudi資料集](https://docs.aws.amazon.com/emr/latest/ReleaseGuide/emr-hudi-work-with-dataset.html)
- Apache Hudi文件:[寫Hudi表](https://hudi.apache.org/docs/0.5.1-writing_data.html)
- Athena對Hudi表不支援使用`MSCK REPAIR TABLE`。如果需要載入非Glue建立的Hudi表,請使用[ALTER TABLE ADD PARTITION](https://docs.aws.amazon.com/athena/latest/ug/alter-table-add-partition.html)
## 4. 建立Hudi表
本部分將提供Athena中建立分割槽和非分割槽Hudi表的建表示例。
如果已經在AWS Glue中建立了Hudi表,那麼可以直接使用Athena查詢。如果在Athena中建立Hudi表,在查詢之前必須執行`ALTER TABLE ADD PARTITION `來載入資料。
### 4.1 Copy on Write (CoW)建表示例
#### 4.1.1 非分割槽CoW表
下面示例會在Athena中建立非分割槽CoW表
```sql
CREATE EXTERNAL TABLE `non_partition_cow`(
`_hoodie_commit_time` string,
`_hoodie_commit_seqno` string,
`_hoodie_record_key` string,
`_hoodie_partition_path` string,
`_hoodie_file_name` string,
`event_id` string,
`event_time` string,
`event_name` string,
`event_guests` int,
`event_type` string)
ROW FORMAT SERDE
'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
STORED AS INPUTFORMAT
'org.apache.hudi.hadoop.HoodieParquetInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
's3://bucket/folder/non_partition_cow'
```
#### 4.1.2 分割槽CoW表
下面示例會在Athena中建立分割槽CoW表
```sql
CREATE EXTERNAL TABLE `partition_cow`(
`_hoodie_commit_time` string,
`_hoodie_commit_seqno` string,
`_hoodie_record_key` string,
`_hoodie_partition_path` string,
`_hoodie_file_name` string,
`event_id` string,
`event_time` string,
`event_name` string,
`event_guests` int)
PARTITIONED BY (
`event_type` string)
ROW FORMAT SERDE
'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
STORED AS INPUTFORMAT
'org.apache.hudi.hadoop.HoodieParquetInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
's3://bucket/folder/partition_cow'
```
下面`ALTER TABLE ADD PARTITION`示例會新增兩個分割槽到`partition_cow` 表
```sql
ALTER TABLE partition_cow ADD
PARTITION (event_type = 'one') LOCATION 's3://bucket/folder/partition_cow/one/'
PARTITION (event_type = 'two') LOCATION 's3://bucket/folder/partition_cow/two/'
```
### 4.2 Merge on Read (MoR)建表示例
Hudi對於MoR型別將會在Hive Metastore中建立兩張表:一張由你指定的表,可提供Read-optimized檢視,另一張以`_rt`結尾的表,可提供Real-time檢視。然而當你在Athena建立MoR表時,也只能查詢read-optimized檢視(real-time檢視支援社群正在進行程式碼Review,不久後可用)。
#### 4.2.1 非分割槽MoR表
下面示例會在Athena中建立非分割槽MoR表
```sql
CREATE EXTERNAL TABLE `nonpartition_mor_ro`(
`_hoodie_commit_time` string,
`_hoodie_commit_seqno` string,
`_hoodie_record_key` string,
`_hoodie_partition_path` string,
`_hoodie_file_name` string,
`event_id` string,
`event_time` string,
`event_name` string,
`event_guests` int,
`event_type` string)
ROW FORMAT SERDE
'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
STORED AS INPUTFORMAT
'org.apache.hudi.hadoop.HoodieParquetInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
's3://bucket/folder/nonpartition_mor'
```
#### 4.2.2 分割槽MoR表
下面示例會在Athena中建立分割槽MoR表
```sql
CREATE EXTERNAL TABLE `partition_mor_ro`(
`_hoodie_commit_time` string,
`_hoodie_commit_seqno` string,
`_hoodie_record_key` string,
`_hoodie_partition_path` string,
`_hoodie_file_name` string,
`event_id` string,
`event_time` string,
`event_name` string,
`event_guests` int)
PARTITIONED BY (
`event_type` string)
ROW FORMAT SERDE
'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
STORED AS INPUTFORMAT
'org.apache.hudi.hadoop.HoodieParquetInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
's3://bucket/folder/partition_mor'
```
下面`ALTER TABLE ADD PARTITION`示例會新增兩個分割槽到`partition_mor_ro`表
```sql
ALTER TABLE partition_cow ADD
PARTITION (event_type = 'one') LOCATION 's3://bucket/folder/partition_mor/one/'
PARTITION (event_type = 'two') LOCATION 's3://bucket/folder/partition_mor/tw