Práctica de Spark SQL para operar tablas HUDI

Conceptos relacionados con la tabla HUDI

  • tipo de tabla

    • vaca
    • mor
  • Tabla particionada/tabla no particionada

    Los usuarios pueden crear tablas particionadas y tablas no particionadas en Spark SQL. Para crear una tabla particionada, debe utilizar la instrucción particionada por para especificar la columna de partición para crear la tabla particionada. Cuando no existe una instrucción by para particionar usando el comando crear tabla, la tabla se considera una tabla no particionada.

  • Mesas internas y externas.

    Generalmente, Spark SQL admite dos tipos de tablas, a saber, tablas internas y tablas externas. Si especifica una ubicación mediante la declaración de ubicación o crea la tabla explícitamente mediante la creación de una tabla externa, entonces es una tabla externa; de lo contrario, se considera una tabla interna.

prestar atención:

  1. A partir de Hudi 0.10.0, se debe especificar PrimaryKey para representar el campo de clave principal al crear una tabla Hudi. Si no especifica el campo clave principal, Hudi usará uuid como campo de clave principal de forma predeterminada. Recomendamos que al crear una tabla Hudi, se especifique el campo de clave principal.
  2. Para la tabla mor, se debe especificar el campo preCombineField para indicar el orden de los datos.
  3. Si la tabla es una tabla particionada, la configuración sudadera con capucha.datasource.write.hive_style_partitioning debe especificarse explícitamente como verdadera, y si la tabla es una tabla no particionada, la configuración sudadera con capucha.datasource.write.hive_style_partitioning debe especificarse explícitamente como falsa .

Usar el catálogo hudi

Después de que Spark en WDP instala hudi, utiliza el catálogo de hudi de forma predeterminada. Los metadatos se guardarán en el metastore de Hive cuando se cree la tabla.

Crear una tabla interna sin particionar

-- 创建cow不分区表,指定主键为uuid
create table hudi_cow_nonpcf_tbl (
  uuid int,
  name string,
  price double
) using hudi
tblproperties (
  type = 'cow',
  primaryKey = 'uuid',
  hoodie.datasource.write.hive_style_partitioning = 'false'
);

-- 创建mor不分区表,指定主键为id,指定预合并字段为ts
create table hudi_mor_tbl (
  id int,
  name string,
  price double,
  ts bigint
) using hudi
tblproperties (
  type = 'mor',
  primaryKey = 'id',
  preCombineField = 'ts',
  hoodie.datasource.write.hive_style_partitioning = 'false'
);

Crear tabla externa particionada

create table hudi_cow_pt_tbl (
  id bigint,
  name string,
  ts bigint,
  dt string,
  hh string
) using hudi
tblproperties (
  type = 'cow',
  primaryKey = 'id',
  preCombineField = 'ts',
  hoodie.datasource.write.hive_style_partitioning = 'true',
  hoodie.datasource.hive_sync.mode = 'hms'
 )
partitioned by (dt, hh)
location '/tmp/hudi/hudi_cow_pt_tbl';

create external table hudi_cow_pt_tbl_2 (
  id bigint,
  name string,
  ts bigint,
  dt string,
  hh string
) using hudi
tblproperties (
  type = 'cow',
  primaryKey = 'id',
  preCombineField = 'ts',
  hoodie.datasource.write.hive_style_partitioning = 'true'
 )
partitioned by (dt, hh);

Puede encontrar más información sobre la configuración de los atributos tblproperties en la tabla en el sitio web oficial: https://hudi.apache.org/docs/basic_configurations

CTAS

Hudi admite el uso de CTAS (Crear tabla como selección) en Spark SQL para crear tablas hudi.

Nota: Para un mejor rendimiento al cargar datos en tablas hudi, CTAS utiliza la inserción masiva como operación de escritura.

El siguiente es un ejemplo del uso del comando CTAS para crear una tabla COW no particionada sin preCombineField.

create table hudi_ctas_cow_nonpcf_tbl
using hudi
tblproperties (primaryKey = 'id')
as
select 1 as id, 'a1' as name, 10 as price;

Utilice CTAS para crear una tabla COW con clave principal y campos de partición

create table hudi_ctas_cow_pt_tbl
using hudi
tblproperties (type = 'cow', primaryKey = 'id', preCombineField = 'ts')
partitioned by (dt)
as
select 1 as id, 'a1' as name, 10 as price, 1000 as ts, '2021-12-01' as dt;

Utilice CTAS para cargar datos de otra tabla.

# create managed parquet table
create table parquet_mngd using parquet location 'file:///tmp/parquet_dataset/*.parquet';

# CTAS by loading data into hudi table
create table hudi_ctas_cow_pt_tbl2 using hudi location 'file:/tmp/hudi/hudi_tbl/' options (
  type = 'cow',
  primaryKey = 'id',
  preCombineField = 'ts'
 )
partitioned by (datestr) as select * from parquet_mngd;

Insertar datos

-- insert into non-partitioned table
insert into hudi_cow_nonpcf_tbl select 1, 'a1', 20;
insert into hudi_mor_tbl select 1, 'a1', 20, 1000;

