DDoS攻击流量涌入时,数据库主从同步面临的不是简单的“连不上”,而是TCP连接被海量SYN包耗尽、带宽被垃圾流量占满、以及中间路由设备因会话表溢出而丢包。在这种极端拥塞环境下,主库产生的Binlog事件根本无法可靠地传输到从库,直接导致从库延迟飙升甚至IO线程中断。解决这个问题的核心思路必须分层:先保住主库的写入能力,再为同步流量开辟一条不受干扰的通道,最后在从库侧做好断点续传与数据补偿。

把同步流量从公网“摘”出来

如果主从复制走的是公网IP,攻击流量和正常业务流量混在一起,任何基于端口的优先级调度都无济于事。最有效的办法是启用专线或内网专线进行同步。云环境下,务必把主从实例放入同一个VPC(虚拟私有云)的同一个地域,使用内网IP通信。内网流量不经过公网网关,DDoS攻击流量即便打满公网带宽,内网链路依然畅通。对于跨机房部署,必须拉一条物理专线或VPN专线,并把MySQL复制的端口(默认3306)单独绑定到这条专线网卡上。在服务器层面,可以通过绑定多网卡实现:主库的复制流量走eth1(专线网卡),业务流量走eth0(公网网卡)。在MySQL配置文件中,用replica_parallel_workers(或slave_parallel_workers)所在会话的源IP绑定到专线IP,确保Binlog dump线程只监听专线地址。

大幅提升Binlog传输的容忍度

攻击期间网络抖动剧烈,默认的slave_net_timeout(60秒)太短,一旦网络丢包超过这个时间,IO线程就会断开。需要将其调整为300秒甚至更高,给网络恢复留足时间。同时,主库的Binlog发送缓冲区也要增大。调整主库的net_write_timeout和net_retry_count,让主库在推送Binlog时不要因为短暂的写阻塞就放弃连接。从库侧的关键参数是replica_net_timeout和master_retry_count,把重试次数设到86400次,让从库像“牛皮糖”一样持续尝试重连。这些参数组合在一起,构建出一个高容错的传输层,能在丢包率高达30%的恶劣网络下仍保持连接不断。

启用半同步复制的“退而求其次”模式

默认的异步复制在攻击期间完全不可控,主库宕机后Binlog可能大量丢失。全同步复制又会因为从库响应慢而拖垮主库。半同步复制是最好的折中。安装rpl_semi_sync_master插件后,主库提交事务时会等待至少一个从库确认收到Binlog,但不会等待其执行完成。在遭受DDoS时,半同步确认包可能无法按时返回,导致主库事务提交挂起。这时需要立即在主库执行:SET GLOBAL rpl_semi_sync_master_wait_for_slave_count = 0; 并动态关闭rpl_semi_sync_master_enabled,让主库降级为异步复制,保证写入不阻塞。等攻击减弱后再重新开启。这个切换过程可以写成脚本,监控从库延迟或连接状态,一旦延迟超过阈值或IO线程断开,自动降级。

TCP层面的深度调优

DDoS攻击造成的丢包会让TCP拥塞窗口剧烈震荡,默认的cubic算法在高速丢包场景下恢复太慢。把主从两端服务器的TCP拥塞控制算法都切换为BBR,它能更激进地探测可用带宽,在丢包环境下维持较高的吞吐量。执行sysctl -w net.ipv4.tcp_congestion_control=bbr。同时,加大TCP重传超时的阈值,降低最小RTO值到200毫秒,让重传更快触发。还要特别注意tcp_syn_retries和tcp_synack_retries,在攻击期间适当减小,避免半连接队列被无效重传占满。但针对复制链路,如果走的是专线,可以单独对专线网卡设置tc qdisc规则,为3306端口流量预留最低带宽保障,使用htb队列规定创建一个高优先级类,确保Binlog数据包总是能优先发出。

从库侧的多源冗余与延迟容忍架构

单一从库在攻击期间很容易成为瓶颈,一旦这个从库延迟过大或断开,整个读扩展能力就废了。应该部署至少两个从库,其中一个专门用于灾备同步,不对外提供读服务。这个灾备从库可以开启延迟复制,故意设置SQL_Delay为3600秒,这样即使攻击期间主库出现误操作或数据损坏,还有一小时的时间窗口可以抢救。更重要的是,利用MySQL 8.0的多源复制特性,让一个从库同时从两个主库拉取Binlog。在DDoS攻击目标为主库时,可以迅速把写入切换到备用主库,从库的多源通道能平滑过渡,不需要重建复制关系。配置多源复制时,每个通道的IO线程独立,互不干扰,一条通道被攻击阻塞,另一条通道仍可正常工作。

