为哈希值生成缓存键(唯一键)

4

我有一个哈希表,例如:

filers = {query: 'nice post', sort: 'time_desc', post_type: 'blog'...limit: 100}

这是用于过滤响应数据的哈希表。我需要为此哈希表创建一个唯一的键,以便缓存响应。我可以将其键和值转换为单个字符串。寻找一些简单而高效的有趣答案。


这个能行吗?http://www.ruby-doc.org/stdlib-1.9.3/libdoc/securerandom/rdoc/SecureRandom.html#method-c-uuid - squiguy
你所提到的“响应”是什么? - sawa
@sawa,我提到响应是使用上述过滤器组合从外部 API 调用的结果集。是的,我正在使用 memcache 缓存响应。 - nkm
3个回答

3

考虑以下两个步骤:

  1. Convert the Hash to a string (or other serialized format) after sorting the output write-order based on key.

    It is important that the values are sorted by key during this transformation so that Hashes with the same key/value pairs (but with a different key order) will yield the same output. More complicated/nested structures need additional handling and should ensure consistent output for equivalent objects.

    To get started with the process, consider:

    sorted_kv_pairs = hash.to_a.sort_by {|k,v| k.to_s}
    
  2. Use a hashing function such as SHA-1 or SHA-256/160 to generate a 40-byte Unique ID from the previously serialized object.

    The huge output space (and cryptography qualities) of these functions make it unfeasible for there to be a purposeful collision and thus lead to "unique IDs".


我建议选择上面的第二个解决方案。 - SreekanthGS
我的错误。是的。我同意。第一步跟着第二步。 - SreekanthGS
你也可以使用其他哈希算法:http://programmers.stackexchange.com/a/145633/82539 - Uri Agassi
@UriAgassi 感谢链接。这是一个方便的答案/参考。然而,我会使用具有输出空间的加密哈希函数。那些哈希/校验和函数(32位输出)用于不同目的(非常快速的校验和或主要哈希与次要相等),但对“唯一性”的保证要少得多。虽然它们没有许多碰撞,并且在随机输入上有很好的分布(〜 200k),但它们比SHA / 加密哈希更容易生成重复项,因为生日问题。 - user2864740

2
一种简单的解决方案是使用Marshal类来转储和读取内容。对于更大规模的应用,您可以使用memcached,有几个Ruby包装器可供选择,或者一些键值数据库,如redis、neo4j等。

0

这是我基于这个答案的解决方案。

# Return a cache key based on params hash
def params_cache_key(params)
  # Normalize parameter hash, change keys to a string, normalize key order, sort array values
  normalized = params.transform_keys(&:to_s)
                   .transform_values { |v| v.is_a?(Array) ? v.sort : v }
                   .sort_by { |k, _| k }.each_with_object('') do |(k, v), cache_key|
    cache_key << "#{k}:#{v}"
  end
  Digest::SHA1.hexdigest(normalized)
end

以下是一些测试用例

describe '#params_cache_key' do

  def cache_key(params)
    controller.send :params_cache_key, params
  end

  it 'should produce a sha1' do
    expect(cache_key(foo: 'bar', bar: 'baz')).to match /^\h{40}$/
  end

  it 'should produce same hash for different key order' do
    expect(cache_key(foo: 'bar', bar: 'baz')).to eq cache_key(bar: 'baz', foo: 'bar')
  end

  it 'should produce same hash for stringified keys' do
    expect(cache_key(foo: 'bar', bar: 'baz')).to eq cache_key(foo: 'bar', 'bar' => 'baz')
  end

  it 'should work with nested parameters in different order' do
    expect(cache_key(category: %w(foo bar))).to eq cache_key(category: %w(bar foo))
  end

end

是的,这段代码很快就会变得复杂起来。不要忘记嵌套数组和哈希表。因此,我认为在给定不同排序顺序的情况下,无法保证生成相同哈希值的相同数据。只需按第一级键进行排序即可。 - Dirk de Kok
你的测试规范中的 cache_key 方法正在引用 controller,请将其移除。 - Dirk de Kok

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接