如何在数百万行上进行这种计算量大的查询

时间:2015-05-30 16:48:09

标签: ruby-on-rails ruby postgresql

我正在使用Idempotence来确保同一条消息不会多次保存到数据库中。为了确保这一点,我需要3列的组合。我不是在一个可能为null的3列上建立索引,而是进行计算和Digest并将其存储在索引且唯一的列上。

我现在需要将此计算应用于之前的所有消息,其中有数百万行。

Message.rb:

 def set_unique_identifier
    part_one   = mm_id || SecureRandom.uuid
    part_two   = c_id
    part_three = s_id
    self.unique_identifier = Digest::SHA1.hexdigest("#{part_one}-#{part_two}-#{part_three}")
  end

然后我有这样的迁移:

Message.find_each.with_index do |message, index|
  message.set_unique_identifier
  message.save
  puts "SETTING UNIQUE IDENTIFIER FOR #{index}" if index % 1000 == 0
end
然而,显然,这需要很长时间才能计算出来。有没有更快的方法来使用原始SQL?

1 个答案:

答案 0 :(得分:2)

无论具有一百万行的解决方案,您都将涉及一定程度的计算。你可以做的是减少数据的移动。 Postgresql的加密模块支持SHA1散列和UUID生成。

使用那些可以使用的逻辑保留服务器中的逻辑并将其作为单个SQL语句执行,或者如果您想以块的形式执行多个语句。

UPDATE message SET unique_identifier = encode(digest(
mm_id || gen_random_uuid() || '-' || c_id || '-' || s_id
,'sha1'),'hex');

但是,您正在做的事情实际上并未检查唯一性,因为随机组件意味着可以允许具有相同mm_id,c_id,s_id的两条消息。

您最好使用唯一的数据库约束。您可以在原始列上创建唯一索引。

CREATE UNIQUE INDEX ON message(mm_id,c_id,s_id);

并依靠postgres来处理这个问题。这是我首先要做的事情,不要担心性能问题,直到您以这种方式尝试并且可以衡量性能。

另一种方法是在函数上创建索引。它将以大致相同的方式运作:

CREATE UNIQUE INDEX ON message (encode(digest(mm_id || c_id || s_id,'sha1'),'hex'));