项目因为一个隐藏的BUG要删除掉重复的评论,如果所有评论挨个比对的话,程序的开销为O(N²),因此可以采用Hash来把开销降低到O(N)。为避免原始数据作为Hash键导致内存占用过多的问题,可以把Hash键改为原始数据内容生成的Hash码。为了避免误删,删除之前还可对疑为重复数据的记录进行比对。

 1 require 'action_pack'
 2 require 'action_view/helpers/number_helper.rb'
 3 include ActionView::Helpers::NumberHelper
 4 
 5 def current_vm_size
 6   `vmmap #{$PID}`.split("\n")[-1].split[1].to_f * 1024 * 1024
 7 end
 8 
 9 def object_size
10  prev_size = current_vm_size
11  yield
12  number_to_human_size(current_vm_size - prev_size)
13 end
14 
15 require 'digest/sha1'
16 
17 columns = %w[account_id target_id section_id rating comment status client_id channel_id device_id locale_id]
18 @hash = {} # avoid to be released in local scope
19 
20 object_size do
21   Comment.find_each(:batch_size => 100) do |c|
22     k = Digest::SHA1.hexdigest(columns.map {|column| c.attributes[column].to_s }.join)
23     if @hash[k]
24       o = Comment.find(@hash[k])
25       c.destroy if columns.select {|column| o.attributes[column] == c.attributes[column] }.size == columns.size
26     else
27       @hash[k] = c.id
28     end
29   end
30 end

在我本机测试,全部载入十万多条原始数据会占用263MB内存,而采用以上hash压缩的方式则降低到13.3MB。如果表里有大文本字段的话,会更有效果。

如果能停掉对数据库表做插入访问,那么也可以直接用SELECT DISTINCT*的SQL语句导入到临时表中,将原始表清空后再重新导入。