试着通过Rails使用Memcached并一睹其真身
本文是对这篇博客文章的再掲。
太长;不看
-
- RailsのActiveSupport::Cache::MemCacheStoreからMemcachedに書き込んだデータを他から使うのはちょっと難しそう
-
- 逆にMemcachedに入ってるRAWなデータをRails側で使うのは問題なさそう
- キーの長さ(250バイト制限)を気にしなくても大丈夫
这里所说的并不是Rails中的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}>
我会稍后详细讨论,但目前来看,对于命名空间和压缩的指定相当有意图。
好吧,让数据进行缓存或提取非常简单。
[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 '^]'.
是的,我特地使用yum安装了telnet客户端,只是为了这个目的。
现如今,可能没有安装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
是的,我终于找到了。
似乎实际上会在指定的键之前自动添加命名空间。
既然这样,我们是否也应该获取一下值呢?
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
这是个宾果。
虽然Bingo很好,但是从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的压缩选项似乎不相关。
当指定为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个字符,我们可以再确认一下这个方面吗?
如果有指定命名空间的话 (如果有指定命名空间)
[45] pry(main)> a.write('1234567890' * 30, 'val')
=> 5692549928996306944
stats cachedump 8 1
ITEM NS_ABC:12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456:md5:116b331c40362c11c387c7bf0eaf8295 [114 b; 1545369320 s]
END
如果没有指定命名空间,
[52] pry(main)> a.write('1234567890' * 30, 'val')
=> 5764607523034234880
stats cachedump 8 1
ITEM 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123:md5:874fa981137f49f7885e1d05a93c21df [114 b; 1545369598 s]
END
明白了,通常情况下,我们在”namespace:指定的键”后面添加各自的MD5值,以确保不超过250个字符。
[62] pry(main)> Digest::MD5.hexdigest('NS_ABC:' + '1234567890' * 30)
=> "116b331c40362c11c387c7bf0eaf8295"
[63] pry(main)> Digest::MD5.hexdigest('1234567890' * 30)
=> "874fa981137f49f7885e1d05a93c21df"
这个会自动为我添加吗?这真方便。