论坛首页 Java企业应用论坛

MemCached Cache Java Client封装优化历程

浏览 41496 次
该帖已经被评为良好帖
作者 正文
   发表时间:2008-09-28  
发了帖子就没来看了,这才发现被加了良好了,真是不敢当.其实仅仅只是封装,同时找到了一些性能可以改进的点.

回robbin大哥:当时初学的时候得却看到两个,一个说是异步写的很不错,但是当时仅仅只是使用,前面这个时间比较早,觉得会成熟一些。其实后来在和作者交流过程中,他也在不断的优化,同时在我看来,对于nio的异步操作在whalin里面也有用(后来的版本)。
其实对于网络方面socket来说,个人觉得如果是短连接,释放比较快,那么就是一个连接池的维护,长连接说会要去共享信道之类的工作提高效率。同时在过去nio出来的时候,主要能够将服务端效率提高,因为服务端在接受请求以后后续还有很多处理,将接受请求和处理分开,那么就可以最大限度的去建立连接和提高处理效率。对于客户端来说,要么自己的socket用完了,否则我的想法异步或者共享没有什么作用,发送数据现在也不会发超过1m的,因为memcache的value最大限度就是1m。所以后来也就自己搞搞了,不过有空去测试比较一下,也可能就是比那个差。

回sdh5724同学:其实我也很赞同你的意见,我以前和别人说过,cache就是不稳定的,干什么要搞这样,但是在效率和可靠性需求上,不得不做出选择,就说我那个场景,大部分一次读入多次读取的数据,例如服务访问者的信息以及一些安全策略,如果不命中就去数据库取,我要搞垮这个网站十分容易,数据库资源会很容易被消耗,我们之前也考虑过内存数据库之类的,一来成熟产品比较贵,二来开源的东西效率和稳定性值得考虑,所以就自己封装了一下,其实也就是一个备份而已,我觉得有一点是肯定的,做任何事情简单就好,我们这边也是简单化,那就多放几份,丢了就部分粘过去。这里对于系统来说没有说增加太大的复杂度,仅仅就是double了一下服务器。

回movingboy同学:在使用Memcache之前我使用过Jboss Tree Cache还有其他的分布式的Cache,但是JGroup的master node对于管理slave node的问题一直困扰着很多人,其实组播本身就不一定可靠,所以我坚持认为还是暂时放弃分布式的Cache,在我看来现在的分布式无非就三种方式:一种就是类似于Memcache的伪分布式,靠客户端来分数据,同时自己在搞一个集群封装,来互相备份。要么就是用刚才说的基于组播的方式。还有就是现在分布式文件系统或者分布式计算中的master,slave模式,由Master来保证slave的选择和数据迁移,但是master的单点怎么解决呢,就又要拉开来了。说多了,赫赫。

其实Memcache出彩的地方在于它的内存分配算法,当然用java也可以去实现,不过既然有成熟的轮子,那就先用这把,把力气用在更需要去琢磨的有商业价值的技术研究上。还是很感谢robin大哥和各位同学(阿里习惯叫开发的朋友同学),因为自己有一个blog,很多东西也懒惰的贴来贴去,所以也就只关注javaeye,而一直潜水。
0 请登录后投票
   发表时间:2008-09-28  
请教一个小问题: 如果某个slab已经被分配满了,当新的对象被set进来的时候,memcached是会挑选已经过期的对象覆盖掉,还是挑选LRU的对象覆盖掉呢?
0 请登录后投票
   发表时间:2008-09-29  
robbin 写道
请教一个小问题: 如果某个slab已经被分配满了,当新的对象被set进来的时候,memcached是会挑选已经过期的对象覆盖掉,还是挑选LRU的对象覆盖掉呢?


