如要最佳化 AlloyDB for PostgreSQL 資料庫查詢效能,您可以使用 AI 輔助洞察資料調整 SQL 查詢。ai.tune_sql 函式採用大型語言模型 (LLM),可分析查詢執行計畫,找出複雜的瓶頸,並提供可行的最佳化建議。
叫用 ai.tune_sql 函式時,函式會分析查詢,並提供下列領域的查詢洞察和建議:
- 查詢摘要:分析查詢效能瓶頸。
- 查詢重寫:建議最佳化技術,將原始查詢重寫為等效且更有效率的查詢。
- 索引建議:建議索引類型,例如運算式索引、一般化反向索引 (GIN) 和可擴充最鄰近項目 (ScaNN) 索引。
- 統計資料收集建議:根據查詢執行計畫,建議
ANALYZE資料表和多個資料欄的統計資料收集。 - Grand Unified Configuration (GUC) 旗標調整:分析執行計畫和系統資訊 (例如記憶體和緩衝區用量和大小),建議新的 GUC 值。
- 分析和清理:根據分析和清理記錄,以及有效和無效元組數量,建議對資料表執行
ANALYZE和VACUUM。
限制
SQL 查詢調整功能有下列限制:
- 預先準備和參數化陳述式:對於預先準備和參數化陳述式,
ai.tune_sql不會提供查詢重寫建議,但仍會提供其他調整建議。如要針對含有參數預留位置 (例如從查詢洞察或pg_stat_statements複製的$1或$2) 的查詢取得查詢重寫建議,請先將預留位置替換為代表性常值,再執行ai.tune_sql。 - 只有在
explain_analyze為TRUE時,才支援唯讀SELECT陳述式:如果explain_analyze設為TRUE,則僅支援單一唯讀SELECT陳述式。在其他陳述式 (例如INSERT、UPDATE、DELETE、SELECT INTO或SELECT FOR UPDATE) 上執行ai.tune_sql,並將explain_analyze設為TRUE,會傳回錯誤訊息。
事前準備
請確認您已備妥下列項目:
- 執行 PostgreSQL 18 以上版本的 AlloyDB 執行個體。詳情請參閱「建立叢集及其主要執行個體」。
psql用戶端已連線至執行個體,或可存取 AlloyDB Studio。- 已啟用 Vertex AI 整合功能。
- 已安裝
google_ml_integration擴充功能 1.6.2 以上版本,並啟用google_db_advisor和hypopg擴充功能。如要啟用ai.tune_sql用來評估及產生索引建議的hypopg擴充功能,請執行CREATE EXTENSION IF NOT EXISTS hypopg;。
啟用 SQL 調整功能
註冊 LLM 並啟用必要的資料庫旗標。
在執行個體區域註冊全域或區域模型端點,為
ai.tune_sql函式建立 Gemini 模型端點:如果模型在執行個體區域中無法使用 (例如
locations/global上的gemini-3.5-flash),請執行下列陳述式,註冊全域模型端點:CALL google_ml.create_model( model_id => 'gemini-3.5-flash-global', model_request_url => 'https://aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/global/publishers/google/models/gemini-3.5-flash:generateContent', model_qualified_name => 'gemini-3.5-flash', model_provider => 'google', model_type => 'llm', model_auth_type => 'alloydb_service_agent_iam');將
PROJECT_ID替換為模型所在專案的 ID。如要在執行個體區域 (例如
locations/REGION_ID上的gemini-2.5-pro) 註冊區域模型端點,請執行下列陳述式:CALL google_ml.create_model( model_id => 'gemini-2.5-pro', model_request_url => 'https://REGION_ID-aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/REGION_ID/publishers/google/models/gemini-2.5-pro:generateContent', model_qualified_name => 'gemini-2.5-pro', model_provider => 'google', model_type => 'llm', model_auth_type => 'alloydb_service_agent_iam');更改下列內容:
REGION_ID:AlloyDB 執行個體所在的區域,例如us-central1。PROJECT_ID:模型所在專案的 ID。
在目前的資料庫工作階段中,設定 SQL 查詢調整建議的模型 ID。指定您在上一個步驟中註冊的
model_id,例如'gemini-3.5-flash-global'或'gemini-2.5-pro':SET google_db_advisor.llm_model_id = 'gemini-3.5-flash-global';設定
google_db_advisor.enable_ai_tune_sql旗標,為目前的資料庫工作階段啟用 SQL 查詢調整功能:SET google_db_advisor.enable_ai_tune_sql = on;由於
SET僅適用於目前的資料庫工作階段,您可以對這兩個旗標執行ALTER DATABASE DATABASE_NAME SET FLAG_NAME = VALUE;,或設定執行個體的資料庫旗標,在不同工作階段中保留這些設定。
手動調整 SQL 查詢
如要手動調整 SQL 查詢,請使用 SELECT 陳述式執行 ai.tune_sql 函式。
將
psql用戶端連線至 AlloyDB 執行個體,或開啟 AlloyDB Studio。如果您使用
SET設定google_db_advisor.llm_model_id和google_db_advisor.enable_ai_tune_sql,請在呼叫ai.tune_sql之前,在同一個資料庫工作階段中執行這些SET陳述式。如要調整 SQL 查詢,請執行
ai.tune_sql函式。這個函式會擷取與查詢相關的結構定義,並將其與查詢一併傳送至 LLM,以便進行效能分析:SELECT ai.tune_sql(query => 'SQL_QUERY', explain_analyze => TRUE);將
SQL_QUERY替換為要分析的 SQL 查詢。根據預設,執行參數 (
explain_analyze) 會設為TRUE。設為TRUE時,函式會執行EXPLAIN ANALYZE來執行查詢並擷取執行計畫。這種做法可提供更準確、實用的查詢調整建議。如果將explain_analyze設為FALSE,函式只會執行EXPLAIN來產生計畫,不會執行查詢。
查看查詢計畫和效能建議
輸出內容會先顯示標準查詢計畫,接著顯示 AI 生成的詳細建議。建議包括查詢摘要、建議的重寫內容,以及統計資料收集、GUC 旗標調整和清理建議。ai.tune_sql 函式可能需要兩到四分鐘才能產生建議。
以下範例顯示反聯結查詢的 ai.tune_sql 輸出內容:
SELECT ai.tune_sql(
'SELECT *
FROM store_sales
WHERE ss_customer_sk NOT IN (
SELECT sr_customer_sk
FROM store_returns
WHERE sr_item_sk = ss_item_sk)');
tune_sql
--------------------------------------------------
NOTE
--------------------------------------------------
Advices generated with the assistance of an LLM. Independent verification of these advices is required due to the potential for inaccuracies.
--------------------------------------------------
ORIGINAL QUERY PLAN
--------------------------------------------------
Seq Scan on store_sales (cost=0.00..111292742.52 rows=1440384 width=115) (actual time=0.092..103093.207 rows=1306312.00 loops=1)
Filter: (NOT (ANY (ss_customer_sk = (SubPlan 1).col1)))
Rows Removed by Filter: 1574198
Buffers: shared hit=72987539
SubPlan 1
-> Index Scan using idx_sr_item_sk on store_returns (cost=0.42..76.74 rows=18 width=4) (actual time=0.005..0.034 rows=19.38 loops=2880510)
Index Cond: (sr_item_sk = store_sales.ss_item_sk)
Index Searches: 2880510
Buffers: shared hit=72933670
Planning Time: 0.257 ms
--------------------------------------------------
QUERY SUMMARY
--------------------------------------------------
The original query plan shows a Sequential Scan on store_sales with a correlated SubPlan for the NOT IN condition. For each of the 1.44 million rows in store_sales, it executes an Index Scan on store_returns, resulting in over 2.88 million index searches. This nested loop execution is the primary bottleneck, taking over 103 seconds. Converting the correlated NOT IN subquery to an anti-join (NOT EXISTS) will allow the optimizer to use a Hash Anti Join, processing both tables in bulk and significantly reducing execution time.
--------------------------------------------------
AI SUGGESTED QUERY 1
--------------------------------------------------
SELECT * FROM store_sales WHERE NOT EXISTS (SELECT 1 FROM store_returns WHERE store_returns.sr_item_sk = store_sales.ss_item_sk AND (store_returns.sr_customer_sk = store_sales.ss_customer_sk OR store_returns.sr_customer_sk IS NULL OR store_sales.ss_customer_sk IS NULL));
Reason: The original query uses a NOT IN subquery which is executed as a correlated subquery (SubPlan), leading to millions of index scans. Rewriting to NOT EXISTS allows the optimizer to use a Hash Anti Join, which is much more efficient for large datasets. The OR IS NULL conditions ensure strict semantic equivalence with NOT IN when dealing with NULL values.
--------------------------------------------------
REWRITTEN QUERY PLAN
--------------------------------------------------
Gather (cost=8541.96..139088.40 rows=154194 width=115)
Workers Planned: 2
-> Parallel Hash Anti Join (cost=7541.96..122669.00 rows=64248 width=115)
Hash Cond: (store_sales.ss_item_sk = store_returns.sr_item_sk)
Join Filter: ((store_returns.sr_customer_sk = store_sales.ss_customer_sk) OR (store_returns.sr_customer_sk IS NULL) OR (store_sales.ss_customer_sk IS NULL))
-> Parallel Seq Scan on store_sales (cost=0.00..65872.20 rows=1200320 width=115)
-> Parallel Hash (cost=6044.76..6044.76 rows=119776 width=8)
-> Parallel Seq Scan on store_returns (cost=0.00..6044.76 rows=119776 width=8)
--------------------------------------------------
AI SUGGESTED INDEX 1
--------------------------------------------------
CREATE INDEX idx_sr_item_customer ON store_returns (sr_item_sk, sr_customer_sk);
Reason: A composite index on (sr_item_sk, sr_customer_sk) allows index-only scans for the subquery or early aggregation, reducing table access and speeding up the anti-join condition evaluation.
Note: Indexes are suggested solely based on the given query, but they don't consider the overall system workload.
The suggested indexes improve the performance of the original query.
--------------------------------------------------
GUC FLAGS TUNING
--------------------------------------------------
SET work_mem = '64MB'; The current work_mem of 4MB is too small for a Hash Anti Join on tables with hundreds of thousands of rows. Increasing it to 64MB allows the hash table for store_returns to fit entirely in memory, preventing costly disk spills during the join.
Execution Time: 103167.475 ms
(50 rows)