-- insert dynamic partition
insert into hudi_cow_pt_tbl partition (dt, hh)
select 1 as id, 'a1' as name, 1000 as ts, '2021-12-09' as dt, '10' as hh;

-- insert static partition
insert into hudi_cow_pt_tbl partition(dt = '2021-12-09', hh='11') select 2, 'a2', 1000;

Aviso:

De forma predeterminada, insertar en usa upsert como tipo de operación de escritura si se proporciona preCombineKey; de lo contrario, se usa insertar.

Admitimos el uso de Bulk_insert como tipo de operación de escritura. Solo necesita establecer dos configuraciones: sudadera con capucha.sql.bulk.insert.enable y sudadera con capucha.sql.insert.mode. Tome lo siguiente como ejemplo:

-- upsert mode for preCombineField-provided table
insert into hudi_mor_tbl select 1, 'a1_1', 20, 1001;
select id, name, price, ts from hudi_mor_tbl;
1   a1_1    20.0    1001

-- bulk_insert mode for preCombineField-provided table
set hoodie.sql.bulk.insert.enable=true;
set hoodie.sql.insert.mode=non-strict;

insert into hudi_mor_tbl select 1, 'a1_2', 20, 1002;
select id, name, price, ts from hudi_mor_tbl;
1   a1_1    20.0    1001
1   a1_2    20.0    1002

consulta de datos

select * from hudi_mor_nonpcf_tbl where price > 10;

A partir de 0.9.0, hudi ha admitido el FileIndex integrado de hudi: HoodFileIndex para consultar tablas de hudi, lo que admite la poda de particiones y la consulta de metatablas. Esto ayudará a mejorar el rendimiento de las consultas. También admite rutas de consulta no globales, lo que significa que los usuarios pueden consultar tablas a través de la ruta base sin especificar "*" en la ruta de consulta. Esta característica está habilitada de forma predeterminada para rutas de consulta no globales. Para rutas de consulta globales, hudi utiliza la ruta de consulta anterior. Para obtener más información sobre todos los tipos de tablas y consultas admitidos, consulte Tipos de tablas y consultas.

consulta de viaje en el tiempo

Hudi admite consultas de viajes en el tiempo a partir de 0.9.0. Actualmente se admiten tres formatos de hora de consulta, como se muestra a continuación.

Nota: solo es compatible con la versión Spark 3.2+

create table hudi_cow_pt_tbl_3 (
  id bigint,
  name string,
  ts bigint,
  dt string,
  hh string
) using hudi
tblproperties (
  type = 'cow',
  primaryKey = 'id',
  preCombineField = 'ts',
  hoodie.datasource.write.hive_style_partitioning = 'true'
 )
partitioned by (dt, hh);


insert into hudi_cow_pt_tbl_3 select 1, 'a0', 1000, '2021-12-09', '10';
select * from hudi_cow_pt_tbl_3;

-- record id=1 changes `name`
insert into hudi_cow_pt_tbl_3 select 1, 'a1', 1001, '2021-12-09', '10';
select * from hudi_cow_pt_tbl_3;

-- time travel based on first commit time, assume `20220725112636518`
select * from hudi_cow_pt_tbl_3 timestamp as of '20220725112636518' where id = 1;
-- time travel based on different timestamp formats
select * from hudi_cow_pt_tbl_3 timestamp as of '2022-07-25 11:26:36.100' where id = 1;
select * from hudi_cow_pt_tbl_3 timestamp as of '2022-07-26' where id = 1;

Actualización de datos

Esto es similar a insertar nuevos datos. Utilice un generador de datos para generar actualizaciones de viajes existentes, cárguelo en un DataFrame y escriba el DataFrame en una tabla hudi.

Spark SQL admite dos tipos de DML para actualizar tablas hudi: Merge-Into y Update.

Actualizar

gramática

UPDATE tableIdentifier SET column = EXPRESSION(,column = EXPRESSION) [ WHERE boolExpression]

Ejemplo

update hudi_mor_tbl set price = price * 2, ts = 1111 where id = 1;

update hudi_cow_pt_tbl set name = 'a1_1', ts = 1001 where id = 1;

-- update using non-PK field
update hudi_cow_pt_tbl set ts = 1001 where name = 'a1';

Nota: La operación de actualización requiere preCombineFieldcampos

Unirse con

gramática

MERGE INTO tableIdentifier AS target_alias
USING (sub_query | tableIdentifier) AS source_alias
ON <merge_condition>
[ WHEN MATCHED [ AND <condition> ] THEN <matched_action> ]
[ WHEN MATCHED [ AND <condition> ] THEN <matched_action> ]
[ WHEN NOT MATCHED [ AND <condition> ]  THEN <not_matched_action> ]

<merge_condition> =A equal bool condition 
<matched_action>  =
  DELETE  |
  UPDATE SET *  |
  UPDATE SET column1 = expression1 [, column2 = expression2 ...]
<not_matched_action>  =
  INSERT *  |
  INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...])

Ejemplo

-- source table using hudi for testing merging into non-partitioned table
create table merge_source (id int, name string, price double, ts bigint) using hudi
tblproperties (
  primaryKey = 'id',
  preCombineField = 'ts',
  hoodie.datasource.write.hive_style_partitioning = 'false'
);

