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執行或服務端返回錯誤。 |