Postgresql 为什么可以';创建表';在Postgres中需要几秒钟?
在我的项目中,有时我们必须将所有数据从一个模式复制到另一个模式。我通过简单的Postgresql 为什么可以';创建表';在Postgres中需要几秒钟?,postgresql,dynamic-sql,postgresql-9.4,Postgresql,Dynamic Sql,Postgresql 9.4,在我的项目中,有时我们必须将所有数据从一个模式复制到另一个模式。我通过简单的truncate/insert-into-select*脚本自动实现了这一点,但我很快意识到这种方式不能容忍源模式中的更改(添加/删除表需要修改脚本)。所以今天我决定将它改为PL/PGSQL脚本,它使用动态查询创建表并复制数据。我的第一个实现是这样的: do $$ declare source_schema text := 'source_schema'; dest_schema text := 'dest_sc
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;
raise notice '%: %', clock_timestamp()::timestamp(3), 'label';
由于目标模式是只读的,所以我创建它时不使用约束以达到最大性能。我不认为NOTNULL约束有什么大不了的,但我决定把一切都保持原样
这个解决方案工作得很好,但我注意到,与静态脚本相比,复制数据需要更长的时间。不是很明显,但稳定地说,它比静态脚本长20-30秒
我决定调查一下。我的第一步是在select*语句中添加注释insert,以了解其他所有操作所需的时间。它表明,清除和重新创建所有表只需半秒钟。我的线索是,INSERT语句在过程上下文中的工作时间更长
然后我添加了执行时间的度量:
ts := clock_timestamp();
execute 'insert into ...';
raise notice 'obj_name: %', clock_timestamp() - ts;
我还使用psql中的\time
执行了旧的静态脚本。但这表明我的假设是错误的。所有insert语句都花费了差不多相同的时间,在动态脚本中主要是更快(我想这是由于psql中每个语句之后的自动提交和网络往返)。然而,动态脚本的总时间再次比静态脚本的总时间长
神秘主义
然后我添加了非常详细的日志记录,时间戳如下:
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;
raise notice '%: %', clock_timestamp()::timestamp(3), 'label';
我发现有时createtable
会立即执行,但有时需要几秒钟才能完成。好的,但是为什么在我的第一个实验中,所有表的所有这些语句只花了几毫秒就完成了呢
然后我基本上将一个循环分为两个:第一个循环创建所有表(我们现在知道它只需要毫秒),第二个循环只插入数据:
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
afterinsert
s有时可能需要很长时间。这是非常令人沮丧的。尽管我解决了我的问题,但我不明白为什么会这样。有人有什么想法吗?小题大做::使用quote_ident()
,甚至format()
来构造动态查询。他们将处理crippelled表名(带有空格、大写等)。这个问题很可能是由DDL期间锁定+解锁目录引起的。@wildplasser它如何解释两个脚本之间的差异?为什么在第一种情况下遇到目录锁的概率更高?