2026 年 Google Cloud DLP API 实操:inspect vs deidentify、自定义 infoType、按 GB 计费的陷阱
Cloud DLP(现在叫 Sensitive Data Protection)官方文档把 API 写清楚了,但跳过了开发者一定要踩的几个坑:自定义 infoType 的 RE2 正则、429 配额、按 GB 计费撞上 BigQuery 大表那一刀。
上面是文章摘要,下面进入正文深读。可以配合目录逐段阅读,不会丢掉上下文。
上一个 sprint 我给一个 fintech 团队搭 PII 脱敏层,把客服 ticket 喂给微调任务前先洗一遍——Google Cloud DLP 是 GCP 原生方案里唯一一个能在 project 内闭环跑的 inline 脱敏选项。第一天就把 content.inspect 跑通了。第二天给客户的内部账号 ID 格式写了个自定义 infoType,对照一份明知有十处命中的样本跑——返回零 findings。第三天我学到了什么是 RE2。
如果你是从 "cloud data loss prevention api" 这种查询跳进来的,那个自定义正则的坑大概率是你第一周要撞的墙。这篇围绕 Cloud DLP API(改名但没改 API 的 Sensitive Data Protection)讲文档跳过的几层:inspect 和 deidentify 和 storage job 分别该什么时候用,自定义 infoType 怎么写才能真匹配,定价数学在量上来之后怎么变难看,哪些 transformation 能反推回去。
改名是文档问题,不是产品问题
Google 在 2023 年某时候把 Cloud Data Loss Prevention 改成了 Sensitive Data Protection,2026 年这个 rollout 还是参差不齐。Console 里产品页写"Sensitive Data Protection",但作为开发者你碰到的所有东西还是 "DLP":
| 触面 | 2026 年实际叫法 |
|---|---|
| 营销页、Console UI | Sensitive Data Protection |
| API endpoint | dlp.googleapis.com |
| gcloud 命令 | gcloud dlp ... |
| Python 客户端 | google-cloud-dlp |
| Service account 角色 | roles/dlp.user, roles/dlp.admin |
| 账单条目 | "Sensitive Data Protection" |
| Stack Overflow 标签 | google-cloud-dlp |
实战影响:搜新名字适合找产品总览和定价页,搜老名字适合找代码示例。两个名字最终落到同一份文档,但首页搜索结果会分裂,不知道是同一个东西的话能空跑半小时。
四个 API surface 分别怎么选
DLP 的端点比大多数团队意识到的要多,挑错一个不是慢就是贵或者两个都中。决策树主要看你的数据是 inline 还是 at rest:
| Surface | 端点 | 同步? | 什么时候用 |
|---|---|---|---|
| Content inspect | projects.content.inspect |
是 | 单个 payload < 0.5MB 的 inline 扫描(聊天消息、ticket、表单提交) |
| Content deidentify | projects.content.deidentify |
是 | 同上,但在路过的时候顺手脱敏 |
| Content reidentify | projects.content.reidentify |
是 | 反推 deterministic 或者 FPE 脱敏 |
| Storage job | projects.dlpJobs.create(INSPECT_JOB 类型) |
否 | 审计整个 GCS bucket / BigQuery 表 / Datastore kind |
| Discovery | projects.discoveryConfigs.create |
否 | 跨多个数据集的持续画像 |
容易踩坑的分歧是 inline 和 at-rest 计费模型不一样。Content 端点按请求里发的 GB 数算钱。Storage job 按它原地扫的 source 数据 GB 算。Discovery 是另一档更低的费率做定期画像。一个团队用循环跑 content.inspect 扫一百万行 BigQuery,付的钱大约是用一次 dlpJobs.create 扫同一张表的 100 倍——因为 content 路径每个请求都计费,job 是按表扫一次摊销的。
二十行跑通 content.inspect
最小可用的 inspect 调用,用 Python 客户端:
from google.cloud import dlp_v2
PROJECT_ID = "your-project"
client = dlp_v2.DlpServiceClient()
parent = f"projects/{PROJECT_ID}/locations/global"
inspect_config = {
"info_types": [
{"name": "EMAIL_ADDRESS"},
{"name": "PHONE_NUMBER"},
{"name": "CREDIT_CARD_NUMBER"},
],
"min_likelihood": dlp_v2.Likelihood.POSSIBLE,
"include_quote": True,
"limits": {"max_findings_per_request": 100},
}
response = client.inspect_content(
request={
"parent": parent,
"inspect_config": inspect_config,
"item": {"value": "Reach me at user@example.com or 415-555-0142."},
}
)
for f in response.result.findings:
print(f"{f.info_type.name}: {f.quote} ({f.likelihood.name})")
输出:
EMAIL_ADDRESS: user@example.com (LIKELY)
PHONE_NUMBER: 415-555-0142 (LIKELY)
三件事要记住:
min_likelihood是最重要的旋钮。 DLP 用五级置信度报 findings:VERY_UNLIKELY、UNLIKELY、POSSIBLE、LIKELY、VERY_LIKELY。设POSSIBLE能抓到大部分真 PII,代价是会有些假阳性(一串随机数字被识别成电话)。设LIKELY噪声少但漏部分命中。默认是POSSIBLE。include_quote: True默认开,但要知道它在干嘛。quote字段返回匹配的子串,调 infoType 选型时是关键调试信息,但它本身就是敏感数据,会被打进日志。生产路径上只需要 count 不需要 value 的时候关掉。parent用locations/global。 DLP 也支持区域端点(比如locations/us-central1)把请求保持在单一区域内做数据驻留。global 在临时扫描时更快,但不给区域保证。
自定义 infoType:regex(RE2)、字典、hotword
150+ 内建 infoType 覆盖了常见标识符(信用卡号、政府 ID、姓名、地址、医疗代码),但每个项目最终都会需要至少一个自定义。三种类型:
正则。 一个让 DLP 在内容里匹配的 pattern。坑在于 DLP 用的是 RE2,不是 PCRE 也不是 Python re。RE2 没有 lookbehind、lookahead、backreference。pattern 用了这些会静默返回零 findings——没有语法错误,没有警告,就是空结果。症状:你的测试样本里明明有命中,API 坚持说没有。
custom_info_types = [
{
"info_type": {"name": "ACCOUNT_ID"},
"regex": {"pattern": r"ACC-[0-9]{8}"},
"likelihood": dlp_v2.Likelihood.LIKELY,
}
]
调试技巧:把 pattern 贴到 regex101.com 把 flavor 切到 RE2 (Go),或者本地 re2 包编一下。那里编不过,DLP 里也不会匹配。
字典。 一组精确匹配的词或短语。DLP 可以接 inline 数组(word_list),也可以接 GCS 上的文件(cloud_storage_path)。inline 上限大约 50KB。更大的列表(员工名、产品 SKU、客户 ID)就把一词一行的文本文件丢 GCS 上引用。
Hotword(邻近规则)。 在某个 "hot word" 出现在 N 字节内时调整另一个 infoType 的 likelihood。典型用法是抓"SSN" 旁边的 9 位数字——单独的数字是 POSSIBLE,靠近 "SSN" 的就升到 VERY_LIKELY。proximity 字段是按字节算的,不是字符,UTF-8 下中文一个字 3 字节,写规则要记得乘。
Deidentify:在上规模之前先选好可逆性
Deidentify transformation 分单向和可逆两组。选错了后期改贵——所有历史脱敏数据都按第一天选的方式编码了。
| Transformation | 可逆? | 什么时候用 |
|---|---|---|
ReplaceConfig |
否 | "把 EMAIL 替换成 [REDACTED_EMAIL]" 最简单 |
RedactConfig |
否 | 整段删掉,不留占位 |
MaskConfig |
否 | "每位数字替换为 X",保形不保值 |
CryptoHashConfig(HMAC) |
否 | 跨次运行稳定的伪 ID,但不能还原 |
CryptoDeterministicConfig |
是 | join key 用的 tokenize——同输入同 token |
CryptoReplaceFfxFpeConfig(FPE) |
是 | 保留格式(16 位输入 → 16 位输出),下游有格式校验时 |
DateShiftConfig |
部分 | 按 context 偏移日期;偏移量即密钥 |
BucketingConfig |
否 | 年龄换成 30-39 这种区间 |
CryptoReplaceFfxFpeConfig(FPE)是团队反复讨论的一个。FPE 保持输出字母表和输入一致——一张被 FPE 脱敏的信用卡号还是 16 位、还能过 Luhn 校验(如果开了的话)、还能塞进 VARCHAR(16) 列。代价是运维:FPE 需要一个 key wrapper(通常是 Cloud KMS 包过的 AES 密钥),密钥轮换的方案要你自己设计。
如果脱敏数据的下游消费者只是模型或者 dashboard,CryptoDeterministicConfig 一般够用——产出 token 稳定但不保格式,schema 能存就行。
量上来之后的定价数学
Cloud DLP 定价是那种单价低、量一上来很惨的服务。按 Google 公开定价(架构定型前请到 Sensitive Data Protection pricing 页确认当前费率):
| 操作 | 按 Google 定价(约) |
|---|---|
content.inspect 超出每月 1GB 免费层 |
约 $1.00/GB 内容 |
content.deidentify / reidentify |
约 $0.50/GB |
| Storage job inspect | 约 $1.00/GB 源数据 |
| Discovery profile | 约 $0.03/GB(前 1TB),阶梯递减 |
[community-verified,具体档次费率会变——sizing 前先到定价页拉当前数字]
三个粗算锚定场景:
- 聊天 app 每条消息 inline 脱敏。 平均一条 200 字节,每天 100 万条。日量 200MB,1GB 免费层之内——免费。
- 每天审计一张 100GB 的 BigQuery 表。 Storage job:每天约 $100,每月约 $3,000。Discovery 同样数据集:初次 profile 约 $3,之后随表增长增量计费。Discovery 便宜两个数量级。
- 一次性扫一份 10TB 历史日志归档。 Storage job:约 $10,240。躲不掉,启动前显式预算这一项。
几乎每个团队都犯过一次的错是用循环跑 content.inspect 扫 BigQuery 行,因为代码比配 job 短。100GB 表用 content 端点逐行扫,账单会比一次 storage job 高一个数量级——因为还要付每请求开销,不只是 GB 量。
配额 429 与退避
DLP 在 project 层级对每个 surface 收配额。生产里常见的几条:
| 配额 | 默认(按 Google 文档,撰文时) | 触发症状 |
|---|---|---|
content.inspect 每分钟每 project |
600 | 429 RESOURCE_EXHAUSTED |
dlpJobs.create 每分钟每 project |
个位数 | 提交 job 时 429 |
| 单请求内容大小 | 0.5MB raw / 1MB structured | 400 INVALID_ARGUMENT |
| 单请求 findings 数 | 3000 硬上限 | findings 被截断,不报错 |
撞 429 的正确反应是带 jitter 的指数退避,Google Cloud 客户端库默认带——调用时传 retry=DEFAULT_RETRY。错误的反应是同线程立刻重试,反而把每请求计数顶得更高、没有进展。
600 RPM 真的成为瓶颈的时候(一般不会,高吞吐实时脱敏才会撞),可以拉的杆是批处理:用 table 字段一次 inspect_content 多条结构化记录,按一次请求计配额,处理量到 structured 1MB 上限。
接下来怎么用这篇
打开 Cloud DLP API 目录页拿端点清单,把上面那二十行 inspect_content 跑在自己的 GCP project 上。能拿到 findings 之后,API 后面就是 infoType 和 transformation 的排列组合——面广但 pattern 重复。接下来要设计的是自定义 infoType 策略:GCS 上的字典文件、RE2 兼容的正则、依赖上下文的 hotword 邻近规则。再之后判断你的真实负载是 inline(content 端点)还是 at-rest(job 和 Discovery),在账单教训你之前先把钱路由对。
快速跳到对应段落
下一步
读完后可以继续回到工具目录,对比具体产品。
去看工具