Windows 上 Flink 实时同步 MySQL 数据:从安装到 CDC 链路跑通
发布时间:2026/10/27 20:48:17 锦皓数字建站

Windows 上把 Flink 跑起来再把 MySQL 的数据实时同步出去这件事听起来像是装个软件、配个连接就完事实际动手的人都知道坑主要集中在三块环境变量与依赖包版本对不上、MySQL 的 binlog 权限和格式没配对、以及 Flink 的 connector jar 放错位置导致任务提交时报类找不到。这篇内容我按从零到跑通一条同步链路的顺序拆开讲把每一步为什么这么做、哪里容易翻车、翻车了怎么查都摊开说清楚。适合两类人看一是在 Windows 本机上想快速验证 Flink 同步方案、不想折腾 Linux 虚拟机的开发者二是已经会写 SQL 但对 Flink 运行机制不熟、需要一份能照着抄的落地清单的同学。全文围绕 Windows 环境下的 Flink 安装、MySQL 源端准备、CDC 同步任务编写与提交、以及常见故障排查展开中间会穿插我自己踩过的坑和参数取舍逻辑。1. 为什么在 Windows 上折腾 Flink 值得先想清楚1.1 本地验证场景和它的真实边界很多人第一反应是Flink 不是跑在 Linux 集群上的吗Windows 装它干嘛。这个疑问合理但要分场景看。Flink 的生产部署确实以 Linux 为主可它的运行时是 JVM 应用Windows 上跑一个单机 Standalone 集群做功能验证、SQL 调试、connector 联调完全够用。我自己的习惯是任何一条同步链路先在 Windows 本机用最小数据集跑通端到端确认源端配置、字段映射、序列化格式都没问题再往测试集群上搬。这样做的价值在于排错成本极低——本地日志直接看端口直接查改配置重启只要几秒。但边界必须说清楚。Windows 单机模式不适合做这几件事一是高并发压测本地磁盘和网络 IO 撑不住真实吞吐二是依赖 Kerberos 认证的源端连接Windows 下的认证配置链路比 Linux 复杂得多三是长时间运行的稳定性验证Windows 的进程管理和内存回收行为和 Linux 有差异跑个几小时可能没问题跑几天就不好说了。所以定位要准Windows 上的 Flink 是开发调试环境不是生产环境。想明白这一点后面很多要不要装集群、要不要配高可用的纠结就可以直接跳过。还有一个现实因素不少同学手头只有一台 Windows 办公机装虚拟机又嫌重。这种情况下单机 Flink 加本地 MySQL是验证 CDC 同步方案性价比最高的组合。我见过有人为了跑一个 demo 专门开云主机其实没必要本地十分钟就能搭起来的东西。1.2 整条链路到底由哪几个部件组成在动手之前先把链路画在脑子里。一条典型的 MySQL 到目标端同步链路涉及四个角色MySQL 源库要开启 binlog提供变更日志、Flink 集群JobManager 负责调度、TaskManager 负责实际计算、CDC ConnectorFlink 的 MySQL CDC 连接器负责读取 binlog 并转成 Flink 的数据流、目标端可以是另一个 MySQL、可以是 Kafka、也可以直接打到文件或控制台。这四个角色里最容易出问题的是第二和第三之间的衔接——也就是 connector jar 和 Flink 版本的匹配。我用一个生活化的类比帮你记住这个结构MySQL 的 binlog 像一本流水账CDC Connector 像抄账的人Flink 像处理账目的会计目标端像最终存档的柜子。抄账的人必须看得懂这本账的格式binlog 格式要设成 ROW会计必须认识抄账的人jar 版本匹配存档柜子必须能接收会计整理好的格式sink 端的序列化要对应。任何一环对不上链路就断。下面这张表把四个角色的关键配置项列出来后面每个章节会逐个展开角色关键配置/依赖常见失败表现MySQL 源库binlog_formatROW、server-id、复制权限账号任务启动报无权读取 binlogFlink 集群JDK 版本、FLINK_HOME、集群启动脚本启动脚本闪退、端口占用CDC Connectorflink-sql-connector-mysql-cdc 的 jar 版本提交任务报 ClassNotFound目标端sink 表结构、主键定义、连接参数写入报主键冲突或字段类型不匹配把这张表存下来后面每配完一项就回来打个勾能省掉大量配了一半忘了哪项的返工。2. Windows 下的 Flink 安装版本选择和目录规划是第一步2.1 JDK 与 Flink 版本的匹配逻辑Flink 对 JDK 版本有明确要求这一步选错后面全是白费功夫。以目前主流的 Flink 1.17 到 1.19 这几个版本为例官方推荐 JDK 11JDK 8 在部分版本上仍可用但已逐步退出支持JDK 17 在较新版本上支持较好但部分 connector 还没跟上。我的建议很直接Flink 1.18 搭配 JDK 11这是当前兼容性最稳的组合CDC connector 生态也跟得最全。为什么强调这个组合因为 CDC connector 的版本是跟着 Flink 版本走的。你装 Flink 1.18就得用对应 1.18 编译的 connector如果 JDK 用了 17某些老版本 connector 在反射调用时会报模块访问异常排查起来非常费劲。我踩过一次JDK 17 加 Flink 1.17 加一个稍旧的 connector任务提交成功但运行几秒就抛InaccessibleObjectException日志里一堆模块化相关的报错最后换回 JDK 11 直接就好了。这种问题不值得花时间深究选对版本组合绕过去才是正解。安装 JDK 时有个细节JAVA_HOME 要指向 JDK 根目录不是 bin 目录而且路径里最好不要有空格和中文。Windows 上默认装在C:\Program Files\Java\...这种带空格的路径下Flink 的启动脚本在某些情况下对带空格路径处理不干净会报找不到主类。我的做法是统一装到C:\dev\jdk11这种短路径下省心。2.2 Flink 解压、环境变量与启动脚本的坑Flink 的安装包从官网下载二进制版解压到比如C:\dev\flink-1.18.1。解压完先别急着启动按顺序做三件事。第一配环境变量。新建FLINK_HOME指向解压目录然后把%FLINK_HOME%\bin追加到Path里。这一步的目的是让你在任何目录下都能直接敲flink命令不用每次 cd 到 bin 目录。第二检查conf\flink-conf.yaml里的关键项。单机验证场景下重点看两个jobmanager.rpc.address保持 localhosttaskmanager.numberOfTaskSlots给个 4 左右本地机器一般够用。内存配置jobmanager.memory.process.size和taskmanager.memory.process.size默认值在本地可能偏大机器内存 16G 以下的话建议各调到 1024m 和 2048m避免启动时直接 OOM。第三启动集群。Windows 下用bin\start-cluster.bat。这里有个高频坑双击 bat 文件窗口一闪而过。原因通常是 JAVA_HOME 没配好或者路径带空格。正确的排查方式是打开 cmd 窗口cd 到 bin 目录手动执行start-cluster.bat这样报错信息会留在窗口里。如果看到The system cannot find the path specified八成是 JAVA_HOME 的问题如果看到 Java 版本相关的报错就是 JDK 版本不匹配。启动成功后浏览器访问http://localhost:8081能看到 Flink Web UIJobManager 和 TaskManager 各一个实例这就说明集群起来了。Web UI 是你后面提交任务、看日志、查背压的主要入口务必确认能正常打开。提示Windows 防火墙有时会拦截 8081 端口如果本机浏览器都打不开先检查防火墙规则或者临时关闭防火墙验证一下。2.3 目录规划把 jar、作业、数据分开放这一步很多人忽略但直接影响后续维护效率。我的目录习惯是这样C:\dev\flink-1.18.1\ # Flink 安装目录不动 C:\dev\flink-work\jars\ # 存放 connector 等外部 jar C:\dev\flink-work\sql\ # 存放 SQL 作业文件 C:\dev\flink-work\logs\ # 存放自己导出的排查日志为什么要把 connector jar 单独放一个目录因为 Flink 的 jar 加载有两个位置lib目录下的 jar 在集群启动时就加载属于全局类路径通过-j参数或 SQL Client 的ADD JAR加载的 jar 是会话级的。把 connector 放lib下虽然省事但一旦版本换错整个集群启动都可能受影响放独立目录、用会话级加载出问题只影响当前会话隔离性好。这个取舍在本地开发环境尤其值得因为你会频繁换 connector 版本做对比测试。3. MySQL 源端准备binlog 配置和账号权限决定成败3.1 binlog 格式、server-id 与保留策略CDC 同步的本质是读 MySQL 的 binlog所以源端配置是整条链路的地基。三个参数必须确认log_bin必须为 ON这是前提没开 binlog 一切免谈。binlog_format必须是 ROW。STATEMENT 和 MIXED 格式下CDC connector 拿不到行级变更的前后镜像同步会失败或数据不准。server-id必须显式设置且唯一。CDC connector 在读取 binlog 时会以从库身份连接没有 server-id 直接连不上。配置方式是在 MySQL 的配置文件Windows 下通常是my.ini里加[mysqld] log_bin mysql-bin binlog_format ROW server-id 1 binlog_row_image FULLbinlog_row_image FULL这个参数容易被漏掉。它控制行镜像记录完整字段还是只记录变更字段CDC 同步需要完整镜像才能正确构造 INSERT/UPDATE/DELETE 事件设成 MINIMAL 会导致部分字段缺失。改完配置必须重启 MySQL 服务然后用SHOW VARIABLES LIKE binlog_format;之类的语句逐项验证别凭记忆认为改生效了。还有一个隐藏坑binlog 保留时间。MySQL 默认expire_logs_days可能是 0永不删除或较短的值。如果同步任务停了几天再启动而 binlog 已经被清理任务会报找不到起始位点。本地验证场景影响不大但如果要做长时间测试建议把保留时间调长比如expire_logs_days 7。3.2 同步账号的最小权限配置给 CDC 用的账号不需要 root按最小权限原则配就行。需要两类权限一是对要同步的库表的 SELECT 权限用于读取表结构做快照二是 REPLICATION SLAVE 和 REPLICATION CLIENT 权限用于读 binlog 和查询位点。CREATE USER cdc_user% IDENTIFIED BY YourPassword123; GRANT SELECT, RELOAD, SHOW DATABASES, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO cdc_user%; FLUSH PRIVILEGES;这里RELOAD和SHOW DATABASES权限是很多人会漏的。RELOAD 用于执行 flush 相关操作SHOW DATABASES 用于枚举库信息某些 connector 版本在初始化阶段会用到。少了它们任务可能启动时报权限不足报错信息还不一定直白。我建议本地环境直接按上面这套给全省得反复试。另外注意 MySQL 8.x 的认证插件问题。MySQL 8 默认用caching_sha2_password部分老版本 JDBC 驱动连不上。解决办法有两个要么把账号改成mysql_native_password插件要么确保你用的 connector 内置的驱动版本足够新。本地验证我倾向后者因为改插件会影响其他应用但如果遇到连接报认证失败先试前者快速排除。3.3 准备测试表和初始数据验证链路需要一张有主键的表主键是 CDC 同步的关键——没有主键的表UPDATE 和 DELETE 事件无法准确定位行connector 会报错或行为异常。建表语句示例CREATE DATABASE IF NOT EXISTS demo_db; USE demo_db; CREATE TABLE user_info ( id BIGINT PRIMARY KEY, user_name VARCHAR(64), age INT, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); INSERT INTO user_info (id, user_name, age) VALUES (1, alpha, 20), (2, beta, 25);字段类型上有个经验尽量避免用无符号大整数、JSON、空间类型做首批验证这些类型在 CDC 到 Flink 的类型映射里容易出兼容问题。先用 BIGINT、VARCHAR、INT、TIMESTAMP 这类基础类型跑通再逐步加复杂类型排错范围可控。4. CDC Connector 的获取与加载版本对齐是核心4.1 connector jar 从哪来、怎么选版本Flink 的 MySQL CDC connector 不在安装包里需要单独下载。获取渠道是 Maven 中央仓库搜索flink-sql-connector-mysql-cdc选和你的 Flink 版本对应的版本号。这里有个命名细节带sql的包是胖包已经把依赖打进去了适合 SQL Client 直接用不带sql的是瘦包需要自己解决依赖。本地验证一律选带 sql 的胖包省掉依赖冲突的麻烦。版本对应关系大致是这样Flink 1.18 对应 connector 3.0.x 系列Flink 1.17 对应 2.4.x 系列。这个对应不是绝对的但跨大版本混用基本必出问题。我见过有人拿 2.4 的 connector 往 1.18 上怼提交任务时报NoSuchMethodError这种错误看着吓人本质就是版本不匹配。下载完把 jar 放到前面规划的C:\dev\flink-work\jars\目录下。文件名类似flink-sql-connector-mysql-cdc-3.0.1.jar。4.2 用 SQL Client 加载 jar 的正确姿势Flink 自带 SQL Client启动方式是bin\sql-client.bat。启动后在会话里执行ADD JAR C:/dev/flink-work/jars/flink-sql-connector-mysql-cdc-3.0.1.jar;注意路径用正斜杠Windows 的反斜杠在 SQL 字符串里会被当转义符容易出问题。加载成功后可以用SHOW JARS;确认。这里有个高频坑ADD JAR 是会话级的SQL Client 一退出就失效。所以每次重新打开 SQL Client 都要重新 ADD。如果你打算把作业写成 SQL 文件用-f参数批量执行记得在文件开头也写上 ADD JAR 语句。我一开始不知道这点写完 SQL 文件直接执行报 connector 找不到查了半天才发现是 jar 没加载。另一个坑是路径中的中文和空格。jar 路径里如果有中文目录某些版本的 SQL Client 解析会失败。这也是前面强调用短英文路径的原因之一。4.3 提交方式的选择SQL Client 还是 flink run两种方式各有适用场景。SQL Client 适合交互式调试边写边看结果改一句执行一句flink run适合把作业打成 jar 提交或者用-f执行 SQL 文件做批处理式提交。本地验证阶段我建议先用 SQL Client 把逻辑调通确认无误后再固化成 SQL 文件用flink run -f提交这样作业在集群里独立运行关掉客户端也不影响。用flink run提交 SQL 文件的命令大致是flink run -c org.apache.flink.table.client.SqlClient %FLINK_HOME%\lib\flink-sql-client-*.jar -f C:\dev\flink-work\sql\sync_job.sql不过更稳妥的做法是用sql-client.bat -f 文件路径的方式参数更简单。具体用哪种看你手头 Flink 版本自带的脚本支持情况实测一下就知道。5. 编写同步 SQL从源表到目标表的完整映射5.1 源表的 DDL 定义与关键参数在 Flink SQL 里源表通过CREATE TABLE ... WITH (...)定义connector 类型指定为 mysql-cdc。一个可用的源表定义CREATE TABLE mysql_source ( id BIGINT, user_name STRING, age INT, update_time TIMESTAMP(3), PRIMARY KEY (id) NOT ENFORCED ) WITH ( connector mysql-cdc, hostname localhost, port 3306, username cdc_user, password YourPassword123, database-name demo_db, table-name user_info, server-id 5401-5404, scan.startup.mode initial );几个参数值得展开说。server-id这里给的是一个范围CDC connector 内部会为每个并行度分配一个 id范围要够大否则并行度调高时会报 id 冲突。scan.startup.mode设为initial表示先做全量快照再接着读增量这是最常用的模式如果只想读增量设成latest-offset。PRIMARY KEY ... NOT ENFORCED是 Flink 的语法要求声明主键但不做强制校验CDC 场景必须这么写。类型映射上MySQL 的 TIMESTAMP 对应 Flink 的TIMESTAMP(3)注意精度声明。VARCHAR 对应 STRING。如果源表有 DECIMAL 字段要写成DECIMAL(p, s)并和源端精度一致否则可能出现精度截断。5.2 目标表的定义与写入模式选择目标端如果还是 MySQL用 JDBC connector。定义示例CREATE TABLE mysql_sink ( id BIGINT, user_name STRING, age INT, update_time TIMESTAMP(3), PRIMARY KEY (id) NOT ENFORCED ) WITH ( connector jdbc, url jdbc:mysql://localhost:3306/demo_db_sink, table-name user_info_sink, username root, password YourPassword123 );写入模式由主键决定定义了主键Flink 会按 upsert 语义写入INSERT 和 UPDATE 都转成 upsertDELETE 转成 delete没定义主键就是 append 模式只能处理 INSERT。CDC 同步场景几乎都要 upsert 语义所以目标表的主键定义不能少。这里有个容易忽略的点目标表在 MySQL 里要真实存在且结构要和 Flink 定义的一致。Flink 的 JDBC connector 默认不会自动建表表不存在会直接报错。我建议先在目标库手动建好表字段类型和源表对齐避免运行到一半才发现字段对不上。5.3 同步语句与一致性语义核心同步语句就是一句 INSERT INTO ... SELECTINSERT INTO mysql_sink SELECT id, user_name, age, update_time FROM mysql_source;看着简单但背后的语义要理解。CDC 源表产出的是变更流I 插入、-U 更新前、U 更新后、-D 删除Flink 在流模式下把这些变更按 upsert 语义写到目标端。默认情况下Flink 的 checkpoint 机制保证 exactly-once 语义——但前提是 checkpoint 开启且目标端支持事务。JDBC connector 在 upsert 模式下配合 checkpoint 可以做到至少一次要做到精确一次需要目标端和事务配置配合。本地验证阶段checkpoint 默认可能没开这时候语义是 at-least-once重复数据可能出现。验证功能没问题但如果要验证一致性得在flink-conf.yaml里配 checkpoint 间隔比如execution.checkpointing.interval: 10s。这个细节很多人验证时忽略结果发现目标端有重复行以为是同步逻辑错了其实是 checkpoint 没开导致的重复投递。6. 跑通之后验证、观察与常见故障定位6.1 端到端验证的完整动作序列任务提交后验证要按顺序做别跳步。第一步看 Flink Web UI 的 Running Jobs 里任务是否处于 RUNNING 状态如果一直重启或失败先看日志。第二步在源库执行一条 INSERT等几秒去目标库查这条记录是否出现。第三步执行 UPDATE确认目标库对应行被更新而不是新增。第四步执行 DELETE确认目标库对应行被删除。这四步覆盖了增删改三种事件全过才算链路通。如果第一步就卡住重点看 TaskManager 日志里的异常栈。常见的是连接失败账号密码或网络问题、权限不足binlog 读取权限、或者表结构不匹配。日志位置在 Flink 安装目录的log子目录下TaskManager 的日志文件名带 taskmanager 字样。6.2 典型报错的排查链路我把本地验证时遇到过的几类报错整理成表方便对照报错关键词根因处理方式ClassNotFoundException: MysqlSourceconnector jar 没加载或版本不对确认 ADD JAR 路径正确、版本匹配Access denied for replication账号缺 REPLICATION 权限补授 REPLICATION SLAVE/CLIENTThe connector requires binlog_formatROWbinlog 格式不对改 my.ini 并重启 MySQLNo valid server-id源表 DDL 没配 server-id 或范围不够补 server-id 参数并扩大范围Table not found目标表不存在先在目标库建表排查顺序建议从下往上先确认目标表存在再确认源端 binlog 配置再确认账号权限最后确认 jar 加载。这个顺序的逻辑是从静态配置到动态加载静态问题先排除动态问题后查效率最高。6.3 本地长时间运行的资源观察任务跑起来后别急着关。让它跑个十几分钟观察 Web UI 里的几个指标TaskManager 的内存使用是否持续上涨内存泄漏的信号、checkpoint 是否按间隔成功完成一致性保障、背压指标是否出现红色处理能力不足。本地机器资源有限如果发现内存吃紧调小 TaskManager 内存或者降低并行度。还有一个实操心得Windows 下 Flink 进程不会随 cmd 窗口关闭而自动退出有时候你以为关了其实 java 进程还在后台跑端口被占着下次启动就报端口冲突。排查方式是任务管理器里找 java.exe或者用netstat -ano | findstr 8081查端口占用。养成习惯验证完手动执行bin\stop-cluster.bat确认进程退出再关窗口。7. 从本地验证到可复用方案的几点经验把这条链路跑通之后我总结出几个让后续工作省力的做法。第一把源表、目标表、同步语句固化成三个 SQL 文件源表和目标表的 DDL 一个文件同步语句一个文件ADD JAR 和 SET 参数一个文件用sql-client.bat -f按顺序执行。这样换一台机器或者换一个库只改连接参数逻辑不用重写。第二连接参数抽出来做成变量Flink SQL Client 支持SET定义变量再在 DDL 里引用虽然语法上稍显啰嗦但改一处生效多处比满文件搜替换靠谱。第三关于 connector 版本管理我的建议是建一个版本对照表存在项目目录里记录Flink 版本 - connector 版本 - JDK 版本三者的组合每次验证通过就记一笔。这个习惯在需要同时维护多个环境时特别有用因为不同环境版本不一致是常态有对照表就不会凭记忆瞎猜。第四本地验证通过不等于生产可用。搬到真实环境前至少要补三件事checkpoint 和重启策略的配置、目标端写入的批量参数调优JDBC connector 的 batch size 和 flush interval、以及监控告警的接入。这三件事在本地验证阶段可以先不做但心里要有数别把 demo 配置直接当生产配置用。最后分享一个排查思路上的小技巧当链路不通时先别急着改 Flink 的配置回到最原始的问题——用 MySQL 客户端手动确认源库的 binlog 在不在、账号能不能连、目标表在不在。这三个手动确认能排除掉大半问题剩下的才轮到 Flink 侧。我踩过的坑里至少一半最后发现是 MySQL 侧的小配置没对而不是 Flink 本身的问题。把排查顺序理顺比记住一堆参数更有价值。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。