Postgres中更快的数据迁移
作者:互联网
本文摘自https://mp.weixin.qq.com/s/SlSBi9FajXfzmC8cczgVQQ
在我的日常工作中,我与许多客户一起将数据迁移到Postgres。有同构源(PostgreSQL),也有异构源(如 Oracle 和 Redshift)。为什么人们选择Postgres?由于PostgreSQL的 丰富性和 存储过程、 JSONB、地理空间工作负载的PostGIS等功能,以及许多有用的Postgres扩展,包括我个人最喜欢的:Citus。
我帮助人们进行的大部分都是到云的Postgres-to-Postgres数据迁移。由于 Azure Database for PostgreSQL运行开源Postgres,在许多情况下,应用程序迁移可以直接进行,无需花费大量精力。大部分工作通常用于决定数据迁移的正确策略。对于那些在Postgres迁移过程中无法承受任何停机时间的人,当然有数据迁移服务可以提供帮助。但是,如果您能够在特定的维护时段(例如周末、夜间等)为迁移提供一些停机时间,则可以使用简单的Postgres应用程序,如pg_dump
和pg_restore
。
在这篇文章中,让我们对pg_dump
和pg_restore
用于Postgres 数据库迁移中考虑权衡,以及如何优化迁移速度。我们还将探讨需要迁移Postgres中非常大的表的场景。对于大表,使用pg_dump
和pg_restore
迁移数据库可能不是最理想的方法。好消息是,我们将介绍一个Python工具,用于在Postgres中迁移大表。使用该工具,我们可以看到一个Postgres大表(~1.4TB)在7小时内完成迁移。使用pg_dump/pg_restore则需要1天以上。
加快pg_dump&pg_restore迁移
pg_dump是用于备份PostgreSQL数据库的标准和传统应用程序。pg_dump
会对您的Postgres数据库进行一致的快照,即使该数据库正在被使用。pg_dump
为您提供了多个命令行选项,您可以使用这些选项来控制正在备份的数据的格式和内容。pg_dump
的一些常见且最有用的命令行选项使您能够执行以下操作:
-
对转储特定模式、特定表、数据等进行细粒度控制。
-
控制转储的格式;选项包括纯文本或自定义或目录格式,默认情况下会进行压缩。
-
使用
--jobs/-j
选项,该选项提供了指定用于转储的并发线程数的功能。每个线程转储一个特定的表,这个选项控制同时转储多少个表。
您可以使用 pg_restore 应用程序从pg_dump
创建的存档恢复PostgreSQL数据库。与pg_dump
类似,pg_restore
还提供了对如何恢复归档的大量控制。例如,可以将还原限制为特定的数据库对象/实体,为还原指定并行作业,等等。
提示:将执行pg_dump/pg_restore的客户端放在尽可能靠近源数据库和目标数据库的位置,以避免出现网络延迟的性能问题。
总之,pg_dump
和pg_restore
是用于同构(Postgres 到 Postgres)数据库迁移的最常用、健壮且经验证的应用程序。当您能够承受停机时间时,使用这些应用程序是执行数据迁移的默认方式。
由于pg_dump
和pg_restore
提供了丰富的命令行选项,因此必须根据当前场景以最佳方式使用这些选项。让我们浏览一下您可能面临的一些场景,以了解如何最好地使用pg_dump
和pg_restore
。
如果需要迁移5个以上的Postgres大表,该怎么办?
假设您的Postgres数据库有多个(比如超过5个)大小适中(大于5GB)的表。您可以使用-j
指定在执行pg_dump
和pg_restore
时要使用的线程数。这样做不仅可以最大化源服务器和目标服务器上的资源(cpu/内存/磁盘)利用率,还可以扩展可用的网络带宽(但是,您应该注意,pg_dump
和pg_restore
不要成为带宽霸主,也不要影响其他工作负载。)。因此,使用pg_dump
和pg_restore
可以显著提高性能。
如果在Postgres服务器上执行脱机迁移时没有其他负载,则可以指定-j
是系统中核心数的倍数,这将最大限度地提高服务器利用率。但是,如果您只是出于备份/恢复原因在具有生产负载的服务器上执行转储/恢复,请确保-j
指定不影响现有负载性能。
您可以使用目录格式(-Fd
),这将内在地提供一个压缩转储(使用的gzip)。在使用-Fd
时,我们有时会看到超过5倍的压缩。对于较大的数据库(例如超过1 TB),压缩转储可以减少磁盘IOPs在捕获转储的服务器上受到瓶颈的影响。
以下是pg_dump
和pg_restore
命令示例,分别使用5个并发进行转储和还原:
pg_dump -d 'postgres://username:password@hostname:port/database' -Fd -j 5 -f dump_dir pg_restore --no-acl --no-owner -d 'postgres://username:password@hostname:port/database' --data-only -Fd -j5 dump_dir
如果大多数表都很小,但其中一个表非常大,如何迁移?
假设您的数据库只有一个大表(超过5GB),而其余的都很小(小于1GB)。您可以将pg_dump
的输出通过管道传输到pg_restore
中,这样您就不必等到转储完成后再开始恢复;两者可以同时运行。这样可以避免将转储存储在客户机上,因为避免将转储存储在客户机上可以显著降低将转储写入磁盘所需的IOPs开销。
在这种情况下,-j
可能没有帮助,因为pg_dump/pg_restore每个表只运行一个线程。在转储和还原最大的表时,应用程序将被限制。此外,不幸的是,当您使用-j
时,无法将pg_dump
的输出通过管道传输到pg_restore
。下面是显示用法的示例命令:
pg_dump -d 'postgres://username:password@hostname:port/source_database' -Fc | pg_restore --no-acl --no-owner -d 'postgres://username:password@hostname:port/target_database' --data-only
以上两部分中的技术可以大大缩短pg_dump
和pg_restore
数据迁移时间,特别是当涉及一个或多个大型表时。
pg_dump/pg_restore是单表级别的单线程,这会降低迁移速度
即使在使用上述优化时,由于pg_dump
和pg_restore
在迁移单个表时只能使用单个线程,因此在特定的一组非常大的表上,整个迁移可能会遇到瓶颈。这导致了以下问题。
如何在PostgreSQL中使用多个线程迁移单个大表?
您可以利用多个线程来迁移单个大表,方法是在逻辑上将Postgres表分块/划分为多个部分,然后使用一对线程,每个部分一个从源读取,一个向目标写入。可以基于水印列对表进行分块。水印列可以是单调递增的列(例如,id列)(或)时间戳列(例如,created_at、updated_at等)。
有许多商业工具可以实现上述逻辑。本着共享的精神,下面是一个名为Parallel Loader的Python脚本,它是上述逻辑的一个示例实现。如果您想自己使用,可以在 Parallel Loader script on GitHub 上找到。
#suppose the filename is parallel_migrate.py import os import sys #source info source_url = sys.argv[1] source_table = sys.argv[2] #dest info dest_url = sys.argv[3] dest_table = sys.argv[4] #others total_threads=int(sys.argv[5]); size=int(sys.argv[6]); interval=size/total_threads; start=0; end=start+interval; for i in range(0,total_threads): if(i!=total_threads-1): select_query = '\"\COPY (SELECT * from ' + source_table + ' WHERE id>='+str(start)+' AND id<'+str(end)+") TO STDOUT\""; read_query = "psql \"" + source_url + "\" -c " + select_query write_query = "psql \"" + dest_url + "\" -c \"\COPY " + dest_table +" FROM STDIN\"" os.system(read_query+'|'+write_query + ' &') else: select_query = '\"\COPY (SELECT * from '+ source_table +' WHERE id>='+str(start)+") TO STDOUT\""; read_query = "psql \"" + source_url + "\" -c " + select_query write_query = "psql \"" + dest_url + "\" -c \"\COPY " + dest_table +" FROM STDIN\"" os.system(read_query+'|'+write_query) start=end; end=start+interval;
如何调用并行加载脚本
python parallel_migrate.py "host=test_src.postgres.database.azure.com port=5432 dbname=postgres user=test@test_src password=xxxx sslmode=require" test_table "host=test_dest.postgres.database.azure.com port=5432 dbname=postgres user=test@test_dest password=xxxx sslmode=require" test_table 8 411187501
使用并行加载脚本,还可以控制用于迁移大型表的线程数。在上面的调用中,number_of_threads参数控制并行度因子。
并行加载脚本的调用示例
python parallel_migrate.py "host=test_src.postgres.database.azure.com port=5432 dbname=postgres user=test@test_src password=xxxx sslmode=require" test_table "host=test_dest.postgres.database.azure.com port=5432 dbname=postgres user=test@test_dest password=xxxx sslmode=require" test_table 8 411187501
上面的实现使用表中单调递增的id列将其分块,并使用并行线程将数据从源表流到目标表。您可以在 pre-requisites and recommendations 上找到一些使用并行加载程序的先决条件和建议。
针对Postgres大表比较并行加载脚本与pg_dump & pg_restore的性能
为了比较并行加载脚本与pg_dump
/pg_restore
的性能,我使用这两种技术将1.4 TB的Postgres表(带索引)从同一区域的一个Postgres数据库迁移到Azure中的另一个数据库。
您可以在下表中看到,并行加载脚本执行此Postgres到Postgres数据迁移的速度比pg_dump
和pg_restore
快3倍以上。
并行加载脚本 | pg_dump & pg_restore | |
---|---|---|
迁移耗时 | 7 hours 45 minutes | > 1 day |
图1:我们观察到迁移过程中每5分钟的网络吞吐量约为9.5GB,峰值为每5分钟27.9GB。
并行加载使用COPY命令提高性能
请注意,并行加载每个线程使用COPY命令从源数据库读取数据并将数据写入目标数据库。COPY命令是Postgres中批量接收的最佳方式。我们已经看到使用COPY命令每秒的摄取吞吐量超过一百万行。
图2:显示由目标数据库上的COPY命令组成的活动(pg_stat_activity)的屏幕截图。这些COPY命令由并行加载脚本在迁移大表时生成。每个COPY命令都转换为脚本生成的单个线程。
一句话:您可以将pg_dump/pg_restore与并行加载程序结合使用,以加快Postgres数据迁移
pg_dump/pg_restore应用程序是从Postgres迁移到Postgres的绝佳工具。但是,当数据库中有非常大的表时,它们可能会大大降低速度。为了解决这个问题,您可以使用本文中介绍的方法:通过使用并行加载脚本将单个大表并行化迁移到Postgres。并行加载脚本可以处理大表,而pg_dump/pg_restore可以用于迁移其余的Postgres表。
标签:restore,Postgres,dump,转储,pg,迁移,数据 来源: https://www.cnblogs.com/huanying47/p/16579504.html