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つにマッピングする必要があります。
rubyのダイジェストモジュールには、このような機能がたくさんあります。 http://Ruby-doc.org/stdlib/libdoc/digest/rdoc/index.html
簡単な例:
require 'digest/sha1'
Digest::SHA1.hexdigest("some string")
小さなライブラリがあります 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)
最も簡単な(そして一貫した)方法はこれです(そして高速です):
"https://www.example.com/abc/def/123?hij=345".sum % 4
これは常に0〜3の整数を生成します。非常に高速であり、かなりよく分散されているはずです(実際に分散でテストを実行したことはありません)。
to_i(36) を試すことができます。
"Hash me please :(".to_i(36)
=> 807137