在我的项目中,我们有时必须将所有数据从一个模式复制到另一个模式。我通过简单的truncate
/ insert into select *
脚本自动执行此操作,但很快就意识到这种方式不能容忍源模式中的更改(添加/删除需要修改脚本的表)。因此,今天我决定将其更改为PL / PGSQL脚本,该脚本使用动态查询创建表和副本数据。我的第一个实现是这样的:
do
$$
declare
source_schema text := 'source_schema';
dest_schema text := 'dest_schema';
obj_name text;
source_table text;
dest_table text;
alter_columns text;
begin
for dest_table in
select table_schema || '.' || table_name
from information_schema.tables
where table_schema = dest_schema
order by table_name
loop
execute 'drop table ' || dest_table;
end loop;
raise notice 'Data cleared';
for obj_name in
select table_name
from information_schema.tables
where table_schema = source_schema
order by table_name
loop
source_table := source_schema || '.' || obj_name;
dest_table := dest_schema || '.' || obj_name;
execute 'create unlogged table ' || dest_table
|| ' (like ' || source_table || ' including comments)';
alter_columns := (
select string_agg('alter column ' || column_name || ' drop not null', ', ')
from information_schema.columns
where table_schema = dest_schema and table_name = obj_name
and is_nullable = 'NO');
if alter_columns is not null then
execute 'alter table ' || dest_table || ' ' || alter_columns;
end if;
execute 'insert into ' || dest_table || ' select * from ' || source_table;
raise notice '% done', obj_name;
end loop;
end;
$$
language plpgsql;
由于目标架构是只读的,因此我在没有约束的情况下创建它以达到最大性能。我不认为NOT NULL约束是一个大问题,但我决定将所有内容保留在原处。
这个解决方案工作得很好,但我注意到与静态脚本相比,复制数据需要更长的时间。不是戏剧性的,但稳定地比静态脚本长20-30秒。
我决定调查一下。我的第一步是评论insert into select *
声明,找出其他所有时间。它表明清理并重新创建所有表只需要半秒钟。我的线索是INSERT语句在程序上下文中以某种方式工作的时间更长。
然后我添加了执行时间的测量:
ts := clock_timestamp();
execute 'insert into ...';
raise notice 'obj_name: %', clock_timestamp() - ts;
我还在psql中使用\timing
执行了旧的静态脚本。但这表明我的假设是错误的。所有插入语句或多或少同时进行,在动态脚本中主要甚至更快(我认为这是由于psql中每个语句之后的自动提交和网络往返)。但是,动态脚本的总时间再次更长,而不是静态脚本的时间。
神秘?
然后我添加了非常详细的日志记录,时间戳如下:
raise notice '%: %', clock_timestamp()::timestamp(3), 'label';
我发现有时create table
会立即执行,但有时需要几秒钟才能完成。好的,但是为什么所有表格的所有这些语句在我的第一个实验中花了几毫秒才完成?
然后我基本上将一个循环分成两个:第一个创建所有表(我们现在知道它只需要几毫秒),第二个只插入数据:
do
$$
declare
source_schema text := 'onto_oper';
dest_schema text := 'onto';
obj_name text;
source_table text;
dest_table text;
alter_columns text;
begin
raise notice 'Clearing data...';
for dest_table in
select table_schema || '.' || table_name
from information_schema.tables
where table_schema = dest_schema
order by table_name
loop
execute 'drop table ' || dest_table;
end loop;
raise notice 'Data cleared';
for obj_name in
select table_name
from information_schema.tables
where table_schema = source_schema
order by table_name
loop
source_table := source_schema || '.' || obj_name;
dest_table := dest_schema || '.' || obj_name;
execute 'create unlogged table ' || dest_table
|| ' (like ' || source_table || ' including comments)';
alter_columns := (
select string_agg('alter column ' || column_name || ' drop not null', ', ')
from information_schema.columns
where table_schema = dest_schema and table_name = obj_name
and is_nullable = 'NO');
if alter_columns is not null then
execute 'alter table ' || dest_table || ' ' || alter_columns;
end if;
end loop;
raise notice 'All tables created';
for obj_name in
select table_name
from information_schema.tables
where table_schema = source_schema
order by table_name
loop
source_table := source_schema || '.' || obj_name;
dest_table := dest_schema || '.' || obj_name;
execute 'insert into ' || dest_table || ' select * from ' || source_table;
raise notice '% done', obj_name;
end loop;
end;
$$
language plpgsql;
令人惊讶的是它修复了一切!这个版本的工作速度比旧的静态脚本快!
我们得出了一个非常奇怪的结论:create table
insert
之后的某个时间可能需要很长时间。这非常令人沮丧。尽管我解决了我的问题,但我不明白为什么会这样。有人有任何想法吗?