暂无图片
暂无图片
11
暂无图片
暂无图片
暂无图片

玩转pg_checksums

2990

作者

Greg Sabino Mullane

译者

张⽂升

数据校验和是PostgreSQL的⼀个很棒的特性。它们⽤于检测任何Postgres存储在磁盘上的数据损坏。我们在 Crunchy Data开发的每个系统默认都启⽤了这个功能。不仅仅是Postgres本身可以利⽤这些校验和,⼀些第 三⽅⼯具,如很棒的pgBackRest程序也可以使⽤它们来增强数据完整性。
遗憾的是,在创建⼀个新的Postgres集群时,启⽤数据校验和仍然不是默认⾏为。当你调⽤initdb程序时, 添加‑‑data‑checksums标志(如果您更喜欢短参数,则添加‑k),你崭新的Postgres集群将启⽤数据校验和。
What's that, you say?您已经创建了没有校验和的数据库,现在要将它们添加到已创建的集群上去?庆幸的 是,现在有⼀种简单的⽅法可以通过pg_checksum实⽤程序实现这⼀点。在过去,启⽤校验和的唯⼀⽅法是 创建⼀个新的集群,并使⽤源库 pg_dump | psql ⽬标库的⽅法将旧的数据库转储到新的数据库(或者使 ⽤逻辑复制、Bucardo等更复杂的迁移)。不幸的是,这些⽅法⾮常⾮常慢,⽽且容易出错。Postgres的第12 版引⼊了⼀个新的内置程序pg_checksum,它可以在现有的⽆校验和数据库上启⽤校验和。
在开始⼀些演示之前,让我们回顾⼀下数据校验和的作⽤。⾸先,它们只对Postgres使⽤的实际表和索引数 据⽂件进⾏校验和,但系统的其他部分没有被检查。包括系统⽬录,⾃由空间映射(FSM,free space map)之类的东⻄——只有数据本身被检查。Postgres将数据作为⼀堆“⻚⾯”存储在磁盘上的⽂件中。这些 ⻚⾯实际上是校验和的。当⻚⾯从内存移动到磁盘时,⻚⾯的校验和被添加到⻚头。如果数据在Postgres之 外发⽣变化(即损坏),校验和将不再匹配,并且Postgres(以及pgBackRest!)将报校验和失败。
下⾯是pg_checksum程序的帮助输出;如你所⻅,⽤法很简单:
    $ pg_checksums --help
    pg_checksums enables, disables, or verifies data checksums in a PostgreSQL
    database cluster.
    Usage:
    pg_checksums [OPTION]... [DATADIR]
    Options:
    [-D, --pgdata=]DATADIR data directory
    -c, --check check data checksums (default)
    -d, --disable disable data checksums
    -e, --enable enable data checksums
    -f, --filenode=FILENODE check only relation with specified filenode
    -N, --no-sync do not wait for changes to be written safely to
    disk
    -P, --progress show progress information
    -v, --verbose output verbose messages
    -V, --version output version information, then exit
    -?, --help show this help, then exit
    接下来是演示!为此,我们将创建⼀个新的Postgres集群(版本14),但没有校验和。在开始之前,我们将 设置PGDATA环境变量,它告诉所有这些 Postgres实⽤程序“data”是我们数据⽬录的名称。它还避免了上⾯ 看到的令⼈困惑的冲突,因为pg_checksums将-D和-d作为参数!
      $ export PGDATA=data
      $ initdb --no-instructions --auth=trust
      The files belonging to this database system will be owned by user "greg".
      This user must also own the server process.
      The database cluster will be initialized with locale "en_US.UTF-8".
      The default database encoding has accordingly been set to "UTF8".
      The default text search configuration will be set to "english".
      Data page checksums are disabled.
      creating directory data ... ok
      creating subdirectories ... ok
      selecting dynamic shared memory implementation ... posix
      selecting default max_connections ... 100
      selecting default shared_buffers ... 128MB
      selecting default time zone ... Livingstone/Zambia
      creating configuration files ... ok
      running bootstrap script ... ok
      performing post-bootstrap initialization ... ok
      syncing data to disk ... ok
      在我们启动数据库之前,让我们合理的启⽤Postgres⽇志记录,并禁⽤所有TCP/IP监听,即使是在 localhost上:
        $ echo -e "logging_collector=on\nlisten_addresses='' " >>
        data/postgresql.conf
        $ pg_ctl start
        waiting for server to start....
        2020-09-05 00:00:47.914 EDT [8499] LOG: redirecting log output to logging
        collector process
        2020-09-05 00:00:47.914 EDT [8499] HINT: Future log output will appear in
        directory "log".
        done
        server started
        即将使⽤另⼀个环境变量。在本例中,我们希望始终使⽤名为“testdb”的数据库。创建数据库后,我们将使 ⽤pgbench⼯具来填充⼀些示例数据:
          $ export PGDATABASE=testdb
          $ createdb
          $ pgbench --initialize --scale=300
          dropping old tables...
          creating tables...
          generating data (client-side)...
          30000000 of 30000000 tuples (100%) done (elapsed 22.60 s, remaining 0.00
          s)
          vacuuming...
          creating primary keys...
          done in 39.61 s (drop tables 0.21 s, create tables 0.01 s, client-side
          generate 22.89 s, vacuum 3.29 s, primary keys 13.21 s).
          在我们这个新的Postgres集群上启⽤校验和之前,让我们先确认当前没有启⽤数据校验和。有两种⽅法可以 做到这⼀点:直接SQL查询和pg_controldata实⽤程序。让我们来看看它们的实际应⽤:
            $ psql -Axtc 'show data_checksums'
            data_checksums|off
            $ pg_controldata | grep -E 'state|checksum'
            Database cluster state: in production
            Data page checksum version: 0
            (另外:虽然我通常更喜欢⻓选项,但在涉及常⻅的psql参数时,我改变了⾃⼰的规则。-Axtc也可以写 成‑‑no‑align ‑expanded ‑‑tuples-only ‑command)。请注意,要使pg_checksums程序⼯作, 数据库必须以⼲净的⽅式关闭(稍后会详细介绍)。所以让我们尝试在正在运⾏的Postgres集群上使⽤ pg_checksums,然后关闭数据库,最后查看新的集群状态
              $ pg_checksums --pgdata data --enable
              pg_checksums: error: cluster must be shut down
              $ pg_ctl stop --mode=fast
              waiting for server to shut down.... done
              server stopped
              $ pg_controldata | grep -E 'state|checksum'
              Database cluster state: shut down
              Data page checksum version: 0
              ⻅证奇迹的时候到了!让我们将这个数据库从⼀个没有校验和的数据库更改为⼀个带有校验和的数据库!我 们将添加‑‑progress选项,假设您没有将输出重定向到某处,它会每秒更新⼀次完成进度的百分⽐。
                $ time bin/pg_checksums --enable --progress
                4518/4518 MB (100%) computed
                Checksum operation completed
                Files scanned: 1239
                Blocks scanned: 578376
                pg_checksums: syncing data directory
                pg_checksums: updating control file
                Checksums enabled in cluster
                real 0m4.719s
                user 0m1.157s
                sys 0m1.669s
                请注意,我在执⾏pg_ctl stop时指定了关闭模式,⽅法是使⽤--mode=fast选项。这会指示Postgres⽴ 即将所有连接踢出去,然后⼲净彻底地关闭。我发现最好总是显式地制定这个模式,即使“fast”是默认模 式,因为其他两种模式有⼀些严重的缺点(译者注:缺点是对于当前使⽤场景⽽⾔)。 
                使⽤smart模式 (--mode=smart)只会在所有客户端断开连接后关闭数据库。如果⽆论如何都要关闭整个数 据库,最好通过--mode=fast从数据库端停⽌任何现有的连接。即immediate(——mode=immediate),应该 被称为“紧急停⽌”,因为它尽可能快地关闭数据库。这可能会导致问题,因为数据库现在处于损坏状态,并 且pg_checksums程序将拒绝运⾏:
                  $ pg_ctl stop --mode=immediate
                  waiting for server to shut down.... done
                  server stopped
                  $ pg_controldata | grep -E 'state|checksum'
                  Database cluster state: in production
                  Data page checksum version: 0
                  $ pg_checksums --pgdata data --enable
                  pg_checksums: error: cluster must be shut down
                  $ psql -Axtc 'show data_checksums'
                  psql: error: could not connect to server: could not connect to server: No
                  such file or directory
                  Is the server running locally and accepting
                  connections on Unix domain socket "/tmp/.s.PGSQL.5432"?
                  如上所示,pg_controldata认为服务器正在⽣产中,但它已关闭,正如psql连接失败所证明的那样。因 此,如果即使您知道数据库已关闭,您的pg_checksums尝试仍将失败,请检查您的pg_controldata。要 修复它,请启动服务器然后再次⼲净地关闭它:
                    $ pg_ctl start; pg_ctl stop --mode=fast
                    waiting for server to start....
                    2020-09-05 00:38:26.866 EDT [7547] LOG: starting PostgreSQL 14.0
                    2020-09-05 00:38:26.866 EDT [7547] LOG: redirecting log output to logging
                    collector process
                    2020-09-05 00:38:26.866 EDT [7547] HINT: Future log output will appear in
                    directory "log".
                    done
                    server started
                    waiting for server to shut down.... done
                    server stopped
                    $ pg_controldata | grep state
                    Database cluster state: shut down
                    除了--enable之外,pg_checksum程序还可以做另外两件事:--check和--disable。让我们看看他们 的实际⾏动。(这个程序曾经被命名为“pg_verify_checksums”,所以它当然可以处理基本的验证)。
                      $ pg_checksums --check
                      Checksum operation completed
                      Files scanned: 1239
                      Blocks scanned: 578376
                      Bad checksums: 0
                      Data checksum version: 1
                      $ time pg_checksums --disable
                      pg_checksums: syncing data directory
                      pg_checksums: updating control file
                      Checksums disabled in cluster
                      real 0m0.180s
                      user 0m0.013s
                      sys 0m0.025s
                      请注意禁⽤的速度有多快!⼏乎是瞬间完成。这是因为disable不会费⼼将现有的校验和“归零”,⽽只是修 改Postgres控制⽂件,并告诉它不要使⽤校验和,即使校验和仍然在数据⽂件本身中设置!这是安全的,因 为pg_checksums是唯⼀已知的程序,可以更改Postgres控制⽂件的校验和。 
                      如果我们再次使⽤‑‑enable,我们将获得相同的计时,即使⻚⾯中已经有校验和。pg_checksums程序为 每个⻚⾯⽣成⼀个校验和,并使⽤新的校验和重写⻚头,即使校验和相同也会覆盖所有存在的校验和。我在 Postgres 15版本中有⼀个补丁来消除这个限制——在你读到这篇⽂章的时候,它可能已经被合并了!:)
                      pg_checksums的⼀个缺点是,它没有内置的⽅法来防⽌Postgres在运⾏时启动,这将是⼀件坏事。解决此 问题的⼀种⽅法是破坏Postgres并阻⽌它启动。虽然有⼈可能认为在postgresql.conf⽂件中放⼊⼀个语法错 误就可以解决问题,但可以使⽤备⽤配置⽂件启动Postgres。我们可以通过临时重命名Postgres 需要的核⼼ ⽂件之⼀来完全阻⽌启动。我喜欢选择pg_twophase⽬录,因为pg_checksums本身不需要它,但它是启动 Postgres的关键。因此,您的完整流程如下所示:
                        $ pg_ctl stop
                        $ mv data/pg_twophase data/pg_twophase.DO_NOT_START_THIS_DATABASE
                        $ pg_checksums --enable --progress
                        $ mv data/pg_twophase.DO_NOT_START_THIS_DATABASE data/pg_twophase
                        $ pg_ctl start
                        启⽤校验和需要多⻓时间?这取决于系统的速度和数据库的⼤⼩。pg_checksums 程序将重写每个数据⽂ 件,因此您可以通过在⼀个类似的系统上使⽤较⼩的数据库运⾏它来获得估计数,然后按⽐例进⾏⼀些乘法 来获得估计值。我的笔记本电脑每秒能够处理⼤约1.5GB的数据——⽣产设备应该⽐这更快。 
                        pg_checksums程序重写了实际的数据⽂件,但它并没有消除数据库膨胀?唯⼀需要被重写的部分是⻚头 ——这意味着对于普通表和膨胀表来说,每个⻚都需要重写。之后您的数据库⼤⼩将完全相同。
                        还有什么⽅法可以加快速度?我提交的补丁意味着只有在校验和发⽣变化时才会修改⽂件,这意味着您可以 在各个阶段执⾏校验和启⽤。运⾏该程序⼀段时间,然后停⽌它,,重新启动数据库,稍后再试⼀次。每次 迭代都会⽐上⼀次更进⼀步,因为将跳过已经通过校验和的⻚⾯。另⼀个未来的想法是启⽤校验和的并⾏启 ⽤。如果您的数据分布在多个⽂件系统中,例如使⽤表空间时,这将是最⼤的优势。 
                        校验和的性能影响如何?由于计算校验和的开销,它们是否会降低所有的运⾏速度?这是另⼀个的话题,但 简短的回答是:影响⾮常⼩。
                        那么,为了启⽤校验和,值得临时折腾吗?绝对值得。使⽤pg_checksums程序,您可以在较⼩的停机时间 内启⽤现有数据库上的校验和,让您在未来的使⽤中⾼枕⽆忧。
                        请点击文章底部“阅读原文”查看原文!




                        PostgreSQL中文社区欢迎广大技术人员投稿
                        投稿邮箱:press@postgres.cn

                        最后修改时间:2021-08-27 10:25:23
                        文章转载自PostgreSQL中文社区,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                        评论