第八周
- MySQL的主从复制架构中,主要包含哪三个核心线程?它们各自的作用是什么?如果从库的SQL线程意外停止了,会导致什么现象?
| 线程名称 (Thread) | 所在位置 (Location) | 核心作用 (Core Function) |
|---|---|---|
| Binary Log Dump 线程 | 主库 (Master) | 负责将二进制日志 (binlog) 的内容发送给从库。当从库连接时,主库会为它创建这个线程来读取并发送日志事件。 |
| I/O 线程 (I/O Thread) | 从库 (Slave) | 负责连接主库,接收主库发来的binlog内容,并将其写入到本地的中继日志 (relay log) 中。 |
| SQL 线程 (SQL Thread) | 从库 (Slave) | 负责读取中继日志 (relay log) 中的内容,将其解析并执行(重放),从而让从库的数据与主库保持一致。 |
如果从库的SQL线程意外停止了,最直接的现象就是主从数据同步中断。具体来说,会发生以下几点:
数据同步停滞,从库数据落后:SQL线程是负责“执行”同步数据的线程。它停止后,从库就不再应用来自主库的更新,导致从库的数据会落后于主库。此时,如果应用程序读取从库,可能会读到过时的数据。
I/O线程通常不受影响,继续接收日志:在大多数情况下,SQL线程的停止不会影响I/O线程的工作。I/O线程仍会继续从主库拉取最新的binlog并写入中继日志。这种设计是为了让从库在故障恢复后能更快地追上主库。可以通过命令SHOW SLAVE STATUS\G观察到Slave_IO_Running为Yes,而Slave_SQL_Running为No的状态。
需要手动介入恢复:SQL线程停止通常是由于中继日志损坏、SQL语句执行错误(如试图插入重复主键)等原因引起的。这需要管理员排查具体原因,然后通过START SLAVE SQL_THREAD;命令手动重启线程来恢复同步。
- 在MySQL的主从复制中,存在哪两个关键的日志文件?它们分别存储在哪个节点,有什么作用?在MySQL 8.0中,原来存储同步信息的
master.info和relay-log.info文件去哪了?
| 名称 (Name) | 核心作用 (Core Function) | 存储位置与形式 (MySQL 8.0) |
| 主库信息日志 (记录I/O线程进度) |
记录从库的I/O线程连接主库所需的配置(如主库地址、端口、复制账号)以及当前已从主库读取到的二进制日志(binlog)文件名和位置。这是I/O线程拉取数据的“进度条”。 | 默认存储在 mysql 系统库的 slave_master_info 表中,这是一张使用InnoDB存储引擎的事务表。 |
| 中继日志信息日志 (记录SQL线程进度) |
记录从库的SQL线程当前已应用中继日志(relay log)到哪个位置了。这是SQL线程重放数据的“进度条”,也是崩溃恢复时的关键依据。 | 默认存储在 mysql 系统库的 slave_relay_log_info 表中,同样是一张InnoDB事务表。 |
master.info和relay-log.info
MySQL 8.0中,它们已经被彻底弃用了 MySQL 8.0 将这些信息存入了 InnoDB 系统表中 SHOW SLAVE STATUS\G;
- MySQL的二进制日志(binlog)主要用于记录什么类型的操作?在进行逻辑备份导出全量数据时,为什么通常需要配合刷新(flush)二进制日志?
binlog主要记录什么类型的操作?
二进制日志(binlog)记录的是对数据库进行“变更”的所有操作,但不包括查询(SELECT/SHOW)这类读操作。
具体来说,它记录的是逻辑层面的SQL语句或行变更事件。根据你设置的日志格式(binlog_format),它的记录方式有两种:
STATEMENT(语句模式):记录实际执行的SQL语句。比如 UPDATE users SET age=18 WHERE name='张三'。
ROW(行模式,MySQL 8.0默认推荐):记录每一行数据被修改前后的具体值。比如“将users表中id=5这行的age从17改为18”。
MIXED(混合模式):MySQL会根据操作类型自动切换,默认用STATEMENT,在可能引发数据不一致时(如使用UUID()函数)自动改用ROW。
需要特别注意:binlog 它也记它也记 CREATE DATABASE 或 DROP TABLE 这类DDL(数据定义语言)操作,所有隐式或显式的提交操作(如COMMIT、BEGIN)也会被记录,以确保事务的完整性。
- 逻辑备份时,为什么通常需要配合刷新(flush)binlog?
在执行逻辑备份(如使用 mysqldump)时,DBA(数据库管理员)常常会加上 --flush-logs 参数,或者手动执行 FLUSH LOGS; 命令。这背后有两个核心目的:
目的1:
备份前的状态:在全量备份过程中,数据库仍在不断写入新数据,binlog也在持续变大。
执行 FLUSH LOGS:这个操作会关闭当前的binlog文件(如 binlog.000010),并创建一个全新的文件(如 binlog.000011) 开始写入。
结果:这意味着,从 binlog.000011 开始记录的所有变更,都是“全量备份完成之后”发生的新操作。
这样需要恢复数据时,逻辑就非常清晰:
先完整导入你的全量备份文件(恢复到备份开始那一刻的状态)。
然后,只需按顺序重放新生成的binlog文件(从 binlog.000011 开始),就能将数据恢复到故障前的最后一秒,而无需处理旧binlog里的庞大数据。
目的2:释放磁盘空间(辅助作用)
在备份脚本中,配合 --expire-logs-days 或 PURGE BINARY LOGS 使用,可以在刷新后安全地清理备份时间点之前的旧binlog文件。因为有了新的全量备份,旧的binlog通常可以归档或删除,从而腾出磁盘空间。
- InnoDB支持的事务隔离级别有哪些?分别能够解决哪些数据并发问题?什么是幻读,哪种隔离级别可以有效解决幻读问题?
| 隔离级别 | 是否允许脏读 | 是否允许不可重复读 | 是否允许幻读 | 加锁方式 |
|---|---|---|---|---|
| READ UNCOMMITTED(读未提交) | ✅ 是 | ✅ 是 | ✅ 是 | 几乎不加锁,性能最高但最不安全 |
| READ COMMITTED(读已提交) | ❌ 否 | ✅ 是 | ✅ 是 | 行级锁,但只在执行时加锁,执行完立即释放 |
| REPEATABLE READ(可重复读,默认) | ❌ 否 | ❌ 否 | ❌ 部分解决(通过间隙锁) | 行级锁 + 间隙锁(Gap Lock),直到事务结束才释放 |
| SERIALIZABLE(串行化) | ❌ 否 | ❌ 否 | ❌ 完全解决(通过表级锁) | 对SELECT语句也加读锁(共享锁),并发最低 |
脏读(Dirty Read):一个事务读到了另一个未提交事务修改过的数据。如果那个事务最终回滚,读到的数据就是“脏”的。
不可重复读(Non-Repeatable Read):同一个事务内,多次查询同一行数据,结果却不同。这是因为在两次查询之间,另一个已提交的事务修改了这行数据。
幻读(Phantom Read):同一个事务内,多次查询满足某个条件的数据行集合,结果集的行数不同。这是因为在两次查询之间,另一个已提交的事务插入或删除了符合该条件的行。
幻读的本质是数据集合的“新增”或“删除”导致的行数变化。
在SQL标准中,只有 SERIALIZABLE 能保证完全解决幻读。 但代价极大(串行执行,几乎无并发)。
在MySQL InnoDB中,REPEATABLE READ(默认级别)也可以有效解决幻读,它通过一种特殊的机制实现:
Next-Key Locks(临键锁):它实际上是行锁(Record Lock)+ 间隙锁(Gap Lock) 的组合。
当你在 REPEATABLE READ 下执行范围查询时,InnoDB不仅会锁定查询命中的所有行,还会锁定这些行之间的“间隙”,防止其他事务在这些间隙中插入新数据。
- MVCC(多版本并发控制)在并发控制中的主要作用是什么?MVCC能完全替代锁机制吗?它通常用来解决什么冲突?
MVCC(多版本并发控制)的核心作用可以用一句话概括:让“读”操作不阻塞“写”操作,同时“写”操作也不阻塞“读”操作,从而大幅提升数据库的并发性能。
MVCC能完全替代锁机制吗?
不能。MVCC和锁是相辅相成的,MVCC无法完全替代锁。
MVCC主要解决了“读写冲突”
MVCC负责“无锁读”以提升性能,锁负责“安全写”和“当前读”以保证数据的绝对一致。
- 生产环境中,发现主从复制出现了严重延迟,监控显示
Seconds_Behind_Master数值非常大,且持续上升。请列出排查此主从同步延迟问题的核心思路和可能的原因。
Seconds_Behind_Master
从库SQL线程执行最后一个事件的时间戳,与主库写入该事件的时间戳之间的差值。
确认延迟发生在哪个环节:
| 指标 | 含义 | 如何判断瓶颈 |
|---|---|---|
| Master_Log_File 与 Read_Master_Log_Pos | IO线程已从主库读取到的binlog位置 | 如果这两个值长时间不变化,说明IO线程可能卡住或网络断了。 |
| Relay_Master_Log_File 与 Exec_Master_Log_Pos | SQL线程已应用到从库的binlog位置 | 如果这两个值远落后于上面的 Read_Master_Log_Pos,说明SQL线程应用速度跟不上IO线程接收速度,即SQL线程延迟。 |
情况A:IO线程延迟(网络/接收慢)
网络带宽打满或丢包率高:用 iperf 或 ping -f 测试主从间的网络质量。
主库binlog生成速度极快:如果主库在批量灌数据,且 binlog_format=ROW,日志量会急剧膨胀,IO线程可能来不及拉取。此时可考虑临时调整 slave_net_timeout 或升级网络。
从库磁盘I/O性能差:IO线程要将binlog写入本地的 relay log 文件,如果磁盘写延迟高(如使用机械盘且RAID配置不当),也会拖慢IO。
情况B:SQL线程延迟(应用日志慢)—— 这是生产中最常见的
从库硬件配置低于主库:这是最常见的根因。主库执行SQL很快,但从库CPU核数少、内存小,重放自然慢。需要升级从库规格。
从库执行了大事务:
例如主库一条 UPDATE 影响了数百万行,产生数十GB的binlog。SQL线程在从库上需要单线程重放这些操作(即使开启了并行复制,也存在并行度限制),耗时极长。
排查:在 SHOW SLAVE STATUS 中看 Last_Errno 和 Last_Error,或者观察 SHOW PROCESSLIST 中SQL线程正在执行的SQL是否很长。
锁等待冲突:
如果从库同时有业务查询(读)在执行,而SQL线程要执行 UPDATE 或 DELETE,可能会因为行锁或MDL锁(元数据锁)而等待。
排查:SHOW ENGINE INNODB STATUS\G 中的 LATEST DETECTED DEADLOCK 或 LOCK WAIT 部分。
从库配置参数不佳:
innodb_flush_log_at_trx_commit 和 sync_binlog 在从库上如果设置过严(如都设为1),会导致每次事务重放都刷盘,严重影响IO性能。从库通常建议设为 2 或 0 以提高性能(前提是容忍少量数据丢失)。
innodb_buffer_pool_size 设置过小,导致热数据无法缓存在内存中,重放时频繁读盘。
从库承担了过多的读流量:很多架构会用从库分担查询,如果读流量过大(如报表分析),会抢占CPU和IO资源,导致SQL线程重放变慢。
SHOW SLAVE STATUS\G
SHOW PROCESSLIST; -- 看SQL线程和所有连接
SHOW ENGINE INNODB STATUS\G; -- 看锁等待和事务
- GTID在MySQL主从复制中的核心作用是什么?相比于传统的binlog+pos复制,基于GTID的复制在主从故障转移过程中有什么明显优势?
GTID的核心作用可以概括为一句话:为在主库上提交的每一个事务,生成一个在复制拓扑中全局唯一且不会重复的标识符。
它的格式是 source_id:transaction_id(例如:3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5)
传统复制(binlog+POS):
当老主库宕机后,你需要在所有从库中,选出一个数据最新的作为新主库。然后,将其余从库指向这个新主库。关键一步是,你必须去原主库的binlog里找到“所有从库都已执行到哪个POS了”,或者在新主库上通过SHOW MASTER STATUS找到当前位点,然后手动计算并配置CHANGE MASTER TO MASTER_LOG_FILE=..., MASTER_LOG_POS=...。这个过程极易出错,一旦位点找错,就会导致数据不一致或复制中断。
如果从库由于网络问题,接收了部分binlog但未完全应用,或者因错误导致复制中断,DBA(数据库管理员)需要手动设置sql_slave_skip_counter或slave_skip_errors来跳过一个或多个事务。这相当于“盲跳”,如果跳过了不该跳的事务,数据就会出错。
需要将一个备份文件(如mysqldump导出的SQL)恢复到一个新从库上,然后让它开始复制。必须从备份文件中找到导出时的MASTER_LOG_FILE和MASTER_LOG_POS,手动输入。
基于GTID的复制:完全不需要关心位点。只需执行CHANGE MASTER TO MASTER_HOST=... , MASTER_AUTO_POSITION=1;。从库连接新主库后,会自动交换彼此的GTID执行记录,新主库会自动计算出从库缺失哪些事务并推送给它。整个过程是自动化、声明式的,彻底消除了人为计算位点的风险。
当复制链路重新建立后,从库会向主库发送自己已执行过的GTID集合。主库在发送binlog时,会自动过滤掉那些从库已执行的GTID。这意味着如果某事务因网络原因被重复发送,从库会检测到该GTID已存在,并自动忽略,无需人工干预,确保了数据的一致性和复制的连续性。
mysqldump备份时会包含SET @@GLOBAL.GTID_PURGED='...';执行CHANGE MASTER TO MASTER_AUTO_POSITION=1;,从库会自动告知主库:“我已经执行了这些GTID,请把后续的发送给我”。
| 对比维度 | 传统复制 (binlog + POS) | GTID 复制 |
|---|---|---|
| 定位方式 | 通过 文件名 + 偏移量 | 通过 全局唯一事务ID |
| 故障转移操作 | 复杂,需手动计算所有从库的共同位点,极易失误 | 极简,一条 MASTER_AUTO_POSITION=1 搞定,全自动协商 |
| 重复事务处理 | 需手动 sql_slave_skip_counter,风险高 | 自动跳过,基于GTID集合自动过滤,无风险 |
| 备份恢复后搭建复制 | 需要从备份文件中手动提取位点信息 | 自动通过 GTID_PURGED 变量协商起点 |
| 对DBA的经验依赖 | 非常高,需要精通binlog解析和位点计算 | 较低,逻辑更清晰,不易出错 |
- 在双主复制(Master-Master)架构中,如果两个节点同时对同一张表的同一行进行更新,会发生什么?在生产环境中,通常如何规划双主架构以避免写冲突问题?
在默认配置下,两个节点同时对同一行更新,一定会发生冲突,导致复制中断。
架构:只有其中一个节点(如节点A)提供写服务,另一个节点(节点B)完全只读(设置 read_only=ON 和 super_read_only=ON),仅作为热备。
如何避免冲突:因为同一时刻只有一个写入点,所有写操作都发生在A上,然后同步到B。B永远不会主动产生写操作,因此绝无写冲突。
故障切换:当A宕机时,手动或由高可用组件(如MHA、Orchestrator)将B的read_only关闭,并将写流量切换到B。此时B变成新主,A修复后作为新备库。这本质是主从复制,但角色可互换。
- MyCat在数据库集群中主要起到什么作用?当使用MyCat实现读写分离时,写操作的数据是如何被同步到各个读节点上的?
MyCat 只负责“路由”读写请求,数据的同步工作是由后端的 MySQL 数据库自身的主从复制机制来完成的。
读写分离:根据配置的策略,自动将 INSERT、UPDATE、DELETE 等写操作路由到主库(Master),而将 SELECT 查询请求路由到一个或多个从库(Slave),从而分散数据库的读写压力。
分库分表:当单表数据量巨大(如超过千万级)时,MyCat 可以将一个大表按照一定规则(如按ID取模、按日期等)水平切分成多个小表,并分布存储在不同的数据库节点上,从而突破单库的容量和性能瓶颈。
高可用与故障切换:MyCat 可以监控后端数据库节点的健康状态。当主库发生故障时,它可以自动或根据配置将写请求切换到备用的主库上,实现一定程度的容灾。
-
写操作被路由到主库
当的应用通过 MyCat 执行一条写操作(例如 UPDATE)时,MyCat 会解析SQL,将其路由到配置好的主数据库(Master) 上执行。 -
MySQL 主从复制负责数据同步
数据从主库同步到从库,依赖的是 MySQL 自身的主从复制机制,这个流程与 MyCat 无关。其核心原理是:
记录变更日志:主库将执行写操作产生的数据变更,按顺序记录到自己的二进制日志(Binary Log) 中。
从库拉取日志:从库的 I/O 线程会主动连接到主库,请求并拉取最新的二进制日志内容。
中转与重放:从库的 I/O 线程将拉取到的日志先写入本地的中继日志(Relay Log)。然后,从库的 SQL 线程会读取中继日志,并将其中记录的变更操作在从库上重新执行一遍,最终实现数据同步。 -
什么是半同步复制?它与传统的异步复制有什么区别?如果半同步复制中,所有的从库都无响应或者断开连接,主库的写操作会发生什么?
半同步复制(Semi-Synchronous Replication) 是MySQL在5.5版本引入的一种复制模式,它位于异步复制和全同步复制之间。它的核心设计目标是:在保证数据不丢失(高可靠性)和保证写入性能(高性能)之间取得一个平衡。
主库在执行完一个事务后,不会立即向客户端返回“提交成功”的响应。
主库会等待至少一个从库(可通过rpl_semi_sync_master_wait_for_slave_count配置,默认为1)发来确认信号(ACK)(收到了binlog并写入了中继日志(relay log)),告知“我已成功收到并写入了该事务的二进制日志(binlog)”
| 对比维度 | 异步复制 (Asynchronous) | **半同步复制 **(Semi-Synchronous) |
|---|---|---|
| 主库写入流程 | 事务提交,binlog写入磁盘后,立即向客户端返回成功。 | 事务提交,binlog写入磁盘后,必须等待至少一个从库的ACK确认,才向客户端返回成功。 |
| 数据一致性风险 | 高。主库宕机时,未来得及同步到从库的事务会丢失。 | 低。至少有一个从库拥有该事务的binlog,主库宕机时可确保数据不丢失(RPO趋近于0)。 |
| 写入性能(TPS) | 高。主库无额外等待,性能损失极小(约1%~5%)。 | 较低。主库需要等待网络往返和从库IO,性能损失约10%~30%(取决于网络延迟)。 |
| 适用场景 | 对数据丢失不敏感、追求极致写入性能的场景(如日志系统)。 | 对数据一致性要求高、绝对不能丢数据的核心交易系统(如金融、订单)。 |
如果所有从库都无响应或断开,主库的写操作会发生什么?
这是半同步复制中最关键的一个降级(Fallback)机制。答案取决于一个关键参数:rpl_semi_sync_master_timeout(超时时间,默认值为10秒)。
情况一:在超时时间内,从库恢复响应
主库的写操作会被阻塞,一直等待直到有从库返回ACK。
此时客户端会感觉“卡住”了,事务一直处于“提交中”状态,直到从库恢复。
情况二:超过超时时间,从库仍未响应
这是生产中最可能发生的情况。此时,主库会自动将半同步复制临时降级为异步复制。
主库不再等待从库的ACK,而是立即向客户端返回提交成功。
同时,主库的日志中会记录一条警告:Semi-sync master failed to receive acknowledgment from slave, fallback to asynchronous replication
- 数据库的水平切分(分表分库)主要用来解决什么问题?进行了水平分片后,业务代码中跨节点的联合查询通常应该如何处理?
| 解决的问题 | 具体表现与原理 |
|---|---|
| 写入性能瓶颈 | 单库的InnoDB锁竞争、B+树索引维护成本极高。当写入QPS(每秒查询数)超过单库上限(如1-2万TPS)时,分片可以将写压力打散到多个节点,总吞吐量随节点数线性提升。 |
| 存储容量瓶颈 | 单表数据量过大(如超过千万级或亿级),B+Tree索引层级加深(超过3-4层),导致查询时的磁盘I/O次数增加,内存缓冲池命中率下降。分片后,每个节点只需存储总量的1/N,索引层级降低。 |
| 运维时间窗口瓶颈 | 在大表上执行DDL(如ALTER TABLE ADD COLUMN)可能耗时数小时甚至锁表,在金融或互联网场景下不可接受。分片后,可以在各个分片上并行或滚动执行DDL,极大缩短维护时间。 |
避免跨节点JOIN(推荐,最常用)
在设计之初就杜绝需要跨库JOIN的查询。具体做法是:
按“主体”分片:例如,以用户ID(userId) 作为分片键。那么一个用户的所有数据(订单、收货地址、购物车)都落在同一个分片上。
查询时强制带分片键:所有SELECT语句的WHERE条件中必须包含 userId,这样MyCat或ShardingSphere可以直接路由到单个分片执行,完全避免跨节点问题。
- 某双主架构环境中,由于误操作,节点A的数据同步报1062主键冲突错误,导致SQL线程停止。请写出通过跳过错误或忽略错误来恢复同步的核心操作步骤及注意事项。
方法一:使用 sql_slave_skip_counter 跳过一个事务(推荐用于紧急恢复)
这是最常用的临时跳过方法,用于跳过当前这个导致报错的事务。它的核心是告诉从库:“跳过下一个事务(事件组)
SET GLOBAL sql_slave_skip_counter = 1;让从库在下次启动时跳过一个事件组
方法二:通过配置文件 slave_skip_errors 忽略错误(谨慎使用)
这种方法会告诉从库,以后遇到指定的错误号时自动跳过,而不再停止复制线程
[mysqld]
slave_skip_errors = 1062
- LVS的NAT模式的工作原理是什么?数据流向是怎样的?在NAT模式下,为什么LVS负载均衡器主机必须开启
ip_forward路由转发功能?
LVS NAT模式的核心工作原理可以概括为“网络地址转换”+“端口映射”。它工作在OSI模型的第四层(传输层),通过修改IP数据包的目标地址和端口,将客户端的请求分发到后端的真实服务器(Real Server)上。
改写目标地址:将数据包的目标IP(VIP,即虚拟IP)修改为选中的Real Server的IP(RIP)。
改写目标端口(可选):如果配置了端口映射,还会将目标端口修改为Real Server上对应的服务端口(例如将80改为8080)。
转发数据包:将修改后的数据包转发给选中的Real Server。
为什么必须开启 ip_forward?
ip_forward 是Linux内核的一个参数,它控制着是否允许系统将从一个网络接口接收到的数据包,路由转发到另一个网络接口。
在NAT模式下,必须开启它的原因有两点:
数据包需要跨网络接口转发:LVS通常配置在两个不同网段之间:一个连接公网的VIP网卡(如 eth0),和一个连接内网Real Server的DIP网卡(如 eth1)。当一个数据包从 eth0 进入,需要从 eth1 出去时,Linux内核默认行为是丢弃该数据包(出于安全考虑)。只有将 net.ipv4.ip_forward 设置为 1,内核才会允许这种跨接口的IP转发行为。
这是DNAT生效的前提:ip_forward 是内核Netfilter框架进行DNAT和SNAT操作的底层依赖。如果没有它,数据包在内核协议栈中走到转发环节时就会被直接丢弃,无法进行后续的地址转换和发送。
- 相比于NAT模式,LVS的DR(直接路由)模式有什么性能上的优势?在DR模式下,为什么后端真实服务器(RS)需要把VIP绑定在回环(lo)接口上,而不是配置在普通的物理网卡别名上?
| 对比维度 | NAT模式 | DR模式 |
|---|---|---|
| 响应数据路径 | 请求和响应都经过Director,构成双向瓶颈 | 请求经Director,响应直接从RS返回客户端,单向路径 |
| Director的负载压力 | 极高,需处理所有入站和出站流量 | 很低,只处理入站请求包(通常较小),吞吐量大幅提升 |
| 最大并发连接数 | 受限于Director的网卡带宽和CPU性能(通常约10~20台RS) | 可支撑更大规模集群(可达100台以上),瓶颈转移到RS的带宽 |
| 对后端网络的影响 | RS需将网关指向Director,限制内部网络拓扑 | RS网关可指向外部路由器,网络拓扑更灵活 |
| 性能数据参考 | 单Director约支持200~500Mbps流量 | 单Director可轻松支撑2~5Gbps以上流量 |
为什么RS要把VIP绑定在回环(lo)接口上 这是DR模式能够正常工作的核心前提,原因涉及网络协议栈的响应机制:
原因一:避免IP地址冲突(ARP响应问题)
DR模式中,Director和所有RS都配置了相同的VIP(对外服务的虚拟IP)。如果RS将VIP配置在物理网卡(如eth0)上,当外网路由器的ARP广播询问“谁的IP是VIP?”时,所有RS的物理网卡都会响应,导致IP冲突,客户端请求可能被错误地发送到某个RS,而不是Director。
解决方法:利用回环接口 + 抑制ARP
将VIP绑定在回环接口(lo) 上,并配合修改ARP响应参数(arp_ignore=1和arp_announce=2),可以做到:
让RS“听不到”ARP请求:内核会忽略来自物理网卡的VIP的ARP广播,只有Director的物理网卡会响应ARP,从而确保所有请求都先到达Director。
RS仅在本地处理数据包:当Director将数据包的目标MAC地址改为RS的MAC地址(而非修改IP),并将其转发到局域网时,RS的物理网卡(eth0)会接收该包。内核检查发现目标IP是VIP(已在lo接口上配置),于是在本地处理该请求,而不是将其转发出去。
- LVS的TUN(隧道)模式通常适用于什么样的网络环境场景?TUN模式下的数据包封装与DR模式有何不同?
TUN模式适用的网络环境场景
TUN模式主要解决地理分布或跨网段的负载均衡问题,具体场景包括:
跨机房/跨数据中心负载均衡:将用户请求分散到不同机房(例如北京、上海、深圳)的服务器集群,实现区域级的容灾和流量调度。
混合云或多云架构:部分服务器部署在自建机房,部分部署在公有云(如阿里云、AWS),通过TUN模式将流量统一调度到这些异构资源上。
后端服务器与负载均衡器不在同一VLAN:当RS(真实服务器)和Director(负载均衡器)部署在不同的网段,且中间存在三层路由器时,DR模式(依赖二层广播)无法工作,TUN模式通过IP隧道(三层转发)可以解决。
| 对比维度 | DR模式 | TUN模式 |
| 操作层级 | 数据链路层(二层) | 网络层(三层) |
| 修改内容 | 修改数据包的目标MAC地址(Director将MAC改为选中的RS的MAC) | 新增一个IP隧道头部(原始数据包被整体封装在另一个IP包内) |
| 封装结构 | [ 以太网帧 | 源IP:CIP | 目标IP:VIP | TCP数据 ] (MAC地址被改写) |
[ 外层IP头 | 内层IP头 ] [ 外层: 源IP:DIP, 目标IP:RIP ] [ 内层: 源IP:CIP, 目标IP:VIP ] |
| RS处理方式 | RS收到数据包后,通过lo接口上的VIP直接处理请求,并直接响应客户端。 | RS收到外层IP包后,解封装(拆除隧道头部),还原出内层的原始数据包,看到目标IP是VIP(已在lo上配置),然后处理请求并直接响应客户端。 |
- LVS中常见的静态调度算法(如RR、WRR)与动态调度算法(如WLC)的核心判断区别是什么?如果集群中后端服务器的硬件性能差异较大,且需要考虑当前连接数,应该优先选择哪种调度算法?
| 维度 | 静态调度算法 | 动态调度算法 |
|---|---|---|
| 判断依据 | 仅根据预先配置的固定规则(如轮询顺序、权重比例)进行分发,完全不考虑后端服务器当前的负载、连接数、响应时间等实时状态。 | 在调度时,会实时获取后端服务器的当前状态(如活动连接数、CPU负载等),并以此作为权重调整的依据,将请求分配给当前最“空闲”的服务器。 |
| 是否关心服务器当前负载 | 不关心。即使某台服务器已经过载,只要轮询轮到了它,依然会继续分配新请求。 | 关心。会主动避开当前负载较高的服务器,将新请求分配给负载较低的服务器。 |
| 典型算法 | RR(Round Robin,轮询)、WRR(Weighted RR,加权轮询)、源地址哈希(SH,即Source Hashing) | LC(Least-Connection,最少连接)、WLC(Weighted LC,加权最少连接)、SED(Shortest Expected Delay,最短预期延迟)、NQ(Never Queue,永不排队) |
| 适用场景 | 后端服务器性能完全一致,且请求处理开销差异很小的场景(如静态文件服务器)。 | 后端服务器性能不一致,或请求处理时间长短不一的场景(如API服务、动态计算)。 |
为什么硬件性能差异大时,应选择 WLC(加权最少连接)?
WLC 算法综合了“权重”和“当前连接数”两个因素,它的调度公式是:
调度值 = 当前活动连接数 / 分配的权重
调度逻辑:LVS 会计算每台后端服务器的这个值,并选择 “值最小” 的服务器来分发新的请求。
- MGR(MySQL组复制)相比于传统的主从复制架构有什么优势?在MGR的单主模式下,如果当前的主节点宕机了,集群是如何处理的,业务会崩溃吗?
| 对比维度 | 传统主从复制 (异步/半同步) | MySQL组复制 (MGR) |
|---|---|---|
| 数据一致性 | 异步复制可能丢数据;半同步虽有改善,但极端情况下仍可能不一致。 | 基于Paxos协议,事务需多数派(N/2+1)节点确认后才能提交,从机制上保证了数据强一致性。 |
| 故障恢复 | 主库宕机通常需要人工介入或借助第三方工具(如MHA)进行主从切换,操作复杂且有风险。 | 内置自动故障检测和选主机制。主节点故障后,集群会自动选举新主,无需人工干预。 |
| 写入能力 | 所有写操作集中在主库,成为瓶颈。 | 支持单主(默认)和多主两种模式。多主模式下,集群中所有节点均可写入,但存在冲突风险,生产环境不推荐。 |
| 集群弹性 | 节点加入、移除需要手动操作,流程繁琐。 | 支持节点的自动加入和移除。新节点加入后会自动从其他节点同步数据,实现自我管理。 |
单主模式下,主节点宕机会发生什么?
业务不会崩溃,但会经历一个短暂的“故障转移”窗口期,表现为服务瞬间的“卡顿”或“中断”。这个过程的处理方式直接决定了业务受影响的程度:
自动检测与选举:集群的故障检测机制会迅速发现主节点失去响应。随后,剩余的从节点会通过Paxos协议进行协商,自动选举出一个新的主节点。
决定服务恢复速度:新主节点产生后,如何恢复服务有两种选择,可通过 group_replication_consistency 参数控制:
可用性优先(默认/旧版行为):为了最快恢复服务,集群会让新主节点立即对外提供服务,同时在后台继续应用之前可能积压的日志。缺点是,在积压应用完之前,客户端可能读到旧数据(因为新主数据尚未完全追上旧主)。
一致性优先:集群会等待新主节点将所有积压的日志应用完,确保数据完全追上旧主后,才对外开放服务。缺点是故障转移的时间会变长,与需要追赶的数据量成正比
- LVS-DR负载均衡集群搭建完成后,发现客户端发起HTTP请求时能到达LVS,但页面一直转圈超时,后端RS主机抓包发现没有返回数据给客户端。请列出排查后端RS主机网络或内核参数(如ARP抑制)的核心排查步骤。
第一步:检查RS上的VIP配置是否正确(最核心、最常见)
在RS上执行
ip addr show lo
第二步:检查ARP抑制参数是否生效(决定RS是否“隐形”)
在RS上执行,查看所有接口的配置
sysctl -a | grep -E "arp_ignore|arp_announce" | grep -E "lo|all|default"
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.lo.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2
第三步:检查RS的默认网关(Gateway)是否指向外部路由器
在RS上执行
ip route show default
第四步:检查RS的物理网卡是否启用了arp_filter
| 步骤 | 检查项 | 正确状态 |
|---|---|---|
| 1 | VIP是否绑定在 lo:0 上? | 存在 lo:0,且掩码为 /32 |
| 2 | arp_ignore 是否为 1? | 所有接口均为 1 |
| 3 | arp_announce 是否为 2? | 所有接口均为 2 |
| 4 | 默认网关是否指向外部路由器? | 指向公网网关IP,不是LVS的DIP |
| 5 | arp_filter 是否未启用? | 所有接口均为 0 |
| 6 | 使用 tcpdump 抓包确认响应包是否发出 | 在RS的 eth0 上抓包,看是否有源IP为VIP的响应包发出 |

浙公网安备 33010602011771号