PostgreSQL从库只读的实现原理

本文探讨了PostgreSQL中实现物理从库只读的原理,通过报错分析和变量追踪,揭示了在执行写操作时如何通过全局变量XactReadOnly和RecoveryInProgress状态判断并防止写操作。在standby实例中,由于RecoveryInProgress始终为true,导致实例保持只读状态。此外,文章还提到XactReadOnly对应的guc参数transaction_read_only无法通过alter system方式修改,并简要讨论了如何将primary实例设置为只读。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作者:杨向博

我们接触到的数据库,基本都提供了只读实例的功能。在业务允许的情况下可以将一些读取数据的请求下发至只读实例,减缓primary的压力,以获得更佳的性能。

物理从库只读已经是一个常识问题,在PostgreSQL中具体是怎么实现的呢,一起来看看。

一、报错分析

从报错入手分析,在只读库里执行写操作,报错如下:

postgres=# select * into tbl_t from tbl_test;
ERROR:  cannot execute SELECT INTO in a read-only transaction
postgres=#

报错的函数为:

/*
 * PreventCommandIfReadOnly: throw error if XactReadOnly
 *
 * This is useful partly to ensure consistency of the error message wording;
 * some callers have checked XactReadOnly for themselves.
 */
void
PreventCommandIfReadOnly(const char *cmdname)
{
   
    if (XactReadOnly)
        ereport(ERROR,
                (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
        /* translator: %s is name of a SQL command, eg CREATE */
                 errmsg('cannot execute %s in a read-only transaction',
                        cmdname)));
}

当XactReadOnly为true时,就抛出报错,XactReadOnly是一个Bool类型的全局变量。那这里大概明白设计思路了,当执行一些操作时调用PreventCommandIfReadOnly函数,如果库是只读状态进行写操作就抛出报错。

报错堆栈:

(gdb) bt
#0  PreventCommandIfReadOnly (cmdname=0xbab6f8 'SELECT INTO') at utility.c:409
#1  0x00000000008c24a5 in standard_ProcessUtility (pstmt=0x1faef00, queryString=0x1fae098 'select * into tbl_t from tbl_test;', context=PROCESS_UTILITY_TOPLEVEL, params=0x0, queryEnv=0x0, dest=0x1fafa18, qc=0x7ffc8ac8a180)
    at utility.c:566
#2  0x00000000008c23cc in ProcessUtility (pstmt=0x1faef00, queryString=0x1fae098 'select * into tbl_t from tbl_test;', context=PROCESS_UTILITY_TOPLEVEL, params=0x0, queryEnv=0x0, dest=0x1fafa18, qc=0x7ffc8ac8a180)
    at utility.c:524
#3  0x00000000008c14e2 in PortalRunUtility (portal=0x1fffa58, pstmt=0x1faef00, isTopLevel=true, setHoldSnapshot=false, dest=0x1fafa18, qc=0x7ffc8ac8a180) at pquery.c:1157
#4  0x00000000008c16ce in PortalRunMulti (portal=0x1fffa58, isTopLevel=true, setHoldSnapshot=false, dest=0x1fafa18, altdest=0x1fafa18, qc=0x7ffc8ac8a180) at pquery.c:1303
#5  0x00000000008c0c6c in PortalRun (portal=0x1fffa58, count=9223372036854775807, isTopLevel=true, run_once=true, dest=0x1fafa18, altdest=0x1fafa18, qc=0x7ffc8ac8a180) at pquery.c:779
#6  0x00000000008bb204 in exec_simp
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值