Google PCD: 应用数据、状态和存储模式 — 学习指南
属于 Google Professional Cloud Developer — 学习指南. 使用经过验证的答案练习: Google 考试中心, 或参加限时模拟考试: ExamRoll.io.
概览
Google Cloud 上的现代应用通常会组合使用多种数据存储,以平衡延迟、一致性、可伸缩性、成本和运维复杂性。选择适合特定用途的服务和模式,并理解它们的故障模式,是弹性设计的核心。本节总结了 Cloud SQL、Cloud Spanner、Firestore、Bigtable、Memorystore 和 Cloud Storage 的实践指南,并探讨了迁移、分区和数据保护等问题。
Cloud SQL 上的关系型数据
Cloud SQL 提供托管的 MySQL、PostgreSQL 和 SQL Server,具有大家熟悉的 RDBMS 语义。
私有连接
- 使用私有 IP 将数据库流量保留在您的 VPC 内。这可以省去公共入站规则和 IP 允许列表,并避免 NAT 出站的复杂性。
- 确保路由和防火墙规则允许 VPC 到实例的流量。启用私有 IP 后,系统会自动处理私有 IP 的名称解析。
- 对于无服务器环境(Cloud Run、App Engine、Cloud Functions),优先使用 Cloud SQL 连接器,即使使用私有 IP,它也能处理 IAM 身份验证和 TLS。
高可用性与副本
- 区域级 HA 将主实例和备用实例放置在不同可用区,并采用同步磁盘复制。预计在故障切换时会出现短暂的连接中断;应用应重试瞬时错误并重新连接。
- 只读副本是异步的,用于分流读取流量。使用跨区域副本用于灾难恢复 (DR) 和读取邻近性,但要理解副本是最终一致的。
- 提升只读副本以进行恢复或计划内的角色交换。定期测试提升流程。
备份和时间点恢复
- 启用自动备份和事务/PITR 日志。在非高峰时段安排备份,以减少 IO 争用。
- 保留多个副本,并定期验证能否恢复到单独的实例。无法恢复的备份在运维上等同于没有备份。
连接池与限制
- Cloud SQL 强制执行最大连接数;过多的短生命周期连接会导致 CPU 抖动和延迟。使用应用侧连接池(例如 HikariCP、PgBouncer、ProxySQL)。
- 根据 CPU 核心数和工作负载并发性来确定池的大小,而不仅仅是实例内存。从小规模开始,根据经验进行扩展。
- 对于临时/无服务器计算,特定语言的 Cloud SQL 连接器会为每个修订版本维护一个连接池;但仍需限制并发,以避免冷启动后的连接风暴。
数据分区与性能
- 按客户或区域对大型多租户模式进行分片,以减少争用。尽可能将热点租户隔离。
- 谨慎创建覆盖索引;索引过多会减慢写入速度并增加存储空间。验证基数和谓词选择性。
- 对热点行使用乐观锁或 SELECT FOR UPDATE;针对持续的写入工作负载,调整 autovacuum (PostgreSQL) 或 InnoDB 设置 (MySQL)。
常见故障模式及缓解措施:
- VM/节点重启后的惊群效应:限制池大小并使用指数退避。
- “写后读”场景下的副本延迟:当需要会话一致性时,将读取操作固定到主实例。
- 因“吵闹的邻居”或维护导致的 HA 故障切换抖动:实现具有幂等性的连接和事务重试。
Cloud Spanner 上的全球级关系型数据库
Cloud Spanner 提供水平可伸缩性及全球一致性选项。
一致性与事务
- 强一致性读取和读写事务使用 TrueTime 提供严格的外部一致性;提交会短暂等待以确保线性化。
- 当可以稍微放宽数据新鲜度要求时,过时读取和有界过时读取可以为读取密集型工作负载降低延迟并提高可用性。
- 只读事务可以在一个时间戳上跨多个读取操作,且无需加锁;可用于获取一致的分析快照。
区域性、可用性与延迟
- 区域级实例在区域内提供高可用性。多区域配置(例如 nam-asia-eur1)可提供极高的可用性,并实现跨大洲的低延迟本地读取和全球一致的写入。
- 选择与用户地理位置相符的实例配置;写入延迟会随着洲际法定数量的增加而增加。
可伸缩性与架构设计
- Spanner 按主键范围将数据分片成多个 split,并分布在各个节点上。当键单调递增时会发生热点。避免在主键的开头位置使用自增 ID 或始终递增的时间戳等键。
- 使用可分散写入的复合主键(例如,customer_hash, customer_id, reverse_timestamp)。
- 交错表将子行与父行并置存储,以实现局部性和高效连接。当子表的基数和访问模式与父表强相关时使用。使用二级索引作为补充;考虑使用 STORING 子句以减少表查找。
- 监控 CPU、存储以及高优先级与尽力而为型操作;扩展节点以在 P95 延迟下保持足够余量。
运维模式
- 客户端使用会话池;调整最小/最大会话数以避免创建风暴。重试应受限且具有幂等性;在遇到 ABORTED 状态时,应使用退避策略重试读写事务。
- 备份是轻量级且一致的;验证能否恢复到单独的实例。变更流和 CDC 集成可为下游系统提供支持。
权衡取舍:
- 强一致性的全球写入会增加提交等待时间;对于用户体验关键、以读为主的路径,使用过时读取。
- 交错可以改善局部性,但也可能集中写入压力;使用类似生产环境的流量进行测试。
NoSQL 操作型存储:Firestore 和 Bigtable
选择与查询模式和吞吐量特征相匹配的 NoSQL 模型。
Firestore (文档)
- 数据模型:集合包含文档;文档可以有子集合。围绕查询模式进行建模;避免对单个“热点”文档进行深度扇出写入。
- 访问与事务:在 Native 模式下,文档读取和查询是强一致性的。使用批量写入实现跨多个文档的至多一次原子性,使用事务实现带争用检查的“读取-修改-写入”操作。
- 索引:单字段索引是自动创建的。当使用多个范围/不等式过滤器或排序顺序时,必须定义多字段复合索引。反规范化是使查询变为仅索引查询的常用方法。
- 客户端同步:实时侦听器以流式方式传输变更;离线缓存使用“最后写入者获胜”的语义进行协调。防止无限制的侦听器扇出;优先使用查询游标和过滤器。
- 限制与故障模式:对单个文档的写入速率是序列化的;对一个文档持续的高 QPS 更新会产生争用。可使用分片计数器(包含 N 个子文档),并在读取时进行聚合。
Cloud Bigtable (宽列)
- 行键设计至关重要。Bigtable 按字典序对行进行分区;前导键段决定了热点问题。避免使用顺序键,如时间戳在前或未分片的用户 ID。
- 模式:
- 在键内反转时间戳以进行时间序列读取:key = device#hash(device_id)#reverse_ts。
- 对第一个组件进行哈希或分桶以分散写入:bucket = crc32(user_id) % 128。
- 存储小而多列的单元格;避免出现跨越 tablet 的大行。利用多个列族来实现访问控制和 GC 策略的分离。
- 吞吐量与服务:
- 使用多个集群进行复制并实现区域读取邻近性;跨集群写入会变为最终一致性。
- 调优应用配置文件和路由;维持充足的客户端线程池和通道池。
- GC 和 TTL:基于版本和时间的 GC 会异步移除旧单元格;数据会一直保留到压缩完成,因此不要依赖立即删除来满足法规截止日期的要求。
缓存与对象存储模式
Memorystore (Redis/Memcached)
- 缓存策略:
- 通读缓存 (Read-through):应用从缓存中获取数据;如果未命中,则从源加载数据并填充到缓存中。
- 通写缓存 (Write-through):同步地将写入操作应用到缓存和数据源。
- 回写缓存 (Write-behind):在缓存中缓冲写入,并异步刷新到数据源;因存在数据丢失风险,需谨慎使用。
- 过期与失效:
- 应用与数据陈旧容忍度一致的 TTL。当真实来源 (source-of-truth) 的数据发生变化时,使相关键失效;对于聚合缓存,使用版本化键以避免缓存踩踏。
- 使用互斥锁 (mutex) 或 single-flight 机制来防止热点键上的缓存踩踏。
- 会话:使用 TTL 存储临时会话数据;如果数据敏感,则加密其值或仅存储不透明的令牌。
- 使用 Redis 进行速率限制:
- 固定窗口:对每个身份的键使用
INCR和EXPIRE。 - 滑动窗口或令牌桶可实现更平滑的限制;考虑使用 Lua 脚本来保证原子性。
- 固定窗口:对每个身份的键使用
- 可用性:基本层级没有故障切换;标准层级提供区域级高可用性 (HA)。将缓存视为易失性存储;绝不能作为权威存储。
示例:简单的固定窗口速率限制
命令:
- 缓存策略:
undefined
-
undefined
Cloud Storage
- 对象与一致性:对于读取、写入、覆盖、删除和列出操作,提供全局强一致性。对象是不可变的;更新操作会创建新一代 (generation) 的对象。
- 签名 URL:将大文件上传/下载直接分流到客户端和存储桶之间,无需通过您的应用进行代理。设置较短的过期时间;限制请求方法、路径和内容标头。
- 可续传上传:用于大于 5MB 的文件和不可靠的网络环境;使用截断指数退避和续传令牌来处理 5xx/429 错误。
- 生命周期:定义规则以转换存储类别、删除旧版本并强制执行保留策略。与对象版本控制结合使用,以确保部署期间的安全性。
- 通知:集成 Pub/Sub 通知,以便在对象完成上传 (finalize) 或删除时触发下游处理,并包含前置条件 (如
ifGenerationMatch) 以防止竞争条件。
示例:上传本地文件
undefined
迁移、一致性、分区和数据保护
数据库迁移
- 选择在线与离线迁移:对于需要最大限度减少停机时间的场景,使用 Database Migration Service 进行在线迁移;当可以接受维护窗口时,为简单起见可选择离线迁移。
- 模式先行:协调数据类型和约束;对于 Spanner,考虑使用工具映射 MySQL/PostgreSQL 的模式和数据,然后调整键和索引以实现良好的数据分布。
- 双跑与切换:在在线迁移期间,进行双写或复制变更日志。在最终切换前,验证行数、校验和以及关键查询的行为。
模式迁移和回滚
- 使用版本化的自动迁移(例如,通过迁移工具)作为 CI/CD 的一部分。设计增量式、向后兼容的变更:添加列和索引,回填数据,部署同时支持新旧格式读写的代码,然后移除已弃用的工件。
- 规划包含数据转换的回滚方案:如果代码部署失败,要准备好禁用新写入并依赖功能标志;避免会阻碍回滚的破坏性迁移。
事务性与最终一致性工作流
- 当不变量必须同步保持时(如资金转账、库存扣减),使用 ACID 事务。
- 对于以读取为主、面向用户的、延迟是主要考量的功能(如信息流、搜索、计数器),优先选择最终一致性。实现幂等键、发件箱/Saga 模式以及带指数退避的重试机制。
- 组合使用:在事务性存储中提交权威状态;发布事件以生成最终一致的投影(projections)。
数据分区与连接管理
- 按租户、地理位置或工作负载类型进行分区,以隔离热点。对于 Bigtable 和 Spanner,将分区键编码到主键中;对于 Cloud SQL,使用每租户一模式(schema-per-tenant)或通过路由器进行表分片。
- 管理连接:
- Cloud SQL:池化并复用连接;限制并发数;错开冷启动时间。
- Spanner:复用会话;在启动时预热池;限制重试次数。
- Memorystore:复用 TCP 连接;避免为每个请求新建连接。
数据保护、归档、恢复验证和删除行为
- 备份与归档:
- Cloud SQL:自动备份 + 时间点恢复(PITR);测试恢复操作。
- Spanner:托管备份;测试将备份恢复到非生产环境。
- Firestore:定时导出到 Cloud Storage;验证导入操作。
- Bigtable:备份和快照;测试克隆和恢复操作。
- Cloud Storage:使用保留策略、对象锁定和存储桶级统一访问进行治理;通过生命周期规则将数据归档到更冷的存储类别。
- 恢复验证:定期将备份恢复到隔离环境,并运行验证查询和应用冒烟测试。根据策略跟踪恢复时间目标(RTO)和恢复点目标(RPO)。
- 删除行为:
- Bigtable 的垃圾回收(GC)和生命周期管理是异步的——不要承诺数据能被立即擦除。
- Cloud Storage 的版本控制会保留对象的各个世代,直到生命周期规则将其移除。
- Firestore 的 TTL 和基于导出的删除是异步的。
- 对于有硬删除服务等级协议(SLA)的场景,需设计相应流程:标记删除、排队、验证移除,并附带审计日志。
- 备份与归档:
实践问题场景
Aurora Outfitters 正在将其单体式电子商务平台迁移到 Google Cloud。他们必须:1) 将 MySQL 直接迁移(lift-and-shift)到云上以降低风险,2) 在不使应用过载的情况下处理 500 MB 的产品媒体上传,3) 扩展产品目录的读取吞吐量,以及 4) 在销售高峰期强制执行每用户速率限制。
方法:
将 MySQL 迁移到具有私有 IP 和区域级 HA 的 Cloud SQL
- 理由:私有 IP 消除了公网暴露和 IP 允许列表的需求,简化了从 GKE 和 Compute Engine 进行安全连接的配置。区域级 HA 可防止区域性故障;预计故障切换期间会出现短暂的连接中断,因此应用将实现可重试的事务和重连逻辑。
启用自动备份和 PITR,并验证恢复
- 理由:自动备份和事务日志使得可以从用户或应用程序错误中进行时间点恢复(PITR)。每周安排一次到非生产实例的恢复操作,以验证备份的可用性并衡量 RTO。
为目录读取添加一个只读副本
- 理由:将目录查询移至只读副本可以减少主实例上的资源争用。当需要写后读一致性时(如购物车/结账),应用从主实例读取;而在浏览目录时,则从副本读取,同时理解并接受副本延迟带来的权衡。
引入应用侧连接池并限制并发
- 理由:使用 PgBouncer/HikariCP 等工具限制和复用连接,避免在自动扩缩容和 HA 故障切换期间出现连接风暴。连接池的大小根据 CPU 核心数而不是最大 Pod 数来设定,以防止数据库过载。
使用签名 URL 和可续传上传将媒体上传任务卸载到 Cloud Storage
- 理由:应用为客户端签发短期有效的签名 URL,使其可以直接上传文件。可续传上传能够适应不可靠的网络环境;媒体服务监听 Pub/Sub 的上传完成通知以触发后续处理。使用前置条件标头(ifGenerationMatch)可防止覆盖竞争。
实施 Memorystore for Redis 用于页面缓存、会话管理和速率限制
- 理由:对于产品页面,使用通读缓存(read-through cache)可以减少数据库负载,其 TTL 与数据更新频率保持一致。会话数据作为临时信息保存在 Redis 中,并设置较短的 TTL;应用状态仍保留在 Cloud SQL 中。采用固定窗口令牌策略,使用 INCR/EXPIRE 命令实现每用户请求上限。缓存被视为非权威数据;应用能够容忍缓存丢失并在未命中时重新填充。
为高吞吐量的目录浏览功能准备分阶段迁移到 Cloud Bigtable 的路径
- 理由:随着流量增长,将反规范化、为读取优化的目录视图迁移到 Bigtable。行键被设计为
bucket#category#reverse_ts的格式,以分散写入并支持时间倒序的列表,从而避免热点问题。
- 理由:随着流量增长,将反规范化、为读取优化的目录视图迁移到 Bigtable。行键被设计为
建立模式迁移和回滚程序
- 理由:迁移是增量式的:添加列/索引,通过幂等作业回填数据,部署同时支持新旧字段读写的代码,之后再移除旧字段。使用功能标志来保护新代码路径;回滚时会禁用对新字段的写入,而无需执行破坏性的 DDL 操作。
设置数据生命周期和保护策略
- 理由:Cloud Storage 存储桶使用生命周期规则将缩略图转换到更冷的存储类别,并删除过期的临时上传文件。定期恢复 Cloud SQL 备份以及(未来采用的)Spanner/Bigtable 备份进行验证。审计日志记录删除工作流;在合规性文档中承认 Bigtable 的 GC 是异步的。
实施客户端和服务器端的带截断指数退避的重试机制
- 理由:在流量高峰期,Cloud Storage 可能会返回 429/5xx 错误;退避机制可以平滑负载并降低错误率。数据库和缓存操作使用幂等键来确保重试的安全性,尤其是在故障切换和网络抖动期间。
该计划通过采用具有私有连接和 HA 的 Cloud SQL 立即降低了风险,通过缓存和签名 URL 上传保持了应用的响应能力和成本效益,并为随着流量增长扩展读取吞吐量和数据弹性构建了清晰的路径。
← API 设计、集成与事件驱动开发 · 所有领域 · 身份、认证与应用安全 →
练习这些题目 → · 在 ExamRoll.io 上限时练习 →
Pass the whole exam — not just this question
You found this answer. Get every verified question and explanation in one place, and save hours of prep. Free to start.
通过考试 →