使 Postgres 数据库暂时只读(用于执行卷快照)

Oth*_*eus 12 postgresql backup maintenance read-only-database

PostgreSQL 内置的备份机制并不总是很合适。有时,您希望将应用程序置于静止状态,因为它具有您要在备份 PG 数据的同时进行备份的外部数据。但是将应用程序置于静止状态的唯一方法是“锁定”数据库。PG 缺乏数据库范围或集群范围的锁定机制。将 PG 置于只读状态将是以下解决方案中的一部分:

  1. 停止应用程序数据(禁用登录)
  2. 停顿数据库(通过使其只读)
  3. 执行 PG 检查点或 pg_xlog_switch()
  4. 创建应用程序和数据卷的快照
  5. 恢复数据库(再次使其为 RW)
  6. 继续申请
  7. 备份快照

Oth*_*eus 13

在互联网上其他地方剔除答案后,我设计了一个解决方案。其他答案本身就是不完整的。所以我在这里提出一个答案,希望它能让其他人受益。

策略

  1. 禁用与数据库(而不是集群)的连接。
  2. 将数据库的default_transaction_read_only设置设置为true.
  3. 终止与该数据库的现有连接。
  4. 重新启用(只读)连接。

完成后,您将(在我的解决方案中):

  1. 执行CHECKPOINT(我认为这是最安全的,但 apg_xlog_switch()将适用于非常高负载的服务器)
  2. 拍摄卷快照
  3. 将前面的步骤反过来。(但这很棘手!)

陷阱

  1. 在交易中途终止连接可能是一个坏主意。最好杀死空闲连接,等待几秒钟,然后杀死空闲连接,再等一些,重复直到它们全部消失。
  2. 在某些时候,您将不得不终止打开/挂起的查询或中止备份。
  3. 在会话事务开始时,Postgresql 获取进程表的一种快照。每次去检查不需要的进程是否仍然存在时,您都必须重置此快照。看pg_stat_clear_snapshot()
  4. 恢复读写状态并不是那么简单。如果现在存在只读连接,您必须终止它们以使新的读写状态生效。但是新的连接可能会在杀死现有连接的同时到达。所以再次,你必须

    1. 禁用与数据库的连接
    2. 将 default_transaction_read_only 状态更改为 false
    3. 杀死现有的连接
    4. 重新启用(r/w)到数据库的连接

替代策略

另一种策略是更改应用程序使用的角色的权限。这可能非常混乱并且不太通用。

例如,您不仅必须撤销/重新授权表,还必须撤销/重新授予序列、大对象,可能还有模式本身。此外,当您更改访问权限时,现有连接的行为究竟是什么?可能没有影响,这意味着您还需要终止这些后端。最后,假设应用程序对大多数表具有读写访问权限,但对模式中的其他表没有。您必须确保您的重新授予也不包括这些对象。

另一种可能性是通过查询目录并执行动态查询来锁定所有表。这对我的口味来说似乎很危险。

执行

暂停服务

数据库实例名称是“gitlabhq”,应用程序的用户名是“gitlab”。用你自己的替换它:

  psql -Upostgres  <<'PAUSE_DB'
    -- 1. disable new connections
    alter database gitlabhq_production with allow_connections = off;
    -- 2. Make DB read-only
    alter database gitlabhq set default_transaction_read_only = true;
    -- 3. Inobtrusively but safely terminate current connections
    DO $X$ BEGIN
        -- kill open idle connections, try up to 9x. Last time, kill regardless
        FOR i IN 1..10 LOOP
          PERFORM pg_terminate_backend(pid) from pg_stat_activity where usename = 'gitlab'
            and (i >= 10 OR state in ('idle', 'disabled' ));
          PERFORM pg_stat_clear_snapshot();
          EXIT WHEN NOT EXISTS ( select pid from pg_stat_activity where usename = 'gitlab' );
          RAISE NOTICE 'pg backends still open: sleeping 2 seconds';
          PERFORM pg_sleep(2);
          PERFORM pg_stat_clear_snapshot();
        END LOOP;
        -- send notice if still open connections
        IF EXISTS ( select pid from pg_stat_activity where usename = 'gitlab' ) THEN
            RAISE NOTICE 'Hung backends. Backup might not be 100%% consistent';
        END IF;
    END;$X$;
    -- 4. Allow read-only connections while checkpointing/snapshotting
    alter database gitlabhq with allow_connections = on;
    CHECKPOINT;
Run Code Online (Sandbox Code Playgroud)

恢复

    alter database gitlabhq_production with allow_connections = off;
    alter database gitlabhq set default_transaction_read_only = false;
    SELECT pg_stat_clear_snapshot();
    SELECT pg_terminate_backend(pid) from pg_stat_activity where usename = 'gitlab';
    alter database gitlabhq with allow_connections = on;
Run Code Online (Sandbox Code Playgroud)

在这最后一步中,您可能会终止长时间运行的只读/SELECT 查询,但根据我的经验,这种长时间运行的查询可能会持续数分钟甚至数小时,为了确保正常运行时间,可以终止这些查询其他所有人。


Cra*_*ger 7

我认为个人希望将此功能作为官方 PostgreSQL 功能。

做起来很简单

如果您不想为 PostgreSQL 扩展编写 C 代码,那么您可以在 PostgreSQL 前面放置一个连接池。像 pgBouncer。

pgBouncer能够暂停内置的应用程序活动。尽管要使其非常有用,您需要直接连接(而不是通过 pgbouncer)并在暂停新连接后取消活动连接。只需select pg_terminate_backend(pid) from pg_stat_activity where pid <> pg_backend_pid().

做对了

但是,如果您愿意亲自动手,则可以使用 C 扩展名来完成。扩展必须:

  • 加载,shared_preload_libraries以便它可以注册一个小的静态共享内存段,带有一个布尔标志,如db_is_locked

  • 注册 aProcessUtility_hookExecutorStart_hook测试 shmem 中的 is-locked 标志,如果设置,则在WaitLatch循环中休眠,直到它看到该标志再次被清除。(您可以改用解析器钩子)。

  • 用 C 编写两个 SQL 可调用函数。一个设置标志。另一个清除标志并迭代PGPROC设置所有用户进程的闩锁,因此他们知道立即唤醒。

  • 可选地编写第三个函数,如果设置了标志,则迭代PGXACT以查找打开的写入事务并通知它们终止。

所有这些都已经作为BDR 扩展的一部分实施,但它是更大系统的一部分。您很可能将相关部分提取到您自己的扩展中。见bdr_locks.cbdr_commandfilter.cbdr_executor.cbdr.c,等。

请注意,这不会使 PostgreSQL在磁盘上成为只读- 检查指针将继续运行,bgwriter 将继续运行,归档程序仍将运行,等等。所以让您在没有原子的情况下进行数据库备份是不够的文件系统快照或pg_start_backup()/ pg_stop_backup()。但是对于您的用例来说很好,暂停数据库中的应用程序活动。