暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySql数据同步到Doris时候遇到的时区问题

原创 雄霸天下 2025-08-26
985

书接上文,《实现MySQL大表定时同步到Doris的Shell脚本》上回讲到MySql数据同步到Doris,使用shell脚本实现。

这次开发同事又反馈了一个棘手的问题:mysql数据源在遥远的印尼,与中国时区相差1小时。flink cdc读取binlog解析出问题了。

报错信息如下:

The MySQL server has a timezone offset (25200 seconds ahead of UTC)

which does not match the configured timezone Asia/Shanghai.

Specify the right server-time-zone to avoid inconsistencies for time-related fields.

本着遇事先不慌原则,不懂问AI。很快我们就获得了一个靠谱的答案:

这个警告信息非常关键,它明确指出了 Flink CDC 与 MySQL 服务器之间存在时区配置不一致的问题,必须解决,否则会导致时间相关字段(TIMESTAMP, DATETIME)的数据不一致甚至错误。


警告信息解读

  • The MySQL server has a timezone offset (25200 seconds ahead of UTC):
    • Flink CDC 在连接 MySQL 服务器时,探测到服务器报告的时区偏移量是 +25200 秒。
    • 25200 秒 = 7 小时。这表明 MySQL 服务器当前运行在 UTC+7 时区(例如:Asia/Jakarta - 印尼雅加达时间)。
  • which does not match the configured timezone Asia/Shanghai:
    • 你在 Flink CDC 的配置中,通过 serverTimeZone("Asia/Shanghai") 参数,告诉 Flink CDC 你认为 MySQL 服务器运行在 UTC+8 时区(中国标准时间)。
  • Specify the right server-time-zone to avoid inconsistencies...:
    • Flink CDC 发现了这个矛盾,因此发出警告,提醒你配置了错误的 serverTimeZone,这会导致时间字段处理不一致。

问题根源

Flink CDC 需要准确知道 MySQL 服务器的时区,才能正确解析 binlog 中的时间信息,尤其是 TIMESTAMP 类型:

  1. TIMESTAMP 存储:MySQL 内部将 TIMESTAMP 值转换为 UTC 时间戳存储。
  2. TIMESTAMP 检索:当从数据库读取 TIMESTAMP 时,MySQL 会根据服务器的 time_zone 设置,将 UTC 时间戳转换回本地时间。
  3. Flink CDC 解析:Flink CDC 读取 binlog 时,看到的是经过 MySQL 服务器时区转换后的“本地时间”值。它需要利用 serverTimeZone 配置,将这个“本地时间”逆向转换回正确的 UTC 时间戳或标准时间,然后传递给 Flink 流。
  4. 错误配置的后果:
  • 你配置了 serverTimeZone("Asia/Shanghai") (UTC+8),但实际服务器是 UTC+7。
  • Flink CDC 会错误地认为 binlog 中的时间是 UTC+8 的本地时间。
  • 当它进行“逆向转换”时,会减去 8 小时,而实际上应该减去 7 小时。
  • 结果:解析出的时间值会比正确值早 1 小时!例如,MySQL 中存储的 2023-10-01 10:00:00 (UTC+7) 会被 Flink 解析为 2023-10-01 09:00:00 (UTC) 或 2023-10-01 17:00:00 (UTC+8),造成严重错误。

解决方案

必须将 Flink CDC 配置中的 serverTimeZone 修改为与 MySQL 服务器实际时区一致的值。

步骤 1: 确认 MySQL 服务器的实际时区

在 MySQL 服务器上执行:

-- 查看全局时区设置
SELECT @@global.time_zone;

-- 查看当前会话时区设置
SELECT @@session.time_zone;

-- 查看系统时区(可能受操作系统影响)
SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP());


步骤 2: 修正 Flink CDC 配置

将代码中 serverTimeZone("Asia/Shanghai") 修改为正确的时区。

方法一:使用时区名称 (推荐)

MySqlSource.builder()
    .hostname("your-mysql-host")
    .port(3306)
    .databaseList("your_db")
    .tableList("your_db.your_table")
    .username("user")
    .password("password")
    // ✅ 修正:使用 MySQL 服务器实际的时区名称
    .serverTimeZone("Asia/Jakarta") // 适用于印尼 (UTC+7)
    // 如果是其他 UTC+7 时区,使用对应名称,如 "Asia/Bangkok" (泰国)
    // ... 其他配置
    .build();


