PostgreSQL源码分析 备库查询冲突 - User was holding shared buffer pin for too long

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介:

背景

PostgreSQL 的基于流复制的物理备库是基于redo的物理块复制备库,允许开放只读的功能,但是需要注意,由于主库可能不断的产生redo,这些redo可能会与备库的QUERY产生冲突。

什么情况下query会堵塞、或与恢复冲突?

当以下操作产生的REDO被复制到备库,并且备库准备拿这些REDO来恢复时。

  • Access Exclusive locks taken on the primary server, including both explicit LOCK commands and various DDL actions, conflict with table accesses in standby queries.
    主库的访问排它锁,与备库对应的锁产生冲突。

例如主库truncate a表, 备库查询a表。
这种情况的冲突面很窄。

  • Dropping a tablespace on the primary conflicts with standby queries using that tablespace for temporary work files.
    主库删除表空间,备库使用这个表空间产生临时文件。 例如主库删除TBS,备库的一个大的查询需要写临时文件,并且这个临时文件是写到这个表空间的。

这种情况非常少见,也很容易规避,新建一个临时表空间不要删除即可。

  • Dropping a database on the primary conflicts with sessions connected to that database on the standby.
    主库删除数据库,备库刚好连在这个数据库上。

这种情况也非常的少见。

  • Application of a vacuum cleanup record from WAL conflicts with standby transactions whose snapshots can still "see" any of the rows to be removed.
    主库回收dead tuple的REDO,同事备库当前的query snapshot需要看到这些记录。

这种情况可以通过参数控制,恢复优先,或查询优先。 可以配置时间窗口。
而且这种冲突出现的概率也非常的小,除非用户在备库使用repeatable read,同时是非常大的事务。
而通常用户用的都是read committed.

  • Application of a vacuum cleanup record from WAL conflicts with queries accessing the target page on the standby, whether or not the data to be removed is visible.
    同上,但是当query访问的页就是要清理垃圾的页时,也是有冲突的。

这是物理复制与逻辑复制唯一有差别的地方,但是对现实场景来说,这种情况出现的概率也不大。

案例

最近收到了一个冲突的例子,是这样的,在备库查询一些函数,导致了这样的报错。

< 2016-08-09 22:03:39.534 CST >STATEMENT:  SELECTxxx('32980770','-1','0','0',20,100,10)
< 2016-08-09 22:03:39.534 CST >ERROR:  canceling statement due to conflict with recovery
< 2016-08-09 22:03:39.534 CST >DETAIL:  User was holding shared buffer pin for too long.
< 2016-08-09 22:03:39.534 CST >CONTEXT:  PL/pgSQL function xxx(integer,bigint[],integer,integer) line 7 at RETURN QUERY
  PL/pgSQL function xxx(integer,integer,bigint,bigint,integer,integer,integer) line 15 at RETURN QUERY

是什么原因造成的呢?

PostgreSQL 备库apply与query之间可能存在的冲突种类如下
src/include/storage/procsignal.h

        /* Recovery conflict reasons */
        PROCSIG_RECOVERY_CONFLICT_DATABASE,
        PROCSIG_RECOVERY_CONFLICT_TABLESPACE,
        PROCSIG_RECOVERY_CONFLICT_LOCK,
        PROCSIG_RECOVERY_CONFLICT_SNAPSHOT,
        PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
        PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,

例子的这个报错的冲突类型属于 PROCSIG_RECOVERY_CONFLICT_BUFFERPIN

它来自这个个函数

/*
 * errdetail_recovery_conflict
 *
 * Add an errdetail() line showing conflict source.
 */
