ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MySQL 主从复制与 ShardingSphere 读写分离:从原理到实战全记录

MySQL 主从复制与 ShardingSphere 读写分离:从原理到实战全记录 主从复制与读写分离单库扛不住读流量时经典方案是一主多从 读写分离。写走主库读走从库从库通过 binlog 同步主库数据。本文覆盖主从复制原理 → 链路搭建 → ShardingSphere-JDBC 读写分离实战。一、整体架构┌─────────────────────────────────────────┐ │ Spring Boot App (ShardingSphere 5.5.0) │ │ ShardingSphereDriver (JDBC 代理) │ └────┬─────────────────────┬───────────────┘ │ WRITE │ READ (round-robin) ▼ ▼ ┌───────────┐ ┌───────────┐ │ master │───────▶│ slave-0 │ │ :3308 │ binlog │ :3309 │ │ server_id1│ 复制 │ server_id2│ │ log_binON│ │ read_onlyON│ └───────────┘ └───────────┘核心数据流向应用层不直连 MySQL所有连接走 ShardingSphereDriverSS 内部持有两个 Hikari 连接池master3308、slave-03309路由规则INSERT/UPDATE/DELETE →masterSELECT非事务内→slave-0SELECT事务内 / 强一致→master主从复制master binlogROW 格式→ slave relay log → SQL 线程重放二、主从复制链路搭建2.1 核心配置my.cnfmaster[mysqld] server-id 1 log_bin /var/lib/mysql/binlog binlog_format ROW sync_binlog 1 innodb_flush_log_at_trx_commit 1slave[mysqld] server-id 2 read_only ON super_read_only ON relay_log /var/lib/mysql/relay-bin log_slave_updates ON2.2 数据同步同点位备份# 1. 主库导出带 binlog 点位mysqldump-h192.168.100.128-P3308-uroot-p123\--single-transaction --master-data2--set-gtid-purgedOFF\--databasestms_ordertms_order_master_snapshot.sql# 2. 从库导入mysql-h192.168.100.128-P3309-uroot-p123tms_order_master_snapshot.sql硬性规则启动复制前必须主从两端数据完全一致否则开复制 90% 概率报 1032 / 1062 错误。2.3 建立复制链路-- 从库执行STOP SLAVE;CHANGE MASTERTOMASTER_HOSTmysql-master,MASTER_PORT3306,MASTER_USERrepl,MASTER_PASSWORDrepl_123,MASTER_LOG_FILEbinlog.000001,MASTER_LOG_POS1234;STARTSLAVE;2.4 校验复制状态SHOWSLAVESTATUS\G-- 必须同时满足-- Slave_IO_Running: Yes ← IO 线程拉 binlog 到 relay log-- Slave_SQL_Running: Yes ← SQL 线程重放 relay log-- Seconds_Behind_Master: 0 ← 0 表示无延迟Slave_IO_RunningNo→ 网络不通 / repl 账号错 / MASTER_LOG_FILE 不存在Slave_SQL_RunningNo→ 90% 是主从数据不一致就开复制1032/1062→ 重新 dump → 清从库 → 重新 import → 重新 CHANGE MASTER三、主从复制原理3.1 binlog 是主节点推送还是从节点拉取结论从节点主动拉取pull 模型。Master Slave │ │ │ ① 写 binlog │ │ (事务 commit 时才落盘) │ │ │ │ ② IO 线程来拉 │ │ ◄─────────────────────────────│ Slave IO Thread │ dump binlog events │ (主动连接 master) │ ─────────────────────────────▶│ │ │ 写入 relay log │ │ │ │ ③ SQL 线程回放 │ │ (读 relay log逐条重放)IO 线程是客户端角色主动连主库拉取不是主库推送STOP SLAVE IO_THREAD只停拉取relay log 里已有的还能被 SQL 线程继续回放STOP SLAVE SQL_THREAD只停回放IO 线程继续拉用这个模拟主从延迟3.2 为什么大事务会导致主从延迟更长瓶颈不在「拉取」而在「回放」。主从延迟的真正瓶颈是Slave SQL 线程单线程串行回放不是网络传输。因果链大事务执行如 100万行 DELETE → 事务期间 binlog 不写未 commit 不落盘 → commit 瞬间整批 binlog 一次性写入一个巨大事务块 → IO 线程拉取快纯网络传输 → SQL 线程回放慢单线程逐条回放 100万行 → 后续所有事务全排队等 → 主从延迟雪球式增长三个关键原因binlog 是事务级的100 万行 DELETE 在 ROW 格式下产生100 万条 row event。IO 线程拉完只需几秒SQL 线程要一条条回放。SQL 线程是单线程的主库 8 个线程并行写入的吞吐从库 1 个线程追天然追不平。MySQL 5.7 支持并行复制但仍有依赖关系限制。大事务堵塞整条管道SQL 线程严格按 relay log 顺序回放一个大事务没回放完后面排队的所有事务都不能动。3.3 为什么大表会导致大事务不是大表自动产生大事务而是大表让同样的操作变成了大事务小表1万行 大表5000万行 DELETE WHERE create_time 2024-01-01 → 影响 500 行 → 影响 500万行 → binlog: 500 条 row event → binlog: 500万条 row event → 事务耗时: 几毫秒 → 事务耗时: 几十秒 ↓ 一个大事务诞生了ROW 格式下binlog 大小 影响行数 × 单行大小。这正好和读写分离必须用 ROW 格式形成张力——ROW 保证主从一致性但代价是大表操作时 binlog 体积爆炸。3.4 反推分库分表为什么能缓解主从延迟分库分表 → 单表变小 → 单条 DML 影响范围限制在单个分片内 → 单个事务 binlog 体积可控 → 从库回放压力下降 → 主从延迟可控四、ShardingSphere-JDBC 读写分离4.1 pom.xml 依赖propertiesshardingsphere.version5.5.0/shardingsphere.version/propertiesdependenciesdependencygroupIdorg.apache.shardingsphere/groupIdartifactIdshardingsphere-jdbc/artifactIdversion${shardingsphere.version}/version/dependencydependencygroupIdorg.glassfish.jaxb/groupIdartifactIdjaxb-runtime/artifactIdversion2.3.9/version/dependency/dependencies版本铁律SS 必须5.5.0。5.4.1 与 Spring Boot 3.2.5 SnakeYAML 死锁5.5.1 拆分核心模块为可选插件yaml 字段改名生态不匹配。官方最新版为 5.5.22025.01但模块拆分策略已变生产实测建议锁 5.5.0。4.2 application.ymlspring:datasource:driver-class-name:org.apache.shardingsphere.driver.ShardingSphereDriverurl:jdbc:shardingsphere:classpath:shardingsphere.yamlusername:rootpassword:4.3 shardingsphere.yaml核心mode:type:Standalone# 5.5.x 必填缺了 SPI-00001dataSources:master:dataSourceClassName:com.zaxxer.hikari.HikariDataSourcedriverClassName:com.mysql.cj.jdbc.DriverjdbcUrl:jdbc:mysql://192.168.100.128:3308/tms_order?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueusername:rootpassword:123maximumPoolSize:10slave-0:dataSourceClassName:com.zaxxer.hikari.HikariDataSourcedriverClassName:com.mysql.cj.jdbc.DriverjdbcUrl:jdbc:mysql://192.168.100.128:3309/tms_order?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueusername:rootpassword:123maximumPoolSize:10rules:-!SINGLEtables:-ds-0.*# 未声明的逻辑表兜底-!READWRITE_SPLITTINGdataSources:ds-0:writeDataSourceName:masterreadDataSourceNames:-slave-0loadBalancerName:round-robinloadBalancers:round-robin:type:ROUND_ROBINprops:sql-show:true# 必开控制台打印 Actual SQL: master / slave-0sql-simple:false4.4 启动命令mvn-s.mvn/settings.xml-Uclean compile mvn-s.mvn/settings.xml-Utest-DtestDay6ReadWriteSeparationTest五、Read-Your-Writes 一致性问题从库停 SQL 线程模拟延迟后主库 INSERT 一条立刻非事务 SELECT → 走 slave-0 →查不到。方案 1事务内读 PRIMARY 策略Transactional包住的方法里 SELECT 全走主库默认transactionalReadQueryStrategyPRIMARY。方案 2Hint 强制写路由try(HintManagerhintManagerHintManager.getInstance()){hintManager.setWriteRouteOnly();// 本线程内所有查询强制走主库returnorderMapper.selectById(xx);}六、硬性规则与踩坑速查6.1 硬性规则 R1~R9#规则原因R1SS 必须5.5.05.4.1 SnakeYAML 死锁5.5.1 拆核心模块为可选插件缺 SPI 错R1-补禁止 starter必须 Driver 模式shardingsphere-jdbc5.3 官方废弃 starterR1-补JAXB 必须2.3.9javax 命名空间JDK 17 无 JAXB4.x jakarta 与 SS 不兼容R2主从 MySQL版本必须一致版本不一致 binlog 兼容性极差R3开复制前必须主→从同步数据--master-data2不一致开复制 90% 报 1032/1062R4SHOW SLAVE STATUS必须 IOYes AND SQLYes只看一个 误判R5模拟延迟用STOP SLAVE SQL_THREAD别 STOP SLAVE否则 IO 也停了R6必须sql-show:true不然看不到 SQL 路由到哪个数据源R7shardingsphere.yaml必填mode.type: Standalone5.5.x mode 节变成必填R8shardingsphere.yaml必填!SINGLE tables: [ds-0.*]5.5.x 不自动加载单表R9必须手动创建 root% mysql_native_passwordDocker 容器 root 默认只有 localhostcaching_sha2_password JDBC 握手偶发失败6.2 环境坑速查表#报错关键词根因解1starter:5.4.1 was not foundstarter 坐标 5.3 废弃换shardingsphere-jdbc:5.5.02Maven 缓存was not found… cached阿里云失败被缓存 D 盘 localRepo 不通.mvn/settings.xml 命令带-s .mvn/settings.xml -U3NoSuchMethod: Representer.init()SS 5.4.1 与 SB 3.2.5 SnakeYAML 死锁升 SS 到 5.5.04ClassNotFoundException JAXB ContextFactoryJDK 17 无 JAXB 4.x jakarta 不兼容加jaxb-runtime:2.3.95Access denied root192.168.100.1Docker root 只有 localhost caching_sha2创建 root% 用 mysql_native_password6SPI-00001 URLLoader classpath:5.5.1 拆 classpath loader 为可选插件用 5.5.07Invalid tag !READWRITE_SPLITTING5.5.1 拆 readwrite-splitting 为可选 jar用 5.5.08StorageUnit NPE/TableNotFoundException缺 mode 节 或 缺 !SINGLE 规则必须 mode.Standalone !SINGLE七、核心 SQL 速查-- 主从复制诊断 SHOWMASTERSTATUS;-- 主取 File PositionSHOWSLAVESTATUS\G;-- 从IO/SQL/Seconds_BehindSTOP SLAVE SQL_THREAD;STARTSLAVE SQL_THREAD;-- 模拟延迟/恢复-- 用户 CREATEUSERroot%IDENTIFIEDWITHmysql_native_passwordBY123;GRANTALLON*.*TOroot%WITHGRANTOPTION;CREATEUSERrepl%IDENTIFIEDBYrepl_123;GRANTREPLICATIONSLAVEON*.*TOrepl%;FLUSHPRIVILEGES;-- 配置验证 SHOWVARIABLESLIKEserver_id;SHOWVARIABLESLIKElog_bin;SHOWVARIABLESLIKEbinlog_format;-- 必须 ROWSHOWVARIABLESLIKEread_only;SHOWVARIABLESLIKEsuper_read_only;-- 数据一致性 SELECTCOUNT(*),MAX(id),MIN(id)FROMtms_order.orders;-- 主/从都要对齐核心速查表主从复制 3 步模型Master 写 binlogcommit 时落盘 → Slave IO Thread 主动拉取 → 写入 relay log → Slave SQL Thread 逐条串行回放主从延迟因果链表大 → 单条 DML 影响行数多 → 单个事务 binlog 巨大ROW 格式 → 从库 SQL 线程单线程回放慢拉取不是瓶颈回放才是 → 后续事务排队堆积 → 主从延迟雪球式增长生产实践要点大表删除/更新必须分批DELETE ... LIMIT 1000 循环每批一个事务DDL 用 pt-online-schema-change / gh-ost避免 ALTER TABLE 产生大事务监控主从延迟SHOW SLAVE STATUS中Seconds_Behind_MasterMySQL 5.7 开启并行复制slave_parallel_workersslave_parallel_typeLOGICAL_CLOCKROW 格式不可妥协一致性优先于 binlog 体积通过控制单事务影响行数管理体积
返回列表