方法二:使用偏移量 (UTC+7)

// ✅ 修正:使用正确的偏移量 .serverTimeZone("+07:00")


步骤 3: 重启 Flink 作业

修改配置后,需要重启 Flink CDC 作业,使新的 serverTimeZone 配置生效。


为什么不能忽略这个警告?

  • 数据错误:如上所述,会导致 TIMESTAMP 字段的时间值错误(通常偏差1小时)。
  • DATETIME 字段的间接影响:虽然 DATETIME 存储的是字面量,但如果数据是通过 NOW() 函数插入的,那么存入的 DATETIME 值本身就代表了服务器本地时间。Flink CDC 需要知道这个“本地时间”的时区含义,才能正确理解其语义。虽然 CDC 会原样同步这个字面量,但下游应用(如 Doris)可能需要知道这个时间的时区背景。
  • 未来风险:如果表结构未来增加了 TIMESTAMP 字段,问题会立刻暴露。
  • 最佳实践:保持配置与实际环境一致是稳定运行的基础。


总结

这个警告是 Flink CDC 在保护你!它发现了配置错误。不要忽略它。


立即行动:

  1. 登录 MySQL 服务器,确认其 time_zone 设置。
  2. 将 Flink CDC 代码中的 .serverTimeZone("Asia/Shanghai") 修改为 .serverTimeZone("Asia/Jakarta") 或 .serverTimeZone("+07:00")
  3. 重启 Flink 作业。


完成这些步骤后,警告将消失,时间字段的同步也将变得准确可靠。


你是不是以为到此就结束了?事情远没有这么简单,我们的开发同事看了这个方案,想了想,又要改配置,那就必须升级重启服务,那还得走一波流程。有没有躺着就能赢的?


so... 接下来,我得重新找回来刚在doris服务器删除的脚本。但是这次开发提了一个更高的要求:


30秒一次,显然之前的crontab是不行了,有同学可能要问:

❌ 为什么 crontab 不支持每秒?

crontab 的时间粒度最小是 分钟(minute)。它的格式是:

* * * * * command
│ │ │ │ │
│ │ │ │ └── 星期几 (0-7, 0 or 7 = Sunday)
│ │ │ └──── 月份 (1-12)
│ │ └────── 日期 (1-31)
│ └──────── 小时 (0-23)
└────────── 分钟 (0-59)

它没有“秒”这一列,所以无法直接实现“每30秒运行一次”。

既然此路不通,那我们就换一条路走呗:

✅ 如何实现“每30秒执行一次”?

虽然 crontab 本身不支持,但可以通过以下几种方式间接实现:


✅ 方法 1:使用 sleep 在脚本内部循环

创建一个脚本,每秒执行一次某个命令:

#!/bin/bash
# save as /path/to/your/secondly_script.sh

while true; do
    # 在这里放你每30秒要执行的命令
    echo "执行任务 $(date)" >> /tmp/secondly.log

    # 等待 30 秒
    sleep 30
done

然后在 crontab 中每分钟启动一次这个脚本(如果它还没在运行):

* * * * * /usr/bin/pgrep -f secondly_script.sh > /dev/null || /path/to/your/secondly_script.sh &

✅ 优点:简单、可靠、资源占用低。 ⚠️ 注意:确保脚本能正确处理重启、日志轮转、避免多个实例冲突。

✅ 方法 2:使用 systemd 定时器(更现代的方式)

systemd 支持秒级精度。

  1. 创建服务文件 /etc/systemd/system/my-secondly-task.service:
[Unit]
Description=My Secondly Task

[Service]
Type=oneshot
ExecStart=/path/to/your/command.sh
  1. 创建定时器文件 /etc/systemd/system/my-secondly-task.timer:
[Unit]
Description=Run my task every 30 second

[Timer]
OnUnitInactiveSec=30
AccuracySec=1s
Persistent=true
# 可选:启动时延迟多久开始第一次触发(避免系统启动风暴)
OnBootSec=10s
# 可选:系统启动后多久开始触发(从系统启动算起)
OnStartupSec=15s
[Install]
WantedBy=timers.target
  1. 启用并启动:
sudo systemctl daemon-reload
sudo systemctl enable my-secondly-task.timer sudo systemctl start my-secondly-task.timer

✅ 优点:系统级支持,日志、重启、依赖管理完善。 📌 推荐用于现代 Linux 系统。


「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论