透明写重定向
TWR(Transparent Write Redirection,透明写重定向)是 Halo 数据库内嵌于内核中的读写判断与路由模块。在主从复制集群中,应用连接只读节点(RO)发起写操作时,TWR 会将该写操作透明地转发到主库(RW)执行,并将执行结果返回应用,对应用完全透明——应用无需区分读写连接,也无需感知主库位置。
TWR 采用 RW(读写节点)+ RO(只读节点)的一主多读架构:
- RW 节点负责写操作,并支持本地读;
- RO 节点处理读请求,收到写请求时通过 TWR 转发至 RW 节点;
- 应用侧无需区分 RW 与 RO 节点角色,数据库连接池可直接复用,通常无需业务代码改造。
TWR 的工作原理
读写请求在集群中的处理流程如下:
读请求:应用发起读请求(如 SELECT)到达 RO 节点后,请求在该节点本地执行并读取本地数据副本,无需转发至 RW 节点,执行结果由 RO 节点直接返回应用。这使得读请求无额外网络转发开销,同时分担了主节点的读压力。
写请求:应用向 RO 节点发起写请求时,TWR 结合 SQL 语义分析、事务状态及对象类型完成读写判断,识别出写请求后,通过内部通道将其转发至 RW 节点执行,执行结果再经 RO 节点返回应用。RW 与 RO 之间的数据一致性由流复制机制维护。
由于写重定向在内核层完成,应用代码零侵入——既有的单机应用无需修改连接或 SQL 逻辑,即可在 TWR 集群上获得"连从库、写自动进主库"的透明能力。
TWR 的部署
执行环境说明:以下命令与输出基于 Halo 1.0.16 实测。示例中 ${主库IP}、${备库IP} 为占位符,部署时替换为实际 IP;密码请使用实际强密码,不要照抄示例。
前提:主从复制已搭建完成
TWR 部署在已有的主从流复制集群之上。部署前应确认主从复制正常:
-- 在主库执行
halo0root=# SELECT * FROM pg_stat_replication;
确认有来自备库的复制连接且状态为 streaming,即表示主从复制已就绪。
主库配置
在主库默认数据库中依次执行以下操作。
1)创建 dlb 扩展
TWR 的内部转发通道基于 dlb_fdw 外部数据包装器实现,需先在主库创建该扩展:
halo0root=# CREATE EXTENSION dlb_fdw CASCADE;
CREATE EXTENSION
2)为 halo 用户设置密码
TWR 转发时使用 halo 用户连接主库,需为其设置密码(也可新建专用用户):
halo0root=# ALTER USER halo PASSWORD '${实际密码}';
ALTER ROLE
3)系统表更新
更新系统表 pg_proc 的只读标记。先在主库默认数据库执行:
halo0root=# SET allow_system_table_mods = on;
halo0root=# UPDATE pg_proc SET proreadonly = 't';
halo0root=# UPDATE pg_proc SET proreadonly = 'f' WHERE proname IN ('nextval','setval','currval', 'lastval');
halo0root=# SET allow_system_table_mods = off;
再对 template0 执行同样更新(需临时打开其连接,更新后关闭):
halo0root=# UPDATE pg_database SET datallowconn = true WHERE datname = 'template0';
halo0root=# \c template0
template0=# SET allow_system_table_mods = on;
template0=# UPDATE pg_proc SET proreadonly = 't';
template0=# UPDATE pg_proc SET proreadonly = 'f' WHERE proname IN ('nextval','setval','currval', 'lastval');
template0=# SET allow_system_table_mods = off;
template0=# \c halo0root
halo0root=# UPDATE pg_database SET datallowconn = false WHERE datname = 'template0';
4)创建 server 与 user mapping
在主库为备库创建外部服务器与用户映射,作为 TWR 转发的连接通道:
halo0root=# CREATE SERVER nac_server_01
FOREIGN DATA WRAPPER dlb_fdw
OPTIONS (host '${主库IP}', port '1921', dbname 'halo0root');
CREATE SERVER
halo0root=# CREATE USER MAPPING FOR halo
SERVER nac_server_01
OPTIONS (user 'halo', password '${实际密码}');
CREATE USER MAPPING
主库与备库参数配置
在主库与备库的数据目录下,修改 postgresql.conf,加入以下两项:
vi $PGDATA/postgresql.conf
nac_servername = 'nac_server_01'
instance_type = 'DLB'
说明:nac_servername 指向 17.3.2 中创建的 server 名;instance_type 设为 DLB 标识本节点参与 TWR 集群。主库与备库均需设置这两项。
重启数据库
修改参数后需重启数据库使配置生效,先重启主库,再重启备库:
pg_ctl restart
至此,TWR 透明重定向配置完成。
TWR 的验证
主库写入测试数据
在主库创建测试表并写入数据:
halo0root=# CREATE TABLE twr_test(id int, msg text);
CREATE TABLE
halo0root=# INSERT INTO twr_test VALUES (1, 'on_master');
INSERT 0 1
在备库确认数据已同步:
-- 在备库执行
SELECT * FROM twr_test;
id | msg
----+-----------
1 | on_master
(1 row)
验证写请求透明转发
以 halo 用户连接备库(只读节点),执行写操作。TWR 生效时,该写请求会被透明转发至主库执行,返回成功:
-- 在备库执行
INSERT INTO twr_test VALUES (2, 'twr_redirect');
INSERT 0 1
回到主库查询,确认数据已写入主库:
-- 在主库执行
SELECT * FROM twr_test ORDER BY id;
id | msg
----+--------------
1 | on_master
2 | twr_redirect
(2 rows)
说明:上述验证表明——应用连接备库(只读节点)发起的写请求,经 TWR 透明转发后在主库执行成功(返回 INSERT 0 1),数据进入主库并经流复制同步回备库。整个过程中应用无需感知主库位置,连接对象始终是备库,对应用完全透明。
TWR 与传统代理方案的对比
传统的读写分离多采用独立部署的代理(PROXY)中间件实现。Halo 的 TWR 采用内核内嵌方案,两者在实现层面存在显著差异:
| 特性维度 | Halo TWR(内核内嵌) | 传统代理(PROXY) |
|---|---|---|
| 架构类型 | 数据库内核内嵌 | 独立代理服务 |
| SQL 理解 | 结合 SQL 语义、事务状态、对象类型深度判断 | 基于配置规则与会话状态路由 |
| 事务一致性 | 支持事务块级与语句级两种路由模式 | 主要基于连接绑定与会话级事务状态 |
| 本地读延迟 | RO 节点本地处理读请求,无额外转发开销 | 请求经代理转发,增加网络往返 |
| 运维管理 | 配置、监控、日志与数据库实例统一管理 | 代理服务需独立部署与维护 |
| 部署方式 | 随数据库编译,无需独立部署 | 独立部署,增加额外运维成本 |
说明:TWR 的核心优势在于通过内核层实现,具备深度的 SQL 语义理解和事务集成能力,对应用零侵入、配置维护简单;其统一服务入口可配合 SLB、LVS 或 F5 等负载均衡组件对外提供。