数据库、缓存与消息系统¶
PostgreSQL 的 MVCC、WAL 和事务隔离如何协同?¶
技术说明
PostgreSQL 通过 MVCC1 为每次修改产生新元组版本,快照决定事务可见哪些版本;旧版本由 VACUUM 后续回收。WAL2 先记录页变更所需日志并遵循 write-ahead,崩溃后重放保证持久性,它不是普通逻辑审计日志。默认 Read Committed 每条语句取新快照;Repeatable Read 保持事务快照但仍需理解写冲突;Serializable 以 SSI3 检测危险依赖,可能主动中止事务,应用必须重试。
长事务和复制槽会保留旧版本/WAL,造成表膨胀或磁盘耗尽;pg_stat_activity、pg_stat_user_tables、pg_stat_replication、pg_replication_slots 与 WAL 目录增长应联合观察。synchronous_commit 等持久性参数改变提交延迟与数据丢失窗口,不能只为性能关闭。事务边界应短,外部 API 调用不放在持锁事务中。
实践经验
某报表事务运行六小时,更新业务正常但表体积和 autovacuum lag 持续增加。证据显示 backend_xmin 很旧、dead tuples 上升且 WAL 被复制槽保留。团队先终止可重跑报表、扩容磁盘并限流写入,再改为分片快照读取和报表副本。终止事务会丢本次报表进度,因此应用增加 checkpoint;长期告警最老事务、槽 lag 和 wraparound 风险,而不是仅看数据库 CPU。
面试回答要点
MVCC 管可见性,WAL 管崩溃恢复/复制,角色不同。
WAL 先记录页变更所需日志并遵循 write-ahead,崩溃后重放保证持久性,它不是普通逻辑审计日志。证据显示 backend_xmin 很旧、dead tuples 上升且 WAL 被复制槽保留。
能说明常用隔离级别及 Serializable 需要重试。
默认 Read Committed 每条语句取新快照;Repeatable Read 保持事务快照但仍需理解写冲突;Serializable 以 SSI 检测危险依赖,可能主动中止事务,应用必须重试。某报表事务运行六小时,更新业务正常但表体积和 autovacuum lag 持续增加。
长事务、旧快照和复制槽会阻碍回收并保留 WAL。
长事务和复制槽会保留旧版本/WAL,造成表膨胀或磁盘耗尽;pg_stat_activity、pg_stat_user_tables、pg_stat_replication、pg_replication_slots 与 WAL 目录增长应联合观察。证据显示 backend_xmin 很旧、dead tuples 上升且 WAL 被复制槽保留。
性能参数调整必须明确提交确认与数据丢失边界。
synchronous_commit 等持久性参数改变提交延迟与数据丢失窗口,不能只为性能关闭。终止事务会丢本次报表进度,因此应用增加 checkpoint;长期告警最老事务、槽 lag 和 wraparound 风险,而不是仅看数据库 CPU。
如何用 EXPLAIN 诊断 PostgreSQL 慢查询并正确设计索引?¶
技术说明
先用 pg_stat_statements 按总耗时、平均耗时和调用数找高影响 SQL,再在隔离克隆或预生产环境执行 EXPLAIN (ANALYZE, BUFFERS, WAL, SETTINGS);ANALYZE 会真实运行语句。只读查询可在受控只读副本验证,但写 DML 不能靠 standby 验证,也不能因包在 ROLLBACK 中就视为安全:锁、I/O、序列以及外部函数/触发器副作用未必可回滚。生产例外必须先证明无外部副作用,设置 statement/lock timeout、资源上限和停止条件。随后关注估算行数与实际行数偏差、扫描方式、循环次数、shared read/hit、临时文件和排序;估算失真常来自统计过旧、列相关性或参数分布,并非“一律缺索引”。
索引列顺序服从过滤、连接、排序与选择性;部分索引适合稳定谓词,覆盖索引减少 heap 访问但增大写放大,表达式索引需查询表达式匹配。低选择性列单独 B-tree 价值有限。用 CREATE INDEX CONCURRENTLY 降低写阻塞,但耗时更长、额外 I/O 且可能留下 invalid index;上线观察写延迟、缓存和计划变化,准备删除回滚。
实践经验
某查询由 50 ms 变 8 s,计划从索引扫描变顺序扫描。实际/估算行数相差千倍,根因是状态与租户列高度相关。团队先限制报表并在副本执行 ANALYZE,增加扩展统计后计划恢复;长期只为常用未完成状态建立部分索引。索引使读取变快但写入和 vacuum 成本上升,因此以总工作负载压测,并监控索引命中与写 WAL,而非见慢 SQL 就堆索引。
面试回答要点
先按总影响找 SQL,再看实际/估算、buffers、loops 和临时 I/O。
先用 pg_stat_statements 按总耗时、平均耗时和调用数找高影响 SQL,再在隔离克隆或预生产环境执行 EXPLAIN (ANALYZE, BUFFERS, WAL, SETTINGS);ANALYZE 会真实运行语句。实际/估算行数相差千倍,根因是状态与租户列高度相关。
统计失真、数据倾斜和参数化计划可能比索引缺失更关键。
随后关注估算行数与实际行数偏差、扫描方式、循环次数、shared read/hit、临时文件和排序;估算失真常来自统计过旧、列相关性或参数分布,并非“一律缺索引”。团队先限制报表并在副本执行 ANALYZE,增加扩展统计后计划恢复;长期只为常用未完成状态建立部分索引。
索引提升读但增加写、WAL、缓存和维护成本。
索引列顺序服从过滤、连接、排序与选择性;部分索引适合稳定谓词,覆盖索引减少 heap 访问但增大写放大,表达式索引需查询表达式匹配。索引使读取变快但写入和 vacuum 成本上升,因此以总工作负载压测,并监控索引命中与写 WAL,而非见慢 SQL 就堆索引。
并发建索引也有 I/O/失败边界,需灰度与回滚。
随后关注估算行数与实际行数偏差、扫描方式、循环次数、shared read/hit、临时文件和排序;估算失真常来自统计过旧、列相关性或参数分布,并非“一律缺索引”。索引使读取变快但写入和 vacuum 成本上升,因此以总工作负载压测,并监控索引命中与写 WAL,而非见慢 SQL 就堆索引。
PostgreSQL 流复制与故障切换怎样保证 RPO/RTO?¶
技术说明
主库把 WAL 通过流复制发送给 standby,异步复制提交不等待副本,故障时可能丢未传输 WAL;同步复制可要求指定 standby 确认到达/刷盘/应用,降低 RPO 但把副本延迟和网络故障带入写延迟/可用性。级联复制减轻主库连接,却增加延迟路径。复制槽防止 WAL 被过早回收,但失联副本会无限占盘,必须限额和告警。
自动故障切换需要可靠 leader 选举、fencing 和时间线管理,最危险的是旧主恢复后继续接受写入形成 split brain。切换流程包含确认主不可达、隔离旧主、选最完整副本、提升、更新路由、重建其他副本并校验数据。RTO 还受 DNS/连接池缓存和应用重连影响;定期测量实际 replay LSN、lag bytes/time 和 failover 演练,而非以“有副本”推断 RPO。
实践经验
某主库节点断网,编排器提升副本,但旧主仍被部分应用访问,出现双写。团队先在网络/电源层 fence 旧主、暂停写入口并以时间线和 LSN 确认新主,再修复路由和对账。长期要求提升前 fencing 成功、客户端只连代理,并演练连接重建。更严格同步会增加跨区延迟,因此关键账务使用同步副本,低价值事件接受明确的分钟级 RPO。
面试回答要点
同步级别是持久性与写可用性/延迟的权衡。
主库把 WAL 通过流复制发送给 standby,异步复制提交不等待副本,故障时可能丢未传输 WAL;同步复制可要求指定 standby 确认到达/刷盘/应用,降低 RPO 但把副本延迟和网络故障带入写延迟/可用性。更严格同步会增加跨区延迟,因此关键账务使用同步副本,低价值事件接受明确的分钟级 RPO。
复制 lag 必须用 LSN/字节/时间和应用位置解释。
主库把 WAL 通过流复制发送给 standby,异步复制提交不等待副本,故障时可能丢未传输 WAL;同步复制可要求指定 standby 确认到达/刷盘/应用,降低 RPO 但把副本延迟和网络故障带入写延迟/可用性。团队先在网络/电源层 fence 旧主、暂停写入口并以时间线和 LSN 确认新主,再修复路由和对账。
切换前 fence 旧主,防 split brain 比“自动提升快”更重要。
自动故障切换需要可靠 leader 选举、fencing 和时间线管理,最危险的是旧主恢复后继续接受写入形成 split brain。某主库节点断网,编排器提升副本,但旧主仍被部分应用访问,出现双写。
RTO 包括路由、连接和应用恢复,需真实演练。
RTO 还受 DNS/连接池缓存和应用重连影响;定期测量实际 replay LSN、lag bytes/time 和 failover 演练,而非以“有副本”推断 RPO。长期要求提升前 fencing 成功、客户端只连代理,并演练连接重建。
PostgreSQL 备份、PITR 和恢复验证如何设计?¶
技术说明
可靠方案通常由一致性 base backup 加连续 WAL 归档组成,恢复时还原基线并重放到目标时间/LSN/事务。备份应加密、跨故障域、不可变并有生命周期;仅做逻辑 dump 可能恢复慢且无法满足细粒度 PITR4,大库常需物理备份,逻辑备份可补充对象级恢复。RPO 受 WAL 归档延迟影响,RTO 受数据量、读取带宽、重放速度和后续校验影响。
监控最近成功 base backup、WAL archive failure/age、链完整性、容量和密钥可用性。恢复演练必须在隔离环境从零开始,验证数据库启动、目标时间、行数/校验和、权限、扩展与应用烟测,并记录实际 RTO;成功上传不等于可恢复。防止恢复环境向真实外部系统发消息,演练凭证和 DNS 要隔离。
实践经验
某团队备份作业连续绿色,事故恢复却缺少两小时 WAL。证据显示上传脚本把部分失败仅写日志、未影响 job 状态。团队先从副本和业务事件尽量重建缺口并暂停非必要写入;长期把 archive lag 作为 page、端到端验证连续性,并每月随机时间点恢复。演练占用存储和计算,因此使用临时隔离环境,但不缩小数据到失去真实 RTO 意义。
面试回答要点
base backup + 连续 WAL 才支持常见物理 PITR。
可靠方案通常由一致性 base backup 加连续 WAL 归档组成,恢复时还原基线并重放到目标时间/LSN/事务。某团队备份作业连续绿色,事故恢复却缺少两小时 WAL。
分开备份成功、归档连续性、RPO 与实际恢复 RTO。
恢复演练必须在隔离环境从零开始,验证数据库启动、目标时间、行数/校验和、权限、扩展与应用烟测,并记录实际 RTO;成功上传不等于可恢复。某团队备份作业连续绿色,事故恢复却缺少两小时 WAL。
在隔离环境验证数据、权限、扩展和应用旅程。
恢复演练必须在隔离环境从零开始,验证数据库启动、目标时间、行数/校验和、权限、扩展与应用烟测,并记录实际 RTO;成功上传不等于可恢复。演练占用存储和计算,因此使用临时隔离环境,但不缩小数据到失去真实 RTO 意义。
备份加密、不可变、跨域,且恢复密钥不能同故障域单点。
备份应加密、跨故障域、不可变并有生命周期;仅做逻辑 dump 可能恢复慢且无法满足细粒度 PITR,大库常需物理备份,逻辑备份可补充对象级恢复。某团队备份作业连续绿色,事故恢复却缺少两小时 WAL。
PostgreSQL Autovacuum、膨胀与事务 ID 回卷如何治理?¶
技术说明
UPDATE/DELETE 留下 dead tuples,VACUUM 标记空间可复用并更新可见性图,通常不会直接缩小文件;ANALYZE 更新统计。autovacuum 依据表变化阈值触发,写热点大表常需按表调低 scale factor、增加 worker/cost 配额。长事务、未释放 prepared transaction、逻辑槽或 standby feedback 会阻止清理。事务 ID 接近回卷边界时需要 aggressive vacuum,忽视可导致数据库为保护一致性停止写入。
监控 n_dead_tup、last_autovacuum、vacuum progress、oldest xmin、age(relfrozenxid)、表/索引增长和 autovacuum 日志。先消除阻塞者再调大资源;VACUUM FULL 会拿强锁并重写表,不是在线默认方案,可用在线重建工具或分区归档。提高 vacuum I/O 可能冲击前台延迟,需在 SLO 保护下逐步调优。
实践经验
某事件表每日更新亿级行,磁盘增长而 autovacuum 一直运行。检查发现单表默认 scale factor 使触发过晚,另有闲置事务持有旧 xmin。团队先结束事务、扩盘并提高该表 vacuum 频率,低峰在线重建索引;长期分区并按生命周期删除。更激进 vacuum 使磁盘 I/O 上升,故以延迟和 replication lag 作为限速反馈,而不是一次拉满 worker。
面试回答要点
VACUUM 回收可复用空间,不通常收缩文件;ANALYZE 更新统计。
UPDATE/DELETE 留下 dead tuples,VACUUM 标记空间可复用并更新可见性图,通常不会直接缩小文件;ANALYZE 更新统计。更激进 vacuum 使磁盘 I/O 上升,故以延迟和 replication lag 作为限速反馈,而不是一次拉满 worker。
热点大表需要按表参数,长事务/槽会阻塞清理。
长事务、未释放 prepared transaction、逻辑槽或 standby feedback 会阻止清理。团队先结束事务、扩盘并提高该表 vacuum 频率,低峰在线重建索引;长期分区并按生命周期删除。
监控 oldest xmin 与 XID age,回卷风险高于普通膨胀。
监控 n_dead_tup、last_autovacuum、vacuum progress、oldest xmin、age(relfrozenxid)、表/索引增长和 autovacuum 日志。检查发现单表默认 scale factor 使触发过晚,另有闲置事务持有旧 xmin。
VACUUM FULL 强锁,生产需评估在线替代和 I/O 副作用。
先消除阻塞者再调大资源;VACUUM FULL 会拿强锁并重写表,不是在线默认方案,可用在线重建工具或分区归档。更激进 vacuum 使磁盘 I/O 上升,故以延迟和 replication lag 作为限速反馈,而不是一次拉满 worker。
如何诊断数据库锁等待和死锁?¶
技术说明
锁等待是事务等待冲突锁,死锁是形成环路且无法自行推进,PostgreSQL 检测后中止其中一个事务。用 pg_stat_activity、pg_locks、pg_blocking_pids() 建阻塞树,结合 query start、transaction start、wait_event 和应用 trace 找根阻塞者。数据库 CPU 不高也可能因锁完全停顿;要区分行锁、表锁、元数据锁和外部资源等待。
止血选择取消查询、终止事务或限流入口,先评估回滚量和业务语义。长期保持事务短小、按一致顺序访问对象、为过滤列建合适索引、避免事务内网络调用,并设置合理 lock_timeout/statement_timeout。应用必须对死锁/序列化失败做有界重试和幂等,盲目重试会形成重试风暴。
实践经验
某订单写入延迟飙升,连接池满而数据库 CPU 20%。阻塞树显示一个后台任务更新大批行并等待外部 API,数百请求被其锁住。团队先取消任务、限制新写并确认回滚进度;随后改成小批次、事务外调用,并统一表更新顺序。设置短 lock timeout 会提高显式失败数,故配合应用退避和用户可重试响应,而非把长等待变成静默超时。
面试回答要点
构造阻塞树并找根 blocker,不逐个杀等待者。
用 pg_stat_activity、pg_locks、pg_blocking_pids() 建阻塞树,结合 query start、transaction start、wait_event 和应用 trace 找根阻塞者。阻塞树显示一个后台任务更新大批行并等待外部 API,数百请求被其锁住。
区分等待、死锁和连接池耗尽的因果关系。
数据库 CPU 不高也可能因锁完全停顿;要区分行锁、表锁、元数据锁和外部资源等待。某订单写入延迟飙升,连接池满而数据库 CPU 20%。
缩短事务、统一加锁顺序、补索引并移出外部调用。
长期保持事务短小、按一致顺序访问对象、为过滤列建合适索引、避免事务内网络调用,并设置合理 lock_timeout/statement_timeout。团队先取消任务、限制新写并确认回滚进度;随后改成小批次、事务外调用,并统一表更新顺序。
超时与重试必须有界、退避且业务幂等。
应用必须对死锁/序列化失败做有界重试和幂等,盲目重试会形成重试风暴。设置短 lock timeout 会提高显式失败数,故配合应用退避和用户可重试响应,而非把长等待变成静默超时。
InnoDB 的 redo、undo、binlog 和“双一”参数有什么关系?¶
技术说明
InnoDB redo log 记录物理页变更用于崩溃恢复,undo 保存旧版本用于回滚和 MVCC;MySQL binlog 记录逻辑/行事件,用于复制和时间点恢复。事务提交需协调 InnoDB 与 binlog,内部两阶段流程避免一边持久而另一边缺失。innodb_flush_log_at_trx_commit=1 通常每次提交把 redo 刷盘,sync_binlog=1 通常每次提交同步 binlog,是常说“双一”,但实际耐久性仍取决于存储缓存、文件系统和硬件保障。
放宽刷盘频率可提升吞吐,却引入操作系统/主机故障时的秒级数据丢失和复制缺口,应由业务 RPO 决定。监控 redo 容量/等待、checkpoint age、binlog 磁盘、fsync 延迟和 purge lag。大事务会占用日志、延迟复制和保留 undo,拆批时又需设计幂等与一致性。
实践经验
某写密集系统为压测调低两项刷盘设置,宿主机故障后主库比应用确认少数秒数据,副本也无法补齐。团队冻结写入、按业务事件对账并补偿,确认不是复制“随机丢”。长期将参数纳入策略门禁,关键账务坚持双一和可靠存储,异步分析数据另行接受 RPO。更强持久性提高 p99,因此用批量提交和优化 I/O,而不隐式牺牲确认语义。
面试回答要点
redo 管崩溃恢复,undo 管回滚/MVCC,binlog 管复制/PITR。
InnoDB redo log 记录物理页变更用于崩溃恢复,undo 保存旧版本用于回滚和 MVCC;MySQL binlog 记录逻辑/行事件,用于复制和时间点恢复。团队冻结写入、按业务事件对账并补偿,确认不是复制“随机丢”。
提交需协调引擎与 binlog,不能各看一半。
事务提交需协调 InnoDB 与 binlog,内部两阶段流程避免一边持久而另一边缺失。更强持久性提高 p99,因此用批量提交和优化 I/O,而不隐式牺牲确认语义。
“双一”降低故障丢失窗口但依赖底层存储真实性。
innodb_flush_log_at_trx_commit=1 通常每次提交把 redo 刷盘,sync_binlog=1 通常每次提交同步 binlog,是常说“双一”,但实际耐久性仍取决于存储缓存、文件系统和硬件保障。长期将参数纳入策略门禁,关键账务坚持双一和可靠存储,异步分析数据另行接受 RPO。
性能调参必须由明确 RPO 驱动并用故障测试验证。
放宽刷盘频率可提升吞吐,却引入操作系统/主机故障时的秒级数据丢失和复制缺口,应由业务 RPO 决定。某写密集系统为压测调低两项刷盘设置,宿主机故障后主库比应用确认少数秒数据,副本也无法补齐。
MySQL GTID 复制、延迟和切换如何管理?¶
技术说明
GTID5 为每个已提交事务提供全局标识,使副本能判断已执行集合并简化自动定位;它不自动保证无数据丢失或避免 split brain。异步复制主库确认不等待副本,半同步只保证至少一个副本接收达到配置阶段,仍需明确是否落盘及超时退化行为。并行复制受事务依赖和热点键限制,Seconds_Behind_Source 可能不准确,应结合 GTID 集差、relay log、worker 状态和业务时间戳。
切换前检查候选副本数据完整、延迟、只读和复制错误,隔离旧主后提升,再更新代理/服务发现并重建拓扑。GTID errant transactions 会阻碍重接,不能直接跳过而不对账。读写分离需处理 read-after-write:会话粘主、等待 GTID 或按业务接受陈旧读取。
实践经验
某副本仪表盘显示延迟为零,切换后却少一批数据。证据发现复制 SQL/applier 线程曾因错误停止,原始状态中的延迟字段已是 NULL/unknown,但 exporter 保留了旧样本并把缺失值归零;监控又只检查 I/O 线程,最终制造了“健康 0 秒”的假象。团队先回切受控主、按 GTID 集合与业务表对账,再修复副本。长期同时告警 applier/receiver 状态、GTID 差、指标新鲜度和心跳表,提升前自动阻断异常候选。额外等待 GTID 增加读延迟,故仅对支付确认等需读己之写路径启用。
面试回答要点
GTID 简化事务身份和定位,不等于同步复制或零丢失。
GTID 为每个已提交事务提供全局标识,使副本能判断已执行集合并简化自动定位;它不自动保证无数据丢失或避免 split brain。证据发现复制 SQL/applier 线程曾因错误停止,原始状态中的延迟字段已是 NULL/unknown,但 exporter 保留了旧样本并把缺失值归零;监控又只检查 I/O 线程,最终制造了“健康 0 秒”的假象。
复制健康看线程、GTID 集、worker 和业务心跳,不只一个 lag 值。
并行复制受事务依赖和热点键限制,Seconds_Behind_Source 可能不准确,应结合 GTID 集差、relay log、worker 状态和业务时间戳。证据发现复制 SQL/applier 线程曾因错误停止,原始状态中的延迟字段已是 NULL/unknown,但 exporter 保留了旧样本并把缺失值归零;监控又只检查 I/O 线程,最终制造了“健康 0 秒”的假象。
提升前 fence 旧主并检查 errant/missing transactions。
切换前检查候选副本数据完整、延迟、只读和复制错误,隔离旧主后提升,再更新代理/服务发现并重建拓扑。证据发现复制 SQL/applier 线程曾因错误停止,原始状态中的延迟字段已是 NULL/unknown,但 exporter 保留了旧样本并把缺失值归零;监控又只检查 I/O 线程,最终制造了“健康 0 秒”的假象。
读一致性按业务选择粘主、等待 GTID 或接受陈旧。
读写分离需处理 read-after-write:会话粘主、等待 GTID 或按业务接受陈旧读取。团队先回切受控主、按 GTID 集合与业务表对账,再修复副本。
数据库连接池为何会成为故障放大器,如何定容?¶
技术说明
连接池复用昂贵连接并限制并发,但每个应用实例的池相乘可能远超数据库上限。容量应从数据库可安全并行数反推:总预算扣除管理、复制和应急连接,再按服务权重分配,而不是每实例固定 100。池指标包括 active/idle、等待队列、获取时间、超时、连接寿命和创建失败;连接并非越多越快,超过 CPU/I/O 并行能力会增加上下文切换和锁竞争。
设置有界获取超时、查询/事务超时、最大寿命与带抖动轮换,避免故障恢复时同时重连。代理池可聚合连接,但事务池模式不兼容会话状态、临时表和某些 prepared statement。扩容应用时连接预算应同步缩小单池或经全局代理控制;保留管理通道防止池满后无法救援。
实践经验
自动扩容把应用从 20 扩到 100 副本,每池 50,数据库瞬间拒绝连接并触发重试。团队先冻结扩容、降低单池上限和拒绝低优先级请求,使用保留管理连接排查;长期按总预算动态配置、加入代理和指数退避。小池会让应用更早排队,这是有意背压,但需返回明确过载而不是无限等待,并用池等待 SLI 调整容量。
面试回答要点
总连接是实例数×池大小,要从数据库安全并行度反推。
容量应从数据库可安全并行数反推:总预算扣除管理、复制和应急连接,再按服务权重分配,而不是每实例固定 100。自动扩容把应用从 20 扩到 100 副本,每池 50,数据库瞬间拒绝连接并触发重试。
池等待、获取超时比“当前连接数”更能显示饱和。
池指标包括 active/idle、等待队列、获取时间、超时、连接寿命和创建失败;连接并非越多越快,超过 CPU/I/O 并行能力会增加上下文切换和锁竞争。团队先冻结扩容、降低单池上限和拒绝低优先级请求,使用保留管理连接排查;长期按总预算动态配置、加入代理和指数退避。
重连需抖动和上限,保留管理/复制连接预算。
容量应从数据库可安全并行数反推:总预算扣除管理、复制和应急连接,再按服务权重分配,而不是每实例固定 100。团队先冻结扩容、降低单池上限和拒绝低优先级请求,使用保留管理连接排查;长期按总预算动态配置、加入代理和指数退避。
代理池有会话语义边界,不能透明套用所有应用。
代理池可聚合连接,但事务池模式不兼容会话状态、临时表和某些 prepared statement。小池会让应用更早排队,这是有意背压,但需返回明确过载而不是无限等待,并用池等待 SLI 调整容量。
如何执行零停机数据库 Schema 变更?¶
技术说明
采用 expand-migrate-contract:先增加向后兼容的列/表/索引,部署能读旧写新或双读校验的应用,分批回填并验证,再切读路径,最后等待旧版本清退后删除旧结构。避免同一发布中 rename/drop 被旧实例访问。DDL 锁行为因数据库和版本不同,必须在相同版本、规模数据上验证;即使“online”也可能在开始/结束拿元数据锁并产生大量 WAL/binlog。
回填按主键小批次、限速、可续跑和幂等,观察复制 lag、锁、I/O、膨胀及业务 SLO。双写不是自动原子,需要事务、outbox 或对账补偿。变更前定义暂停/回滚:加列通常易回滚应用,删列难恢复;破坏性步骤在至少一个兼容周期后单独执行,并有备份/恢复证据。
实践经验
某团队直接给大表加非空默认值,在所用旧版本触发表重写,复制延迟数小时。团队取消 DDL、降写流量并等待副本追赶;随后先加可空列,小批回填,验证无空值后再加约束。迁移周期变长且代码暂时兼容两种 schema,但换来可回滚性。长期把锁时长、估算重写量和副本 lag 纳入变更门禁。
面试回答要点
expand→迁移/验证→切换→contract,跨版本保持兼容。
采用 expand-migrate-contract:先增加向后兼容的列/表/索引,部署能读旧写新或双读校验的应用,分批回填并验证,再切读路径,最后等待旧版本清退后删除旧结构。迁移周期变长且代码暂时兼容两种 schema,但换来可回滚性。
在线 DDL 仍需验证锁、日志量和具体数据库版本语义。
DDL 锁行为因数据库和版本不同,必须在相同版本、规模数据上验证;即使“online”也可能在开始/结束拿元数据锁并产生大量 WAL/binlog。团队取消 DDL、降写流量并等待副本追赶;随后先加可空列,小批回填,验证无空值后再加约束。
回填有界、幂等、可续跑,并受 SLO/复制 lag 限速。
回填按主键小批次、限速、可续跑和幂等,观察复制 lag、锁、I/O、膨胀及业务 SLO。团队取消 DDL、降写流量并等待副本追赶;随后先加可空列,小批回填,验证无空值后再加约束。
破坏性步骤独立发布,回滚与恢复证据提前准备。
变更前定义暂停/回滚:加列通常易回滚应用,删列难恢复;破坏性步骤在至少一个兼容周期后单独执行,并有备份/恢复证据。迁移周期变长且代码暂时兼容两种 schema,但换来可回滚性。
Redis 数据结构、持久化和一致性边界如何选择?¶
技术说明
Redis6 的价值来自内存数据结构与单线程命令执行语义(具体版本也有 I/O 线程),String、Hash、Set、Sorted Set、Stream 应按访问模式选,复杂 Lua/大键仍会长时间阻塞。RDB 周期快照恢复快、可能丢最近窗口;AOF 记录写命令,fsync 策略决定耐久与延迟,可配合使用。复制通常异步,主节点确认后故障仍可能丢未复制写入,因此不能把 Redis 默认当强一致账本。
缓存可容忍丢失时应能从权威数据重建;若作为队列/状态库,明确 RPO、内存和恢复语义。监控 used_memory、RSS、碎片、evictions、blocked clients、fork/持久化耗时、复制 lag 和 keyspace。maxmemory-policy 需匹配数据类别,混放不可淘汰状态与可淘汰缓存会造成不可预测丢失。
实践经验
某团队用 Redis 保存唯一任务状态并采用每分钟 RDB,主机故障后近一分钟状态消失。团队暂停消费者、从数据库事件和下游幂等记录重建,再恢复处理;长期将权威状态迁到数据库,Redis 只作加速,关键 Stream 则启用合适 AOF 并演练恢复。更强 fsync 增加写延迟,因此以业务 RPO 分层实例,而非全局一个策略。
面试回答要点
数据结构按访问复杂度选,注意大键/长脚本阻塞事件循环。
Redis 的价值来自内存数据结构与单线程命令执行语义(具体版本也有 I/O 线程),String、Hash、Set、Sorted Set、Stream 应按访问模式选,复杂 Lua/大键仍会长时间阻塞。团队暂停消费者、从数据库事件和下游幂等记录重建,再恢复处理;长期将权威状态迁到数据库,Redis 只作加速,关键 Stream 则启用合适 AOF 并演练恢复。
RDB、AOF 和 fsync 是 RPO、恢复速度与延迟权衡。
RDB 周期快照恢复快、可能丢最近窗口;AOF 记录写命令,fsync 策略决定耐久与延迟,可配合使用。团队暂停消费者、从数据库事件和下游幂等记录重建,再恢复处理;长期将权威状态迁到数据库,Redis 只作加速,关键 Stream 则启用合适 AOF 并演练恢复。
异步复制不提供默认零丢失,缓存需可由权威源重建。
缓存可容忍丢失时应能从权威数据重建;若作为队列/状态库,明确 RPO、内存和恢复语义。团队暂停消费者、从数据库事件和下游幂等记录重建,再恢复处理;长期将权威状态迁到数据库,Redis 只作加速,关键 Stream 则启用合适 AOF 并演练恢复。
不同耐久/淘汰需求的数据不要混在同一实例。
maxmemory-policy 需匹配数据类别,混放不可淘汰状态与可淘汰缓存会造成不可预测丢失。更强 fsync 增加写延迟,因此以业务 RPO 分层实例,而非全局一个策略。
Redis Sentinel 与 Cluster 的高可用机制和风险是什么?¶
技术说明
Sentinel 监控主从、通过多数判断主观/客观下线并选举完成故障转移,提供单主复制组的高可用但不分片。Cluster 用 hash slot 把键分布到多个主节点,每主可有副本,客户端处理 MOVED/ASK 重定向;多键操作需同 slot(可用 hash tag)。两者复制通常异步,网络分区和故障切换均可能丢最近写,min-replicas 等设置只能在可用性与风险间约束。
部署要跨故障域分布仲裁与数据节点,避免多数与主机同失;客户端必须支持拓扑刷新、超时和有界重试。切换验证包括旧主隔离、配置纪元、数据 lag、连接恢复和热键迁移。Cluster 扩缩会迁 slot,消耗网络/CPU,需限速;Sentinel 数量多不代表仲裁独立,放同一节点没有意义。
实践经验
某 Redis 主节点与三个 Sentinel 进程全在同一宿主机,唯一副本又因维护离线;宿主故障后既无可服务的数据主,也无 Sentinel 仲裁。团队临时恢复主机并从 AOF 校验,再把主从数据节点和 Sentinel 分散到三个故障域,演练断网切换。客户端 DNS 缓存导致恢复慢,随后改用支持 Sentinel 拓扑发现的驱动。跨域副本提高延迟与成本,但仲裁和数据容灾才真正独立,不能用“进程数=HA”自我安慰。
面试回答要点
Sentinel 做单主 HA,Cluster 同时做分片与每分片 HA。
Sentinel 监控主从、通过多数判断主观/客观下线并选举完成故障转移,提供单主复制组的高可用但不分片。某 Redis 主节点与三个 Sentinel 进程全在同一宿主机,唯一副本又因维护离线;宿主故障后既无可服务的数据主,也无 Sentinel 仲裁。
异步复制仍有 RPO,分区时需在写可用性和数据风险间取舍。
两者复制通常异步,网络分区和故障切换均可能丢最近写,min-replicas 等设置只能在可用性与风险间约束。团队临时恢复主机并从 AOF 校验,再把主从数据节点和 Sentinel 分散到三个故障域,演练断网切换。
仲裁、主从要跨独立故障域,客户端也需拓扑感知。
部署要跨故障域分布仲裁与数据节点,避免多数与主机同失;客户端必须支持拓扑刷新、超时和有界重试。某 Redis 主节点与三个 Sentinel 进程全在同一宿主机,唯一副本又因维护离线;宿主故障后既无可服务的数据主,也无 Sentinel 仲裁。
验证切换、旧主隔离、重连和 slot 迁移副作用。
切换验证包括旧主隔离、配置纪元、数据 lag、连接恢复和热键迁移。团队临时恢复主机并从 AOF 校验,再把主从数据节点和 Sentinel 分散到三个故障域,演练断网切换。
如何防止缓存穿透、击穿、雪崩和一致性问题?¶
技术说明
穿透是反复查询不存在数据,可用参数校验、短 TTL negative cache 或 Bloom Filter,但后者有假阳性且更新复杂;击穿是热点 key 到期瞬间大量回源,可用 singleflight/互斥重建、逻辑过期和提前刷新;雪崩是大量 key 同时失效或缓存整体不可用,需要 TTL 抖动、多级缓存、回源限流与降级。所有方案都必须保护权威存储,缓存不可用时不能无限透传。
一致性常采用 cache-aside:更新数据库后删除缓存,读 miss 再回填;并发下仍可能旧读回填,可用版本号、延迟二次删除、CDC 失效或业务容忍窗口。写穿/写回适用边界不同,写回增加丢数据风险。监控命中率要分 key 类别,同时看回源 QPS、重建并发、热点、eviction 和陈旧年龄;高命中率也可能缓存错误值。
实践经验
促销零点大量商品 TTL 同时到期,数据库连接和 CPU 被回源打满。团队先限流非核心查询、延长热点逻辑有效期并预热关键 key;证据显示过期事件与回源峰值严格重合。长期 TTL 加随机抖动、热点 singleflight 和数据库舱壁。延长旧值会牺牲短时新鲜度,因此价格/库存不用任意 stale,而按业务版本校验并提供明确失败。
面试回答要点
准确区分穿透、击穿、雪崩及各自机制。
穿透是反复查询不存在数据,可用参数校验、短 TTL negative cache 或 Bloom Filter,但后者有假阳性且更新复杂;击穿是热点 key 到期瞬间大量回源,可用 singleflight/互斥重建、逻辑过期和提前刷新;雪崩是大量 key 同时失效或缓存整体不可用,需要 TTL 抖动、多级缓存、回源限流与降级。长期 TTL 加随机抖动、热点 singleflight 和数据库舱壁。
回源必须有限流、舱壁和降级,不能默认数据库兜底。
穿透是反复查询不存在数据,可用参数校验、短 TTL negative cache 或 Bloom Filter,但后者有假阳性且更新复杂;击穿是热点 key 到期瞬间大量回源,可用 singleflight/互斥重建、逻辑过期和提前刷新;雪崩是大量 key 同时失效或缓存整体不可用,需要 TTL 抖动、多级缓存、回源限流与降级。促销零点大量商品 TTL 同时到期,数据库连接和 CPU 被回源打满。
cache-aside 仍有并发陈旧窗口,按业务一致性处理。
一致性常采用 cache-aside:更新数据库后删除缓存,读 miss 再回填;并发下仍可能旧读回填,可用版本号、延迟二次删除、CDC 失效或业务容忍窗口。延长旧值会牺牲短时新鲜度,因此价格/库存不用任意 stale,而按业务版本校验并提供明确失败。
命中率之外监控回源、热点、重建、淘汰与陈旧度。
监控命中率要分 key 类别,同时看回源 QPS、重建并发、热点、eviction 和陈旧年龄;高命中率也可能缓存错误值。团队先限流非核心查询、延长热点逻辑有效期并预热关键 key;证据显示过期事件与回源峰值严格重合。
Redis 热 key、大 key、内存和淘汰问题如何诊断?¶
技术说明
热 key 会集中单节点 CPU/网络,大 key 会让单命令、复制、fork、删除和迁移产生长停顿。用 slowlog、latency doctor、命令统计、采样 hotkeys、MEMORY USAGE 与离线 RDB 分析定位;生产避免直接全量 KEYS,SCAN 也需限速。内存要区分数据、allocator 碎片、复制/AOF buffer 和 fork Copy-on-Write,used_memory 小不代表 RSS 安全。
治理热 key 可本地缓存、请求合并、只读副本或拆分 key;大集合分页/分桶,删除用 UNLINK 等异步方式但仍监控后台释放。设 maxmemory 给操作系统与 fork 留余量,淘汰策略按 TTL/访问模式选择;若所有 key 不可淘汰,达到上限会拒绝写。扩容/重分片可能把瓶颈转移到网络,需压测。
实践经验
某排行榜单个 Sorted Set 数 GB,过期删除时实例延迟秒级并触发故障转移。slowlog、key 内存与事件延迟证实大 key,而非网络抖动。团队先暂停刷新、切读降级快照并异步删除,随后按时间/分区拆桶和限制成员数。拆桶增加聚合复杂度,因此应用维护 top-N 合并与正确性测试;容量告警同时看最大 key 和 fork 峰值,不只总内存。
面试回答要点
区分数据内存、RSS、碎片、buffer 与 fork COW 峰值。
内存要区分数据、allocator 碎片、复制/AOF buffer 和 fork Copy-on-Write,used_memory 小不代表 RSS 安全。拆桶增加聚合复杂度,因此应用维护 top-N 合并与正确性测试;容量告警同时看最大 key 和 fork 峰值,不只总内存。
用采样和离线分析找热点/大键,避免生产阻塞扫描。
用 slowlog、latency doctor、命令统计、采样 hotkeys、MEMORY USAGE 与离线 RDB 分析定位;生产避免直接全量 KEYS,SCAN 也需限速。拆桶增加聚合复杂度,因此应用维护 top-N 合并与正确性测试;容量告警同时看最大 key 和 fork 峰值,不只总内存。
分桶、本地缓存、合并请求和异步删除各有一致性成本。
治理热 key 可本地缓存、请求合并、只读副本或拆分 key;大集合分页/分桶,删除用 UNLINK 等异步方式但仍监控后台释放。团队先暂停刷新、切读降级快照并异步删除,随后按时间/分区拆桶和限制成员数。
maxmemory 需留系统余量,淘汰策略按数据语义选择。
设 maxmemory 给操作系统与 fork 留余量,淘汰策略按 TTL/访问模式选择;若所有 key 不可淘汰,达到上限会拒绝写。团队先暂停刷新、切读降级快照并异步删除,随后按时间/分区拆桶和限制成员数。
Kafka 的分区、副本、ISR 和确认机制如何决定可靠性?¶
技术说明
Kafka7 Topic 分区是并行和顺序边界,单分区内按 offset 有序;每分区有 leader 和 followers,ISR8 是与 leader 保持足够同步的副本集合。生产者 acks=all 要求达到 broker 规则的确认,配合 min.insync.replicas 才能在副本不足时拒绝写;若允许 unclean leader election,可能用落后副本恢复可用性但丢已确认数据。复制因子、故障域放置和 rack awareness 必须共同设计。
分区数决定最大消费并行度,也增加控制面、文件句柄和恢复成本,不能无限预建。监控 under-replicated/offline partitions、ISR shrink、produce/fetch 延迟、磁盘/网络、controller 事件和副本 lag。保留按时间/大小且以分区为单位,容量需要峰值写入×保留×副本因子×余量,并考虑压缩率与重放流量。
实践经验
某集群一台 broker 磁盘故障,业务仍写成功,随后第二台故障导致部分消息丢失。配置检查发现 acks=1 且允许不干净选主。团队先停止非关键生产、恢复最完整副本并按业务日志对账;长期关键 topic 使用 acks=all、合适 min ISR 和跨机架副本。副本不足时写会失败,这是有意以可用性换持久性,因此生产者需有界重试和落盘缓冲。
面试回答要点
分区是顺序/并行边界,副本与 ISR 是可用/持久基础。
Topic 分区是并行和顺序边界,单分区内按 offset 有序;每分区有 leader 和 followers,ISR 是与 leader 保持足够同步的副本集合。副本不足时写会失败,这是有意以可用性换持久性,因此生产者需有界重试和落盘缓冲。
acks=all 必须结合 min.insync.replicas 和选主策略解释。
生产者 acks=all 要求达到 broker 规则的确认,配合 min.insync.replicas 才能在副本不足时拒绝写;若允许 unclean leader election,可能用落后副本恢复可用性但丢已确认数据。团队先停止非关键生产、恢复最完整副本并按业务日志对账;长期关键 topic 使用 acks=all、合适 min ISR 和跨机架副本。
分区数有控制面和恢复成本,不只看吞吐。
分区数决定最大消费并行度,也增加控制面、文件句柄和恢复成本,不能无限预建。团队先停止非关键生产、恢复最完整副本并按业务日志对账;长期关键 topic 使用 acks=all、合适 min ISR 和跨机架副本。
容量和告警覆盖磁盘、ISR、控制器及重放流量。
保留按时间/大小且以分区为单位,容量需要峰值写入×保留×副本因子×余量,并考虑压缩率与重放流量。团队先停止非关键生产、恢复最完整副本并按业务日志对账;长期关键 topic 使用 acks=all、合适 min ISR 和跨机架副本。
Kafka 幂等生产、事务和 Exactly-once 的真实边界是什么?¶
技术说明
幂等生产者使用 producer ID 与序列号,使 broker 在单生产会话/分区范围内去除重试重复;配置需保持兼容的 acks、retries 和 in-flight 语义。事务生产者用稳定 transactional.id 将多个分区写入及消费 offset 原子提交,消费者设 read_committed 才不读未提交记录。Kafka Streams 可封装 consume-process-produce 的 exactly-once 处理,但外部数据库、HTTP 或邮件不自动加入 Kafka 事务。
端到端仍需业务幂等键、outbox/inbox 或可协调事务。事务超时、僵尸 producer fencing 和 abort 记录会影响延迟与存储;transactional.id 不能在并行实例间随意复用。把“broker 不重复”宣传为“用户永远不重复扣款”是错误抽象,必须逐段说明交付与副作用语义。
实践经验
支付消费者处理数据库成功后在提交 offset 前崩溃,重启再次扣款;生产者幂等并未解决外部副作用。团队先按支付业务键对账和退款,再在数据库事务内写 inbox/结果,以唯一约束实现重复安全,offset 后提交。增加去重表带来存储和清理成本,因此按业务保留期分区清理;监控重复命中和补偿结果,验证真实端到端语义。
面试回答要点
幂等 producer 去除 Kafka 重试重复,不覆盖外部副作用。
幂等生产者使用 producer ID 与序列号,使 broker 在单生产会话/分区范围内去除重试重复;配置需保持兼容的 acks、retries 和 in-flight 语义。支付消费者处理数据库成功后在提交 offset 前崩溃,重启再次扣款;生产者幂等并未解决外部副作用。
Kafka 事务与 read_committed、offset 原子提交配套。
事务生产者用稳定 transactional.id 将多个分区写入及消费 offset 原子提交,消费者设 read_committed 才不读未提交记录。团队先按支付业务键对账和退款,再在数据库事务内写 inbox/结果,以唯一约束实现重复安全,offset 后提交。
transactional ID、超时和 fencing 需正确管理。
事务超时、僵尸 producer fencing 和 abort 记录会影响延迟与存储;transactional.id 不能在并行实例间随意复用。增加去重表带来存储和清理成本,因此按业务保留期分区清理;监控重复命中和补偿结果,验证真实端到端语义。
端到端 exactly-once 通常靠业务幂等/outbox,而非一句配置。
端到端仍需业务幂等键、outbox/inbox 或可协调事务。增加去重表带来存储和清理成本,因此按业务保留期分区清理;监控重复命中和补偿结果,验证真实端到端语义。
Kafka Consumer Group、再均衡和 offset 应如何管理?¶
技术说明
同一 group 内一个分区同一时刻由一个 consumer 处理,实例数超过分区数会空闲。成员变化、订阅或分区变化触发 rebalance;停止世界式分配会暂停处理, cooperative sticky 等策略可渐进转移但应用仍要正确处理 revoke/assign。session.timeout、heartbeat 和 max.poll.interval 分别约束存活与处理时长,慢处理超过轮询间隔会被踢出并造成重复。
offset 表示下一条消费位置。自动提交可能在业务完成前推进造成丢处理,完成后提交则崩溃时会重复;常用方式是批次处理成功后同步/异步提交并保证幂等。分区并发处理时不能越过未完成的低 offset 提交。监控 rebalance 次数/时长、assigned partitions、poll idle、commit failure 和 lag。滚动发布时要区分两种目标:graceful LeaveGroup 会触发再均衡但可让分区快速交接;静态成员用稳定 group.instance.id,在 session timeout 内短暂重启并保持成员身份时可避免立即重新分配。具体关闭行为和超时由客户端版本及 classic/new consumer protocol 决定,不能把两者笼统当成同一种“免 rebalance”机制。
实践经验
某消费者每批调用慢 API 需十分钟,超过 max.poll.interval 后反复再均衡,lag 持续上升。日志显示 member removed 与重复处理交替,broker 正常。团队先减少批次、扩大下游并发舱壁并临时调整间隔,随后把慢任务异步化、按完成 offset 提交。放大间隔会延迟真正失效检测,所以不是永久解法;长期告警 rebalance storm 和重复率。
面试回答要点
分区数限制 group 并行度,rebalance 会影响暂停和重复。
成员变化、订阅或分区变化触发 rebalance;停止世界式分配会暂停处理, cooperative sticky 等策略可渐进转移但应用仍要正确处理 revoke/assign。放大间隔会延迟真正失效检测,所以不是永久解法;长期告警 rebalance storm 和重复率。
区分 heartbeat/session 与 max.poll.interval 的语义。
session.timeout、heartbeat 和 max.poll.interval 分别约束存活与处理时长,慢处理超过轮询间隔会被踢出并造成重复。某消费者每批调用慢 API 需十分钟,超过 max.poll.interval 后反复再均衡,lag 持续上升。
offset 提交必须与业务完成顺序和幂等策略一致。
自动提交可能在业务完成前推进造成丢处理,完成后提交则崩溃时会重复;常用方式是批次处理成功后同步/异步提交并保证幂等。团队先减少批次、扩大下游并发舱壁并临时调整间隔,随后把慢任务异步化、按完成 offset 提交。
区分快速 LeaveGroup 交接、协作分配和静态成员短重启的语义。
滚动发布时要区分两种目标:graceful LeaveGroup 会触发再均衡但可让分区快速交接;静态成员用稳定 group.instance.id,在 session timeout 内短暂重启并保持成员身份时可避免立即重新分配。日志显示 member removed 与重复处理交替,broker 正常。
Consumer Lag 飙升时如何系统排查?¶
技术说明
lag 是 log end offset 与已提交/已处理位置之差,必须同时看增长速率和“最老消息年龄”;消息大小/处理成本变化时单纯条数会误导。按分区查看是否整体不足或单分区倾斜,再检查生产速率、消费吞吐、实例/分区分配、rebalance、poll/处理耗时、下游错误、GC、CPU 和网络。lag 为负/跳变也可能是保留或 offset 重置造成。
止血可扩消费者到分区上限、限流生产、降级昂贵步骤、增加下游容量或将失败消息隔离,但不能盲目加实例超过分区数。重置 offset 会跳过或重放数据,是业务数据操作,需审批、备份原位置与对账。恢复后估算清 backlog 时间,避免实时流量继续大于净消费能力。
实践经验
某 topic lag 只集中在一个分区,扩容消费者无效。按 key 频率发现单一大租户占 60%,该分区下游写也被限流。团队先对租户限速并旁路非关键富化,积压下降;长期改善分区键、对热点租户拆 topic,并确保消费者幂等迁移。改 key 会改变顺序边界,故只在业务实体允许的层级拆分,并做双读对账。
面试回答要点
同看 lag 条数、增长率、最老年龄和分区分布。
lag 是 log end offset 与已提交/已处理位置之差,必须同时看增长速率和“最老消息年龄”;消息大小/处理成本变化时单纯条数会误导。某 topic lag 只集中在一个分区,扩容消费者无效。
区分生产突增、消费不足、分区倾斜和下游阻塞。
按分区查看是否整体不足或单分区倾斜,再检查生产速率、消费吞吐、实例/分区分配、rebalance、poll/处理耗时、下游错误、GC、CPU 和网络。团队先对租户限速并旁路非关键富化,积压下降;长期改善分区键、对热点租户拆 topic,并确保消费者幂等迁移。
扩实例受分区上限约束,恢复需计算净排空能力。
止血可扩消费者到分区上限、限流生产、降级昂贵步骤、增加下游容量或将失败消息隔离,但不能盲目加实例超过分区数。某 topic lag 只集中在一个分区,扩容消费者无效。
offset reset 是高风险数据决策,必须可回滚和对账。
重置 offset 会跳过或重放数据,是业务数据操作,需审批、备份原位置与对账。改 key 会改变顺序边界,故只在业务实体允许的层级拆分,并做双读对账。
Kafka 如何做容量规划、保留和灾难恢复?¶
技术说明
容量从峰值 ingress/egress、平均/最大消息、保留时间、复制因子、压缩、消费者重放和故障余量估算。磁盘不能按标称用满,要为 segment、重分配、单 broker 故障和 page cache 留余量;网络需考虑 leader 写、副本复制和多消费者读取。分区分布、leader 均衡和单盘吞吐比集群总容量更关键。
同城 HA 依靠跨故障域副本;跨区域 DR 可用异步复制工具,需明确 topic 配置、ACL、consumer offset、schema 和事务语义是否同步。异步复制决定非零 RPO,故障切换还要防双边生产与消息 ID 冲突。演练包含停止原侧、确认复制水位、切生产/消费、对账和回切。压缩 topic 的删除/墓碑及保留语义需单独验证。
实践经验
某集群磁盘达 85% 后扩容,重分配反而把网络打满并扩大 ISR 抖动。团队先暂停批量重放、限速迁移并增加临时容量;只有经 topic owner 确认数据已被消费或另有可验证备份、且不承担审计/DR 重放职责后,才审批缩短少量非关键 topic 的保留。该操作会不可逆删除到期 segment,执行前保存原配置、记录预期删除范围并验证消费者水位。长期在 65% 触发采购/扩容,模型计入一节点故障和重放。更早扩容增加闲置成本,因此结合交付提前期和增长区间,而非固定追求低利用率。
面试回答要点
容量包含写、复制、读取/重放、保留和故障余量。
容量从峰值 ingress/egress、平均/最大消息、保留时间、复制因子、压缩、消费者重放和故障余量估算。团队先暂停批量重放、限速迁移并增加临时容量;只有经 topic owner 确认数据已被消费或另有可验证备份、且不承担审计/DR 重放职责后,才审批缩短少量非关键 topic 的保留。
按分区、broker、磁盘与网络瓶颈建模,不看总 TB 即止。
磁盘不能按标称用满,要为 segment、重分配、单 broker 故障和 page cache 留余量;网络需考虑 leader 写、副本复制和多消费者读取。某集群磁盘达 85% 后扩容,重分配反而把网络打满并扩大 ISR 抖动。
跨区复制通常异步,RPO、offset、ACL/schema 都需设计。
同城 HA 依靠跨故障域副本;跨区域 DR 可用异步复制工具,需明确 topic 配置、ACL、consumer offset、schema 和事务语义是否同步。团队先暂停批量重放、限速迁移并增加临时容量;只有经 topic owner 确认数据已被消费或另有可验证备份、且不承担审计/DR 重放职责后,才审批缩短少量非关键 topic 的保留。
以真实切换和对账验证 DR,防止双生产。
演练包含停止原侧、确认复制水位、切生产/消费、对账和回切。团队先暂停批量重放、限速迁移并增加临时容量;只有经 topic owner 确认数据已被消费或另有可验证备份、且不承担审计/DR 重放职责后,才审批缩短少量非关键 topic 的保留。
通用消息队列的 Ack、重试和 DLQ 应如何设计?¶
技术说明
Ack 时点定义语义:处理前 ack 可能丢业务,处理完成后 ack 通常至少一次并可能重复。消费者必须以稳定业务键幂等;失败区分瞬时、永久和未知,只有瞬时错误按指数退避加抖动重试。立即无延迟重回主队列会形成热循环,挤占健康消息;可用延迟队列/分级重试并设最大次数和总时限。
DLQ 不是垃圾桶,消息应附原 topic/queue、错误类型、尝试次数、首次/末次时间、trace ID 与 schema 版本,但不泄露敏感载荷。设 owner、积压/最老年龄告警、检索工具和受控 replay;重放前修复原因、验证幂等、限速并记录新旧 ID。过期消息可能已无业务价值,应进入人工判定或补偿而非盲重放。
实践经验
一次下游 429 导致消费者立即 nack,消息每秒循环数百次,队列 CPU 飙升且正常事件饿死。团队先暂停问题消费者、把失败流量移入延迟重试并保护下游;长期按错误分类、指数退避、重试预算和 DLQ9 owner。延迟会增加最终完成时间,因此关键实时事件在预算耗尽后显式失败并通知用户,而不是无限重试制造假成功。
面试回答要点
Ack 与业务提交顺序决定丢失/重复边界。
Ack 时点定义语义:处理前 ack 可能丢业务,处理完成后 ack 通常至少一次并可能重复。团队先暂停问题消费者、把失败流量移入延迟重试并保护下游;长期按错误分类、指数退避、重试预算和 DLQ owner。
重试只针对可恢复错误,必须退避、抖动且有总预算。
消费者必须以稳定业务键幂等;失败区分瞬时、永久和未知,只有瞬时错误按指数退避加抖动重试。团队先暂停问题消费者、把失败流量移入延迟重试并保护下游;长期按错误分类、指数退避、重试预算和 DLQ owner。
DLQ 有 owner、上下文、告警、修复和受控重放流程。
设 owner、积压/最老年龄告警、检索工具和受控 replay;重放前修复原因、验证幂等、限速并记录新旧 ID。团队先暂停问题消费者、把失败流量移入延迟重试并保护下游;长期按错误分类、指数退避、重试预算和 DLQ owner。
幂等、限流和过期业务语义是重放安全前提。
过期消息可能已无业务价值,应进入人工判定或补偿而非盲重放。团队先暂停问题消费者、把失败流量移入延迟重试并保护下游;长期按错误分类、指数退避、重试预算和 DLQ owner。
如何实现跨数据库与消息系统的一致事件发布?¶
技术说明
直接“写数据库后发消息”存在进程在两步间崩溃的双写缺口;先发消息再写库则消费者可能看到不存在状态。Transactional Outbox 在同一数据库事务里写业务行和 outbox 事件,再由 relay/CDC10 发布到 broker;发布至少一次,所以事件含唯一 ID,消费者 inbox/唯一约束幂等。不能把两阶段提交默认套到所有系统,运维复杂度和阻塞故障往往更高。
outbox 需定义顺序键、schema、重试、保留和清理,监控最老未发布年龄、发布失败、重复及数据库增长。CDC 读取 WAL/binlog 要管理复制槽/位置,否则失联会占满磁盘;relay 发布后标记也可能重复。业务事件表达已提交事实,消费者演进使用向后兼容 schema,删除字段经过兼容周期。
实践经验
某订单服务数据库已提交但 broker 超时,重试请求又产生重复订单。团队先用业务键对账并补发缺失事件;长期将订单与 outbox 同事务写入,relay 按事件 ID 发布,消费者唯一约束去重。CDC 中断曾使 WAL 保留增长,因此增加 slot lag 和未发布年龄告警。多一次表写和清理是代价,但换来可恢复的明确状态。
面试回答要点
说明双写在两个顺序下各自的故障窗口。
直接“写数据库后发消息”存在进程在两步间崩溃的双写缺口;先发消息再写库则消费者可能看到不存在状态。多一次表写和清理是代价,但换来可恢复的明确状态。
Outbox 保证数据库事实与待发事件原子,发布仍可能重复。
Transactional Outbox 在同一数据库事务里写业务行和 outbox 事件,再由 relay/CDC 发布到 broker;发布至少一次,所以事件含唯一 ID,消费者 inbox/唯一约束幂等。团队先用业务键对账并补发缺失事件;长期将订单与 outbox 同事务写入,relay 按事件 ID 发布,消费者唯一约束去重。
消费端幂等、schema 演进、顺序和清理不可省略。
outbox 需定义顺序键、schema、重试、保留和清理,监控最老未发布年龄、发布失败、重复及数据库增长。多一次表写和清理是代价,但换来可恢复的明确状态。
CDC/复制槽自身有容量和恢复风险,需要监控。
CDC 读取 WAL/binlog 要管理复制槽/位置,否则失联会占满磁盘;relay 发布后标记也可能重复。多一次表写和清理是代价,但换来可恢复的明确状态。
如何统一设计有状态系统的备份、恢复演练和一致性验证?¶
技术说明
先按数据集定义权威源、RPO、RTO、保留、加密和故障域,再选择快照、日志增量、逻辑导出或跨区复制。复制不是备份:误删、勒索和逻辑损坏会同步传播。备份目录记录数据版本、依赖顺序、密钥和 schema;不可变、跨账号/区域保存,并对删除设置双人保护。恢复顺序考虑数据库、缓存、搜索索引和消息位置的一致时间点。
演练从空环境执行,测下载、重放、索引构建和应用启动的实际耗时。验证不仅是进程可启动,还要做行数/校验和、约束、关键业务旅程、消息重复/缺失和权限检查。对跨系统一致性可选择共同恢复点、重建派生数据或执行补偿;演练隔离外部副作用并记录证据,结果反馈容量与自动化。
实践经验
某灾备演练数据库恢复成功,但搜索索引来自更晚快照,用户看到已取消订单。团队停止对外验证流量、以数据库为权威重建索引并校验消息水位;长期为每次备份记录一致性标记和依赖恢复顺序,派生系统默认重建。完全重建延长 RTO,因此预置并行构建容量,但不牺牲正确性换“服务先绿”。
面试回答要点
复制不防逻辑损坏,备份需不可变且独立故障域。
复制不是备份:误删、勒索和逻辑损坏会同步传播。团队停止对外验证流量、以数据库为权威重建索引并校验消息水位;长期为每次备份记录一致性标记和依赖恢复顺序,派生系统默认重建。
每个数据集明确权威源、RPO/RTO 和恢复依赖顺序。
先按数据集定义权威源、RPO、RTO、保留、加密和故障域,再选择快照、日志增量、逻辑导出或跨区复制。团队停止对外验证流量、以数据库为权威重建索引并校验消息水位;长期为每次备份记录一致性标记和依赖恢复顺序,派生系统默认重建。
从零恢复并验证业务正确性、权限和跨系统水位。
对跨系统一致性可选择共同恢复点、重建派生数据或执行补偿;演练隔离外部副作用并记录证据,结果反馈容量与自动化。团队停止对外验证流量、以数据库为权威重建索引并校验消息水位;长期为每次备份记录一致性标记和依赖恢复顺序,派生系统默认重建。
演练实测反馈容量与自动化,不以“备份成功”结案。
对跨系统一致性可选择共同恢复点、重建派生数据或执行补偿;演练隔离外部副作用并记录证据,结果反馈容量与自动化。某灾备演练数据库恢复成功,但搜索索引来自更晚快照,用户看到已取消订单。
大规模数据迁移如何控制一致性、回滚和业务风险?¶
技术说明
迁移分为盘点/兼容、初始全量、增量 CDC、验证、灰度读切换、写切换和清理。用源端稳定快照加日志位置衔接,避免全量与增量之间缺口;目标 schema、字符集、时区、精度和主键语义提前校验。双写可能部分失败和顺序颠倒,优先 outbox/CDC,若必须双写则记录每侧结果并有补偿队列。
验证采用行数、分桶 checksum、抽样字段、业务不变量与影子读差异,注意在线变化导致直接全表比较假差异。切换按租户/分片灰度,设置错误、延迟、差异率和 lag 停止条件;回滚要明确旧系统在切换后是否仍保持可追平。最终下线前经过稳定窗口、备份和读取审计,避免隐藏消费者仍依赖旧库。
实践经验
某迁移全量完成后直接切写,发现金额字段精度转换造成少量差异。团队停止新批次、路由回旧库,并用变更日志回放切换窗口;证据来自按租户 checksum 和财务不变量。长期加入 schema 契约、影子读和 1% 租户灰度,目标持续接 CDC 以保证可回滚。双系统运行增加成本和操作复杂度,因此限定迁移窗口,但不提前拆除回退路径。
面试回答要点
全量快照与 CDC 日志位置必须无缝衔接。
用源端稳定快照加日志位置衔接,避免全量与增量之间缺口;目标 schema、字符集、时区、精度和主键语义提前校验。团队停止新批次、路由回旧库,并用变更日志回放切换窗口;证据来自按租户 checksum 和财务不变量。
验证包含 checksum、抽样和业务不变量,不只比行数。
验证采用行数、分桶 checksum、抽样字段、业务不变量与影子读差异,注意在线变化导致直接全表比较假差异。团队停止新批次、路由回旧库,并用变更日志回放切换窗口;证据来自按租户 checksum 和财务不变量。
按租户/分片灰度,定义停止条件与可追平回滚路径。
切换按租户/分片灰度,设置错误、延迟、差异率和 lag 停止条件;回滚要明确旧系统在切换后是否仍保持可追平。团队停止新批次、路由回旧库,并用变更日志回放切换窗口;证据来自按租户 checksum 和财务不变量。
下线前排查隐藏消费者,并保留备份和稳定观察窗口。
最终下线前经过稳定窗口、备份和读取审计,避免隐藏消费者仍依赖旧库。双系统运行增加成本和操作复杂度,因此限定迁移窗口,但不提前拆除回退路径。
-
MVCC 是 Multi-Version Concurrency Control(多版本并发控制),通过保留多个数据版本让读写事务按快照并发执行。 ↩
-
WAL 是 Write-Ahead Logging(预写式日志),要求数据页落盘前先持久化对应日志,以支持崩溃恢复和复制。 ↩
-
SSI 是 Serializable Snapshot Isolation(可串行化快照隔离),通过检测危险依赖提供可串行化效果,并可能中止冲突事务。 ↩
-
PITR 是 Point-in-Time Recovery(时间点恢复),通过基线备份与连续日志把数据库恢复到指定时间或日志位置。 ↩
-
GTID 是 Global Transaction Identifier(全局事务标识符),用于唯一标记 MySQL 复制拓扑中的已提交事务。 ↩
-
Redis 是以内存数据结构为核心的数据存储,常用于缓存、状态和消息场景。参见 Redis 官方文档。 ↩
-
Apache Kafka 是分布式事件流平台,以 topic、分区和副本保存并传递事件。参见 Kafka 官方文档。 ↩
-
ISR 是 In-Sync Replicas(同步副本集合),表示与 Kafka 分区 leader 保持在允许滞后范围内的副本。 ↩
-
DLQ 是 Dead-Letter Queue(死信队列),用于隔离无法继续自动处理的消息,等待调查、修复或受控重放。 ↩
-
CDC 是 Change Data Capture(变更数据捕获),持续读取数据库变更并将其传播到其他系统。 ↩