【问题标题】:Performance of data validation数据验证的性能
【发布时间】:2022-09-27 17:59:50
【问题描述】:

我有一个接受传入数据、检查错误并导入数据库的端点。传入数据最多可达 300 000 行。 Stack 是 - Ruby on Rails、Postgres、Redis、Sidekiq、干式验证。电流:

  • 将数据加载到Redis;
  • 准备/转换;
  • 验证每一行并将其标记为有效/无效;
  • 获取有效行并批量导入它们。

我需要关于如何提高此处验证步骤的性能的建议,因为有时验证大文件需要超过一天的时间。


一些细节

它基本上循环遍历后台的每一行并应用验证规则,例如

rows.each do |row|
  result = validate(row)
  set_status(row, result) # mark as valid/invalid
end

一些验证规则是唯一性检查——它们很重因为他们检查整个数据库的唯一性。例子:

rule(:sku, :name) do
  if Product.where(sku: values[:sku]).where.not(name: values[:name]).exists?
    # add error
  end
end

不用说,数据库和日志在验证期间会发疯。

我尝试的另一种方法是 pluck 必要的字段来自全部数据库记录,然后循环遍历并将每一行与该数组进行比较,而不是发出数据库请求。但与庞大的阵列相比,似乎更加缓慢。

def existing_data
  @existing_data ||= Product.pluck(:sku, :name, ...)
end

rule(:sku, :name) do
  conflict = existing_data.find do |data|
    data[0] == values[:sku] && data[1] != values[:name]
  end
  if conflict.present?
    # add error
  end
end

    标签: ruby-on-rails validation import


    【解决方案1】:

    我认为您可以通过按照第二种方法做一些事情来提高性能,只是您应该尝试获取尽可能少的现有产品,最好只获取与您的验证相关的产品。仅查看提供的代码,在我看来,您可以通过从新收到的rows 聚合 SKU 并使用它们过滤产品表来减少您正在加载的产品数量

    skus = skus_from_rows(rows)
    @existing_products = existing_products(skus)
    rows.each do |row|
      result = validate(row)
      set_status(row, result) # mark as valid/invalid
    end
    
    
    def skus_from_rows(rows)
      rows.map { |row| row[:sku] }.uniq
    end
    
    def existing_products(skus)
      Product.where(sku: skus).pluck(:sku, :name, ...)
    end
    
    rule(:sku, :name) do
      conflict = @existing_products.find do |data|
        data[0] == values[:sku] && data[1] != values[:name]
      end
      if conflict.present?
        # add error
      end
    end
    

    此外,我会在 sku 列中添加一个索引(如果还没有的话),以提高过滤 sku 的查询的性能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-12
      • 2014-10-04
      相关资源
      最近更新 更多