簡體   English   中英

增量導入數據到PostgreSQL數據庫

[英]Incrementally importing data to a PostgreSQL database

情況:

我有一個PostgreSQL數據庫,該數據庫正在現場部署的單元中記錄來自傳感器的數據(我們稱其為源數據庫 )。 該設備的硬盤空間非常有限,這意味着如果不加改動,數據記錄將導致數據庫所在的磁盤在一周內被填滿。 我有一個到數據庫的(非常有限的)網絡鏈接(所以我想壓縮轉儲文件),並且在所說鏈接的另一邊,我有另一個PostgreSQL數據庫(讓我們稱之為目標數據庫 ),其中有很多自由空間(就論點而言,讓我們說,關於空間,源非常有限,關於空間,目的地是無限的)。

我需要對源數據庫進行增量備份,將自上次備份以來已添加的行追加到目標數據庫,然后從源數據庫中清除添加的行。

現在,自從上次執行備份以來,源數據庫可能已被清理,也可能尚未被清理,因此目標數據庫僅需要能夠以自動化(腳本化)過程導入新行,但是pg_restore在嘗試從數據庫還原時卻失敗了。具有與目標數據庫相同的主鍵號的轉儲。

所以問題是:

什么是從源中僅還原尚未在目標數據庫中的行的最佳方法?

到目前為止,我想出的唯一解決方案是將數據庫pg_dump並使用pg_restore將轉儲還原到目標端的新輔助數據庫,然后使用簡單的sql來整理主目錄中已經存在的行。目標數據庫。 但是似乎應該有更好的方法...

額外的問題:在這樣的應用程序中使用PostgreSQL時,我完全錯了嗎?我願意接受其他數據收集替代方案的建議...)

一個好的開始方法可能是對pg_dump使用--inserts選項。 從文檔(重點是我的):

將數據轉儲為INSERT命令(而不是COPY)。 這會使恢復非常緩慢。 它主要用於制作可以裝入非PostgreSQL數據庫的轉儲。 但是,由於此選項為每一行生成一個單獨的命令,因此在重新加載行時發生錯誤只會導致該行丟失,而不是整個表內容丟失 請注意,如果您重新排列了列順序,則還原可能會完全失敗。 --column-inserts選項可以安全地防止列順序更改,盡管速度更慢。

我現在沒有使用pg_restore進行測試的方法,但這對於您的情況可能就足夠了。

您還可以使用以下事實:從9.5版開始,PostgreSQL為INSERT提供了ON CONFLICT DO...。 使用一種簡單的腳本語言將它們添加到轉儲中,就可以了。 不幸的是,我沒有找到pg_dump自動添加的選項。

您可以在Google上“散布連接數據庫同步”以查看相關解決方案。

就我所知,這並不是一個解決得很徹底的問題-有一些常見的解決方法,但是我不知道以數據庫為中心的現成解決方案。

解決此問題的最常見方法是使用消息總線在計算機之間移動事件。 例如,如果您的“源數據庫”只是一個數據存儲,沒有其他邏輯,則您可以擺脫它,並使用消息總線說“事件x已經發生”,然后將該消息總線的端點指向您的“目標計算機”,然后將其寫入數據庫。

您可以考慮使用Apache ActiveMQ或閱讀“ 企業集成模式 ”。

#!/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身份運行,您可能需要更改此設置
  • 水印方法將確保僅傳輸新記錄,從而最大程度地減少了帶寬使用
  • 該方法是原子的 ,如果腳本崩潰,則不會丟失任何內容
  • 此處僅提取一張表,但您可以添加更多表
  • 因為我很偏執,所以我為登台表使用了不同的名稱並將其放入單獨的架構中
  • 整個腳本在遠程計算機上執行兩個查詢(一個用於獲取一個用於刪除)。 可以結合這些。
  • 但是涉及一個腳本(從local = target計算機執行)。
  • DELETE尚未激活; 它只做一個count(*)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM