RailsからMemcachedを使いつつその実体を垣間見てみる
RailsからMemcachedと言ってもconfig.cache_store = :mem_cache_store
的な話ではなく、任意のデータをMemcachedに突っ込んだら何がどうなってるのかを調べてみた感じのものです。
まずはMemcacheにアクセスするためのインスタンスを作ります。
[7] pry(main)> a = ActiveSupport::Cache::MemCacheStore.new('localhost:11211', expires_in: 60, namespace: 'NS_ABC', compress: true) => #<ActiveSupport::Cache::MemCacheStore:0x00007fc807dba2a0 @data=#<Dalli::Client:0x00007fc807db9e40 @options={}, @ring=nil, @servers=["localhost:11211"]>, @options={:namespace=>"NS_ABC", :expires_in=>60, :compress=>true}>
かなり恣意的にnamespace
とcompress
を指定していますが後ほど触れていきます。
さて、データをキャッシュさせたり取り出したりするのはものすごくカンタンです。
[9] pry(main)> a.write('key', 'val') => 5044031582654955520 [13] pry(main)> a.read('key') => "val"
存在しないキーで参照した場合はnil
が返ります。
[8] pry(main)> a.read('abc') => nil
さて、基礎的なお話はこれくらいにして、じゃあ実際にMemcached側にどんな感じでデータが入ってるのか、Memcachedに直接繋いで確認してみることにします。
[root@localhost ~]# telnet localhost 11211 Trying ::1... Connected to localhost. Escape character is '^]'.
はい、これだけのためにわざわざtelnetクライアントをyumでインストールしました。
イマドキはtelnetコマンドが入っていないのがフツーかもしれません。
stats items STAT items:1:number 1 STAT items:1:age 232 STAT items:1:evicted 0 STAT items:1:evicted_nonzero 0 : stats cachedump 4 1 ITEM NS_ABC:key [114 b; 1545367943 s] END
はい、やっと見つけました。
どうやら実際に指定したキーの前にnamespace
が自動的に追加されるようです。
せっかくなので値も取得してみましょうか。
get NS_ABC:key VALUE NS_ABC:key 1 114 o: ActiveSupport::Cache::Entry : @version0:@created_atf1545877723.9305558:@expires_in6e1 END
おっと。
良く分からないものが表示されました。
が、最初のo:
はオブジェクト、その後に型(クラス)があって、@expires_in
の6e1
は60の指数表記でしょう。
つまりActiveSupport::Cache::Entry
にデータを詰め込んでいい感じのシリアライズをすると同じようなものが作れそうです。
[29] pry(main)> b = ActiveSupport::Cache::Entry.new('val', expires_in: 60) => #<ActiveSupport::Cache::Entry:0x00007fc8072b3730 @created_at=1545878427.2002478, @expires_in=60.0, @value="val", @version=nil>
あ、なんかそれっぽいのができました。
シリアライズはどうやらMarshal
というクラスを使うのがスタンダードっぽいのでとりあえず試してみます。
[32] pry(main)> Marshal.dump(b) => "\x04\bo: ActiveSupport::Cache::Entry\t:\v@valueI\"\bval\x06:\x06ET:\r@version0:\x10@created_atf\x171545878427.2002478:\x10@expires_inf\b6e1"
あ、概ね同じに見えますね。
ちょっとそのまま表示してみましょうか。
[33] pry(main)> puts Marshal.dump(b) o: ActiveSupport::Cache::Entry : @version0:@created_atf1545878427.2002478:@expires_in6e1 => nil
ビンゴですね。
ビンゴなのはいいんですが、ActiveSupport::Cache::MemCacheStore
からMemcachedに入れたデータを他で利用するのはちょっと難易度が高そうです。
ところで逆はどうでしょう?
Memcachedに直接データを入れてRails側で参照してみます。
set NS_ABC:hoge 0 60 8 hogehoge STORED [36] pry(main)> a.read('hoge') => "hogehoge"
はい、取得できました。
今度はMemcached側でデータを圧縮しつつ入れてみます。
set NS_ABC:hoge 1 60 8 hogehoge STORED [40] pry(main)> a.read('hoge') => nil
あらら、これはダメなようです。
ActiveSupport::Cache::MemCacheStore
のcompress
はここでは関係がないようです。
compress
にtrue
を指定した時は、データ容量が特定の閾値(compress_threshold
で指定も可能)を超えた際に圧縮するという設定のようです。
やってみましょう。
[68] pry(main)> a.write('key', 'val' * 1000) => 72057594037927936 get NS_ABC:key VALUE NS_ABC:key 3 217 .-(�/*��rNL�H��r�+)���v(K�)MU2������fw�]Ԑr]�!һ(��y�q��) C��+^��Ԣ<+���ĒԔCSS cCCK= 3 3C��Ԋ�̢��TC+Q�������&��Y8<��vpQ4�F�(E�h��A�ج�\C@��5%��0 �END
あばばばばばばば。
良くは分かりませんが圧縮されてそうです。
きっとActiveSupport::Cache::Entry
をシリアライズしたものを圧縮してるんでしょう、という予測を立ててみます。
圧縮と言えばみんな大好きDeflate
でしょう。
やってみます。
[147] pry(main)> puts Zlib::Deflate.deflate( Marshal.dump( ActiveSupport::Cache::Entry.new('val' * 1000, expires_in: 60) ) ) .-(�/*��rNL�H��r�+)���v(K�)MU2������fw�]Ԑr]�!һ(��y�q��) C��+^��Ԣ<+���ĒԔCSS # =CKS3��Ԋ�̢��TC+Q�������&��Y8<��vpQ4�F�(E�h��A�ج�\C@��5%A�0 �=> nil
あばばばばばばばばばばば。
制御コードまみれなので全く同じ表示にはなりませんがきっとこれで合ってると思います(ぉぃ
さて、Memcachedと言えばキーに250文字制限があるのですがその辺りもちょっと確認しておきましょうか。
namespace
の指定がある場合
[45] pry(main)> a.write('1234567890' * 30, 'val') => 5692549928996306944 stats cachedump 8 1 ITEM NS_ABC:12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456:md5:116b331c40362c11c387c7bf0eaf8295 [114 b; 1545369320 s] END
namespace
の指定がない場合
[52] pry(main)> a.write('1234567890' * 30, 'val') => 5764607523034234880 stats cachedump 8 1 ITEM 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123:md5:874fa981137f49f7885e1d05a93c21df [114 b; 1545369598 s] END
なるほど、通常通り「namespace
:指定したキー」にしつつ、250文字を超えないように後ろにそれぞれのMD5値
[62] pry(main)> Digest::MD5.hexdigest('NS_ABC:' + '1234567890' * 30) => "116b331c40362c11c387c7bf0eaf8295" [63] pry(main)> Digest::MD5.hexdigest('1234567890' * 30) => "874fa981137f49f7885e1d05a93c21df"
を自動で追加してくれるんですね。
これは便利。
マンガとアニメとゲームから錬成された宇宙大好きエンジニア。 軌道エレベーターで行ける静止軌道上のコロニーに住まいを移し、ゲームやってマンガ読んでアニメ見て爆睡、ゲームやってマンガ読んでアニメ見て爆睡、という生活を夢見ながら今日もコードを書き続けるのだった。
TAG
- Android
- AWS
- Bitrise
- CodePipeline
- Firebase
- HTML
- iOS
- IoT
- JavaScript
- KPI
- Linux
- Mac
- Memcached
- MGRe
- MGReのゆるガチエンジニアブログ
- MySQL
- PHP
- PICK UP
- PR
- Python
- Ruby
- Ruby on Rails
- SEO
- Swift
- TIPS
- UI/UX
- VirtualBox
- Wantedly
- Windows
- アクセス解析
- イベントレポート
- エンジニアブログ
- ガジェット
- カスタマーサクセス
- サーバ技術
- サービス
- セキュリティ
- セミナー・展示会
- テクノロジー
- デザイン
- プレスリリース
- マーケティング施策
- マネジメント
- ラボ
- リーンスタートアップ
- 企画
- 会社紹介
- 会社紹介資料
- 勉強会
- 実績紹介
- 拡張性
- 採用
- 日常
- 書籍紹介
- 歓迎会
- 社内イベント
- 社員インタビュー
- 社長ブログ
- 視察
- 開発環境