ETL到csv文件,拆分然后推送到s3以通过redshift消耗

时间:2018-06-15 01:43:49

标签: amazon-s3 amazon-redshift kiba-etl

  

刚刚开始使用Kiba,没有找到任何明显的,但我可能只是引导我内心的孩子(通过盯着天花板寻找他们的鞋子)。

我想将一个非常大的表转储到Amazon Redshift。似乎最快的方法是将一堆CSV文件写入S3存储桶,然后告诉Redshift(通过COPY命令)将它们拉入。神奇的缩放gremlins将完成其余工作。 / p>

所以,我认为我希望Kiba为每10k行数据写一个CSV文件,然后将其推送到s3,然后开始写入新文件。最后,对COPY

进行后处理调用

那么,我可以“管道”工作还是应该是一个大的嵌套的Destination类?

source -> transform -> transform ... -> [ csv -> s3 ]{every 10000}; post-process

3 个答案:

答案 0 :(得分:0)

我不确定这里的确切问题。但是,我认为你的解决方案似乎总体上是正确的,但很少有建议。

  1. 您可能也会考虑在每个CSV文件中记录多于10K的记录,并在发送到S3时记录gzip个记录。
  2. 您希望查看包含多个文件列表的menifest创建,然后运行copy命令提供menifest文件作为输入。

答案 1 :(得分:0)

Kiba作者在这里。谢谢你试试吧!

目前,实现此目标的最佳方法是创建我称之为“缓冲目的地”的内容。 (其中一个版本很可能会在Kiba Common中结束)。

(请彻底测试一下,我今天早上为你写了这篇文章,虽然我过去使用的通用版本较少,但我还没有运行它。同时请记住这一点版本为你的10k行使用内存缓冲区,所以将数字增加到更大的数据会消耗内存。但是也可以创建最少内存消耗的版本,这会在你获得它们时将行写入文件)

class BufferingDestination
  def initialize(buffer_size:, on_flush:)
    @buffer = []
    @buffer_size
    @on_flush = on_flush
    @batch_index = 0
  end

  def write(row)
    @buffer << row
    flush if @buffer.size >= buffer_size
  end

  def flush
    on_flush.call(batch_index: @batch_index, rows: @buffer)
    @batch_index += 1
    @buffer.clear
  end

  def close
    flush
  end
end

这是你可以像这样使用的东西,例如这里重用Kiba Common CSV destination(尽管你也可以写自己的):

require 'kiba-common/destinations/csv'

destination BufferingDestination,
  buffer_size: 10_000,
  on_flush: -> { |batch_index, rows|
    filename = File.join("output-#{sprintf("%08d", batch_index)}")
    csv = Kiba::Common::Destinations::CSV.new(
      filename: filename,
      csv_options: { ... },
      headers: %w(my fields here)
    )
    rows.each { |r| csv.write(r) }
    csv.close
  }

然后,您可以在生成文件后触发COPY块中的on_flush权限(如果您希望立即开始上传),或者在post_process块中触发hostContains只有在所有CSV准备就绪后才会启动,如果您愿意,这可以是确保某种形式的交易全局上传的功能。

如果你确实需要这个(但是请小心使用僵尸线程等),你可能会想象并开始一个线程队列以实际并行处理上传。

另一种方法是采取多个步骤&#34; ETL流程,其中一个脚本用于生成CSV,另一个脚本用于上传,同时运行(例如我在RubyKaigi 2018的演讲中解释过这一点)。

让我知道事情对你有用!

答案 2 :(得分:0)

Thibaut,我做了类似的事情,除了我将它流式传输到Tempfile,我认为......

require 'csv'

# @param limit [Integer, 1_000] Number of rows per csv file
# @param callback [Proc] Proc taking one argument [CSV/io], that can be used after
#        each csv file is finished
module PacerPro
  class CSVDestination
    def initialize(limit: 1_000, callback: ->(obj) { })
      @limit = limit
      @callback = callback

      @csv = nil
      @row_count = 0
    end

    # @param row [Hash] returned from transforms
    def write(row)
      csv << row.values
      @row_count += 1
      return if row_count < limit

      self.close
    end

    # Called by Kiba when the transform pipeline is finished
    def close
      csv.close

      callback.call(csv)

      tempfile.unlink

      @csv = nil
      @row_count = 0
    end

    private

    attr_reader :limit, :callback
    attr_reader :row_count, :tempfile

    def csv
      @csv ||= begin
        @tempfile = Tempfile.new('csv')
        CSV.open(@tempfile, 'w')
      end
    end
  end
end