首先,在memcache分配的时候,初始化回去分配一系列的slab,例如初始的slab为80k,然后factor为1.2,那么你会发现开始的时候就会有:80,96,116,....一直到1M大小的slab各一个,假如你的对象集中在其中某一个区间,那么很快那个slab就会分配满,此时如果内存还有,那么就会新建一个同样大小的slab作为链挂在第一个同等大小的slab上,如果说内存也满了,slab也满了,那么就开始LRU算法了。
但是从上面我描述的来看,其实Memcached的LRU算法是针对slab的,而非全局的,因此如果数据集中在一个slab上,那么初始化的时候其他几个slab肯定就浪费了,同时,如果slab的大小和你对象的大小有比较大的size差异,那么浪费的将会更加巨大。所以在你评估使用memcache初始大小和factor的时候需要注意这些,选择适合的初始化size和factor,减少slab分配的浪费。
0 请登录后投票
   发表时间:2008-09-29  
多谢robbin和cenwenchu的指点,让我对MemCached又多了一些认识
0 请登录后投票
   发表时间:2008-09-29  
我很喜欢MEMCACHED。 MEMCACHED非常的稳定, 我有一群机器, MEMCACHED进程已经运行运行了大约1年半了。 表现的算是比较稳定了。

cenwenchu 你说的情况, 我很明白, 就是利用一个不存在KEY去查询数据库, 很容易导致数据库搞跨。 我也在试图解决这个问题。 在理论上, 有很多技术能消除这个问题。 目前, 最流行的技术就  BloomFilter. 这东西能解决不存在的key造成性能的损耗的问题。 不过, 目前, 好像我们的应用没有遭受这样的攻击。 这个实现不是很紧急。  由于,我们这儿使用了很多种不同的CACHE技术。 为了整个系统有一致的cache接口, 方便不同技术的相互切换。 我封装了一个统一的JAVA API对 cache进行操作,通过引用不同CacheProvider实现。 另外,我同时让API代码更容易适合在spring bean 容器下的配置。 自我感觉这是个很好的主意, 因为我们有的团队发生过采用了不适当的cache导致要大规模修改代码的事情。 这也是值得规模使用cache的团队采用的方法。 现在我只需要稍微修改下配置就能简单切换不同的cache产品。 目前我封装了5种cache。  这实际上也很方便做代码测试, 我可以简单的使用Mock的机制做我的代码测试。


0 请登录后投票
   发表时间:2008-09-29  
cenwenchu 写道
robbin 写道
请教一个小问题: 如果某个slab已经被分配满了,当新的对象被set进来的时候,memcached是会挑选已经过期的对象覆盖掉,还是挑选LRU的对象覆盖掉呢?


首先,在memcache分配的时候,初始化回去分配一系列的slab,例如初始的slab为80k,然后factor为1.2,那么你会发现开始的时候就会有:80,96,116,....一直到1M大小的slab各一个,假如你的对象集中在其中某一个区间,那么很快那个slab就会分配满,此时如果内存还有,那么就会新建一个同样大小的slab作为链挂在第一个同等大小的slab上,如果说内存也满了,slab也满了,那么就开始LRU算法了。
但是从上面我描述的来看,其实Memcached的LRU算法是针对slab的,而非全局的,因此如果数据集中在一个slab上,那么初始化的时候其他几个slab肯定就浪费了,同时,如果slab的大小和你对象的大小有比较大的size差异,那么浪费的将会更加巨大。所以在你评估使用memcache初始大小和factor的时候需要注意这些,选择适合的初始化size和factor,减少slab分配的浪费。

 
   这个你有经验么, 我好久没有去维护这些东西了, 现在我的每个CACHE 进程大约使用了4-5G内存。 我没有设置特别的参数。 看来我要好好改善下了。
0 请登录后投票
   发表时间:2008-09-29  
sdh5724 写道
我很喜欢MEMCACHED。 MEMCACHED非常的稳定, 我有一群机器, MEMCACHED进程已经运行运行了大约1年半了。 表现的算是比较稳定了。

