跳到主要内容

透明写重定向

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 等负载均衡组件对外提供。