• 1. Memcached: 你知道和不知道的事主讲人:鲜果酸酸哥
  • 2. Memcached 是国外社区网站 LiveJournal 的开发团队开发的高性能的分布式内存缓存服务器。用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提供动态、数据库驱动网站的速度。
  • 3. memcached的特征协议简单 基于libevent的事件处理 内置内存存储方式 memcached不互相通信的分布式
  • 4. 协议简单memcached的服务器客户端通信并不使用复杂的XML等格式,而使用简单的基于文本行的协议。因此,通过telnet也能在memcached上保存数据、取得数据。下面是例子。 telnet 192.168.1.145 11211
  • 5. 基于libevent的事件处理libevent是个程序库,它将Linux的epoll、BSD类操作系统的kqueue等事件处理功能封装成统一的接口。即使对服务器的连接数增加,也能发挥O(1)的性能。memcached使用这个libevent库,因此能在Linux、BSD、Solaris等操作系统上发挥其高性能。关于事件处理这里就不再详细介绍,可以参考Dan Kegel的The C10K Problem。 libevent: http://www.monkey.org/~provos/libevent/ The C10K Problem: http://www.kegel.com/c10k.html 9
  • 6. 内置内存存储方式为了提高性能,memcached中保存的数据都存储在memcached内置的内存存储空间中。由于数据仅存在于内存中,因此重启memcached、重启操作系统会导致全部数据消失。另外,内容容量达到指定值之后,就基于LRU(Least Recently Used)算法自动删除不使用的缓存。memcached本身是为缓存而设计的服务器,因此并没有过多考虑数据的永久性问题。关于内存存储的详细信息,后面会详细讲解。
  • 7. memcached不互相通信的分布式memcached尽管是“分布式”缓存服务器,但服务器端并没有分布式功能。各个memcached不会互相通信以共享信息。那么,怎样进行分布式呢?这完全取决于客户端的实现。后面也将介绍memcached的分布式。
  • 8. memcached安装安装步骤: 先安装libevent 再安装memcached主程序
  • 9. 假设安装目录为/usr/local 安装libevent $ tar xzvf libevent-1.4.14b-stable.tar.gz $ cd libevent-1.4.14b-stable $ ./configure --prefix=/usr/local/libevent-1.4.14b-stable $ make $ make all 安装memcached $ tar xzvf memcached-1.4.5.tar.gz $ cd memcached-1.4.5 $ ./configure --with-libevent=/usr/local/libevent-1.4.14b-stable --prefix=/usr/local/memcached-1.4.5 $ make $ make all
  • 10. memcached启动与关闭常用启动参数说明: $ /usr/local/bin/memcached -h -d 启动一个守护进程 -vv 调试信息和错误输出到控制台 -m 分配给memcache使用的内存数量,单位是mb,默认是64mb -u 运行memcached的用户 -l 监听的服务器ip地址 -p 设置memcached监听的端口,最好是1024以上的端口 -c 设置最大运行的并发连接数,默认是1024 -P 设置保存memcached的pid文件 例如: /usr/local/bin/memcached -p 11211 -m 64m -vv /usr/local/bin/memcached -p 11211 -m 64m -d
  • 11. Memcached基本操作实例# 连接memcached telnet 192.168.1.145 11211 存储命令set/add/replace 读取命令get 删除命令delete 计数命令incr/decr 统计命令status
  • 12. 存储命令set/add/replaceset liu 32 0 4 java STORED//正确 get liu VALUE abc 32 4 java END set liu 32 0 4 cplus CLIENT_ERROR bad data chunk ERROR//长度错误set liu 32 0 4 java STORED add liu 32 0 5 cplus NOT_STORED //已存在不能add get liu VALUE abc 32 4 java END add song 32 0 5 cplus STORED //不存在可以addset liu 32 0 4 java STORED replace liu 32 0 5 cplus STORED //已存在可以replace get liu VALUE cplus 32 5 liu END replace yang 32 0 5 cplus NOT_STORED //不存在不能replacedatablock长度必须正确add只能添加不存在的keyreplace只能替换已有的key
  • 13. 格式: []\r\n \r\n \r\n command set无论如何都进行存储 add只有数据不存在时进行添加 repalce只有数据存在时进行替换 append往后追加:append datablock ? prepend往前追加:prepend datablock cas按版本号更改 key 字符串,<250个字符,不包含空格和控制字符 flags 客户端用来标识数据格式的数值,如json,xml,压缩等 exptime 存活时间s,0为永远,<30天60*60*24*30为秒数,>30天为unixtime bytes byte字节数,不包含\r\n,根据长度截取存/取的字符串,可以是0,即存空串 datablock 文本行,以\r\n结尾,当然可以包含\r或\n
  • 14. 读取命令get/gets格式: *\r\n VALUE []\r\n \r\n … VALUE []\r\n \r\n END\r\n command: get普通查询,gets用于查询带版本的值 get liu song yang VALUE liu 32 4 java VALUE song 32 5 cplus END //查询多个键值gets liu VALUE liu 32 4 12 java END //取得版本号 replace liu 32 0 4 java STORED //增加版本号 get liu VALUE liu 32 4 java END gets liu VALUE liu 32 4 13 java END版本号+1
  • 15. 计数命令incr/decr格式: incr/decr 要求:key必须存在,value必须是数字 incr liu 2 CLIENT_ERROR cannot increment or decrement non-numeric valuevalue不是数字不能计数delete count DELETED incr count 1 NOT_FOUNDkey不存在不能计数set count 32 0 1 1 STORED incr count 8 9 decr count 2 7实现计数器
  • 16. 删除命令delete格式: delete [
  • 17. 统计命令stats格式: stats []\r\n STAT \r\n END\r\n stats STAT pid 23178 STAT uptime 1039318 STAT time 1292036037 STAT version 1.4.2 STAT pointer_size 64 STAT rusage_user 1011.574217 STAT rusage_system 1677.713948 STAT curr_connections 114 STAT total_connections 73801 STAT connection_structures 149 STAT cmd_get 79114939 STAT cmd_set 27302514 STAT cmd_flush 0 STAT get_hits 79114939 STAT get_misses 24322507 STAT delete_misses 133928 STAT delete_hits 402569 STAT incr_misses 0 STAT incr_hits 0STAT decr_misses 0 STAT decr_hits 0 STAT cas_misses 0 STAT cas_hits 0 STAT cas_badval 0 STAT auth_cmds 0 STAT auth_errors 0 STAT bytes_read 59348603658 STAT bytes_written 425549797158 STAT limit_maxbytes 4294967296 STAT accepting_conns 1 STAT listen_disabled_num 0 STAT threads 4 STAT conn_yields 0 STAT bytes 3832761746 STAT curr_items 2854731 STAT total_items 27302514 STAT evictions 18456987 STAT reclaimed 0 END
  • 18. 名称描述pidMemcached进程IDuptimeMemcached运行时间,单位:秒timeMemcached当前的UNIX时间versionMemcached的版本号rusage_user该进程累计的用户时间,单位:秒rusage_system该进程累计的系统时间,单位:秒curr_connections当前连接数量total_connectionsMemcached运行以来接受的连接总数connection_structuresMemcached分配的连接结构的数量cmd_get查询请求总数get_hits查询成功获取数据的总次数get_misses查询成功未获取到数据的总次数cmd_set存储(添加/更新)请求总数bytesMemcached当前存储内容所占用字节数bytes_readMemcached从网络读取到的总字节数bytes_writtenMemcached向网络发送的总字节数limit_maxbytesMemcached在存储时被允许使用的字节总数curr_itemsMemcached当前存储的内容数量total_itemsMemcached启动以来存储过的内容总数evictionsLRU释放对象数,用来释放内存分析CPU占用是否高分析连接数是否太多分析命中率是否太低分析字节数流量分析对象数LRU频率
  • 19. stats slabs区块统计stats slabs STAT 1:chunk_size 96 STAT 1:chunks_per_page 10922 ... STAT 40:chunk_size 1048576 STAT 40:chunks_per_page 1 STAT 40:total_pages 15 STAT 40:total_chunks 15 STAT 40:used_chunks 14 STAT 40:free_chunks 1 STAT 40:free_chunks_end 0 STAT 40:mem_requested 9348752 STAT 40:get_hits 9593 STAT 40:cmd_set 4828 STAT 40:delete_hits 40 STAT 40:incr_hits 0 STAT 40:decr_hits 0 STAT 40:cas_hits 0 STAT 40:cas_badval 0 STAT active_slabs 40 STAT total_malloced 4294496616 END名称描述chunk_sizechunk大小,bytechunks_per_page每个page的chunk数量total_pagespage数量total_chunkschunk数量*page数量get_hitsget命中数cmd_setset数delete_hitsdelete命中数incr_hitsincr命中数decr_hitsdecr命中数cas_hitscas命中数cas_badvalcas数据类型错误数used_chunks已被分配的chunk数free_chunks剩余chunk数free_chunks_end分完page浪费chunk数mem_requested请求存储的字节数active_slabsslab数量total_malloced总内存数量被浪费内存数=(total_chunks * chunk_size) - mem_requested 如果太大,需要调整factor
  • 20. 2、Memcache内存分配机制
  • 21. Slab Allocation机制:整理内存以便重复使用最近的memcached默认情况下采用了名为Slab Allocator的机制分配、管理内存。在该机制出现以前,内存的分配是通过对所有记录简单地进行malloc和free来进行的。但是,这种方式会导致内存碎片,加重操作系统内存管理器的负担,最坏的情况下,会导致操作系统比memcached进程本身还慢。Slab Allocator就是为解决该问题而诞生的。 下面来看看Slab Allocator的原理。 Slab Allocator的基本原理是按照预先规定的大小,将分配的内存分割成特定长度的块,以完全解决内存碎片问题。 Slab Allocation的原理相当简单。将分配的内存分割成各种尺寸的块(chunk),并把尺寸相同的块分成组(chunk的集合)(图1)。 图1
  • 22. 而且,slab allocator还有重复使用已分配的内存的目的。也就是说,分配到的内存不会释放,而是重复利用。
  • 23. Slab Allocation的主要术语Page 分配给Slab的内存空间,默认是1MB。分配给Slab之后根据slab的大小切分成chunk。 Chunk 用于缓存记录的内存空间。 Slab Class 特定大小的chunk的组。
  • 24. 在Slab中缓存记录的原理下面说明memcached如何针对客户端发送的数据选择slab并缓存到chunk中。 memcached根据收到的数据的大小,选择最适合数据大小的slab(图2)。memcached中保存着slab内空闲chunk的列表,根据该列表选择chunk,然后将数据缓存于其中。
  • 25. Page为内存分配的最小单位Memcached的内存分配以page为单位,默认情况下一个page是1M,可以通过-I参数在启动时指定。如果需要申请内存 时,memcached会划分出一个新的page并分配给需要的slab区域。page一旦被分配在重启前不会被回收或者重新分配(page ressign已经从1.2.8版移除了)
  • 26. Slabs划分数据空间Memcached并不是将所有大小的数据都放在一起的,而是预先将数据空间划分为一系列slabs,每个slab只负责一定范围内的数据存储。如 下图,每个slab只存储大于其上一个slab的size并小于或者等于自己最大size的数据。例如:slab 3只存储大小介于137 到 224 bytes的数据。如果一个数据大小为230byte将被分配到slab 4中。从下图可以看出,每个slab负责的空间其实是不等的,memcached默认情况下下一个slab的最大值为前一个的1.25倍,这个可以通过修 改-f参数来修改增长比例。
  • 27. (本页无文本内容)
  • 28. Chunk才是存放缓存数据的单位Chunk是一系列固定的内存空间,这个大小就是管理它的slab的最大存放大小。例如:slab 1的所有chunk都是104byte,而slab 4的所有chunk都是280byte。chunk是memcached实际存放缓存数据的地方,因为chunk的大小固定为slab能够存放的最大值, 所以所有分配给当前slab的数据都可以被chunk存下。如果时间的数据大小小于chunk的大小,空余的空间将会被闲置,这个是为了防止内存碎片而设 计的。例如下图,chunk size是224byte,而存储的数据只有200byte,剩下的24byte将被闲置。
  • 29. (本页无文本内容)
  • 30. Memcached在启动时通过-m指定最大使用内存,但是这个不会一启动就占用,是随着需要逐步分配给各slab的。 如果一个新的缓存数据要被存放,memcached首先选择一个合适的slab,然后查看该slab是否还有空闲的chunk,如果有则直接存放进去;如 果没有则要进行申请。slab申请内存时以page为单位,所以在放入第一个数据,无论大小为多少,都会有1M大小的page被分配给该slab。申请到 page后,slab会将这个page的内存按chunk的大小进行切分,这样就变成了一个chunk的数组,在从这个chunk数组中选择一个用于存储 数据。如下图,slab 1和slab 2都分配了一个page,并按各自的大小切分成chunk数组。
  • 31. (本页无文本内容)
  • 32. slab内存结构图:二维数组链表每个slab都是1MBchunk填充item后会有空间浪费双向链表剩余空 间指针slab指针列表回收空间指针
  • 33. 调优LRU: 首先,在memcache分配的时候,初始化会去分配一系列的slab,例如初始的slab为88k,然后factor为1.25,那么你会发现开始的时候 就会有:88,112,44,....一直到1M大小的slab各一个,假如对象集中在其中某一个区间,那么很快那个slab就会分配满,此时如果内存还有,那么就会新建一个同样大小的slab作为链挂在第一个同等大小的slab上,如果说内存也满了,slab也满了,那么就开始LRU算法了。 但是Memcached的LRU算法是针对slab的,而非全局的,如果数据集中在一个slab上,那么初始化的时候其他几个slab肯定就浪费了,同时,如果slab的大小和对象的大小有比较大的差异,那么浪费的将会更加巨大。所以在评估使用 memcache初始大小和factor的时候需要注意这些,选择适合的初始化size和factor,减少slab分配的浪费。
  • 34. 使用合适的factor,减少浪费 -f参数:默认为1.25,曾经为2 值越小,slab中chunk size差距越小,内存浪费越小 很显然,factor越小,chunk匹配得就越精准,但是slab组就会分得越多,而产生LRU的机会也会增加,factor越大,分组就越少,产生LRU的机会就越小,但是chunk匹配精准度会有所下降,1.25较为理想 1.25适合缓存几百字节的对象 建议:计算一下数据的预期平均长度,调整factor,以获得最恰当的设置 根据数据分布调整factor 非均匀分布,即数据长度集中在几个区域内 如保存用户Session 更极端的状态是等长数据 如定长键值,定长数据 多见于访问、在线统计或执行锁
  • 35. 一切都是为了更快低CPU消耗(瓶颈在于网络IO) libevent事件机制 slab内存预分配机制 适合使用大量低CPU的机器搭建集群 32位机器最大2GB,64GB无限制 -m分配内存为数据区,memcached本身也需要占用内存,因此不可将物理内存全部分配 使用连接池维持连接
  • 36. 因为优秀,所以不足Can’t dump 无法备份,重启无法恢复 Can’t iterate over keys 无法查询 Not persistent 没有持久化,重启全部丢失 Not redundant 单点故障failover No Sessions 崩溃没法查找原因 No security 任何机器都可以telnet,需要放在防火墙后 内存问题 LRU是slab局部,没有全局 有空间浪费 日志问题 没有合理的日志 集群问题 集群增加机器成本高
  • 37. 3、memcached分布式
  • 38. memcached的分布式是什么意思?memcached虽然称为“分布式”缓存服务器,但服务器端并没有“分布式”功能 下面假设memcached服务器有node1~node3三台,应用程序要保存键名为“tokyo”“kanagawa”“chiba”“saitama”“gunma”的数据。
  • 39. 分布式简介:准备 首先向memcached中添加“tokyo”。将“tokyo”传给客户端程序库后,客户端实现的算法就会根据“键”来决定保存数据的memcached服务器。服务器选定后,即命令它保存“tokyo”及其值。
  • 40. 分布式简介:添加时同样,“kanagawa”“chiba”“saitama”“gunma”都是先选择服务器再保存。 接下来获取保存的数据。获取时也要将要获取的键“tokyo”传递给函数库。函数库通过与数据保存时相同的算法,根据“键”选择服务器。使用的算法相同,就能选中与保存时相同的服务器,然后发送get命令。只要数据没有因为某些原因被删除,就能获得保存的值。
  • 41. 分布式简介:获取时这样,将不同的键保存到不同的服务器上,就实现了memcached的分布式。memcached服务器增多后,键就会分散,即使一台memcached服务器发生故障无法连接,也不会影响其他的缓存,系统依然能继续运行。
  • 42. 分布式算法之根据余数计算分散$nodes = array('node1','node2','node3'); $keys = ('tokyo', 'kanagawa', 'chiba', 'saitama', 'gunma'); foreach($keys as $key) { $crc = crc32($key); # CRC値 $mod = $crc % count($nodes); $server = $nodes[ $mod ]; # 根据余数选择服务器  PHP crc32() 函数计算一个字符串的 crc32 多项式。  该函数可用于验证数据的完整性。 首先求得字符串的CRC值,根据该值除以服务器节点数目得到的余数决定服务器。上面的代码执行后输入以下结果: tokyo => node2 kanagawa => node3 chiba => node2 saitama => node1 gunma => node1
  • 43. 根据余数计算分散的缺点余数计算的方法简单,数据的分散性也相当优秀,但也有其缺点。那就是当添加或移除服务器时,缓存重组的代价相当巨大。添加服务器后,余数就会产生巨变,这样就无法获取与保存时相同的服务器,从而影响缓存的命中率 在Web应用程序中使用memcached时,在添加memcached服务器的瞬间缓存效率会大幅度下降,负载会集中到数据库服务器上,有可能会发生无法提供正常服务的情况。
  • 44. Consistent Hashing:一致性hash算法一致性hash思想: 首先求出memcached服务器(节点)的哈希值,并将其配置到0~2的32方的圆(continuum)上。然后用同样的方法求出存储数据的键的哈希值,并映射到圆上。然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过232仍然找不到服务器,就会保存到第一台memcached服务器上
  • 45. (本页无文本内容)
  • 46. 从上图的状态中添加一台memcached服务器。余数分布式算法由于保存键的服务器会发生巨大变化而影响缓存的命中率,但Consistent Hashing中,只有在continuum上增加服务器的地点逆时针方向的第一台服务器上的键会受到影响。
  • 47. (本页无文本内容)
  • 48. Consistent Hashing最大限度地抑制了键的重新分布。而且,有的Consistent Hashing的实现方法还采用了虚拟节点的思想。使用一般的hash函数的话,服务器的映射地点的分布非常不均匀。因此,使用虚拟节点的思想,为每个物理节点(服务器)在continuum上分配100~200个点。这样就能抑制分布不均匀,最大限度地减小服务器增减时的缓存重新分布。 使用Consistent Hashing算法的memcached客户端函数库进行测试的结果是,由服务器台数(n)和增加的服务器台数(m)计算增加服务器后的命中率计算公式如下: (1 - n/(n+m)) * 100 第一个支持Consistent Hashing和虚拟节点的memcached客户端函数库是名为libketama的PHP库,由last.fm开发。
  • 49. 关于php一致性hash算法: 酸酸哥sina博客: http://blog.sina.com.cn/s/blog_700e11ff0101190z.html php与memcached服务器交互的分布式实现源码分析 [memcache版]: http://blog.csdn.net/gonxi/article/details/6130624
  • 50. 有酸酸哥在,你们还怕学不会memcached吗?谢谢:)