再大的 DDL 变更操作都不怕了!一条命令直接搞定

开发 前端
如果操作是在一个繁忙的表上执行的,则可以使用pg-osc的kill-backend功能,来终止可能与pg-osc操作竞争的其他后端,以便在短时间内获取锁。

介绍

pg-online-schema-change (pg-osc) 是一个工具,用于以最小的锁在 PostgreSQL 表中进行模式更改(任何ALTER语句),以帮助实现在生产环境工作负载下进行零停机的模式更改。

pg-osc使用了影子表的概念来执行模式更改。在较高级别上,它会创建一个在结构上与主表相同的影子表,对影子表执行模式更改,将内容从主表复制到影子表,并在最后交换表名称,同时使用触发器(通过审计表)保留对主表的所有更改。

pg-osc受到了pt-online-schema-change (MySQL) 和pg_repack等工具的设计和工作原理的启发。可在下面阅读更多内容,关于它是如何工作的、亮点特性、注意事项和示例。

用法

pg-online-schema-change help perform

Usage:
  pg-online-schema-change perform -a,--alter-statement=ALTER_STATEMENT -d,--dbname=DBNAME -h,--host=HOST -p,--port=N -s,--schema=SCHEMA -u,--username=USERNAME

Options:
-a,--alter-statement=ALTER_STATEMENT # The ALTER statement to perform the schema change
-s,--schema=SCHEMA                   # The schema in which the table is. Default: public
-d,--dbname=DBNAME                   # Name of the database
-h,--host=HOST                       # Server host where the Database is located
-u,--username=USERNAME               # Username for the Database
-p,--port=N                          # Port for the Database. Default: 5432
-w,[--password=PASSWORD] # DEPRECATED: Password for the Database. Please pass PGPASSWORD environment variable instead.
-v,[--verbose],[--no-verbose] # Emit logs in debug mode
-f,[--drop],[--no-drop] # Drop the original table in the end after the swap
-k,[--kill-backends],[--no-kill-backends] # Kill other competing queries/backends when trying to acquire lock for the shadow table creation and swap. It will wait for --wait-time-for-lock duration before killing backends and try upto 3 times.
-w,[--wait-time-for-lock=N] # Time to wait before killing backends to acquire lock and/or retrying upto 3 times. Default: 10. It will kill backends if --kill-backends is true, otherwise try upto 3 times and exit if it cannot acquire a lock.
-c,[--copy-statement=COPY_STATEMENT] # Takes a .sql file location where you can provide a custom query to be played (ex: backfills) when pgosc copies data from the primary to the shadow table. More examples in README.
-b,[--pull-batch-count=N] # Number of rows to be replayed on each iteration after copy. Default: 1000. This can be tuned for faster catch up and swap. Best used with delta-count.
-e,[--delta-count=N] # Indicates how many rows should be remaining before a swap should be performed. Default: 20. This can be tuned for faster catch up and swap, especially on highly volume tables. Best used with pull-batch-count.
-o,[--skip-foreign-key-validation],[--no-skip-foreign-key-validation] # Skip foreign key validation after swap. You shouldn't need this unless you have a very specific use case, like manually validating foreign key constraints after swap.
Usage:
  pg-online-schema-change --version, -v

print the version

亮点特性

  • • pg-osc支持在添加、删除或重命名列时运行,而不会丢失数据。
  • • pg-osc在整个过程中只获取最少的锁(阅读下面的警告)。
  • • 复制索引和外键。
  • • 可以在最后删除或保留旧表(可选)。
  • • 减少膨胀(因为 pg-osc 会创建一个新表,并在交换后删除旧表)。
  • • 调整审计/日志表中重放速度的快慢(重放更大的工作负载)。
  • • 在将数据从主表复制到影子表时,回填旧列/新列,然后执行交换。示例。

示例

重命名列

export PGPASSWORD=""
pg-online-schema-change perform \
  --alter-statement 'ALTER TABLE books RENAME COLUMN email TO new_email' \
  --dbname "postgres" \
  --host "localhost" \
  --username "jamesbond"

多个 ALTER 语句

export PGPASSWORD=""
pg-online-schema-change perform \
  --alter-statement 'ALTER TABLE books ADD COLUMN "purchased" BOOLEAN DEFAULT FALSE; ALTER TABLE books RENAME COLUMN email TO new_email;' \
  --dbname "postgres" \
  --host "localhost" \
  --username "jamesbond" \
  --drop

5 秒后杀死其他后端

如果操作是在一个繁忙的表上执行的,则可以使用pg-osc的kill-backend功能,来终止可能与pg-osc操作竞争的其他后端,以便在短时间内获取锁。pg-osc获得的ACCESS EXCLUSIVE锁只持有一小段时间,然后就释放了。您可以调整在杀死其他后端之前pg-osc应该等待多长时间(或者pg-osc是否应该在一开始就杀死后端)。

export PGPASSWORD=""
pg-online-schema-change perform \
--alter-statement 'ALTER TABLE books ADD COLUMN "purchased" BOOLEAN DEFAULT FALSE;' \
--dbname "postgres" \
--host "localhost" \
--username "jamesbond" \
--wait-time-for-lock5 \
--kill-backends \
--drop

重放更大的工作负载

如果表的写入吞吐量较高,则默认的重放速度可能不够用。也就是说,您可能会看到pg-osc从审计表中一次性重放 1000 行(pull-batch-count)。pg-osc还会等到审计表中的剩余行数(delta-count)为 20 后再进行交换。您可以将这些值调整为更高的值,以便更快地赶上此类工作负载。

