Hologres DataFrame

更新时间:
复制 MD 格式

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,依赖要求如下。

依赖包

最低版本

hologres-client

>= 0.1.2

psycopg[binary,pool]

>= 3.1

pandas

>= 1.3

pyarrow

>= 12.0

alibabacloud-fc20230330

>= 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,建议通过环境变量或阿里云凭据链获取。

连接池参数

参数

默认值

说明

min_size

1

连接池最小连接数。

max_size

10

连接池最大连接数,也是并发上限。

timeout

30.0

等待可用连接的最长秒数。

max_idle

600.0

空闲连接回收时间,单位为秒。

max_lifetime

3600.0

单个连接的最大生命周期,单位为秒。

application_name

hologres-dataframe

pg_stat_activity中显示的应用名。

fc_config

None

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

返回值

适用场景

show(n=10)

None

在终端查看少量结果。

collect()

list[Row]

将完整结果加载到内存。

to_pandas()

pandas.DataFrame

进入pandas处理流程。

count()

int

服务端统计行数。

first()

Row | None

获取第一行。

take(n)

list[Row]

获取前n行。

iter_rows(batch_size=1000)

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()
)

参数说明如下。

参数

取值范围

说明

priority

1~5

查询优先级,5为最高。

required_cores

0~102400

请求的Core数,0表示自动估算。

max_cores

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

行为

ignore

默认值,目标存在时不处理。

errorifexists

目标存在时由Hologres报错。

replace

在事务中删除并重建。

手动刷新和查询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类型

BooleanType()

boolean

ByteType()

"char"

ShortType()

smallint

IntegerType()

integer

LongType()

bigint

FloatType()

real

DoubleType()

double precision

DecimalType(38, 2)

numeric(38,2)

StringType()

text

StringType(64)

varchar(64)

BinaryType()

bytea

DateType()

date

TimeType()

time

TimestampType()

timestamp

TimestampType(TimestampTimeZone.TZ)

timestamptz

JsonbType()

jsonb

GeographyType()或GeometryType()

PostGIS类型

ArrayType(StringType())

text[]

VectorType(float, 768)

vector(768)

说明

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}")

异常类型说明如下。

异常

说明

InvalidArgumentError

参数类型、取值或接口组合不合法。

CompilationError

逻辑计划无法编译为SQL。

UnsupportedOperationError

当前引擎版本或能力不支持该操作。

ExecutionError

连接、SQL执行或服务端返回错误。