cenwenchu 你说的情况, 我很明白, 就是利用一个不存在KEY去查询数据库, 很容易导致数据库搞跨。 我也在试图解决这个问题。 在理论上, 有很多技术能消除这个问题。 目前, 最流行的技术就  BloomFilter. 这东西能解决不存在的key造成性能的损耗的问题。 不过, 目前, 好像我们的应用没有遭受这样的攻击。 这个实现不是很紧急。  由于,我们这儿使用了很多种不同的CACHE技术。 为了整个系统有一致的cache接口, 方便不同技术的相互切换。 我封装了一个统一的JAVA API对 cache进行操作,通过引用不同CacheProvider实现。 另外,我同时让API代码更容易适合在spring bean 容器下的配置。 自我感觉这是个很好的主意, 因为我们有的团队发生过采用了不适当的cache导致要大规模修改代码的事情。 这也是值得规模使用cache的团队采用的方法。 现在我只需要稍微修改下配置就能简单切换不同的cache产品。 目前我封装了5种cache。  这实际上也很方便做代码测试, 我可以简单的使用Mock的机制做我的代码测试。



是的,这也是我封装它第一个目的,接口化,后端实现随时动态替换,只需要在自己实现的jar里面配置一个文件即可。因为我现在的产品比较特殊,是集成第三方服务的平台,自由开发者只需要注册一个开发应用即可使用淘宝,支付宝等API,所以不仅仅是自身安全问题,同时一旦被攻击,后端的服务也将会不能使用,因此才需要这样的考虑,第三方的我没有太多地去找,因为还是觉得有些东西简单化即可,搞太多的依赖反而维护成本会增加。
0 请登录后投票
   发表时间:2008-09-29  
sdh5724 写道
cenwenchu 写道
robbin 写道
请教一个小问题: 如果某个slab已经被分配满了,当新的对象被set进来的时候,memcached是会挑选已经过期的对象覆盖掉,还是挑选LRU的对象覆盖掉呢?


首先,在memcache分配的时候,初始化回去分配一系列的slab,例如初始的slab为80k,然后factor为1.2,那么你会发现开始的时候就会有:80,96,116,....一直到1M大小的slab各一个,假如你的对象集中在其中某一个区间,那么很快那个slab就会分配满,此时如果内存还有,那么就会新建一个同样大小的slab作为链挂在第一个同等大小的slab上,如果说内存也满了,slab也满了,那么就开始LRU算法了。
但是从上面我描述的来看,其实Memcached的LRU算法是针对slab的,而非全局的,因此如果数据集中在一个slab上,那么初始化的时候其他几个slab肯定就浪费了,同时,如果slab的大小和你对象的大小有比较大的size差异,那么浪费的将会更加巨大。所以在你评估使用memcache初始大小和factor的时候需要注意这些,选择适合的初始化size和factor,减少slab分配的浪费。

 
   这个你有经验么, 我好久没有去维护这些东西了, 现在我的每个CACHE 进程大约使用了4-5G内存。 我没有设置特别的参数。 看来我要好好改善下了。


嗬嗬,这个绝对不是我看官方文档YY得,实际测试过的,同时slab的链过长也会影响查询速度。你可以自己测试看看。
3 请登录后投票
   发表时间:2008-09-29  
cenwenchu你好,请教几个问题:

cenwenchu 写道
在使用KeySet的时候有一个参数,类型是Boolean,这个字段的存在是因为,在Memcached中数据的删除并不是直接删除,而是标注一下,这样会导致实现keySet的时候取出可能已经删除的数据


我测试了一下,set一个对象以后,再delete它,然后stats items, stats cachedump itemid 0,发现memcached里面已经查询不到这个对象了。按照你的说法,应该可以遍历到这个对象的key,但实际上我遍历不到。

cenwenchu 写道
从上面我描述的来看,其实Memcached的LRU算法是针对slab的,而非全局的,因此如果数据集中在一个slab上,那么初始化的时候其他几个slab肯定就浪费了


我观察到的现象不是这样的,例如我分配了1.4GB的内存,那么初始化的时候,memcached会按照默认的1.2的factor一直分配到slabs class 38,如下:

