BigQuery:查询、权限与扫描量

验证:静态检查 · 核验于 2026-10-13
适用版本
云环境版本未实测;适用条件见正文
验证范围
已检查文档构建、链接与脚本语法;未执行云端实验。

分析查询,不用于承接逐笔订单事务;Console:BigQuery。

推荐做法#

  • 查询先 dry run,再设 maximum_bytes_billed;大表按查询方式设计分区与聚簇。
  • 查询项目授 Job User,目标 Dataset 授 Data Viewer;创建 Job 与读取数据分开。
  • 查询限定时间范围和必要列;租户隔离使用访问策略,不依赖 WHERE 条件。

常用操作#

替换为实际资源;本段不依赖 lab.env。

PROJECT_ID='REPLACE_PROJECT_ID'
LOCATION='REPLACE_LOCATION'
DATASET='REPLACE_DATASET'

bq --project_id="$PROJECT_ID" --location="$LOCATION" show "$PROJECT_ID:$DATASET"
# 查询示例与 dry run 参数见下方;先替换表路径和过滤条件。

容易踩的坑#

LIMIT 不保证减少扫描或费用。 先 dry run,再设置 maximum_bytes_billed;限制返回行数不能替代扫描量限制。

  • 聚簇收益取决于数据与过滤,dry run 不能精确预测所有 block pruning。
配置说明与完整示例(按需展开)

前置:环境篇;bq 随 Cloud SDK 提供。BigQuery 面向分析,不用来承接每次订单事务。查询关注扫描量,数据集位置与数据源位置要兼容。

source lab.env
gcloud services enable bigquery.googleapis.com
bq --location="$REGION" mk --dataset "$PROJECT_ID:lab_analytics"
cat > orders.ndjson <<'EOF'
{"id":"a","amount":100,"created_at":"2026-09-01T00:00:00Z"}
{"id":"b","amount":250,"created_at":"2026-09-02T00:00:00Z"}
EOF
bq --location="$REGION" load --source_format=NEWLINE_DELIMITED_JSON --time_partitioning_field=created_at "$PROJECT_ID:lab_analytics.orders" orders.ndjson id:STRING,amount:INTEGER,created_at:TIMESTAMP
bq --location="$REGION" query --use_legacy_sql=false --dry_run "SELECT SUM(amount) FROM \`$PROJECT_ID.lab_analytics.orders\` WHERE created_at >= TIMESTAMP('2026-09-01')"
bq --location="$REGION" query --use_legacy_sql=false --maximum_bytes_billed=100000000 "SELECT SUM(amount) total FROM \`$PROJECT_ID.lab_analytics.orders\` WHERE created_at >= TIMESTAMP('2026-09-01')"

验收:结果为 350;先 dry run 再执行。生产查询身份通常需要查询项目上的 BigQuery Job User,以及目标数据集的 Data Viewer;创建作业权限不自动授予读数据权限。查询费用控制

故障练习:把字节上限调到低于 dry run 估算值,确认查询被拒绝;小表还存在最低计费粒度,不能据返回行数估计费用。

订单分析:分区、聚簇与成本门禁#

在同一 lab_analytics 数据集中生成 100 万条合成订单,分布在 30 天、100 个租户;避免拿真实客户数据做实验。建表与查询均消耗配额/可能计费,本例设置 1 GB 查询上限,不等于月度费用上限。

cat > warehouse.sql <<'SQL'
CREATE TABLE lab_analytics.orders_raw AS
SELECT CAST(i AS STRING) id, CONCAT('tenant-', CAST(MOD(i,100) AS STRING)) tenant,
DATE_ADD(DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY), INTERVAL MOD(DIV(i,100),30) DAY) order_date,
100 + MOD(i,10000) amount_cents, REPEAT('x',200) payload
FROM UNNEST(GENERATE_ARRAY(1,1000000)) i;
CREATE TABLE lab_analytics.orders_partitioned PARTITION BY order_date
OPTIONS(require_partition_filter=true, partition_expiration_days=90) AS SELECT * FROM lab_analytics.orders_raw;
CREATE TABLE lab_analytics.orders_clustered PARTITION BY order_date CLUSTER BY tenant
OPTIONS(require_partition_filter=true, partition_expiration_days=90) AS SELECT * FROM lab_analytics.orders_raw;
SQL
bq --project_id="$PROJECT_ID" --location="$REGION" query --use_legacy_sql=false --maximum_bytes_billed=1000000000 < warehouse.sql

使用最近 30 天的数据,90 天分区保留期仅适用于本实验;实际保留时间应来自审计及业务要求。

用完全相同的过滤条件比较三张表,关闭结果缓存,记录每次扫描量:

