【Explanation】GCP FinOps— BigQuery Cost Optimisation Best Practices Summary

Kellen
9 min readJan 26, 2024

--

TL;DR

實施 FinOps 不是一次性活動,而是一項持續的紀律和旅程,每個組織都應該將其作為其雲端策略的一部分進行投資,以下提供幾種可以省下預算的方法

  1. 選擇正確的定價模型
  2. 設計 Partition 分區和 Clustering
  3. 查詢
  4. 避免執行查詢來探索表數據
  5. 查詢前使用工具進行估算與控制費用
  6. 建立意外錯誤的控制措施 — 設定查詢上限
  7. 使用 INT64 列進行 ORDER BY 或 JOIN
  8. 設定表過期時間
  1. 選擇正確的定價模型
    📙【Reference】GCP FinOps — 估算 BigQuery Storage 成本

放置在第一點是覺得滿重要的,首要得要先了解、評估組織內的使用場景以才能選擇對的付費模型,其中需要留意的有儲存運算需要留意。儲存的模式可以選擇 logicalphysical 兩種不同的計價模型,而超過 90 天不對資料更改,也可以採用長期儲存方案。而 on-demand billing 或使用 Capacity billing 購置 Slots 也在 FinOps 白皮書列為 Low effort/High savings 首選。

2. 設計 Partition 分區和 Clustering
📙【Reference】GCP Tips — BiqQuery useful tips
📙
【GCP Blog】ost optimization best practices for BigQuery

消除 BigQuery 浪費的最重要技能是設計資料庫結構並以最小化查詢掃描的資料量的方式設計查詢。我們首先使用時間對表進行分區,然後在查詢中添加一個僅掃描我們需要的分區的條件,從而顯著降低了 BigQuery 成本。

建議是盡可能對資料進行 Partition,這有助於降低處理查詢的成本並提高效能。可以根據時間、日期或任何時間戳列對資料進行 Partition。假設對 sales 包含過去 12 個月資料的表格進行分割。這會導致包含每天資料的較小Partition。Partition後,可以對資料再進行 Clustering

3. 查詢

  • 執行前檢查查詢成本
  • 避免選擇 *
  • 忘記掉地端的 limit
  • 使用預覽功能取代 Select * FROM <table> 的習慣

執行前檢查查詢成本

畫面右上角可以了解要處理的查詢字節數大小

實際的查詢成本取決於多個因素,如數據大小、查詢複雜性等。在實際執行查詢之前,建議可以多使用這種方法來估算其成本以便做出更好的判斷。這段程式碼使用 dry_run=True,表示僅模擬查詢而不真正執行它。通過調用query_job.total_bytes_processed,可以獲取模擬查詢所使用的總字節數。然後,將其轉換為GB來計算估計的查詢成本。

from google.cloud import bigquery

project_id = 'your-project-id'
query = 'SELECT * FROM your_dataset.your_table'

def check_query_cost():
client = bigquery.Client(project=project_id)

job_config = bigquery.QueryJobConfig(dry_run=True)
query_job = client.query(query, job_config=job_config)

query_cost = query_job.total_bytes_processed / (1024 * 1024 * 1024) # 轉換成GB
print(f"Estimated query cost: {query_cost:.2f} GB")

check_query_cost()

避免選擇 * + 忘記掉地端的 limit

避免使用 “Select * FROM <Table>”,因為這樣 BigQuery 會讀取資料集中所有的數據,另外 limit 也忘了吧!它不會改變您的帳單,你 limit 查詢 100 行或 1,000,000 行,得到的費用仍然不變。

4. 避免執行查詢來探索表數據

可以改使用預覽功能取代 Select * FROM <table> 的習慣,避免僅為了預覽資料而使用「select * from <table>」。每當您這樣做時,您就需要向 Google 付費,您可以轉到表格並點擊「預覽」,就可以在那裡看到所有資料和所有列及其名稱

5. 查詢前使用工具進行估算與控制費用

QueryJobConfig.dry_run 屬性設定為True。如果提供了試運行查詢配置,Client.query() 將始終傳回已完成的 QueryJob

# result
This query will process 65935918 bytes

【GCP Docs.】📙 Query a public dataset with the BigQuery client libraries

6. 建立意外錯誤的控制措施 — 設定查詢上限

只是關鍵欄位下錯多查找幾 GB 大小,可能會花費你幾美分,這對大多數企業來說是可以接受的。但是,當您的資料集表的量級為 TB 或 PB,且由可能會有多個使用者存取時,錯誤的查找可能會導致巨大的查詢成本。建議設定要收費最大位元組數,如果即將超出限制,查詢將失敗並且不會收費

使用 bq

 bq query --maximum_bytes_billed=1000000 \
--use_legacy_sql=false \
'SELECT
word
FROM
`bigquery-public-data`.samples.shakespeare'

使用 Python 並可以用成 framework tool 加以包裝後提供 N users

【GCP Docs.】📙 Python Class QueryJobConfig (3.17.0)

from google.cloud import bigquery

first_query = ____ # Your code goes here

safe_config = bigquery.QueryJobConfig(maximum_bytes_billed=10**10)
first_query_job = client.query(first_query, job_config=safe_config)

# API request - run the query, and return a pandas DataFrame
first_results = first_query_job.to_dataframe()

# View top few rows of results
print(first_results.head())

# 若超出查詢的上限話
google.api_core.exceptions.InternalServerError: 500 Query exceeded limit
for bytes billed: 1000. 66060288 or higher required

7. 使用 INT64 列進行 ORDER BY 或 JOIN

每當您在 BigQuery 中連接或按表排序時,請嘗試使用 INT64 格式。如果列是 INT64,因為是只有 8 bytes 相較 string 會小很多,因此每個資料都會花費較少的執行時間,因為以這種方式連接表的效率要高得多,例如包括若有顧客主檔 Cust_Id,可以改由 Index 或 serial_num 的 INT64 來取代原先為 Cust_Id string type。

8. 設定表過期時間

除了引入時當下可以設定外,在表的資訊也可以直接修改

也可以使用 project.dataset_id.INFORMATION_SCHEMA.TABLE_OPTIONS 查詢

SELECT *
FROM fubar.demo.INFORMATION_SCHEMA.TABLE_OPTIONS
WHERE
option_name = 'expiration_timestamp';

--

--

Kellen

Backend(Python)/K8s and Container eco-system/Technical&Product Manager/host Developer Experience/早期投入資料創新與 ETL 工作,近期堆疊 Cloud☁️ 解決方案並記錄實作與一些雲端概念💡