web-dev-qa-db-ja.com

文字列のコンテンツのみに基づく一貫したString#hash

GOAL:サーバーで処理されるすべてのURLを0、1、2、または3にマップし、できるだけ均一に配信します。

RubyのString#hashメソッドの documentation は、「文字列の長さと内容に基づいてハッシュを返す」と述べていますが、これは明らかに全体の話ではありません。指定された文字列のハッシュは、インタープリターの呼び出し間で一貫していません。

$ irb
Ruby-1.9.2-p180 :001 > "foo".hash
 => 360517580588231756 
Ruby-1.9.2-p180 :002 > ^D

$ irb
Ruby-1.9.2-p180 :001 > "foo".hash
 => -2716152678666510148 

つまり、特定の文字列のハッシュ値は、たとえばサーバー間で異なる可能性があります。 Railsは内部でString#hashを使用して、URLパスを4つのアセットホストの1つにマッピングします(アプリのasset_Hostが そう構成されている場合 の場合)が、この機能はマシン間の不整合が原因で、効率が大幅に低下します。異なるサーバーが同じURLを異なるアセットホストにマッピングし、キャッシュ、曇り空、お茶のカップを早めに冷却し、さもなければ優れたプログラマーの評判を誤解する可能性があります。

典型的なアプリのURLスペース全体にハッシュを効果的かつ迅速に分散できる代替ハッシュ関数を提案できますか。できれば、Fixnumを生成するハッシュ関数を使用して、最終的には4つのアセットホストの1つにマッピングする必要があります。

29
Rob Davis

rubyのダイジェストモジュールには、このような機能がたくさんあります。 http://Ruby-doc.org/stdlib/libdoc/digest/rdoc/index.html

簡単な例:

require 'digest/sha1'
Digest::SHA1.hexdigest("some string")
31
keymone

小さなライブラリがあります xxHash

XXhash.xxh32('qwe') #=> 2396643526
XXhash.xxh64('qwe') #=> 9343136760830690622

衝突が増えるかもしれませんが、SHA1よりも10倍高速です。

Benchmark.bm do |x|
  n = 100_000
  str = 'qweqweqwe'
  x.report('xxhash32') { n.times { XXhash.xxh32(str) } }
  x.report('xxhash64') { n.times { XXhash.xxh64(str) } }
  x.report('hexadigest') { n.times { Digest::SHA1.hexdigest(str) } }
end;1

#       user     system      total        real
# xxhash32  0.020000   0.000000   0.020000 (  0.021948)
# xxhash64  0.040000   0.000000   0.040000 (  0.036340)
# hexadigest  0.240000   0.030000   0.270000 (  0.276443)
2
Lev Lukomsky

最も簡単な(そして一貫した)方法はこれです(そして高速です):

"https://www.example.com/abc/def/123?hij=345".sum % 4

これは常に0〜3の整数を生成します。非常に高速であり、かなりよく分散されているはずです(実際に分散でテストを実行したことはありません)。

1
Jason

to_i(36) を試すことができます。

"Hash me please :(".to_i(36)
=> 807137
1
retro