export QUERY_SUFFIX=$(date -u +%Y%m%dT%H%M%SZ)
for TABLE in orders_raw orders_partitioned orders_clustered; do
  QUERY="SELECT SUM(amount_cents) total FROM \`$PROJECT_ID.lab_analytics.$TABLE\` WHERE order_date=DATE_SUB(CURRENT_DATE(), INTERVAL 20 DAY) AND tenant='tenant-7'"
  bq --project_id="$PROJECT_ID" --location="$REGION" query --use_legacy_sql=false --dry_run "$QUERY"
  bq --project_id="$PROJECT_ID" --location="$REGION" --job_id="${TABLE}_${QUERY_SUFFIX}" query --use_legacy_sql=false --use_cache=false --maximum_bytes_billed=1000000000 "$QUERY"
  bq --project_id="$PROJECT_ID" --location="$REGION" show --job=true --format=prettyjson "${TABLE}_${QUERY_SUFFIX}"
done

验收:三次金额一致;查看 statistics.query 的 totalBytesProcessed、totalBytesBilled、cacheHit。分区表应减少扫描;聚簇在小分区中未必进一步降低计费,不虚构固定节省比例。聚簇的 dry run 不保证精确预测块裁剪,最终以实际作业统计为准。分区与聚簇

下面两条预期失败,单独执行并保留错误,不能删掉限制让它通过:

bq --project_id="$PROJECT_ID" --location="$REGION" query --use_legacy_sql=false 'SELECT SUM(amount_cents) FROM lab_analytics.orders_partitioned'
bq --project_id="$PROJECT_ID" --location="$REGION" query --use_legacy_sql=false --use_cache=false --maximum_bytes_billed=1 'SELECT SUM(amount_cents) FROM lab_analytics.orders_raw'

第一条缺少分区过滤;第二条超扫描上限。生产查询模板需强制时间范围、只选必要列;SELECT LIMIT 不保证降低扫描量。

权限验收#

由管理员创建无密钥测试身份,授予查询项目的 roles/bigquery.jobUser,只在 lab_analytics 数据集授予 roles/bigquery.dataViewer。调用者仅在这个测试 SA 上获得 Service Account Token Creator,使用 Service Account impersonation 执行:

export ANALYST_SA="lab-bq-analyst@${PROJECT_ID}.iam.gserviceaccount.com"
export OPERATOR='user:REPLACE_OPERATOR_EMAIL'
gcloud services enable iamcredentials.googleapis.com
gcloud iam service-accounts create lab-bq-analyst
gcloud projects add-iam-policy-binding "$PROJECT_ID" --member="serviceAccount:$ANALYST_SA" --role=roles/bigquery.jobUser
gcloud iam service-accounts add-iam-policy-binding "$ANALYST_SA" --member="$OPERATOR" --role=roles/iam.serviceAccountTokenCreator
bq --project_id="$PROJECT_ID" --location="$REGION" query --use_legacy_sql=false "GRANT \`roles/bigquery.dataViewer\` ON SCHEMA \`$PROJECT_ID.lab_analytics\` TO 'serviceAccount:$ANALYST_SA'"
# Use a dedicated configuration; do not run other operations against it during this test.
(
  ORIGINAL_IMPERSONATION=$(gcloud config get-value auth/impersonate_service_account 2>/dev/null)
  restore_identity() {
    if [ -n "$ORIGINAL_IMPERSONATION" ] && [ "$ORIGINAL_IMPERSONATION" != '(unset)' ]; then
      gcloud config set auth/impersonate_service_account "$ORIGINAL_IMPERSONATION"
    else
      gcloud config unset auth/impersonate_service_account
    fi
  }
  trap restore_identity EXIT
  gcloud config set auth/impersonate_service_account "$ANALYST_SA" || exit 1
  bq --project_id="$PROJECT_ID" --location="$REGION" query --use_legacy_sql=false --maximum_bytes_billed=1000000000 'SELECT SUM(amount_cents) FROM lab_analytics.orders_partitioned WHERE order_date=DATE_SUB(CURRENT_DATE(), INTERVAL 20 DAY)'
  # 预期拒绝:只有数据读取授权,没有修改表结构授权。
  bq --project_id="$PROJECT_ID" --location="$REGION" query --use_legacy_sql=false 'ALTER TABLE lab_analytics.orders_raw ADD COLUMN forbidden STRING'
)

本段 subshell 退出时通过 trap 恢复之前的 configuration 属性;恢复后仍检查实际身份再清理。使用专用 configuration,不与其他终端并发共享;若进程被强制杀死,仍需人工检查恢复。环境变量或额外凭据配置也可能覆盖该属性,测试前确认调用路径。上述 impersonation 采用 官方 BigQuery 认证方式。若 ALTER TABLE 成功,检查身份从 Project / Folder 继承的 Editor、Data Editor 等角色,修正后重测。数据集级权限不提供租户行隔离;多租户报表另用行访问策略或授权视图,不能把 WHERE tenant 当权限边界。

清理#

bq rm -r -f -d "$PROJECT_ID:lab_analytics"
gcloud projects remove-iam-policy-binding "$PROJECT_ID" --member="serviceAccount:$ANALYST_SA" --role=roles/bigquery.jobUser
gcloud iam service-accounts delete "$ANALYST_SA"
rm orders.ndjson warehouse.sql

搜索文档

输入关键词,搜索所有文档。

↑ ↓ 选择 · Enter 打开 · Esc 关闭