MySQL 到 Oracle 数据同步的 NULL 处理陷阱

最近我们团队在做一个业务系统的实时数据同步项目,需要把 MySQL 中的数据同步到 Oracle。整个过程看起来挺常规的,结果却踩了一个不大不小的坑:MySQL 端明明已经 DELETE 掉的记录,在 Oracle 里却死活删不掉

表面上看一切正常——同步工具没报错,日志也没异常,业务却反馈数据对不上。查了半天,终于把问题根源挖出来了,跟 NULL 和空字符串的处理差异 有直接关系。

问题现象

业务反馈:MySQL 里某条记录通过 DELETE 操作已经删除了,但在 Oracle 对应表里,这条记录依然存在。

  • MySQL 查询:SELECT COUNT(*) FROM t WHERE receivable_record_id = ‘3007’; → 返回 0(已删除)
  • Oracle 查询:对应记录仍然存在

同步工具显示“同步成功”,没有任何错误日志。

排查过程

先常规检查了一圈:同步服务正常、网络通畅、权限没问题、日志也干净。

后来发现问题记录有个关键字段 route_id 在 Oracle 中是 NULL,而这个字段正好是分片键。

再去解析 MySQL 的 binlog,发现 DELETE 操作对应的 route_id 值是空字符串 ”:

SQL

### @52=NULL
### @53=''        <-- 这里是空字符串

而同步工具生成的 Oracle DELETE 语句是这样的:

SQL

DELETE FROM t WHERE id = ? AND route_id = ?;
-- 第二个参数绑定的是 ''

这就出问题了。

根本原因:MySQL 和 Oracle 对空字符串的处理完全不同

数据库空字符串 ” 的处理” = ” 比较结果
MySQL视为普通有效字符串TRUE
Oracle自动转换为 NULLNULL(在 WHERE 中视为 FALSE)

在 MySQL 里,route_id = ” 可以正常匹配并删除。

到了 Oracle,” 被转成 NULL 后,route_id = NULL 的比较结果是 NULL(三值逻辑),WHERE 条件不成立,DELETE 自然就不会生效。

同样的坑也出现在 INSERT 上:MySQL 允许往 NOT NULL 字段插入空字符串,而 Oracle 会把 ” 当成 NULL,导致违反 NOT NULL 约束。

-- MySQL中允许插入(非空字段也能插空字符串)
INSERT INTO t (col) VALUES ('');

-- Oracle中触发约束失败
-- 空字符串被视为NULL,违反NOT NULL约束

解决方案

我们最终采用了以下组合方案:

  1. 同步工具层面改造(主要方案)
    • 配置数据映射规则:把 MySQL 的空字符串 ” 统一转换为一个特殊标记值,比如 ‘##EMPTY##’
    • 避免直接传递空字符串给 Oracle
  2. 历史数据清洗
    • 对 Oracle 中已存在的空字符串字段进行批量更新,替换成统一标记值
  3. 业务侧预防
    • 后续开发规范中明确:分片键、关键业务字段尽量不要使用空字符串,统一用 NULL 表示“无值”
    • 在数据接入层增加校验,对空字符串插入进行告警

另外也考虑过在 Oracle 端写函数做转换,但长期来看还是在同步工具和数据规范层面解决更彻底。

Leave a Comment