【问题标题】:PSQL COPY tables to CSV - Data consistency on production databasePSQL COPY 表到 CSV - 生产数据库上的数据一致性
【发布时间】:2017-12-11 09:45:07
【问题描述】:

我正在使用下面的 shell 脚本转储数据库以分隔 CSV 文件:

PGENGINE=$PGHOME/bin
PGPASSWORD=$1 $PGENGINE/psql -p $2 -h $3 -U $4 -Atc "select tablename from pg_tables where schemaname='public'" $5 |\
while read TBL; do
    echo "Exporting table "$TBL
    PGPASSWORD=$1 $PGENGINE/psql -p $2 -h $3 -U $4 -c "COPY public.$TBL TO STDOUT WITH CSV HEADER DELIMITER '"$SEPARATEUR_CSV"'" $5 > /$ROOT_PATH/$6/$TBL.csv
    echo -e $TBL ": Export done\n"
done

这在我的测试数据库上运行良好,但我担心在生产数据库上运行它会发生什么。

我看到很多主题说 pg_dump 获取数据锁定,但我不知道 psql COPY,包括我正在对所有表进行循环这一事实。我需要确保如果用户更新我的一个表,COPY 命令仍将获得正确的数据和正确的 FK。

我的问题:

  1. 您认为这是一种正确的做法吗?存储过程在数据一致性方面是否更安全?

  2. 实现这一目标的最有效方法是什么? (因为这个生产数据库非常大 - 有些表超过 3000 万行)。

【问题讨论】:

  • 您实际上是在复制 pg_dump 正在做的事情。 pg_dump 获取的“锁”与普通 SELECT 将获取的内容以及您的 COPY 语句也获取的内容相同。它只是为了防止该表上的并发 DDL 语句,不会阻塞其他任何内容。
  • 感谢您的精确。有没有办法锁定所有表或所有相关表(关于 FK)?或者也许我可以通过存储过程来实现这一点?
  • 你为什么要这样做?为什么你认为你需要锁定所有的表?
  • 假设有一个表 A 与表 B 的 FK。如果某些用户可以在 B 锁定时修改表 A,反之亦然,当我尝试重新加载时可能会出现一些 FK 冲突来自创建的 CSV 文件的数据库。不是吗?
  • pg_dump 创建数据库的一致转储(截至数据库启动时)。无需阻止对数据库的更改。

标签: postgresql psql postgresql-9.4 consistency pg-dump


【解决方案1】:

通过在 REPEATABLE READ 隔离模式下启动事务并在读取所有数据时结束它来实现实时数据库中表的一致读取。您的脚本必须进行转换,以便只有一个 psql 调用,如下所示:

psql [connection arguments] << EOF
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
\copy table1 TO file1.csv
\copy table2 TO file2.csv
\copy table3 TO file3.csv
COMMIT;

EOF

请注意\copy 而不是COPY,因为它们都分组在同一个 psql 调用中。 psql 本身将每个 \copy 的数据路由到每个客户端输出文件。

这也是一个两步工作流程:首先生成上述脚本(例如通过在 bash 中循环 psql -c 'select tablename....' 或任何其他方法的结果),然后执行该脚本。

为什么不能简化为一步?

循环不能在 psql 脚本中实现,因为 psql 没有循环,除了 \gexec,但它在这里不适用,因为\copy 是一个元命令,\gexec 只处理 SQL 命令.

循环也不能在 plpgsql 中实现,除非更改问题的上下文,因为COPY TO STDOUT 的每个输出都不会被路由到相应的每个表的客户端文件。当所有内容连接成单个流时,它将返回给客户端。如果使用 SQL 命令COPY TO file 它可以工作,但您需要成为超级用户,并且文件最终在服务器上,而不是在客户端上。

【讨论】:

  • 要遍历数据库的表,我需要在存储过程中进行吗?
  • 您的问题中的表格已经有一个循环。存储过程根本没有帮助。关键是,不是在循环中使用 COPY 调用 psql,而是在该循环中构建一个脚本,然后当您在该循环之外时执行该脚本。一次。
  • 好的,有道理。谢谢你。也许您应该编辑您的答案以添加此内容,因此如果有人检查该主题,这将成为您答案的一部分。
【解决方案2】:

我终于得到了这个解决方案:

PGENGINE=$PGHOME/bin
    CHEMIN_SCRIPT_TRANSACTION=/$ROOT_PATH/plc/proc/tmp/dump_transaction.sql
    DOSSIER_DUMP_FICHIERS=/$ROOT_PATH/dump/dump_$6/dump_fichiers

    echo "BEGIN; SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;" > $CHEMIN_SCRIPT_TRANSACTION

    PGPASSWORD=$1 $PGENGINE/psql -p $2 -h $3 -U $4 -Atc "select tablename from pg_tables where schemaname='public'" $5 |\
    while read TBL; do
        echo "\copy $TBL TO $DOSSIER_DUMP_FICHIERS/$TBL.csv WITH CSV HEADER DELIMITER ';';" >> $CHEMIN_SCRIPT_TRANSACTION
        echo "\echo " >> $CHEMIN_SCRIPT_TRANSACTION
    done
    echo "COMMIT;" >> $CHEMIN_SCRIPT_TRANSACTION

    PGPASSWORD=$1 $PGENGINE/psql -p $2 -h $3 -U $4 -d $5 -f $CHEMIN_SCRIPT_TRANSACTION

我正在另一个文件中创建一个脚本,然后我使用 psql -f 来播放这个脚本。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-10
    • 2011-04-09
    相关资源
    最近更新 更多