MaxScale readwritesplit 默认值排障:max_slave_connections=255 与 max_replication_lag=0s
一条客户端连接在 MaxScale 的 readwritesplit 上并不等于一条后端连接。
默认参数下,路由器为每个客户端会话最多建立 255 条从库连接(max_slave_connections,默认 255,上限也是 255);新会话按 255 这个数铺初始连接(slave_connections,同样默认 255)。max_replication_lag 默认值是 0s——不是“零容忍”,而是这个过滤功能没开。
所以「读写分离配上去,从库为什么被连接打满」「延迟很久的从库为什么照样接读」,不用先看 SQL 解析逻辑,先看这两个默认值。
文档:mariadb.com/docs/maxscale/reference/maxscale-routers/maxscale-readwritesplit,教程:MariaDB MaxScale 24.02 Read-Write Splitting,仓库:mariadb-corporation/MaxScale。
后端连接数怎么算
readwritesplit 是 query-level 路由:解析每条语句,写发主库,autocommit 的普通 SELECT 发从库。要这么做,会话必须同时在主库和从库上持有连接。
max_slave_connections:一个会话同一时刻最多用多少条从库连接。默认 255,最小值 0。它按会话计,不是跨会话复用的连接池。slave_connections:新会话初始铺几条从库连接。默认 255。
两者是硬上限与软目标的关系:slave_connections 只是软限制,如果负载均衡算法发现某台还没连上的从库比已连的更好,实际连接数可以超过它。
单条客户端连接的从库连接数上界是 min(max_slave_connections, 从库实际台数)——不会为不存在的从库开连接。1 主 3 从、max_slave_connections=2 时,一个会话是 1 条主库 + 2 条从库,读只在这 2 台之间平摊。
这个参数兑换的是动态性与连接开销:调低,每会话连接少、可选从库集合小,负载均衡变钝;调高,连接多,几乎总能给单条查询挑到最快的那台。
短连接场景的写法:
# 客户端连接生命周期短(PHP-FPM、每次请求新建连接)
slave_connections=1
max_slave_connections=0:全走主库,但保留重放与重连
设成 0 时,readwritesplit 把所有读也路由到当前主库,但事务重放、断线重连仍然生效。
用途是灰度或回退:从库延迟不可控时先把读切回主库止血,不用改应用连接串、也不用换 router 模块。
这个模式下 master_failure_mode=fail_on_write 有一处变化:主库挂了又没有新主库可用时来一个读,会话会被直接关闭,而不是临时开一条从库连接——那条连接在新主库出现后不会被关掉,会漏。
max_replication_lag 默认 0s 意味着关着
- 类型 duration,默认
0s,即功能关闭。 - 23.02 之前叫
max_slave_replication_lag,旧名仍作为 alias 可用。 - 语义是“从库落后秒数必须严格小于该值才有资格接读”。
max_replication_lag=1s的实际含义接近只接受基本追平的从库。 - 粒度是秒,写毫秒会被直接拒绝,即使换算后大于 1 秒。
- readwritesplit 自己不测延迟,靠 monitor(mariadbmon)刷新状态;
monitor_interval决定这个判据的时效。 - Galera 集群没有从库延迟这个概念,这个参数不适用。
- 从 23.08.7 / 24.02.3 / 25.01.1 起,某台落后超过
MAX(300, 2 × max_replication_lag)秒时,到该服务器的连接会被主动丢弃。
它不等于因果读:即便所有从库都满足延迟阈值,也不能保证“刚写的那条”能被同一个连接读到。
选从库的判据
slave_selection_criteria 默认 least_current_operations:
least_current_operations(默认):按当前活跃操作数挑最闲的,优化吞吐。adaptive_routing:按服务器平均响应时间与估算负载挑,选“下一个查询最先跑完”的那台。异构硬件、跨机房、机器上还跑着备份或其他客户端时用。它能间接把外部负载算进去。least_behind_master:按复制延迟挑最新的从库,基于SHOW REPLICA STATUS的Seconds_Behind_Master,只适用于传统主从,Galera 不行。它提供的是“更可能读到新数据”,不是保证。least_router_connections/least_global_connections:不要用。文档写明是兼容性遗产,用连接数这种粗粒度指标均衡单条 SQL,结果是负载倾斜。
23.08.1 起全大写写法已弃用,运行时改成小写并持久化为小写;旧大写值目前还能认。
主库故障:三个值
master_failure_mode 默认 fail_on_write(23.08 默认为 fail_instantly):
fail_instantly:检测到主库故障立刻关客户端连接。fail_on_write:收到写查询且无主库可用时关连接;纯读照常。error_on_write:收到写查询且无主库可用时返回只读错误,连接保留。
fail_on_write 与 error_on_write 下,只要从库还在,新会话仍会被接受;空闲会话即使后端连接全断也不会被切掉,留时间等重连。代价是客户端不主动关就会一直挂着,得靠 wait_timeout 兜。
error_on_write 的坑:连主库丢了之后,即使新主库已经选出来,客户端没重连 MaxScale 写还是报只读。除非 master_reconnection 开着,让会话在主库被提升时恢复。
master_reconnection 默认值按版本分叉:24.02 起 true,23.08 及以前 false。它依赖会话命令历史(sescmd history):24.02 起配了 disable_sescmd_history,master_reconnection 会被自动禁用。主库切换还要求 prune_sescmd_history 开着、或 max_sescmd_history 未超限,并且会话里没有开放事务。
推荐组合:
master_reconnection=true
master_failure_mode=fail_on_write
strict_tmp_tables=true 会把重连悄悄禁掉
- 24.02 起
strict_tmp_tables默认true(23.08 及以前false)。 - 为
true时,只要会话里还有临时表就阻止主库重连;主库在这期间丢失,会话直接关。 - 于是建了临时表但从不 DROP 的会话,等于在整个会话期间把重连能力关掉。
- 为
false时,重连后临时表会莫名消失,这是升级前的老行为。
配合 strict_multi_stmt / strict_sp_calls(都默认 false):多语句查询和存储过程调用本身会被当写发主库,开启 strict 后它们之后的所有查询也钉主库,用一致性换主库压力。
维护模式不会踢掉空闲连接和长事务
maxctrl set server db2 maintenance 之后,正在执行的请求允许跑完再关连接;如果这台是主库,开放事务也允许提交完。空闲会话和长事务都不会被 readwritesplit 主动切。要真断加 --force:
maxctrl set server db2 maintenance --force
Draining 状态不同:已有连接照常用,只在需要新建连接时避开 Draining/Drained 的服务器。滚动升级用 Draining 比 maintenance 平滑。
一份起点配置
[Read-Write-Service]
type=service
router=readwritesplit
servers=db1,db2,db3
user=maxscale
password=***
slave_connections=1
max_slave_connections=2
max_replication_lag=1s
slave_selection_criteria=least_current_operations
master_reconnection=true
master_failure_mode=fail_on_write
retry_failed_reads=true
retry_failed_reads 默认 true:autocommit 的只读查询在从库上失败可以在另一台重试;结果已部分下发则不重试,除非开 transaction_replay。delayed_retry 默认 false,delayed_retry_timeout 默认 10s;21.06.18 / 22.08.15 / 23.02.12 / 23.08.8 / 24.02.4 / 25.01.1 起,已经发出去的写查询不再重发,避免重复执行。
改完参数只用 maxctrl alter service,但只对新会话生效:
maxctrl alter service Read-Write-Service max_slave_connections=2
maxctrl show service Read-Write-Service
观测点:maxctrl list servers 看状态与连接数;show service 确认会话数;日志里延迟从库被剔除/重新纳入时会有成对的 warning(只在有查询被路由、且状态翻转时才打)。
一条判断顺序
- 从库连接数是不是被
max_slave_connections × 客户端连接数顶满? max_replication_lag是否还是默认0s,延迟从库压根没被排除?- 读全落主库?先看
master_accept_reads(默认false,无从库时才走主),再看是否max_slave_connections=0、或会话全在写事务里。 - 主库切换后应用写不进去?看
master_failure_mode是否为error_on_write、master_reconnection是否被disable_sescmd_history连带关掉。 - 升级到 24.02 之后重连行为变了?看
strict_tmp_tables从false变true带来的临时表效应。