Binlog传输的压缩与切片

大事务在攻击期间是致命的。一个UPDATE百万行的SQL会产生巨大的Binlog事件,传输过程中一旦丢包就要整个重传。在主库上必须开启Binlog事务压缩,设置binlog_transaction_compression=ON和binlog_transaction_compression_level_zstd=3,压缩率通常能达到70%以上,极大减少了需要传输的数据量。同时,调整max_binlog_size为256MB,让Binlog文件更小、更频繁地轮转,这样从库请求下一个Binlog文件时,主库不需要从头扫描一个巨大的文件。在从库侧,开启replica_compressed_protocol(或slave_compressed_protocol),让主从之间的协议层也进行压缩,进一步节省带宽。

GTID与自动定位的断点续传

基于位点的复制在连接断开后,从库重连时需要重新协商位点,这个过程容易出错,尤其在攻击期间主库已经轮转了多个Binlog文件。必须启用GTID(全局事务标识符),它给每个事务打上全局唯一的ID。从库重连后,自动对比自己已执行的GTID集合和主库的GTID集合,精确知道从哪里继续同步,不会多一个事务也不会少一个。在主库上设置gtid_mode=ON和enforce_gtid_consistency=ON,从库同样开启。当IO线程断开后,从库的SQL线程可以继续执行中继日志中已接收的事务,IO线程重连后通过GTID自动协商,实现无缝续传。这个机制在攻击导致的频繁断连场景下,价值无可替代。

监控与自动化切换脚本

不能等到从库完全落后几个小时才发现问题。需要部署秒级监控,核心指标是Seconds_Behind_Master和IO线程的Running状态。写一个守护脚本,每2秒检查一次。当发现IO线程断开或延迟超过预设阈值(如60秒),脚本立即执行降级操作:关闭半同步、检查专线连通性、如果专线也中断则触发告警并尝试通过中间代理节点中转。脚本逻辑如下:

#!/bin/bash
while true; do
  io_status=$(mysql -u monitor -p'pass' -e "SHOW REPLICA STATUS\G" 2>/dev/null | grep "Replica_IO_Running:" | awk '{print $2}')
  delay=$(mysql -u monitor -p'pass' -e "SHOW REPLICA STATUS\G" 2>/dev/null | grep "Seconds_Behind_Source:" | awk '{print $2}')
  
  if [ "$io_status" != "Yes" ] || [ "$delay" -gt 60 ]; then
    mysql -u admin -p'pass' -h master -e "SET GLOBAL rpl_semi_sync_master_enabled = OFF;"
    # 尝试通过专线IP重连
    mysql -u admin -p'pass' -e "STOP REPLICA; CHANGE REPLICATION SOURCE TO SOURCE_HOST='专线IP'; START REPLICA;"
  fi
  sleep 2
done
攻击结束后的数据一致性校验

DDoS攻击停止后,不能立即认为数据已经一致。长时间的复制中断和网络丢包可能导致部分Binlog事件在中继日志中损坏。必须使用Percona Toolkit中的pt-table-checksum工具,在主库上对核心业务表生成校验和,然后在从库上执行pt-table-sync进行比对和修复。这个过程要逐表进行,先校验写入频繁的表。如果发现数据块不一致,不要直接整表同步,而是用pt-table-sync的--replicate选项只修复不一致的行,减少对线上业务的影响。同时,检查从库的中继日志是否完整,执行SHOW REPLICA STATUS查看Last_IO_Error和Last_SQL_Error,确保没有任何残留错误。

架构层面的终极防线

如果业务允许极短时间的数据丢失,可以考虑在主库前部署消息队列,写操作先入MQ,再由消费者同时写入主库和从库。这样即使主从复制中断,从库也能通过MQ消费保持数据接近实时。对于金融级场景,则必须采用Paxos或Raft协议的多主数据库,如TiDB或CockroachDB,它们天然不依赖传统的Binlog复制,每个节点都是全量数据,单个节点被DDoS攻击不会影响整体数据同步。如果仍要使用MySQL,可以在应用层做双写,但必须处理好写冲突,通常结合分布式锁或版本号机制。这些方案成本较高,但在DDoS攻击常态化的大环境下,是值得投入的长期防线。