试着通过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"

这个会自动为我添加吗?这真方便。

广告
将在 10 秒后关闭
bannerAds