在我的Postgres服务器上,我有一些商务智能应用程序不断访问的表,所以理想情况下它们应该在大多数时间保持可用状态。我们的ETL管道每晚刷新和重新加载表(我知道......由于一些遗留设置,我不能在这里使用增量更新)。加载过程需要很长时间,并且还没有防弹稳定性。
为了提高表的可用性,我决定先使用登台表从ETL管道加载下游数据,如果加载成功,则将数据复制到实际的生产表中。
这是我为此目的创建的复制功能:
CREATE OR REPLACE FUNCTION guarded_copy(src text, dest text) RETURNS void AS
$$
DECLARE
c1 INTEGER;
c2 INTEGER;
BEGIN
EXECUTE FORMAT('SELECT COUNT(*) FROM %I', src) INTO c1;
EXECUTE FORMAT('SELECT COUNT(*) FROM %I', dest) INTO c2;
IF c1>=c2 THEN
EXECUTE FORMAT('TRUNCATE TABLE %I CASCADE;', dest);
EXECUTE FORMAT('INSERT INTO %I SELECT * FROM %I;', dest, src);
END IF;
END
$$
LANGUAGE plpgsql VOLATILE;
如果dest
表(登台表)的行数多于{{1},那么想法是截断src
表并使用src
表中的数据加载它。表(实际生产表)。这实际上有效。
请注意,实际生产表(dest
)具有约束和索引,而登台表(dest
)配置了NO索引或约束,以加速ETL的加载过程。
上面我的函数的问题是,由于src
表上的索引和约束,数据副本可能非常昂贵。
dest
上的索引,然后将其添加回来。如何在SQL函数中执行此操作? Postgres版本:
在x86_64-unknown-linux-gnu上的PostgreSQL 9.2.6,由gcc编译(Ubuntu / Linaro 4.6.3-1ubuntu5)4.6.3,64位
表格约束:
在表dest
上,我在列dest
上有(唯一的)主键,并在时间戳列上有索引。
This question和this question确实有帮助。对于上面的选项 3 ,我认为下面的代码接近我想要的。
id
另一个想法:在仅用于分析目的的表上可能不需要PK和FK。所以索引是唯一关注的问题。
CREATE OR REPLACE FUNCTION guarded_swap(src text, dest text) RETURNS void AS
$$
DECLARE
c1 INTEGER;
c2 INTEGER;
_query TEXT;
BEGIN
EXECUTE FORMAT('SELECT COUNT(*) FROM %I', src) INTO c1;
EXECUTE FORMAT('SELECT COUNT(*) FROM %I', dest) INTO c2;
IF c1>=c2 THEN
-- create indexes in src table
FOR _query IN
SELECT FORMAT('%s;', REPLACE(pg_get_indexdef(ix.indexrelid), dest, src))
FROM pg_class t, pg_class i, pg_index ix
WHERE t.oid = ix.indrelid
AND i.oid = ix.indexrelid
AND t.relkind = 'r' and i.relkind = 'i'
AND t.oid= dest::regclass
ORDER BY
t.relname, i.relname
LOOP
EXECUTE _query;
END LOOP;
-- drop indexes in dest table
FOR _query IN
SELECT FORMAT('DROP INDEX %s;', i.relname)
FROM pg_class t, pg_class i, pg_index ix
WHERE t.oid = ix.indrelid
AND i.oid = ix.indexrelid
AND t.relkind = 'r' and i.relkind = 'i'
AND t.oid= dest::regclass
ORDER BY
t.relname, i.relname
LOOP
EXECUTE _query;
END LOOP;
-- create constraints in src table
FOR _query IN
SELECT
FORMAT ('ALTER TABLE %s ADD CONSTRAINT %s %s;', src,
REPLACE(conname, dest, src),
pg_get_constraintdef(oid))
FROM pg_constraint
WHERE contype = 'p' AND conrelid = dest::regclass
LOOP
EXECUTE _query;
END LOOP;
-- drop all constraints in dest table
FOR _query IN
SELECT
FORMAT ('ALTER TABLE %s DROP CONSTRAINT IF EXISTS %s;', dest, conname)
FROM pg_constraint
WHERE conrelid = dest::regclass
LOOP
EXECUTE _query;
END LOOP;
-- swap the table names
EXECUTE FORMAT('ALTER TABLE %I RENAME TO %I;', dest, CONCAT(dest, '_old'));
EXECUTE FORMAT('ALTER TABLE %I RENAME TO %I;', src, dest);
EXECUTE FORMAT('ALTER TABLE %I RENAME TO %I;', CONCAT(dest, '_old'), src);
END IF;
END
$$
LANGUAGE plpgsql VOLATILE;
答案 0 :(得分:0)
如果根据表(如视图和外键)没有其他对象,则删除现有表dest
并重命名新表src
是一种简单的操作。登记/>
您的列表中的项目 3。,只是没有这个考虑因素:
但这需要将一个表上的索引复制到另一个。
您不“复制”索引。只需在后台的新表src
上创建新的相同索引即可。然后表就完全准备好了,切换器只需几毫秒。但是,如果您在表上同时加载该路由,则必须准备好在并发事务中获取这些错误消息:
ERROR: could not open relation with OID 123456
相关答案以及dba.SE上的详细说明和示例代码:
答案 1 :(得分:0)
您可以构建复制系统,并且BI作业不会影响主数据库服务器,这是数据仓库和BI上非常常见的情况。 你保留了一个主服务器数据库,在另一个配置为slave的服务器上,你可以启动你的(通常是很难的)bi查询,并使用真实的新数据来启动。但是来自另一个孤立的源但具有完全相同的数据
http://www.rassoc.com/gregr/weblog/2013/02/16/zero-to-postgresql-streaming-replication-in-10-mins/