上周又有人问我为什么他们的同步任务跑了两天PostgreSQL 那边的磁盘就满了。远程连上去一看复制槽还在connector 已经挂了WAL 从任务失败那一刻开始一路堆硬生生把数据盘撑爆。这类问题在 CDC 场景里太常见了跟代码写得好不好关系不大关键是链路上有几个沉默的开关没人告诉你。这篇就围绕 Debezium RocketMQ Connect 把 PostgreSQL 的增量变更实时同步到消息队列这条链路从原理、参数、配置到踩坑完整讲一遍。Debezium负责从 PostgreSQL 的 WAL 里把 INSERT/UPDATE/DELETE 抠出来RocketMQ Connect负责把抠出来的东西稳定地送进消息队列两者之间靠复制槽和 offset 存储衔接。适合正在做异构数据同步、缓存刷新、数仓入湖、业务解耦的开发者也适合被定时跑批导数折磨过、想换实时方案但一直没敢下手的团队。下面所有配置和参数都是我在实际项目里跑过的组合版本差异的地方我会单独标出来。1. 手动导数到底卡在哪为什么非得上 CDC先说清楚为什么要换方案不然很容易陷入为了用新技术而用技术的坑。定时任务导数最典型的问题是窗口期。假设每 5 分钟跑一次WHERE updated_at last_run那么两次任务之间的删除操作是永远抓不到的中间态的更新也会被合并掉下游只看到最终值看不到从 A 改到 B 再改到 C的过程。对账的时候发现两边行数对不上回头查日志发现是某条记录在两次跑批之间被删了又重新插入主键相同但业务含义已经变了。这类问题排查一次能耗掉半天。1.1 三种同步路子的现实对比我把常见的几种做法列出来你可以对照自己的场景选方案实时性对源库压力能捕获删除断点续传落地成本定时扫updated_at分钟级周期性高大表扫全索引不能靠时间戳易漏低触发器写变更表秒级每次写入多一次事务开销能好中但侵入业务逻辑复制 CDC毫秒到秒级读 WAL几乎无额外查询能复制槽天然支持中高需要运维配合触发器方案看着简单实际坑很深。业务表上挂触发器等于把同步逻辑耦合进了写入路径一旦变更表写失败主业务事务跟着回滚变更表本身还会无限膨胀得配清理任务。逻辑复制是从 WAL 层面读的业务写入完全无感这是它最大的优势。注意逻辑复制要求 PostgreSQL 版本在 10 以上pgoutput插件是 10 开始内置的。9.6 虽然也支持逻辑复制但插件生态和 Debezium 的兼容性差很多不建议在 9.6 上折腾。1.2 Debezium 和 RocketMQ Connect 各自负责什么很多人把这两个东西混在一起理解其实分工很清楚。Debezium 是一个 CDC 引擎它内置了 PostgreSQL 的 connector负责连上数据库、创建复制槽、拉取 WAL、把二进制变更解析成结构化事件。RocketMQ Connect 是一个连接器运行时框架负责管理 connector 的生命周期、保存 offset、处理任务失败重试、把事件写进 RocketMQ 的 Topic。两者之间的接口是 Connect 规范里的事件模型。Debezium 输出的是SourceRecord包含 key、value、topic 名、分区信息RocketMQ Connect 的 source 任务拿到这些记录后再投递到 RocketMQ。所以你在配置里看到的key.converter、value.converter、transforms这些都是 Connect 层的概念不是 Debezium 独有的。这里有个必须提前说清楚的现实问题。Debezium 原生发行包里的 PostgreSQL connector 是按 Kafka Connect 的Connector/SourceTask接口编译的而 RocketMQ Connect 走的是自己的一套 Connector SPI两者不是二进制兼容的。实际落地通常有两条路一条是用社区里基于 Debezium 引擎封装好的 RocketMQ source connector把 EmbeddedEngine 包一层另一条是让 Debezium 跑在 Kafka Connect 运行时上再用 RocketMQ Connect 的 Kafka source 把数据接进 RocketMQ。两条路的业务配置九成是一样的差别在 offset 存储和 schema history 的落点。我下面给的配置模板是 Debezium 标准字段套到哪条路上都能用类名和存储相关的字段按你手里的发行包调整。2. 链路原理拆解一条 UPDATE 是怎么走到 RocketMQ 的理解这条链路比背配置项重要得多。因为出问题的时候你得知道卡在哪一段是数据库没产生 WAL还是 slot 没推进还是 connector 没拉到还是 RocketMQ 没写进去。四段链路对应四套排查手段混着查只会浪费时间。2.1 逻辑复制槽是整条链路的心脏PostgreSQL 的逻辑复制槽本质上是数据库给你的一个承诺只要这个槽还在从某个 LSN 开始的所有 WAL 我都给你留着不管你消费到哪儿。这个机制保证了断点续传但也意味着如果消费者停了WAL 会一直堆直到把磁盘撑满。槽有几个关键状态字段。restart_lsn是数据库承诺保留的最早位置confirmed_flush_lsn是消费者已经确认消费到的位置两者之间的差值就是还没被确认的 WAL 量。active表示当前有没有连接在用这个槽。pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)算出来的是端到端的延迟字节数这个指标比任何应用层监控都真实。SELECT slot_name, plugin, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) AS consumer_lag FROM pg_replication_slots WHERE slot_name cdc_order_slot;喝杯水的功夫跑一下这条 SQLretained_wal一直在涨而consumer_lag不降说明消费侧已经堵了。这时候别急着删槽先看 connector 状态和下游 RocketMQ 的堆积情况。删槽是最粗暴的止损手段代价是同步位点丢失重建之后要做一次全量对齐数据量大就是几小时起步。2.2 快照和增量的切换点最容易出问题Debezium 启动时如果snapshot.modeinitial会先做一次全量快照再把位点切到快照开始那一刻的 LSN继续消费增量。这个切换过程不是原子的中间有一小段时间窗口处理不好就会出现重复或者丢失。Debezium 的做法是快照期间记录一个一致性位点快照完成后从这个位点开始读 WAL保证不丢重复的部分靠下游幂等消化。快照本身在 PostgreSQL 10 之后用的是pg_export_snapshot属于一致性快照不会长时间阻塞写入但会持有一个长事务。长事务的危害是阻止 vacuum 回收死元组大表快照跑几个小时表膨胀会很明显。我的习惯是把snapshot.fetch.size调大默认 1024 太小大表快照会把时间拉得很长调到 8192 到 16384 之间比较合适具体看单行宽度和网络带宽。还有一个细节快照阶段如果源表有 DDL 变更比如加了一列快照读到的数据和后续增量解析的 schema 会对不上。生产环境做结构变更要么避开快照窗口要么先把同步停掉。2.3 消息格式决定了消费端好不好过Debezium 默认输出的是变更事件结构一个 value 里包含before、after、source、op四个部分。op是操作类型c是插入、u是更新、d是删除、r是快照读。source里带lsn、txId、ts_ms这些元数据做去重和对账全靠它。这个嵌套结构直接扔给业务消费方对方大概率会骂人。我的做法是在 connector 里挂一个ExtractNewRecordState单消息转换把after提出来作为消息体把op、lsn、ts_ms作为额外字段塞进去。这样消费端拿到的就是一张扁平的表记录加几个元数据字段理解成本低很多。删除事件要特别注意。展开之后after是 null如果直接序列化成 JSON消息体会是null有些消费端会当成异常。用delete.handling.moderewrite可以让删除事件也带上一份被删记录的字段值消费端就能按主键做删除。至于 tombstone 消息也就是 key 有值、value 为 null 的那种是给 Kafka 的日志压缩用的RocketMQ 没有对应机制建议直接关掉。3. 数据库侧准备参数、权限、复制槽这一段是最容易被跳过、也最容易出事的部分。很多团队拿到 connector 配置就直接启动结果报错信息全是权限不足或者参数没开来回折腾。我建议按下面的顺序一次性配好后面省很多事。3.1 必须改的 PostgreSQL 参数核心就三个改完必须重启reload不生效wal_level logical max_replication_slots 20 max_wal_senders 20wal_levellogical是前提它会让 WAL 里记录足够的逻辑信息供解码使用代价是 WAL 体积比replica模式略大。max_replication_slots要按你预期的 connector 数量留余量每个 connector 至少占一个槽再加上运维排查时临时建的槽我一般按实际数量的两倍配。max_wal_senders是并发发送 WAL 的连接数不能小于槽的数量否则槽激活不了。还有一个参数值得单独说max_slot_wal_keep_sizePostgreSQL 13 引入的。它限制单个槽最多保留多少 WAL超过之后槽会被标记为失效WAL 得以回收磁盘保住了但同步链路断了需要重建。这是个双刃剑。我的建议是生产环境一定要设设成一个能容忍的值比如磁盘容量的 20%宁可同步中断也不要整库不可写。磁盘满了 PostgreSQL 会进入只读保护那才是真正的故障。wal_keep_size也常被提到但它和槽是两套机制。wal_keep_size是给物理复制留的缓冲逻辑复制槽的保留不受它控制。指望调大wal_keep_size来防止槽堆积方向就错了。3.2 账号权限按最小化配别图省事用超级用户CDC 账号需要两类权限复制权限和读表权限。复制权限是连接级别的要求读表权限是快照阶段需要的。增量阶段其实不查表但快照要SELECT。CREATE ROLE cdc_user LOGIN REPLICATION PASSWORD 换成强密码; GRANT CONNECT ON DATABASE order_db TO cdc_user; GRANT USAGE ON SCHEMA public TO cdc_user; GRANT SELECT ON ALL TABLES IN SCHEMA public TO cdc_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO cdc_user;这里有个坑GRANT SELECT ON ALL TABLES只对当前已存在的表生效后面新建的表不会自动授权。所以ALTER DEFAULT PRIVILEGES那行必须加上而且要注意它只对执行这条命令的角色之后创建的表生效。如果表是别的角色建的得用那个角色执行或者干脆在发布流程里加一步授权。publication 的创建权限在 PostgreSQL 15 之前要求是表 owner 或者超级用户15 之后放宽了一些但添加表到 publication 仍然需要表 owner 权限。稳妥做法是让 DBA 预先建好 publicationconnector 配置里把自动创建关掉避免运行时报权限错误。3.3 手工建槽和 publication 的正确姿势我倾向于手工建不用 connector 自动创建。原因很简单自动创建的槽名和 publication 名不好统一管理出问题的时候排查成本高。-- 建 publication明确列出需要的表不要用 FOR ALL TABLES CREATE PUBLICATION cdc_order_pub FOR TABLE public.orders, public.order_items; -- 检查 SELECT * FROM pg_publication_tables WHERE pubname cdc_order_pub;为什么不用FOR ALL TABLES因为心跳表的问题。后面会讲心跳机制Debezium 需要定期往源库写一条记录来推进位点如果心跳表也在 publication 里每次心跳都会产生一条变更事件事件又触发心跳形成自我放大的循环。所以心跳表必须排除在 publication 之外用FOR TABLE明确列出业务表是最省心的做法。复制槽可以不手工建connector 首次启动时会用slot.name指定的名字创建。但如果你希望提前验证权限可以手工建一个再删掉SELECT pg_create_logical_replication_slot(cdc_order_slot, pgoutput); SELECT pg_drop_replication_slot(cdc_order_slot);槽名有硬性限制只能用小写字母、数字、下划线长度不超过 63 个字符。用大写或者带横线的名字会直接报错这个错误信息不太直观容易卡住新人。4. Connector 配置逐项落地配置这件事网上的模板一大把但大部分只告诉你这么写能跑不告诉你为什么这么写以及改哪一项会出什么事。我把关键项拆开讲。4.1 一份能跑起来的 source 配置{ name: pg-orders-cdc, connector.class: io.debezium.connector.postgresql.PostgresConnector, tasks.max: 1, database.hostname: 10.0.0.12, database.port: 5432, database.user: cdc_user, database.password: 换成实际密码, database.dbname: order_db, plugin.name: pgoutput, slot.name: cdc_order_slot, publication.name: cdc_order_pub, publication.autocreate.mode: disabled, table.include.list: public.orders,public.order_items, topic.prefix: cdc_order, snapshot.mode: initial, snapshot.fetch.size: 8192, heartbeat.interval.ms: 10000, heartbeat.action.query: INSERT INTO public.cdc_heartbeat (id, ts) VALUES (1, now()) ON CONFLICT (id) DO UPDATE SET ts now(), decimal.handling.mode: string, time.precision.mode: adaptive_time_microseconds, tombstones.on.delete: false, max.batch.size: 2048, max.queue.size: 8192, poll.interval.ms: 500, key.converter: org.apache.kafka.connect.json.JsonConverter, value.converter: org.apache.kafka.connect.json.JsonConverter, key.converter.schemas.enable: false, value.converter.schemas.enable: false, transforms: unwrap, transforms.unwrap.type: io.debezium.transforms.ExtractNewRecordState, transforms.unwrap.drop.tombstones: true, transforms.unwrap.delete.handling.mode: rewrite, transforms.unwrap.add.fields: op,source.lsn,source.ts_ms }topic.prefix这个字段在 Debezium 1.9 之前叫database.server.name升级的时候会碰到配置不识别的问题先确认版本再抄。tasks.max设成 1 是因为单个复制槽只能被一个任务消费设多了没有意义反而会互相抢连接。plugin.name用pgoutput别用wal2json或者decoderbufs。pgoutput是 PostgreSQL 内置的不需要额外装扩展运维成本最低。wal2json需要装插件而且输出格式和 Debezium 的解析逻辑耦合更紧升级时容易出兼容问题。4.2 几个决定成败的参数snapshot.mode的选择直接影响首次启动的体验。initial是全量加快照适合新接入的表schema_only只读表结构不做全量适合表里已经有数据、下游自己负责对齐的场景never是完全不做快照直接从当前位点开始适合临时排查。我一般新接一张表用initial后续重启就改成schema_only或者干脆不动因为 Debezium 会记住 offset重启不会重做快照。decimal.handling.mode这个参数必须调。默认值是precise会把 numeric 类型编码成 Base64 字符串消费端拿到手里完全看不懂。改成string之后numeric 直接以字符串形式输出精度不丢业务端解析也方便。这个坑我见过太多次消息体里一坨AMfA这样的字符新人根本不知道是什么。heartbeat.interval.ms配合heartbeat.action.query是防 WAL 堆积的关键。业务低峰期没有写入的时候复制槽的confirmed_flush_lsn不会推进数据库就不知道消费者还活着WAL 会一直留着。心跳机制让 connector 定期往源库写一条记录产生一条 WAL从而推动位点前进。心跳表要建好、授权好而且绝对不能放进 publicationCREATE TABLE public.cdc_heartbeat ( id int PRIMARY KEY, ts timestamptz NOT NULL DEFAULT now() ); INSERT INTO public.cdc_heartbeat (id) VALUES (1) ON CONFLICT DO NOTHING; GRANT INSERT, UPDATE ON public.cdc_heartbeat TO cdc_user;max.batch.size和max.queue.size是一对。batch 是每次从数据库拉多少条queue 是内存里最多攒多少条等待发送。queue 一般设成 batch 的 2 到 4 倍太小会导致拉取被阻塞太大会吃内存。2048 和 8192 是我在中等流量下比较稳的组合单表每秒几千次写入也能扛住。4.3 消息扁平化与顺序性ExtractNewRecordState这个转换器做的事就是把嵌套事件拍平。add.fields里加op、source.lsn、source.ts_ms三个字段消费端就能知道这条消息是插入还是删除、对应哪个 WAL 位点、源库时间是多少。source.lsn做去重特别有用同一个 LSN 重复投递说明发生了重试消费端直接跳过就行。顺序性是另一个必须提前想清楚的问题。RocketMQ 的顺序消息要求同一个业务主键的消息落到同一个队列靠的是发送时指定队列选择器。Connect 框架一般会按消息 key 做哈希分区所以 key 的设置很关键。Debezium 默认用表主键作为消息 key如果你在message.key.columns里改过要确认改完之后同一个业务实体的消息还能落到同一个分区。Topic 名也需要注意。Debezium 默认生成的 topic 名是topic.prefix.schema.table这种带点号的格式RocketMQ 对 Topic 名的字符校验比较严格点号、大写字母容易在创建时被拒。稳妥做法是统一转成小写加下划线通过转换器或者 connector 的 topic 映射配置处理别等运行时报错再回头改。5. 消费端落地幂等、顺序、对账消息进了 RocketMQ 只是完成了一半消费端写不好前面全白搭。CDC 消息的特点是可能重复、可能乱序如果没做顺序消费、可能突然来一波大的业务批量操作。消费逻辑必须按最坏情况设计。5.1 幂等写入的三种做法第一种是数据库唯一索引加ON CONFLICT。目标表按主键建唯一约束写入时用 upsert 语法重复消息直接覆盖。这种做法最简单适合目标端也是关系型数据库的场景。缺点是每次都是全字段覆盖如果两个消息顺序颠倒了旧值可能覆盖新值。第二种是带版本号的条件更新。消息里带上source.lsn或者源库的updated_at目标表加一列存版本号更新时加WHERE version :new_version。这样即使消息乱序旧版本也写不进去。代价是要多一列而且删除操作不好用版本号表达得单独处理。第三种是去重表。消费前先查 Redis 或者一张去重表key 用topic partition offset或者lsn处理成功后再写标记。这种做法最通用但要考虑去重表本身的可靠性和过期策略。我一般用 Redis 存 24 小时key 的过期时间按业务能容忍的最大重试窗口来定。实际项目里我通常是两种组合主键 upsert 兜底加一个短期的去重表挡住瞬时重复。纯靠一种方案总会有边角场景漏掉。5.2 顺序消费怎么保证RocketMQ 的顺序消费要用MessageListenerOrderly配合消费端线程数和队列数的匹配。队列数在创建 Topic 时就定死了后期扩容只能加队列不能减而且加队列会导致历史消息的分区分布变化顺序性在切换瞬间会受影响。所以队列数要一次性规划好按预期的消费并发峰值来定。发送端要保证同一个主键的消息进同一个队列。如果用的是 Connect 框架它会按 key 哈希你只要确保 key 是业务主键就行。如果自己写发送逻辑用MessageQueueSelector按主键取模选队列别用默认的轮询。这里有个容易被忽略的点顺序消费只保证同一个队列内有序跨队列不保证。如果你的业务要求订单创建一定在订单支付之前被处理那这两个事件必须同一个 key、同一个队列。设计消息 key 的时候要把这个关系想清楚不能简单按表名或者随机值做 key。5.3 对账脚本怎么写再好的同步链路也需要对账因为总会有极端情况网络闪断导致消息丢失、消费端异常退出导致位点回退、DDL 变更导致字段错位。对账不用做得太复杂按时间窗口统计行数和关键字段的聚合值就能覆盖 90% 的问题。-- 源端 SELECT count(*) AS cnt, sum(amount) AS total, max(updated_at) AS last_ts FROM orders WHERE updated_at 2024-06-01 AND updated_at 2024-06-02; -- 目标端同样跑一遍比对三个值行数、金额汇总、最大更新时间这三个对上了基本就没大问题。如果对不上再按主键做分片比对一次取一万个主键去目标端查找出差异记录。全表 join 太重生产环境别这么干。注意对账 SQL 要加时间范围别全表扫。源表如果有几千万行全表 count 会把数据库压得很难受。用updated_at索引做范围过滤或者按主键区间分批。6. 踩坑实录与排查速查下面这些坑有的是我踩的有的是帮别人排查时遇到的。按出现频率排序前面的基本每个项目都会碰到。6.1 复制槽不推进导致磁盘打满这是最高频的故障没有之一。表现是数据库磁盘使用率持续上涨pg_wal目录越来越大pg_replication_slots里的retained_wal一直涨。原因通常是 connector 挂了但槽还在或者下游 RocketMQ 写不进去导致消费停滞。排查顺序先看 connector 状态是不是 RUNNING再看 worker 日志有没有反复重启然后看 RocketMQ 的 Topic 是否可写、队列是否有堆积。如果 connector 已经彻底起不来而磁盘又撑不住了只能用pg_drop_replication_slot先止损。删之前一定要确认 connector 已经停止否则会报槽正在被使用。删槽之后重建snapshot.mode要设成initial重新做一次全量因为位点已经丢了。这也是为什么我建议生产环境一定要配max_slot_wal_keep_size让数据库在极端情况下自动让槽失效保住整库可用。6.2 快照阶段的锁和超时大表快照跑到一半报超时或者卡住不动八成是遇到了长事务或者锁等待。快照本身持有的是ACCESS SHARE锁不阻塞读写但会被ACCESS EXCLUSIVE锁阻塞比如有人在做ALTER TABLE。查一下pg_locks和pg_stat_activity把阻塞的会话找出来。另一种情况是快照时间太长超过了连接的空闲超时。PostgreSQL 的idle_in_transaction_session_timeout如果设得比较短快照事务会被强制断开。临时调大这个参数或者把snapshot.fetch.size调大加快速度。快照期间还要注意 WAL 的增长。快照读的是表的某个时间点但 WAL 从快照开始的那一刻就要保留大表快照跑两小时这两小时的 WAL 全得留着。数据量大的库建议在低峰期做首次快照并且提前确认磁盘余量。6.3 类型和字符集那些事numeric变成 Base64 是最常见的类型问题前面说了改decimal.handling.modestring。timestamp带时区的字段Debezium 默认会转成 UTC 的微秒数消费端要自己转回本地时区别直接当字符串用。bytea类型会输出 Base64 字符串二进制内容要在消费端解码。数组类型、枚举类型、JSONB 的 schema 变化比较频繁下游如果做了强类型映射加一个枚举值就可能解析失败。还有 TOAST 的问题。PostgreSQL 的大字段存在 TOAST 表里如果一次 UPDATE 没有改动大字段WAL 里可能只记录了未变更的标记Debezium 解析出来这个字段是缺失的。如果业务依赖这个字段的完整值要么把表的REPLICA IDENTITY设成FULL要么接受这个行为在消费端做合并。设FULL的代价是每次更新都会把整行旧值写进 WALWAL 体积会明显变大要权衡。6.4 常见问题速查表现象大概率原因排查动作处理方式connector 显示 RUNNING 但没数据表不在 publication 里或槽没激活查pg_publication_tables和pg_replication_slots.active补 publication重启 connector磁盘持续上涨槽未推进消费停滞查retained_wal和 RocketMQ 堆积恢复消费必要时删槽重建快照卡住不动长事务或锁等待查pg_stat_activity、pg_locks杀掉阻塞会话低峰重跑UPDATE 消息缺字段TOAST 未变更或 REPLICA IDENTITY 不足查表的复制标识设置设FULL或在消费端合并消息体里数字是乱码decimal.handling.mode用了默认值看消息体是否为 Base64改成stringconnector 反复重启schema history 或 offset 存储不可达看 worker 日志的存储连接错误修复存储别丢历史 Topic消费端重复处理位点提交和业务写入非原子查消费日志的重试记录加幂等用 LSN 去重Topic 创建失败名称含点号或大写字母看 RocketMQ 的命名校验报错统一转小写下划线这张表建议打印出来贴在工位上出问题的时候按行对照比漫无目的翻日志快得多。7. 长期运维调优、升级和监控链路跑起来只是开始真正考验人的是三个月后的一次数据库升级、一次大促流量高峰、一次磁盘告警。这部分讲讲怎么让这条链路活得久一点。7.1 参数调优的经验值max.batch.size和max.queue.size的比例前面说过2 到 4 倍。poll.interval.ms控制拉取频率设太小会空转浪费 CPU设太大延迟会变高500 毫秒是个折中值。如果业务对延迟敏感可以降到 100 毫秒但要观察 CPU 使用率。snapshot.fetch.size只影响快照阶段增量阶段不起作用。大表快照可以调到 16384但要注意单次拉取的数据量乘以行宽不能超过内存预期否则会 OOM。一行 10KB 宽的表16384 行就是 160MB得掂量一下。RocketMQ 侧的调优主要在发送端。批量发送能显著提升吞吐但会增加单条消息的延迟。Connect 框架一般支持配置批量大小和等待时间按业务对延迟的容忍度调。消费端的并发数不要超过队列数超了也没用线程会空等。7.2 DDL 变更和版本升级Debezium 不会把 DDL 同步到下游它只是记录一个 schema 变更事件告诉你表结构变了。下游的表结构得自己维护。所以每次源库加列、改类型都要有一个配套的下游变更流程不能只改一边。schema history 的存储特别重要。Debezium 靠它来记录表结构的历史版本丢了之后 connector 重启会解析不了 WAL。如果 schema history 存在消息队列的 Topic 里这个 Topic 的保留策略要设成永久别配过期删除。升级 connector 版本之前先备份这个 Topic出问题可以回滚。版本升级的顺序是先升 connector观察一天再升运行时。别反过来运行时不兼容会导致所有 connector 起不来。升级前在预发环境跑一遍全链路重点验证类型解析和消息格式有没有变化。7.3 监控指标盯哪几个应用层的指标很多但真正需要告警的就那么几个。第一个是复制槽的延迟字节数超过阈值就告警这个最能反映链路健康度。第二个是 connector 的状态非 RUNNING 就告警。第三个是 RocketMQ 的消费堆积按队列维度的堆积量比总量更有意义。第四个是数据库磁盘使用率配合pg_wal目录大小的趋势。Debezium 自身也暴露一些指标比如MilliSecondsBehindSource表示当前处理的事件落后源库多少毫秒NumberOfEventsFiltered表示被过滤掉的事件数。这些指标通过 JMX 或者运行时自带的指标端点获取接进现有的监控体系就行。我自己的习惯是给复制槽延迟设两级阈值超过 100MB 发预警超过 1GB 打电话。因为从 100MB 涨到 1GB 通常只需要十几分钟预警到告警之间的窗口刚好够人响应。最后分享一个我用了很久的小习惯每次新建一条同步链路先在预发环境用一张几十万行的小表跑满 24 小时观察复制槽延迟、WAL 增长、消费堆积三条曲线。曲线平稳了再上生产比直接上生产再救火省心得多。这套流程看着慢实际上省下来的排查时间远超前期投入。
企业数字化 ERP 产品动态
相关推荐
YOLO模型训练GPU选型与性能优化实战 1. 项目概述:YOLO模型训练与GPU选型实战在计算机视觉领域,YOLO(You Only Look Once)系列模型因其出色的实时检测性能而广受欢迎。但许多开发者在实际训练过程中都会遇到一个关键问题:如何选择性价比最高的GPU硬件?这个问题看似简单… · 2026/11/3 20:52:12
从零开始学AI工程:系统学习路线与项目实战方法 我从零开始做AI工程的学习与实践,这套方法帮我少走了不少弯路。写这篇内容,是想把整个过程拆开揉碎讲清楚,给那些想认真进入这个领域、但又被海量资料和复杂生态搞得不知从哪下手的读者,提供一条可复制的路线。如果你属于以下三种… · 2026/11/3 20:52:12
大模型部署优化实战:量化、推理引擎与显存管理全解析 很多做模型应用的朋友应该都有类似经历:从开源社区拉下一个大模型,满怀期待地跑起来,结果不是显存直接爆掉,就是生成一句话要等半分钟。真正要把一个大模型用到实际业务里,Model-Optimizer这类优化工具链几乎是绕不开的… · 2026/11/3 20:52:12
Vue el-switch 列表开关全部关闭的排查与规范写法 一个后台管理页的列表,四十多行数据,每行右侧一个 el-switch 控制启用状态。接口返回里 status 明明白白地 1 和 0 混着,但页面上四十几个开关齐刷刷全是灰的,一个都没亮。这种"vue el-switch 列表开关状态显示有误、全部关闭… · 2026/11/3 21:27:39
Model-Optimizer:从量化剪枝到算子融合的模型部署加速实践 0. 写在前面:Model-Optimizer 到底是什么如果你手里正好有一个训练完的深度学习模型,准备部署到线上服务或者边缘设备,那你大概率会遇到这几个问题:模型太大加载慢、推理速度跟不上业务指标、显存或者内存吃紧。这些问题的根源其实… · 2026/11/3 21:27:39
TensorFlow 生产级实践:模型训练、部署与调优全攻略 TensorFlow 这个项目我前后折腾了小半年,从最初跟着教程跑 MNIST 都费劲,到后来能把模型搬到生产环境做推理服务,中间踩过的坑、绕过的弯路,掰着手指头都数不清。趁着最近把项目重构了一遍,正好把这一路的经验做个系统… · 2026/11/3 21:27:39
EMD与PSO优化SVM在大坝变形预测中的应用 1. 大坝变形预测的技术挑战与创新方案在大坝安全监测领域,变形预测一直是个棘手问题。传统方法往往难以应对水压、温度等多因素耦合作用下的非线性变形特征。我在参与三峡大坝安全监测项目时,就曾为预测精度不足的问题头疼不已。直到尝试将EMD࿰… · 2026/11/3 21:27:39
解决OpenClaw国内下载慢的镜像配置指南 1. 为什么OpenClaw在国内下载这么慢?OpenClaw作为一款开源的AI开发工具链,其核心组件和依赖库主要托管在GitHub、Docker Hub等国际平台上。国内开发者在使用openclaw install命令时,经常会遇到以下几种典型情况:安装进度条卡在Dow… · 2026/11/3 21:27:39
MIPI CSI-2摄像头调试:D-PHY带宽、拆包与多摄避坑 简介:MIPI CSI-2 是 MIPI Alliance 制定的摄像头串行接口规范,广泛用于手机、无人机与车载电子设备中摄像头模组与主机处理器之间的高速图像数据传输。这份 PDF 为 2009 年 4 月发布的草案版本 1.01.00 r0.04,面向从事摄像头驱动、图像采集、… · 2026/11/3 21:26:38
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46