欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Memcache工作原理总结

程序员文章站 2022-05-23 18:22:51
...

 

<!--[if !supportLists]-->1.  <!--[endif]-->分片原理

 

咱们废话话不多说了,直接看Memcache的原理。首先memcache解决的最大的一个问题就是内存多次读取的内存碎片问题。内存碎片分为内存内部碎片和内存外部碎片。一般是指在外部碎片中出现了不连续的细小内存片段,不能够被进程利用。因为不连续,不能组合成大而的连续空间,导致这部分空间很可惜的浪费了。内存碎片是因为在分配一个内存块后,使之空闲,但不将空闲内存归还给最大内存块而产生的。

 

那么memcache启动进程的时候就按照预先设定好的大小(默认是64mb)相内存开辟出一段连续的内存空间,之后再将这段内存空间分成不同的片段。

 

相信下面的图大家都见过了

 


Memcache工作原理总结
            
    
    博客分类: 开源项目 memcachenosqlredismongodbjava 
 

Memcache单进程最大可开的内存是2GB,如果想缓存更多的数据,建议还是开辟更多的memcache进程(不同端口)或者使用分布式memcache进行缓存,将数据缓存到不同的物理机或者虚拟机上。

 

Memcache进程启动,在内存开辟了连续的区域。咱们用上面的图形来举例,这段连续的区域就好像上面的slab1+slab2+slab3+……+slab(n).分配区域相同的构成了slab(分片组)。Slab下面可不直接就是存储区域片(就是图中的chunks)了。而是page,如果一个新的缓存数据要被存放,memcached首先选择一个合适的slab,然后查看该slab是否还有空闲的chunk,如果有则直接存放进去;如果没有则要进行申请。slab申请内存时以page为单位,所以在放入第一个数据,无论大小为多少,都会有1M大小的page被分配给该slab。申请到page后,slab会将这个page的内存按chunk的大小进行切分,这样就变成了一个chunk的数组,在从这个chunk数组中选择一个用于存储数据。(http://blog.csdn.net/21aspnet/article/details/7022827)。在Page中才是一个个小存储单元——chunks,一个page默认1mb,那么可以放多少个88字节单位的chunks呢?1024*1024/88约等于11915个。如果放入记录是一个100字节的数据,那么在88字节的chunks112字节的chunks中如何调配呢。答案当然是紧着大的用,不可能将请求过来的数据再做个分解、分离存储、合并读取吧。这样也就带来了一个小问题,还是有空间浪费掉了。112-100=12字节,这12字节就浪费了。

 

在缓存的清除方面,memcache是不释放已分配内存。当已分配的内存所在的记录失效后,这段以往的内存空间,memcache自然会重复利用起来。至于过期的方式,也是采取get到此段内存数据的时候采取查询时间戳,看是否已经超时失效。基本不会有其他线程干预数据的生命周期。至于清空的策略等同于ehcache的默认策略——最近很少使用清空策略——也就是英文常用的LRU——Least Recently Used

 

memcache鉴定内存不足是在什么情况下呢:无法从slab里面获取新的存储单元了。这个对内存十分贪婪的东东。基本服务器都得是2~4GB以上方能吃得消(非时效性,或者说时效性较低的数据)。

 

Memcache借助了操作系统的libevent工具做高效的读写。libevent是个程序库,它将LinuxepollBSD类操作系统的kqueue等事件处理功能封装成统一的接口。即使对服务器的连接数增加,也能发挥高性能。memcached使用这个libevent库,因此能在LinuxBSDSolaris等操作系统上发挥其高性能。Memcache号称可以接受任意数量的连接请求。事实真的是这样吗?

 

<!--[if !supportLists]-->1.  <!--[endif]-->存储过程分析

假设我们现在往memcache中存储一个缓存记录,首先在使用memcache客户端程序的时候要制定一个初始化的服务机器路由表,比如Java的客户端程序

cachedClient = new MemCachedClient();  
//获取连接池实例  
SockIOPool pool = SockIOPool.getInstance();  
  
//设置缓存服务器地址,可以设置多个实现分布式缓存  
pool.setServers(new String[]{"127.0.0.1:11211","192.176.17.90:11211"});

 那么在做存储的时候memcache客户端程序会hash出一个码,之后再根据路由表去将请求转发给memcache服务端,也就是说memcache的客户端程序相当于做了一个类似负载均衡的功能。下面这个图也是大家以前看过的

 


Memcache工作原理总结
            
    
    博客分类: 开源项目 memcachenosqlredismongodbjava 
 

 

memcacheserver上面的进程仅仅负责监听服务和接受请求、存储数据的作用。分发不归他管。所以这么看的话,散列到每台memcache服务机器,让每台机器分布存储得均匀是客户端代码实现的一个难点。这个时侯Hash散列算法就显得格外重要了吧。

 

<!--[if !supportLists]-->1.  <!--[endif]-->读取过程分析

 

理解了memcache的存储就不难理解memcache的读取缓存的过程了。在读取的时候也是根据key算出一个hash,之后在算出指定的路由物理机位置,再将请求分发到服务机上。


Memcache工作原理总结
            
    
    博客分类: 开源项目 memcachenosqlredismongodbjava 
 

 

memcache分布式读写的存储方式有利有弊。如果node2宕机了,那么node2的缓存数据就没了,那么还得先从数据库load出来数据,重新根据路由表(此时只有node1node3),重新请求到一个缓存物理机上,在写到重定向的缓存机器中。灾难恢复已经实现得较为完备。弊端就是维护这么一个高可用缓存,成本有点儿大了。为了存储更多的数据,这样做是否利大于弊,还是得看具体的应用场景再定。

 

<!--[if !supportLists]-->1.  <!--[endif]-->ehcache的争论

 

Ehcache的争论之前就说过了,总是在性能上来说这两个的性能如何。还有大家在网上常见的一个列表进行了比较。笔者觉得,这两个最大的差异是原理的差异决定了应用场景的差异。比如做单点应用缓存的时候,就可以使用ehcache直接向本地内存进行缓存的读写。而做集群缓存的时候一般是借由一个集中式管理server来做缓存,既然是集中式server就少不了网络传输了,这个时侯memcache较为适合。不是说ehcache不能做集群式的缓存,而是做了集群的缓存的代价(RMIJMSJGroups)、网络资源的占用确实比memcache高一些。至于内存的读写操作效率,这个不太好说。Ehcachejava的随机读写类操作二进制的bufferMemcache底层是基于libevent程序库的C服务。这个相信效率都差不多。关键的消耗还是在网络IO资源上。

 

  • Memcache工作原理总结
            
    
    博客分类: 开源项目 memcachenosqlredismongodbjava 
  • 大小: 19.5 KB
  • Memcache工作原理总结
            
    
    博客分类: 开源项目 memcachenosqlredismongodbjava 
  • 大小: 13.8 KB
  • Memcache工作原理总结
            
    
    博客分类: 开源项目 memcachenosqlredismongodbjava 
  • 大小: 19.5 KB