linux console:~> memcached -m 1400 -vv 
slab class   1: chunk size    104 perslab 10082
slab class   2: chunk size    136 perslab  7710
slab class   3: chunk size    176 perslab  5957
slab class   4: chunk size    224 perslab  4681
slab class   5: chunk size    280 perslab  3744
slab class   6: chunk size    352 perslab  2978
slab class   7: chunk size    440 perslab  2383
slab class   8: chunk size    552 perslab  1899
slab class   9: chunk size    696 perslab  1506
slab class  10: chunk size    872 perslab  1202
slab class  11: chunk size   1096 perslab   956
slab class  12: chunk size   1376 perslab   762
slab class  13: chunk size   1720 perslab   609
slab class  14: chunk size   2152 perslab   487
slab class  15: chunk size   2696 perslab   388
slab class  16: chunk size   3376 perslab   310
slab class  17: chunk size   4224 perslab   248
slab class  18: chunk size   5280 perslab   198
slab class  19: chunk size   6600 perslab   158
slab class  20: chunk size   8256 perslab   127
slab class  21: chunk size  10320 perslab   101
slab class  22: chunk size  12904 perslab    81
slab class  23: chunk size  16136 perslab    64
slab class  24: chunk size  20176 perslab    51
slab class  25: chunk size  25224 perslab    41
slab class  26: chunk size  31536 perslab    33
slab class  27: chunk size  39424 perslab    26
slab class  28: chunk size  49280 perslab    21
slab class  29: chunk size  61600 perslab    17
slab class  30: chunk size  77000 perslab    13
slab class  31: chunk size  96256 perslab    10
slab class  32: chunk size 120320 perslab     8
slab class  33: chunk size 150400 perslab     6
slab class  34: chunk size 188000 perslab     5
slab class  35: chunk size 235000 perslab     4
slab class  36: chunk size 293752 perslab     3
slab class  37: chunk size 367192 perslab     2
slab class  38: chunk size 458992 perslab     2


slab class 38的chunk size是448KB。假设我的应用程序不会put这么大的对象进去的话,按照你的说法,slab class 38是肯定被浪费掉了,因为预分配了2个,总共浪费2MB内存。

但实际上我观察下来,并非如此。这些预分配的超大slab如果一直空闲,迟早会被memcached回收,比方说JavaEye的memcached现在是这个样子的:

linux console:~> memcached-tool localhost:11211
  #  Item_Size   Max_age  1MB_pages Count   Full?
  1     104 B   101444 s       1    4882      no
  2     136 B   101373 s       1    7005      no
  3     176 B   100223 s       3   17871     yes
  4     224 B    44825 s      12   56172     yes
  5     280 B    33624 s       9   33696     yes
  6     352 B    45249 s      56  166768     yes
  7     440 B    40679 s      12   28596     yes
  8     552 B    44904 s      12   22788     yes
  9     696 B    42437 s      67  100902     yes
 10     872 B    53838 s      37   44474     yes
 11     1.1 kB   41718 s      22   21032     yes
 12     1.3 kB   49149 s      36   27432     yes
 13     1.7 kB   55130 s      35   21315     yes
 14     2.1 kB   37793 s      34   16558     yes
 15     2.6 kB   51071 s      27   10476     yes
 16     3.3 kB   41532 s      31    9610     yes
 17     4.1 kB   44724 s      33    8184     yes
 18     5.2 kB   45328 s      38    7524     yes
 19     6.4 kB   38455 s      43    6794     yes
 20     8.1 kB   42549 s      49    6223     yes
 21    10.1 kB   45387 s      60    6060     yes
 22    12.6 kB   42531 s      70    5670     yes
 23    15.8 kB   42652 s      92    5888     yes
 24    19.7 kB   42559 s     140    7140     yes
 25    24.6 kB   36928 s      70    2870     yes
 26    30.8 kB   41898 s      67    2211     yes
 27    38.5 kB   44852 s      72    1872     yes
 28    48.1 kB   45952 s      65    1365     yes
 29    60.2 kB   46564 s      71    1207     yes
 30    75.2 kB   43544 s     151    1963     yes


slab class 31到slab 38全部都被回收了,并没有被浪费。


0 请登录后投票
   发表时间:2008-09-29  
sdh5724 写道
 
   这个你有经验么, 我好久没有去维护这些东西了, 现在我的每个CACHE 进程大约使用了4-5G内存。 我没有设置特别的参数。 看来我要好好改善下了。


一个memcached使用4-5G内存,还不如启动4-5个进程,每个进程1GB这样更能够有效利用内存。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics