为哈希生成缓存密钥(唯一密钥)

时间:2014-04-25 05:29:18

标签: ruby caching

我有一个哈希,例如:

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

用于过滤响应数据。我需要为此哈希创建一个唯一键来缓存响应。我可以考虑获取其键和值并将它们转换为单个字符串。寻找一些简单而有效的有趣答案。

3 个答案:

答案 0 :(得分:3)

请考虑以下两个步骤:

  1. 在基于键排序输出写入顺序后,将哈希值转换为字符串(或其他序列化格式)。

    在此转换期间按值对键进行排序非常重要,因此具有相同键/值对(但具有不同键顺序)的哈希将产生相同的输出。更复杂/嵌套的结构需要额外的处理,并应确保等效对象的一致输出

    要开始使用该流程,请考虑:

    sorted_kv_pairs = hash.to_a.sort_by {|k,v| k.to_s}
    
  2. 使用散列函数(如SHA-1或SHA-256/160)从先前序列化的对象生成40字节的唯一ID。

    这些函数的巨大的输出空间(以及加密质量)使得有目的的碰撞变得不可行,从而导致“唯一ID”。

答案 1 :(得分:2)

一个简单的解决方案是使用Marshal类来转储和读取内容。对于较大的比例,您可以使用memcached,其中有多个Ruby包装器,或者一些键值数据库,如redis,neo4j。

答案 2 :(得分:0)

以下是基于this answer

的解决方案
# 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