将数据逐步导入PostgreSQL数据库

时间:2016-11-07 16:34:20

标签: sql database postgresql

情况:

我有一个PostgreSQL数据库,它从现场部署的单元中的传感器记录数据(让我们称之为 源数据库 )。该单元具有非常有限的硬盘空间,这意味着如果保持不变,数据记录将导致数据库所在的磁盘在一周内填满。我有一个(非常有限的)数据库的网络链接(所以我想压缩转储文件),在所述链接的另一边我有另一个PostgreSQL数据库(让我们称之为 < em>目标数据库 ),它有很多可用空间(为了论证,请说源的空间非常有限,而且目的地在空间方面是无限的。)

我需要对源数据库进行增量备份,将自上次备份以来添加的行追加到目标数据库,然后从源数据库中清除添加的行。

现在,自上次备份以来,源数据库可能已清除,也可能未清除,因此目标数据库只能在自动(脚本)进程中导入新行,但pg_restore失败尝试从与目标数据库具有相同主键号的转储进行还原时,可悲。

所以问题是:

仅从源中还原目标数据库中的行的最佳方法是什么?

到目前为止,我提出的唯一解决方案是pg_dump数据库并使用pg_restore将转储恢复到目标端的新辅助数据库,然后使用简单sql来整理主目标数据库中已存在的行。但似乎应该有更好的方式......

额外问题:在这样的应用程序中使用PostgreSQL我完全错了吗?我可以接受其他数据收集替代方案的建议......)

3 个答案:

答案 0 :(得分:1)

一种好的开始方式可能是使用--inserts选项pg_dump。从文档(强调我的):

  

将数据转储为INSERT命令(而不是COPY)。这将使   恢复很慢;它主要用于制作可以转储的转储   被加载到非PostgreSQL数据库中。 但是,因为这个选项   为每一行生成一个单独的命令,重新加载一行时出错   导致只丢失该行而不是整个表格内容。   请注意,如果重新排列,恢复可能会完全失败   列顺序。 --column-inserts选项对列顺序是安全的   变化,但更慢。

我目前无法使用pg_restore对其进行测试,但这可能适合您的情况。

您还可以使用从版本9.5开始,PostgreSQL为INSERT提供ON CONFLICT DO ...的事实。使用简单的脚本语言将这些添加到转储中,你应该没问题。遗憾的是,我没有找到pg_dump自动添加这些选项的选项。

答案 1 :(得分:1)

您可能会谷歌“偶尔连接数据库同步”以查看相关解决方案。

据我所知,这不是一个整齐解决的问题 - 有一些常见的解决方法,但我不知道以数据库为中心的开箱即用解决方案。

最常见的处理方法是使用消息总线在您的计算机之间移动事件。例如,如果您的“源数据库”只是一个数据存储,没有其他逻辑,您可以摆脱它,并使用消息总线说“事件x已发生”,并指向该消息总线的端点您的“目标计算机”,然后将其写入您的数据库。

您可以考虑Apache ActiveMQ或阅读“Patterns of enterprise integration”。

答案 2 :(得分:0)

#!/bin/sh

PSQL=/opt/postgres-9.5/bin/psql

TARGET_HOST=localhost
TARGET_DB=mystuff
TARGET_SCHEMA_IMPORT=copied
TARGET_SCHEMA_FINAL=final

SOURCE_HOST=192.168.0.101
SOURCE_DB=slurpert
SOURCE_SCHEMA=public

########
create_local_stuff()
{
${PSQL} -h ${TARGET_HOST} -U postgres ${TARGET_DB} <<OMG0

CREATE SCHEMA IF NOT EXISTS  ${TARGET_SCHEMA_IMPORT};
CREATE SCHEMA IF NOT EXISTS  ${TARGET_SCHEMA_FINAL};
CREATE TABLE IF NOT EXISTS  ${TARGET_SCHEMA_FINAL}.topic
        ( topic_id INTEGER NOT NULL PRIMARY KEY
        , topic_date TIMESTAMP WITH TIME ZONE
        , topic_body text
        );

CREATE TABLE IF NOT EXISTS  ${TARGET_SCHEMA_IMPORT}.tmp_topic
        ( topic_id INTEGER NOT NULL PRIMARY KEY
        , topic_date TIMESTAMP WITH TIME ZONE
        , topic_body text
        );
OMG0
}
########
find_highest()
{
${PSQL} -q -t -h ${TARGET_HOST} -U postgres ${TARGET_DB} <<OMG1
SELECT MAX(topic_id) FROM ${TARGET_SCHEMA_IMPORT}.tmp_topic;
OMG1
}
########
fetch_new_data()
{
watermark=${1-0}

echo ${watermark}

${PSQL} -h ${SOURCE_HOST} -U postgres ${SOURCE_DB} <<OMG2

\COPY (SELECT topic_id, topic_date, topic_body FROM ${SOURCE_SCHEMA}.topic WHERE topic_id >${watermark}) TO '/tmp/topic.dat';
OMG2
}
########

insert_new_data()
{
${PSQL} -h ${TARGET_HOST} -U postgres ${TARGET_DB} <<OMG3

DELETE FROM ${TARGET_SCHEMA_IMPORT}.tmp_topic WHERE 1=1;

COPY ${TARGET_SCHEMA_IMPORT}.tmp_topic(topic_id, topic_date, topic_body) FROM '/tmp/topic.dat';

INSERT INTO ${TARGET_SCHEMA_FINAL}.topic(topic_id, topic_date, topic_body)
SELECT topic_id, topic_date, topic_body
FROM ${TARGET_SCHEMA_IMPORT}.tmp_topic src
WHERE NOT EXISTS (
        SELECT *
        FROM ${TARGET_SCHEMA_FINAL}.topic nx
        WHERE nx.topic_id = src.topic_id
        );
OMG3
}
########

delete_below_watermark()
{
watermark=${1-0}

echo ${watermark}

${PSQL} -h ${SOURCE_HOST} -U postgres ${SOURCE_DB} <<OMG4

-- delete not yet activated; COUNT(*) instead
-- DELETE
SELECT COUNT(*)
FROM ${SOURCE_SCHEMA}.topic WHERE topic_id <= ${watermark}
        ;
OMG4
}
######## Main

#create_local_stuff

watermark="`find_highest`"
echo 'Highest:' ${watermark}

fetch_new_data ${watermark}
insert_new_data

echo 'Delete below:' ${watermark}
delete_below_watermark ${watermark}

# Eof

这只是一个例子。一些说明:

  • 我假设表的非递减序列PK;在大多数情况下,它也可能是一个时间戳
  • 为简单起见,所有查询都以用户postgres运行,您可能需要更改此
  • 水印方法将保证只传输新记录,最大限度地减少带宽使用
  • 方法是 atomic ,如果脚本崩溃,什么都不会丢失
  • 这里只提取了一个表,但您可以添加更多
  • 因为我是偏执狂,我为临时表提供了一个不同的名称将它放入一个单独的架构中
  • 整个脚本在远程计算机上执行两个查询(一个用于获取一个用于删除);你可以结合这些。
  • 但是只有一个脚本(从local = target机器执行)。
  • DELETE尚未激活;它只执行count(*)