static int
errdetail_recovery_conflict(void)
{
        switch (RecoveryConflictReason)
        {
                case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
                        errdetail("User was holding shared buffer pin for too long.");
                        break;

备库apply redo时,产生的冲突,调用如下
screenshot

screenshot

源码
备库在apply redo时,如果遇到vacuum操作,则会触发以下。

而vacuum必须要拿到block的exclusive lock才能继续下去。

void
LockBufferForCleanup(Buffer buffer)
{
...
    for (;;)
    {
        /* Try to acquire lock */
        LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
        LockBufHdr(bufHdr);
...
        /* Wait to be signaled by UnpinBuffer() */
        if (InHotStandby)
        {
            /* Publish the bufid that Startup process waits on */
            SetStartupBufferPinWaitBufId(buffer - 1);
            /* Set alarm and then wait to be signaled by UnpinBuffer() */
            ResolveRecoveryConflictWithBufferPin();
            /* Reset the published bufid */
            SetStartupBufferPinWaitBufId(-1);
        }

如果是standby,则判断是否超时,如果没有超时,vacuum redo会继续等待。

void
ResolveRecoveryConflictWithBufferPin(void)
{
        TimestampTz ltime;

        Assert(InHotStandby);

        ltime = GetStandbyLimitTime();

        if (ltime == 0)
        {
                /*
                 * We're willing to wait forever for conflicts, so set timeout for
                 * deadlock check only
                 */
                enable_timeout_after(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout);
        }
        else if (GetCurrentTimestamp() >= ltime)
        {
                /*
                 * We're already behind, so clear a path as quickly as possible.
                 */
                SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
        }

冲突超时时间获取,超时则触发 SendRecoveryConflictWithBufferPin , 导致这个CASE的错误。

/*
 * Determine the cutoff time at which we want to start canceling conflicting
 * transactions.  Returns zero (a time safely in the past) if we are willing
 * to wait forever.
 */
static TimestampTz
GetStandbyLimitTime(void)
{
        TimestampTz rtime;
        bool            fromStream;

        /*
         * The cutoff time is the last WAL data receipt time plus the appropriate
         * delay variable.  Delay of -1 means wait forever.
         */
        GetXLogReceiptTime(&rtime, &fromStream);
        if (fromStream)
        {
                if (max_standby_streaming_delay < 0)
                        return 0;                       /* wait forever */
                return TimestampTzPlusMilliseconds(rtime, max_standby_streaming_delay);
        }
        else
        {
                if (max_standby_archive_delay < 0)
                        return 0;                       /* wait forever */
                return TimestampTzPlusMilliseconds(rtime, max_standby_archive_delay);
        }
}

如何避免或降低冲突

PostgreSQL提供了3种解决备库上查询与恢复冲突的办法

  • 在主库配置vacuum_defer_cleanup_age来解决以上最后两种冲突。
vacuum_defer_cleanup_age = 0   # number of xacts by which cleanup is delayed
  • 在备库配置recovery延迟来解决以上所有冲突,给备库的QUERY设置一个执行窗口
max_standby_archive_delay = 30s        # max delay before canceling queries
# when reading WAL from archive;
# -1 allows indefinite delay
# 
max_standby_streaming_delay = 30s      # max delay before canceling queries
# when reading streaming WAL;
# -1 allows indefinite delay
  • 在备库配置hot_standby_feedback,备库会反馈给主库QUERY情况(可能是快照,而不是QUERY本身)(如果是级联环境,则会反馈给最上层的主库)
    从而主库知道备库在干什么,在cleanup dead tuple时,会考虑备库的情况,防止冲突。
hot_standby_feedback = off             # send info from standby to prevent query conflicts
# 
wal_retrieve_retry_interval = 1s 

祝大家玩得开心,欢迎随时来 阿里云促膝长谈业务需求 ,恭候光临

阿里云的小伙伴们加油,努力 做好内核与服务,打造最贴地气的云数据库

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
1天前
|
SQL 自然语言处理 关系型数据库
PolarDB上实现一个自然语言查询系统
PolarDB上实现一个自然语言查询系统
|
7天前
|
存储 关系型数据库 分布式数据库
PolarDB产品使用问题之在处理超过5000万条记录的查询时,性能表现如何
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
9天前
|
关系型数据库 MySQL 分布式数据库
PolarDB产品使用问题之mysql迁移后查询不走索引了,该如何解决
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
9天前
|
消息中间件 存储 关系型数据库
PolarDB产品使用问题之查询归档后的表,是否会占用当前实例负载资源
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
26天前
|
SQL 关系型数据库 数据库
Python查询PostgreSQL数据库
木头左教你如何用Python连接PostgreSQL数据库:安装`psycopg2`库,建立连接,执行SQL脚本如创建表、插入数据,同时掌握错误处理和事务管理。别忘了性能优化,利用索引、批量操作提升效率。下期更精彩!💡 csvfile
Python查询PostgreSQL数据库
|
7天前
|
关系型数据库 MySQL 分布式数据库
PolarDB操作报错合集之当使用DTS(数据传输服务)同步的表在目标库中进行LEFT JOIN查询时遇到异常,是什么导致的
在使用阿里云的PolarDB(包括PolarDB-X)时,用户可能会遇到各种操作报错。下面汇总了一些常见的报错情况及其可能的原因和解决办法:1.安装PolarDB-X报错、2.PolarDB安装后无法连接、3.PolarDB-X 使用rpm安装启动卡顿、4.PolarDB执行UPDATE/INSERT报错、5.DDL操作提示“Lock conflict”、6.数据集成时联通PolarDB报错、7.编译DN报错(RockyLinux)、8.CheckStorage报错(源数据库实例被删除)、9.嵌套事务错误(TDDL-4604)。
|
9天前
|
SQL 关系型数据库 分布式数据库
PolarDB产品使用问题之如何开启并行查询
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
9天前
|
运维 关系型数据库 分布式数据库
PolarDB产品使用问题之如何加快数据库查询速度
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
9天前
|
SQL 缓存 关系型数据库
PolarDB产品使用问题之已经修改了expire_logs_days参数并确认已生效,但在SQL查询中仍然显示为0,该怎么办
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
9天前
|
存储 关系型数据库 分布式数据库
PolarDB产品使用问题之可以通过什么操作来监听从库的binlog
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。

相关产品

  • 云原生数据库 PolarDB