Hologres DataFrame API(hologres-dataframe)是面向Hologres的Python DataFrame接口。您可以用它以链式表达式构建查询,由Hologres完成计算,仅将结果返回Python。本文为您介绍Hologres DataFrame的选型、安装、连接、查询、写入及高级功能的使用方法。
-
接口的转换操作只构建惰性逻辑计划,在触发Action时编译为PostgreSQL方言SQL并下推到Hologres执行,计算在Hologres内完成,只将最终结果返回Python。
-
该SDK仅提供snake_case接口(例如
group_by),不提供groupBy等驼峰别名。 -
该SDK已开源,源码详情请参见holo-dataframe-py。
选型指南
Hologres DataFrame与Holo Client Python的对比如下。
|
维度 |
Hologres DataFrame |
Holo Client Python |
|
定位 |
高层DataFrame API |
底层读写SDK |
|
API风格 |
pandas风格的DataFrame操作 |
Put、Get、Scan、Copy |
|
数据模型 |
DataFrame(列式) |
行、键值对 |
|
写入方式 |
惰性求值,SQL下推 |
批量Put、Fixed Copy流式写入、Stage写入 |
|
查询能力 |
复杂分析查询(JOIN、聚合、窗口函数) |
主键点查、范围扫描 |
|
并发性能 |
单进程,依赖Hologres服务端并发 |
多进程并行写入(绕过GIL) |
Hologres DataFrame底层依赖Holo Client Python(hologres-dataframe依赖hologres-client0.1.2及以上版本),两者不是替代关系,而是不同抽象层级的互补工具。
不同场景的推荐方式如下。
|
场景 |
推荐方式 |
|
数据分析与ETL(聚合、JOIN) |
Hologres DataFrame |
|
复杂SQL查询(报表、BI) |
Hologres DataFrame |
|
pandas生态集成 |
Hologres DataFrame |
|
高吞吐批量写入(日志、监控数据) |
Holo Client Python(Put API+多进程) |
|
高QPS主键点查(实时特征查询) |
Holo Client Python(Get API) |
|
流式数据写入(Kafka、Flink消费) |
Holo Client Python(Fixed Copy协议) |
前提条件
-
已创建Hologres实例,并已获取连接信息(Endpoint、端口、数据库名称、AccessKey)。
-
Python版本为3.9及以上。
安装hologres-dataframe:
pip install hologres-dataframe
安装指定版本:
pip install hologres-dataframe==0.1.1
pip安装包名为hologres-dataframe,开源仓库中的源码目录名为holo-dataframe-py。
当前最新版本为0.1.1,依赖要求如下。
|
依赖包 |
最低版本 |
|
|
>= 0.1.2 |
|
|
>= 3.1 |
|
|
>= 1.3 |
|
|
>= 12.0 |
|
|
>= 4.7.9, < 5 |
基础安装已包含from_pandas()、to_pandas()和本地CSV、JSON、Parquet文件读取所需的依赖,也默认包含Function Compute UDF与UDTF自动部署SDK。已有的hologres-dataframe[fc]安装命令仍然兼容,但不再需要额外指定[fc]。
部分功能对Hologres实例版本有额外要求:从pandas DataFrame创建时,Hologres V4.1及以上版本使用Internal Stage;读写OSS文件需要Hologres V4.1及以上版本;覆盖写入在Hologres V3.1及以上版本使用原生INSERT OVERWRITE。详情请参见对应章节。
连接Hologres
基本用法
Session管理连接池、数据库上下文、临时Stage或临时表以及Function Compute配置。建议作为上下文管理器使用,退出时自动释放连接并清理临时对象。
import hologres.dataframe as hg
with hg.connect(
host="<ENDPOINT>",
port=80,
dbname="my_db",
user="<ACCESS_KEY_ID>",
password="<ACCESS_KEY_SECRET>",
sslmode="require",
) as session:
session.table("public.orders").limit(10).show()
生产环境请勿在代码中硬编码AccessKey,建议通过环境变量或阿里云凭据链获取。
连接池参数
|
参数 |
默认值 |
说明 |
|
|
|
连接池最小连接数。 |
|
|
|
连接池最大连接数,也是并发上限。 |
|
|
|
等待可用连接的最长秒数。 |
|
|
|
空闲连接回收时间,单位为秒。 |
|
|
|
单个连接的最大生命周期,单位为秒。 |
|
|
|
|
|
|
|
Function Compute UDF与UDTF的部署配置,详情请参见Function Compute UDF与UDTF。 |
其他关键字参数会透传给psycopg或libpq,例如sslmode、connect_timeout、keepalives和options。
切换Schema和Database
session.use_schema("analytics")
print(session.current_schema())
session.use_database("another_db")
print(session.current_database())
use_schema()修改当前search_path。PostgreSQL连接绑定Database,因此use_database()会关闭当前连接池,并使用相同的连接参数重建连接池。
创建DataFrame
从Hologres表创建
orders = session.table("public.orders")
session.table()会读取并缓存表Schema,返回惰性DataFrame。
从SQL查询创建
paid_orders = session.sql(
"SELECT order_id, user_id, amount FROM public.orders WHERE status = 'paid'"
)
session.sql()返回惰性DataFrame,原生SQL在触发Action时执行。
从Python数据创建
推荐通过Session创建,使DataFrame可以直接执行Action或写入操作。
df = session.from_dict({"id": [1, 2], "name": ["alice", "bob"]})
df = session.from_records(
[(1, "alice"), (2, "bob")],
columns=["id", "name"],
)
小数据会编译为SQL VALUES。超过inline_limit时自动转为Arrow数据并上传到Hologres。
顶层hg.from_dict()和hg.from_records()适合仅构建SQL的场景。未绑定Session的DataFrame不能执行Action。
从pandas DataFrame创建
df = session.from_pandas(pdf)
df.write("public.target")
数据在首次Action时上传。Hologres V4.1及以上版本使用Internal Stage,更低版本使用临时表兼容。Session关闭时自动清理框架创建的Stage或临时表。
从本地文件创建
支持CSV、JSON和Parquet格式,通过pyarrow读取。文件读取发生在调用read_files()时,上传延迟到首次Action。
csv_df = session.read_files("data/*.csv")
parquet_df = session.read_files("data/orders.parquet")
csv_df.group_by("region").count().show()
查询与转换数据
所有转换操作均为惰性执行,只构建逻辑计划,不会立即访问Hologres。
列表达式
hg.col()用于引用列,hg.lit()用于创建字面量。表达式中包含Column时只构建SQL表达式,不会在Python中逐行计算。
amount = hg.col("amount")
amount * 0.06
amount.between(100, 1000)
hg.col("region").isin("east", "west")
hg.col("name").ilike("ali%")
(amount > 100) & (hg.col("status") == "paid")
逻辑组合使用&、|和~,每个比较条件需要加括号。Python的and、or和not不能重载,不适用于Column表达式。
投影、过滤与排序
result = (
session.table("public.orders")
.filter((hg.col("amount") > 100) & hg.col("region").is_not_null())
.with_columns(
{
"tax": hg.col("amount") * 0.06,
"amount_with_tax": hg.col("amount") * 1.06,
}
)
.drop("internal_note")
.rename({"region": "sales_region"})
.select("order_id", "sales_region", "amount", "tax")
.distinct()
.order_by(hg.col("amount").desc())
.limit(100)
.offset(20)
)
drop()和rename()只修改查询投影,不会执行ALTER TABLE。
分组聚合
使用group_by()和agg()进行分组聚合计算。
summary = (
session.table("public.orders")
.group_by("region")
.agg(
hg.sum("amount").alias("total_amount"),
hg.avg("amount").alias("avg_amount"),
hg.count("*").alias("order_count"),
)
)
单一聚合可使用快捷方法。
session.table("public.orders").group_by("region").sum("amount")
session.table("public.orders").group_by("region").count()
对于不常用的SQL函数或特殊SQL语法,可以使用hg.function()和hg.expr()。
df.group_by("region").agg(
hg.function("stddev", hg.col("amount")).alias("amount_stddev"),
hg.expr("count(*) filter (where status = 'paid')").alias("paid_count"),
)
Join与Union
orders = session.table("public.orders")
users = session.table("public.users")
same_name_key = orders.join(users, on="user_id", how="left")
different_key = orders.join(
users,
on=orders["buyer_id"] == users["user_id"],
how="inner",
)
all_rows = orders.union_all(session.table("public.orders_archive"))
deduplicated = orders.union(session.table("public.orders_archive"))
on传字符串或字符串列表时编译为USING,传Column表达式时编译为ON。how支持inner、left、right和full。
数组展开
session.table("public.documents").explode("tags").show()
session.table("public.documents").explode("tags", outer=True).show()
explode()编译为Hologres支持的SELECT-list unnest()。设置outer=True会保留数组为空或为NULL的原始行。
获取查询结果
DataFrame的Action方法会触发实际执行,将SQL下推到Hologres并获取结果。
|
Action |
返回值 |
适用场景 |
|
|
|
在终端查看少量结果。 |
|
|
|
将完整结果加载到内存。 |
|
|
|
进入pandas处理流程。 |
|
|
|
服务端统计行数。 |
|
|
|
获取第一行。 |
|
|
|
获取前n行。 |
|
|
Row迭代器 |
分批读取大结果集。 |
for row in session.table("public.orders").iter_rows(batch_size=2000):
print(row.order_id, row.amount)
collect()和to_pandas()会把完整结果加载到客户端内存,大结果集应优先使用iter_rows()分批读取。
写入数据到Hologres表
目标表必须已经存在,建表和改表请使用session.sql()。write()是立即执行的Action,数据统一通过INSERT ... SELECT写入。
追加写入
source = session.table("public.orders_staging").select("order_id", "user_id", "amount")
source.write("public.orders")
内存数据也使用相同接口。
session.from_records(
[(1, "east", 100), (2, "west", 200)],
columns=["id", "region", "amount"],
).write("public.orders")
主键冲突处理
通过on_conflict参数指定主键冲突时的处理策略。
df.write("public.target", on_conflict=("ignore", ["id"]))
df.write("public.target", on_conflict=("update", ["id"]))
df.write("public.target", on_conflict=("replace", ["id"]))
keys必须与目标表主键一致。
覆盖写入
df.write("public.target", overwrite=True)
df.write(
"public.partitioned_target",
overwrite=True,
partition={"ds": "2026-01-01"},
)
Hologres V3.1及以上版本使用原生INSERT OVERWRITE,更低版本自动使用事务内TRUNCATE/DELETE + INSERT的兼容方案。
overwrite=True与on_conflict互斥,不能同时使用。目标表启用Binlog时,覆盖写入不能生成完整的逐行CDC记录,默认会拒绝写入;确认下游允许Binlog缺口后,可以显式传入allow_binlog_gap=True。
其他重点功能
读写OSS文件
Hologres V4.1及以上版本可以通过EXTERNAL_FILES读取和导出OSS中的文件。
读取OSS文件
支持CSV、Parquet和ORC格式。
oss_df = session.read_files(
"oss://bucket/input/",
format="parquet",
oss_endpoint="oss-cn-hangzhou-internal.aliyuncs.com",
role_arn="acs:ram::<ACCOUNT_ID>:role/hologres-oss-role",
)
oss_df.filter(hg.col("amount") > 100).show()
导出查询结果到OSS
session.table("public.orders").write_files(
"oss://bucket/output/",
format="csv",
oss_endpoint="oss-cn-hangzhou-internal.aliyuncs.com",
role_arn="acs:ram::<ACCOUNT_ID>:role/hologres-oss-role",
target_file_size_mb=128,
)
写OSS仅支持CSV格式,路径必须是oss://协议。role_arn是Hologres访问OSS时扮演的RAM角色,已通过实例凭据授权时可以不传。如需导出为本地文件,可以先调用to_pandas(),再使用pandas的文件接口。
Serverless Computing
通过serverless()方法将查询标记为使用Serverless Computing资源执行,Serverless Computing的详细说明请参见Serverless Computing概述。该方法为惰性标记,返回新的DataFrame,执行Action时框架在同一事务和连接上设置SET LOCAL,不会将配置泄漏到连接池中的后续查询。
rows = (
session.table("public.orders")
.filter(hg.col("amount") > 100)
.serverless(priority=4, required_cores=64, max_cores=128)
.collect()
)
参数说明如下。
|
参数 |
取值范围 |
说明 |
|
|
1~5 |
查询优先级,5为最高。 |
|
|
0~102400 |
请求的Core数,0表示自动估算。 |
|
|
0~102400 |
单查询Core上限,0表示不设置查询级上限。 |
Hologres仍会判断SQL是否符合Serverless执行条件。不支持相关GUC的实例会直接报错,客户端不会静默回退。
Dynamic Table
通过as_dynamic_table()将DataFrame查询定义为Dynamic Table,Dynamic Table的详细说明请参见Dynamic Table。该方法是立即执行的DDL Action,返回None。
summary = (
session.table("public.orders")
.join(session.table("public.users"), on="user_id", how="left")
.select("order_id", "user_id", "user_name")
)
summary.as_dynamic_table(
"public.dt_orders",
freshness="10 minutes",
refresh_mode="incremental",
auto_refresh=True,
mode="ignore",
options={"cdc_format": "binlog"},
)
mode参数控制目标表已存在时的行为。
|
mode |
行为 |
|
|
默认值,目标存在时不处理。 |
|
|
目标存在时由Hologres报错。 |
|
|
在事务中删除并重建。 |
手动刷新和查询Dynamic Table。
session.refresh_table("public.dt_orders")
session.table("public.dt_orders").show()
当Dynamic Table启用了auto_refresh=True时,自动刷新可能需要数秒完成。在此期间调用refresh_table()会被服务端拒绝,返回last refresh is not done。建议等待自动刷新完成后再手动触发。
AI Function
hg.ai将Hologres AI Function暴露为惰性Column表达式,可以在DataFrame查询中直接使用AI能力。
questions = session.table("public.questions")
questions.select(
"question",
hg.ai.gen(
hg.function("concat", hg.lit("请简要回答:"), hg.col("question"))
).alias("answer"),
).show()
session.table("public.docs").select(
"id",
hg.ai.embed(hg.col("content")).alias("embedding"),
).show()
session.table("public.comments").select(
"comment",
hg.ai.sentiment(hg.col("comment")).alias("sentiment"),
).show()
除上述示例外,还支持rank、chunk、classify、extract、mask、fix_grammar、summarize、translate、similarity、parse_document、to_file和prompt。具体函数的可用性取决于Hologres实例版本和模型配置,支持的函数列表请参见AI Function列表。
Function Compute UDF与UDTF
Hologres没有内嵌Python Runtime。@hg.udf和@hg.udtf会在首次使用它们的Action执行前,将Python代码自动部署到Function Compute,再注册为LANGUAGE function_compute的Remote UDX。
配置Function Compute
Function Compute配置放在Session中,AccessKey使用阿里云标准凭据链,不要写在装饰器中。
session = hg.connect(
host="<ENDPOINT>",
port=80,
dbname="my_db",
user="<ACCESS_KEY_ID>",
password="<ACCESS_KEY_SECRET>",
fc_config={
"endpoint": "<ACCOUNT_ID>.cn-hangzhou-internal.fc.aliyuncs.com",
"region": "cn-hangzhou",
"role_arn": "acs:ram::<ACCOUNT_ID>:role/fc-execution-role",
},
)
标量UDF
@hg.udf(packages=["numpy"])
def grade(score: int) -> str:
return "high" if score >= 80 else "low"
session.table("public.scores").select(
"id", grade("score").alias("grade")
).show()
UDTF
UDTF使用类和process()方法定义。
@hg.udtf(output_schema=hg.StructType([hg.StructField("word", hg.StringType())]))
class SplitWords:
def process(self, text: str):
for word in text.split():
yield (word,)
session.table("public.docs").select("id", SplitWords("text")).show()
Hologres Remote UDX当前不能返回PostgreSQL的record类型,因此UDTF的output_schema必须只有一个字段。Function Compute和Hologres必须在同一地域,并使用Function Compute的内网Endpoint。
Catalog与原生SQL
通过Session的Catalog方法查看数据库对象。
session.list_schemas()
session.list_tables()
session.list_tables("analytics")
session.current_schema()
session.current_database()
执行任意Hologres SQL,包括DDL和DML。
session.sql("CREATE TABLE public.t (id bigint)").collect()
session.sql("INSERT INTO public.t VALUES (1)").collect()
session.sql()本身是惰性的,即使传入DDL或DML,也必须调用Action(如collect())才会执行。
数据类型映射
DataFrame API提供的Python类型与Hologres或PostgreSQL类型的对应关系如下。
|
Python API |
Hologres或PostgreSQL类型 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PostGIS类型 |
|
|
|
|
|
|
StructType和StructField用于描述DataFrame行结构、文件Schema和UDTF输出结构,不是Hologres的单列类型。
异常处理
所有公开异常都继承自HoloDataFrameError。
import hologres.dataframe as hg
try:
session.table("public.orders").write(
"public.target",
overwrite=True,
on_conflict=("update", ["id"]),
)
except hg.InvalidArgumentError as exc:
print(f"参数错误: {exc}")
except hg.UnsupportedOperationError as exc:
print(f"当前 Hologres 版本不支持: {exc}")
except hg.ExecutionError as exc:
print(f"Hologres 执行失败: {exc}")
异常类型说明如下。
|
异常 |
说明 |
|
|
参数类型、取值或接口组合不合法。 |
|
|
逻辑计划无法编译为SQL。 |
|
|
当前引擎版本或能力不支持该操作。 |
|
|
连接、SQL执行或服务端返回错误。 |