insert into merge_source values (1, "old_a1", 22.22, 900), (2, "new_a2", 33.33, 2000), (3, "new_a3", 44.44, 2000);

-- 该语法会导致使用HIVE JDBC,所以源表需要指定同步方式为hms
merge into hudi_mor_tbl as target
using merge_source as source
on target.id = source.id
when matched then update set *
when not matched then insert *
;

-- source table using parquet for testing merging into partitioned table
create table merge_source2 (id int, name string, flag string, dt string, hh string) using parquet;
insert into merge_source2 values (1, "new_a1", 'update', '2021-12-09', '10'), (2, "new_a2", 'delete', '2021-12-09', '11'), (3, "new_a3", 'insert', '2021-12-09', '12');

merge into hudi_cow_pt_tbl as target
using (
  select id, name, '1000' as ts, flag, dt, hh from merge_source2
) source
on target.id = source.id
when matched and flag != 'delete' then
 update set id = source.id, name = source.name, ts = source.ts, dt = source.dt, hh = source.hh
when matched and flag = 'delete' then delete
when not matched then
 insert (id, name, ts, dt, hh) values(source.id, source.name, source.ts, source.dt, source.hh)
;

Eliminación de datos

gramática

DELETE FROM tableIdentifier [ WHERE BOOL_EXPRESSION]

Ejemplo

delete from hudi_cow_nonpcf_tbl where uuid = 1;

delete from hudi_mor_tbl where id % 2 = 0;

-- delete using non-PK field
delete from hudi_cow_pt_tbl where name = 'a1';

Insertar sobrescribir

Para trabajos ETL por lotes, esta operación puede ser más rápida que upsert, que vuelve a calcular toda la partición de destino a la vez (en lugar de actualizar incrementalmente la tabla de destino). Esto se debe a que podemos omitir por completo la indexación, la precombinación y otros pasos de repartición en la ruta de escritura upsert.

Insertar y sobrescribir una tabla particionada utiliza el tipo de operación de escritura INSERT_OVERWRITE, mientras que una tabla no particionada utiliza INSERT_OVERWRITE_TABLE.

-- insert overwrite non-partitioned table
insert overwrite hudi_mor_tbl select 99, 'a99', 20.0, 900;
insert overwrite hudi_cow_nonpcf_tbl select 99, 'a99', 20.0;

-- insert overwrite partitioned table with dynamic partition
insert overwrite table hudi_cow_pt_tbl select 10, 'a10', 1100, '2021-12-09', '10';

-- insert overwrite partitioned table with static partition
insert overwrite hudi_cow_pt_tbl partition(dt = '2021-12-09', hh='12') select 13, 'a13', 1100;

Más comandos Spark SQL

Modificar tabla

La evolución del esquema se puede lograr mediante el comando ALTER TABLE. A continuación se muestran algunos ejemplos básicos.

gramática:

-- Alter table name
ALTER TABLE oldTableName RENAME TO newTableName

-- Alter table add columns
ALTER TABLE tableIdentifier ADD COLUMNS(colAndType (,colAndType)*)

-- Alter table column type
ALTER TABLE tableIdentifier CHANGE COLUMN colName colName colType

-- Alter table properties
ALTER TABLE tableIdentifier SET TBLPROPERTIES (key = 'value')

Ejemplo

--rename to:
ALTER TABLE hudi_cow_nonpcf_tbl RENAME TO hudi_cow_nonpcf_tbl2;

--add column:
ALTER TABLE hudi_cow_nonpcf_tbl2 add columns(remark string);

--change column:
ALTER TABLE hudi_cow_nonpcf_tbl2 change column uuid uuid bigint;

--set properties;
alter table hudi_cow_nonpcf_tbl2 set tblproperties (hoodie.keep.max.commits = '10');

Comandos SQL de partición

gramática:

-- Drop Partition
ALTER TABLE tableIdentifier DROP PARTITION ( partition_col_name = partition_col_val [ , ... ] )

-- Show Partitions
SHOW PARTITIONS tableIdentifier

Ejemplo

--show partition:
show partitions hudi_cow_pt_tbl;

--drop partition:
alter table hudi_cow_pt_tbl drop partition (dt='2021-12-09', hh='10');

Actualmente, los resultados de mostrar particiones se basan en las rutas de las tablas del sistema de archivos. No es exacto eliminar todos los datos de la partición o eliminar directamente una partición.

Trámites

gramática

--Call procedure by positional arguments
CALL system.procedure_name(arg_1, arg_2, ... arg_n)

--Call procedure by named arguments
CALL system.procedure_name(arg_name_2 => arg_2, arg_name_1 => arg_1, ... arg_name_n => arg_n)

Ejemplo

--show commit's info
call show_commits(table => 'test_hudi_table', limit => 10);

El comando Llamar ya admite algunos procedimientos de confirmación y optimizadores de tablas. Consulte Procedimientos para obtener más detalles .

Supongo que te gusta

Origin blog.csdn.net/weixin_39636364/article/details/128343038
Recomendado
Clasificación