export PGPASSWORD=""
pg-online-schema-change perform \
--alter-statement 'ALTER TABLE books ADD COLUMN "purchased" BOOLEAN DEFAULT FALSE;' \
--dbname "postgres" \
--host "localhost" \
--username "jamesbond" \
--pull-batch-count 2000
--delta-count 500
--wait-time-for-lock5 \
--kill-backends \
--drop

回填数据

在将数据插入到影子表时,您可以传入自定义 sql 文件,来执行复制和任何其他工作,而不仅仅是从主表复制所有列和行。例如:回填某些列。通过提供copy-statement,pg-osc将改为运行查询以执行复制操作。

重要提示:

• 可能会意外违反约束或不复制数据,因此请谨慎操作。

• 在自定义 SQL 中连接时,必须使用 OUTER JOIN,否则会丢失与连接表不匹配的行。

• ALTER语句可能会更改表的结构,因此请谨慎操作。

• 保留%{shadow_table},因为它会替换为影子表的目标。

• 强烈建议用户在生产环境上使用之前,先测试和验证结果!

-- file: /src/query.sql
INSERT INTO %{shadow_table}(foo, bar, baz, rental_id, tenant_id)
SELECT a.foo,a.bar,a.baz,a.rental_id,r.tenant_id AS tenant_id
FROM ONLY examples a
LEFT OUTER JOIN rentals r
ON a.rental_id = r.id
pg-online-schema-change perform \
  --alter-statement 'ALTER TABLE books ADD COLUMN "tenant_id" VARCHAR;' \
  --dbname "postgres" \
  --host "localhost" \
  --username "jamesbond" \
  --copy-statement "/src/query.sql" \
  --drop

使用 Docker 运行

docker run --network host -it --rm shayonj/pg-osc:latest \
    pg-online-schema-change perform \
    --alter-statement 'ALTER TABLE books ADD COLUMN "purchased" BOOLEAN DEFAULT FALSE; ALTER TABLE books RENAME COLUMN email TO new_email;' \
    --dbname "postgres" \
    --host "localhost" \
    --username "jamesbond" \
    --drop

注意事项

• 目前尚不支持分区表。欢迎提供 PR 请求和想法。

• 表上应存在主键;没有的话,pg-osc会引发异常

• 这是因为,目前没有其他方法可以在重放期间唯一标识行。

• pg-osc会在操作期间两次获取父表上的ACCESS EXCLUSIVE锁。

• 第一次,在设置触发器和影子表时。

• 下一次,在执行交换和更新外键引用时。

• 注意:如果指定了kill-backends,它会尝试终止在这两个时间内的任何竞争操作。

• 根据设计,pg-osc不会终止正在执行的任何其他 DDL。在操作过程中,最好不要对父表运行任何 DDL。

• 由于复制表的性质,磁盘上需要有足够的空间来支持该操作。

• 索引、约束和序列名称会被更改,并失去其原始命名。

• 可以在将来的版本中修复。如有需要,可创建特性请求。

• 外键会被删除,并重新以NOT VALID的方式添加到引用表。接着运行VALIDATE CONSTRAINT操作。

• 要确保保持完整性,并在重新引入外键时不用获取额外的锁,因此才会使用NOT VALID。

它是如何工作的

此工具中有 3 种类型的表:

  • • 主表:可能要对其运行模式更改的表
  • • 影子表:现有主表的副本表
  • • 审计表:用于存储主表上的任何更新/插入/删除的表

how-it-workshow-it-works

1. 创建一个审计表,以记录对父表所做的更改。

2. 获取一个简短的ACCESS EXCLUSIVE锁,以在父表上添加触发器,记录插入、更新、删除操作到审计表。

3. 创建一个新的影子表,并在影子表上运行 ALTER 或迁移。

4. 复制旧表中的所有行。

5. 在新表上构建索引。

6. 将审计表中累积的所有更改重放到影子表中。

• 在审计审计表中的行时,删除这些行。

7. 一旦差量行数(剩余行数)到 ~20,则在事务中获取父表上的ACCESS EXCLUSIVE锁,然后:

• 交换表名称(影子表 <> 父表)。

• 删除外键,并重新以NOT VALID的方式创建,以更新其他表中的外键引用。

8. 在新表上运行ANALYZE。

9. 验证所有添加的NOT VALID的外键。

10. 删除父表(现在是旧表)(可选)。

责任编辑:武晓燕 来源: 红石PG
相关推荐

2019-06-17 05:03:37

memcache内核架构

2020-12-08 09:13:51

MySQLDDL变更

2011-03-21 17:19:12

LAMPUbuntu

2024-02-20 13:43:12

2022-01-01 18:26:21

nginx

2024-02-01 18:07:37

2021-12-02 15:30:55

命令内存Linux

2023-02-28 17:27:02

分库分表中间件

2020-05-26 09:08:23

命令循环Linux

2024-06-12 13:36:24

2015-07-28 11:06:28

日志记录Linux命令

2023-11-28 15:19:00

Linux运维btop命令

2021-04-13 11:12:58

电脑WindowsIT

2021-08-25 14:58:47

MacOSGreenplumDocker

2010-04-13 16:57:01

2021-02-15 15:07:45

Windows 10Windows微软

2019-03-28 10:09:49

内存CPU硬盘

2009-08-05 10:43:19

CISSPBCPDRP

2024-07-29 09:49:00

SQLMySQL执行

2021-10-26 22:25:24

Windows 11Windows微软
点赞
收藏

51CTO技术栈公众号