Memcached是一个自由开源的,高性能,分布式内存对象缓存系统。
Memcached是以LiveJournal旗下Danga Interactive公司的Brad Fitzpatric为首开发的一款软件。现在已成为mixi、hatena、Facebook、Vox、LiveJournal等众多服务中提高web应用扩展性的重要因素。
Memcached是一种基于内存的key-value存储,用来存储小块的任意数据(字符串、对象)。这些数据可以是数据库调用、API调用或者是页面渲染的结果。
Memcached简洁而强大。它的简洁设计便于快速开发,减轻开发难度,解决了大数据量缓存的很多问题。它的API兼容大部分流行的开发语言。
本质上,它是一个简洁的key-value存储系统。
一般的使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度、提高可扩展性。
Memcached基本的工作原理
Memcached是以守候程序的方式运行于一个或者多个服务器中,随时等待客户端的链接请求,通过启动Memcached服务端,配置相应的监听IP、端口等参数,客户端可通过指定的服务器IP 将数据以key-value的方式存储到Memcached实例中。
如下图所示,有N个Memcached实例部署在一台专门的机器上,应用程序在查询数据时首先去Memcached里面查询,如果有数据则直接返回客户端使用,如果Memcached里面没有需要的数据或者数据过期了,则应用程序先去查询数据库,然后把查询的结果放到Memcached实例里面去,最后返回给客户端。
Memcached服务特点?
a、完全基于内存缓存的
b、节点之间相互独立
c、C/S模式架构,C语言编写,总共2000行代码。
d、异步I/O 模型,使用libevent作为事件通知机制。
e、被缓存的数据以key/value键值对形式存在的。
f、全部数据存放于内存中,无持久性存储的设计,重启服务器,内存里的数据会丢失。
g、当内存中缓存的数据容量达到启动时设定的内存值时,就自动使用LRU算法删除过期的缓存数据。
h、可以对存储的数据设置过期时间,这样过期后的数据自动被清除,服务本身不会监控过期,而是在访问的时候查看key的时间戳,判断是否过期。
j、memcache会对设定的内存进行分块,再把块分组,然后再提供服务。
Memcached的两阶段哈希
第一阶段哈希是计算"key-value"存储在哪个Memcached实例上,第二阶段哈希是计算"key-value"是存储存储在Memcached实例的哪个chunk里面。
客户端存取数据时,首先参考Memcached实例列表计算出key的哈希值(阶段一哈希),进而选中一个Memcached实例;客户端将请求发送给选中的Memcached实例节点,然后该Memcached节点通过内部的哈希算法(阶段二哈希) 进行真正的数据(Item)存取。 Memcached的服务器和客户端通信并不使用复杂的XML等格式,而是使用简单的基于文本行的协议。
如下图所示:
比如我们要在Memcached上存储 key为"Hello",value为"World" 的这么一个键值对。
阶段一哈希: 首先在客户端根据key计算出该"键值对"会存储在哪个Memcached实例中,假如是m2
阶段二哈希: 在Memcached实例二中 再次根据key计算出该"键值对"在m2实例中存在的位置。
Memcached的数据存储方式
Memcached的数据存储方式被称为Slab Allocator,其基本方式是:
(a)先把内存分成很多个Slab,这个大小是预先规定好的,以解决内存碎片问题。
分配给Slab的内存空间被称为Page,默认是1M。一个Slab下可以有多个Page。
(如下图:在Memcached实例中先把内存分了n个slab,然后每个slab里面分了n个page)
(b)然后把一个Page分成很多个chunk块,chunk块是用于缓存记录的空间。Chunk的大小是先有一个基本值,然后根据增长因子来增大。
Slab Allocation的原理——将分配的内存分割成各种尺寸的块(chunk), 并把尺寸相同的块分成组(chunk的集合),每个chunk集合被称为slab。
©slab class:内存区类别(48byte-1M),每个类别有一个slab classId
(d)Memcached里面保存着slab内存空间的chunk列表,当收到要保存的item时,它会根据item的大小去选择一个最适合的slab,然后找到空闲的chunk,把数据存放进去。
Memcached新建Item分配内存过程
第一步: 快速定位slab classid,先计算Item长度
key键长+flag+suffix(16字节)+value值长+结构大小(32字节),比如计算出来有90byte
如果Item长度计算出来>1M,则无法存储丢弃
取最小冗余的slab class进行存储,比如有:48、96、120, 存储90就会选96
第二步:按顺序寻找可用的chunk
(a) slot : 检查slab回收空间slot里是否有剩余chunk
delete: delete时标记到slot
exptime: get时检查的过期对象标记到slot
(b)end_page_ptr: 检查page中是否有剩余chunk
©memory: 内存还有剩余空间可以用于开辟新的slab
(d)LRU
(PS:Memcached的数据存储方式的缺点,由于chunk的大小是预先分配好的特定长度,因此如果数据不能完全填满chunk,那么chunk中剩余的空间就浪费了。)
Memcached的数据过期方式
(1)Lazy Expiration(延迟/惰性 过期)
Memcached不会监控记录是否过期,而是当客户端来获取数据的时候,才会检查记录的时间戳,因此成为Lazy Expiration
(2)LRU(Least Recently Used 最近最少使用)
当空间不足的时候,Memcached会优先使用已经过期的数据空间,如果还不够的话,那么就会把最近最少使用的对象的空间释放出来。
(ps:要特别注意,Memcached的LRU不是全局的,而是针对slab的,可以说是区域性的)
(3)惰性删除机制
删除item对象时,不释放内存,作删除标记,指针放入slot回收插槽,下次分配的时候直接使用。
Memcached与spring集成
(1)先把jar包添加到本地仓库中:
mvn install:install-file -Dfile=Java_memcached-release_2.6.6.jar -DgroupId=com.danga -DartifactId=memcached -Dversion=2.6.6 -Dpackaging=jar -DgeneratePom=true
(ps:注意 -Dfile=java_memcached-release_2.6.6.jar 一定要输入jar包的全路径名 )
(2)在pom中添加memcached的lib依赖和common-pool的lib依赖:
1 <dependency>
2 <groupId>commons-pool</groupId>
3 <artifactId>commons-pool</artifactId>
4 <version>1.5.6</version>
5 </dependency>
6 <dependency>
7 <groupId>com.danga</groupId>
8 <artifactId>memcached</artifactId>
9 <version>2.6.6</version>
10 </dependency>
(3)在spring的applicationContext.xml文件中加入memcached的配置
1 <bean id="memcachedPool" class="com.danga.MemCached.SockIOPool" factory-method="getInstance"
2 init-method="initialize" destroy-method="shutDown">
3 <constructor-arg>
4 <value>neeaMemcachedPool</value>
5 </constructor-arg>
6 <property name="servers">
7 <list>
8 <value>192.168.1.81:3333</value>
9 </list>
10 </property>
11 <property name="weights">
12 <list>
13 <value>1</value>
14 </list>
15 </property>
16 <property name="initConn">
17 <value>5</value>
18 </property>
19 <property name="minConn">
20 <value>5</value>
21 </property>
22 <property name="maxConn">
23 <value>5</value>
24 </property>
25 <property name="maintSleep">
26 <value>30</value>
27 </property>
28 <property name="nagle">
29 <value>false</value>
30 </property>
31 <property name="maxIdle">
32 <value>6000</value>
33 </property>
34 <property name="socketTO">
35 <value>3000</value>
36 </property>
37 </bean>
38
39 <bean id="memCachedClient" class="com.danga.MemCached.MemCachedClient">
40 <constructor-arg>
41 <value>neeaMemcachedPool</value>
42 </constructor-arg>
43 </bean>
在程序中就可以通过 MemCachedClient的get、set方法来往Memcached里面获取和设置item了
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。