将CSV从GCS迁移到PostgreSQL

时间:2018-10-03 08:33:29

标签: python postgresql csv google-cloud-platform google-bigquery

我正在尝试使用python脚本将已从BigQuery导出的csv文件从Google Cloud Storage(GCS)迁移到PostgreSQL Google Cloud sql实例。

我希望使用Google API,但在the documentation中找到了它:

  

PostgreSQL实例不支持使用Cloud SQL Admin API导入CSV数据。

作为替代方案,我可以使用psycopg2库并将csv文件的行流式传输到SQL实例中。我可以通过三种方式做到这一点

  • 逐行:阅读每一行,然后提交插入命令,然后提交
  • 批处理流:读取每一行,然后提交插入命令,然后在10行或100等之后提交。
  • 整个csv:阅读每一行并提交插入命令,然后仅在文档末尾提交。

我担心的是,这些csv文件可能包含数百万行,因此对上述三个选项中的任何一个运行此过程对我来说都是一个坏主意。

我有什么选择? 本质上,我在BigQuery中有一些原始数据,在导出到GCS之前,我们会对其进行一些预处理,以准备导入PostgreSQL实例。 我需要将此预处理后的数据从BigQuery导出到PostgreSQL实例。

这不是此question的重复版本,因为我最好是寻找将数据从BigQuery导出到PostgreSQL实例的解决方案,无论它是通过GCS还是直接进行的。

4 个答案:

答案 0 :(得分:2)

您可以按照@GrahamPolley的建议,使用Cloud Dataflow进行导入过程。的确,此解决方案涉及一些额外的工作(熟悉Dataflow,设置所有内容等)。即使进行了额外的工作,这仍将是适合您情况的首选解决方案。但是,还有其他解决方案可用,我将在下面说明其中一种。

要使用Dataflow设置迁移过程,本关于exporting BigQuery to Google Datastore的教程就是一个很好的例子


云数据流的替代解决方案

用于PostgreSQL的Cloud SQL不支持从.CSV导入,但是它支持.SQL文件。

  

指定uri的文件类型。
   SQL:该文件包含SQL语句。
  CSV:文件包含CSV数据。   PostgreSQL实例不支持使用Cloud SQL Admin API导入CSV数据。

一种直接的解决方案是使用某种工具(我不知道,但在线提供的工具很多)将.CSV文件集转换为.SQL,然后导入到PostgreSQL

如果您想以更“程序化”的方式实施此解决方案,我建议使用Cloud Functions,这是我尝试执行此操作的示例:

  1. 设置triggers when a file is uploaded to a Cloud Storage bucket
  2. 的云功能
  3. 编写函数代码以获取上载的文件并检查其是否为.CSV。如果是这样,请使用csv-to-sql API(example of API here)将文件转换为.SQL
  4. 将新文件存储在Cloud Storage中
  5. 导入PostgreSQL

答案 1 :(得分:1)

我发现pyscopg2模块具有copy_from(),它允许加载整个csv文件,而不是单独地流传输行。 使用此方法的缺点是,仍然需要从GCS下载csv文件并将其存储在本地。

以下是使用pyscopg2'copy_from()'的详细信息。 (摘自here

import psycopg2

conn = psycopg2.connect("host=localhost dbname=postgres user=postgres")
cur = conn.cursor()
with open('user_accounts.csv', 'r') as f:
    # Notice that we don't need the `csv` module.
    next(f)  # Skip the header row.
    cur.copy_from(f, 'users', sep=',')

conn.commit()

答案 2 :(得分:0)

您可以只使用一个类来使从互联网上提取的文本表现得像文件。我已经使用过几次了。

import io
import sys


class IteratorFile(io.TextIOBase):
    """ given an iterator which yields strings,
    return a file like object for reading those strings """

    def __init__(self, obj):
        elements = "{}|" * len(obj[0])
        elements = (unicode(elements[:-1]).format(*x) for x in obj)
        self._it = elements
        self._f = io.cStringIO()

    def read(self, length=sys.maxsize):

        try:
            while self._f.tell() < length:
                self._f.write(next(self._it) + "\n")

        except StopIteration as e:
            # soak up StopIteration. this block is not necessary because
            # of finally, but just to be explicit
            pass

        except Exception as e:
            print("uncaught exception: {}".format(e))

        finally:
            self._f.seek(0)
            data = self._f.read(length)

            # save the remainder for next read
            remainder = self._f.read()
            self._f.seek(0)
            self._f.truncate(0)
            self._f.write(remainder)
            return data

    def readline(self):
        return next(self._it)

答案 3 :(得分:0)

开始之前,请确保:

  

您要导入的数据库和表必须   您的Cloud SQL实例上已经存在。

     

CSV文件格式要求CSV文件每一行必须一行   的数据,并具有逗号分隔的字段。

然后,您可以按照后续步骤[GCLOUD] import data to a Cloud SQL instance using a CSV file出现在GCS存储桶中

  1. 描述您要从中导出的实例:

gcloud sql instances describe [INSTANCE_NAME]

  1. 复制serviceAccountEmailAddress字段。

  2. 以编写者身份将服务帐户添加到存储桶ACL:

gsutil acl ch -u [SERVICE_ACCOUNT_ADDRESS]:W gs://[BUCKET_NAME]

  1. 将服务帐户作为读取器添加到导入文件中:

gsutil acl ch -u [SERVICE_ACCOUNT_ADDRESS]:R gs://[BUCKET_NAME]/[IMPORT_FILE_NAME]

  1. 导入文件

gcloud sql import csv [INSTANCE_NAME] gs://[BUCKET_NAME]/[FILE_NAME] \ --database=[DATABASE_NAME] --table=[TABLE_NAME]

  1. 如果您不需要保留先前设置的ACL提供的权限,请删除该ACL:

gsutil acl ch -d [SERVICE_ACCOUNT_ADDRESS] gs://[BUCKET_NAME]