我正在使用下面的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。
我的问题:
您认为这是一种正确的方法吗?存储过程对数据的一致性是否更安全?
实现这一目标的最有效方法是什么? (因为这个生产数据库非常大 - 有些表超过3000万行)。
答案 0 :(得分: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
的数据路由到每个客户端输出文件。
这也是一个两步工作流程:首先生成上面的脚本(例如通过在psql -c 'select tablename....'
或任何其他方法的结果中使用bash循环),然后执行脚本。
为什么不能简化为一步?
循环无法在psql脚本中实现,因为psql没有循环,除了某些\gexec
,但它在这里不适用,因为\copy
是一个元命令,{{1只处理SQL命令。
循环也无法在plpgsql中实现,除非更改问题的上下文,因为\gexec
的每个输出都不会路由到相应的每个表的客户端文件。它将返回到客户端,因为所有内容都连接成一个流。如果使用SQL命令COPY TO STDOUT
它可以工作,但你需要成为超级用户,文件最终在服务器上,而不是在客户端上。
答案 1 :(得分:0)
我最终得到了这个解决方案:
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来播放这个脚本。