企业级Memcached服务应用实践

一, Memcached介绍

 

1.1 Memcached与常见同类软件对比

(1)Memcached是什么?

  • Memcached是一个开源的,支持高性能,高并发的分布式内存缓存系统,由C语言编写,总共2000多行代码。从软件名称上看,前3个字符“Mem”就是内存的意思,而接下来的后面5个字符“cache”就是缓存的意思,最后一个字符d,是daemon的意思,代表是服务器端守护进程模式服务。
  • Memcached服务分为服务器端和客户端两部分,其中,服务器端软件的名字形如Memcached-1.4.24.tar.gz,客户端软件的名字形如Memcache-2.25.tar.gz
  • Memcached软件诞生于2003年,最初由LiveJournal的Brad Fitzpatrick开发完成。Memcache是整个项目的名称,而Memcached是服务器端的主程序名,因其协议简单,应用部署方便,且支持高并发,因此被互联网企业广泛使用,直到现在仍然如此。其官方网站地址:http://memcached.org/.

(2)Memcached的作用

  • 传统场景中,多数Web应用都将数据保存到关系型数据库中(例如:MySQL),Web服务器从中读取数据并在浏览器中显示。但随着数据量的增大,访问的集中,关系型数据库的负担就会出现加重,响应缓慢,导致网站打开延迟等问题,影响用户体验。
  • 这时就需要Memcached软件出马了。使用Memcached的主要目的是,通过在自身内存中缓存关系型数据库的查询结果,减少数据库被访问的次数,以提高动态Web应用的速度,提高网站架构的并发能力和可扩展性。
  • Memcached服务的运行原理是通过在事先规划好的系统内存空间中临时缓存数据库中的各类数据,以达到减少前端业务服务对数据库的直接高并发访问,从而提升大规模网站集群中动态服务的并发访问能力。 
    -生产场景的Memcached服务一般被用来保存网站中经常被读取的对象或数据,就像我们的客户端浏览器也会把经常访问的网页缓存起来一样,通过内存缓存来存取对象或数据要比磁盘存取快很多,因为磁盘是机械的,因此,在当今的IT企业中,Memcached的应用范围很广泛。
 

1.2 互联网常见内存缓存服务软件

image_1cuh41nusa4714isn8s1kicfi4gu.png-182.2kB

 

二,Memcached的用途与应用场景

 

2.1 Memcached常见用途工作流程

Memcached是一种内存缓存软件,在工作中经常用来缓存数据库的查询数据,数据被缓存在事先与分配的Memcached管理的内存中,可以通过API或命令的方式存取内存中缓存的这些数据,Memcached服务内存中缓存的数据就像一张巨大的hash表,每条数据都是以key-value对的形式存在。

 

2.1.1网站读取Memcached数据时工作流程

从逻辑上来说,当程序访问后端数据库获取数据时会优先访问Memcached缓存,如果缓存中有数据就直接返回给客户端用户,如果没有合适的数据(没有命中),再去后端的数据库读取数据,读取到需要的数据后,就会把数据返回给客户端,同时还会把读取到的数据缓存到Memcached内存中,这样客户端用户再次请求相同的数据时就会直接读取Memcached缓存的数据了,这就大大地减轻了后端数据库的压力,并提高了整个网站的响应速度,提升了用户体验。

展示了Memcached缓存系统和后端数据库系统的协作流程

image_1cuh43ric86b1me4udahb3103hb.png-92.9kB

如上图所示:使用Memcached缓存查询的数据来减少数据库压力的具体工作流程如下:

(1)Web程序首先检查客户端请求的数据是否在Memcached缓存中存在,如果存在,直接把请求的数据返回给客户端,此时不再请求后端数据库。

(2)如果请求的数据在Memcached缓存中不存在,则程序会去请求数据库服务,把从数据库中取到的数据返回给客户端,同时把新取到的数据缓存一份到Memcached缓存中。

 

2.1.2 网站更新Memcached数据时的工作流程

具体流程如下:

(1)当程序更新或删除数据时,会首先处理后端数据库中的数据。

(2)在处理后端数据库中数据的同时,也会通知Memcached,告诉它对应的旧数据失效,从而保证Memcached中缓存的数据始终和数据库中一致,这个数据一致性非常重要,也是大型网站分布式缓存集群最头疼的问题所在。

(3)如果是在高并发读写场合,除了要程序通知Memcached过期的缓存失效外,还可能要通过相关机制,例如在数据库上部署相关程序(如在数据库中设置触发器使用UDFs),实现当数据库有更新时就把数据更新到Memcached服务中,这样一来,客户端在访问新数据时,因预先把更新过的数据库数据复制到Memcached中缓存起来了,所以可以减少第一次查询数据库带来的访问压力,提升Memcached中缓存的命中率,甚至新浪门户还会把持久化存储Redis做成MySQL数据库的从库,实现真正的主从复制。

下图为Memcached网站作为缓存应用更新数据的流程

image_1cuh44v997gpfq9bt61rlsk2hho.png-78.4kB

下图为Memcached服务作为缓存应用通过相关软件更新数据的流程

image_1cuh45ffs1c5m9ab1e6n11oha4mi5.png-74.1kB

在生产工作中,网站Web服务器作为缓存应用更新数据的方案更为常用,即由网站程序负责更新Memcached缓存。

 

2.2 Memcached在企业中的应用场景

 

2.2.1 作为数据库的查询数据缓存

(1)完整数据缓存

例如:电商的商品分类功能不是经常变动的,因此可以事先放到Memcached里,然后再对外提供数据访问。这个过程被称之为“数据预热”。 
此时只需读取缓存,无需读取数据库就能得到Memcached缓存里的所有商品分类数据了,所以数据库的访问压力就会大大降低。 
为什么商品分类数据可以事先放在缓存里呢? 
因为,商品分类几乎都是由内部人员管理的,如果需要更新数据,更新数据库后,就可以把数据同时更新到Memcached里。 
如果把商品分类数据做成静态化文件,然后,通过在前端Web缓存或者使用CDN加速效果更好。

(2)热点数据缓存

热点数据缓存一般是用于由用户更新的商品,例如淘宝的卖家,在卖家新增商品后,网站程序就会把商品写入后端数据库,同时把这部分数据,放入Memcached内存中,下一次访问这个商品的请求就直接从Memcached内存中取走了。这种方法用来缓存网站热点的数据,即利用Memcached缓存经常被访问的数据。

提示: 
这个过程可以通过程序实现,也可以在数据库上安装相关软件进行设置,直接由数据库把内容更新到Memcached中,就相当于Memcached是MySQL的从库一样。

  • 如果碰到电商双11,秒杀高并发的业务场景,必须要事先预热各种缓存,包括前端的Web缓存和后端的数据库缓存。
  • 也就是先把数据放入内存预热,然后逐步动态更新。此时,会先读取缓存,如果缓存里没有对应的数据,再去读取数据库,然后把读到的数据放入缓存。如果数据库里的数据更新,需要同时触发缓存更新,防止给用户过期的数据,当然对于百万级别并发还有很多其他的工作要做。
  • 绝大多数的网站动态数据都是保存在数据库当中的,每次频繁地存取数据库,会导致数据库性能急剧下降,无法同时服务更多的用过户(比如MySQL特别频繁的锁表就存在此问题),那么,就可以让Memcached来分担数据库的压力。增加Memcached服务的好处除了可以分担数据库的压力以外,还包括无须改动整个网站架构,只须简单地修改下程序逻辑,让程序先读取Memcached缓存查询数据即可,当然别忘了,更新数据时也要更新Memcached缓存。
 

2.2.2 作为集群节点的session会话共享存储

即把客户端用户请求多个前端应用服务集群产生的session会话信息,统一存储到一个Memcached缓存中。由于session会话数据是存储在内存中的,所以速度很快。

下图为Memcached服务在企业集群架构中的常见工作位置:

image_1cuh4adqa10fpf5o1k9vuhj1sv8k2.png-269.7kB

 

三,Memcached的特点与工作机制

 

3.1 Memcached的特点

Memcached作为高并发,高性能的缓存服务,具有如下特点:

  • 协议简单。Memcached的协议实现很简单,采用的是基于文本行的协议,能通过telnet/nc等命令直接操作memcached服务存储数据。
  • 支持epoll/kqueue异步I/O模型,使用libevent作为事件处理通知机制。
  • 简单的说,libevent是一套利用c开发的程序库,它将BSD系统的kqueue,Linux系统的epoll等事件处理功能封装成一个接口,确保即使服务器端的连接数增加也能发挥很好的性能。Memcached就是利用这个libevent库进行异步事件处理的。
  • 采用key/value键值数据类型。被缓存的数据以key/value键值形式存在,例如:
 
  1. benet-->36,key=benet,value=36
  2. yunjisuan-->28,key=yunjisuan,value=28
  3. #通过benet key可以获取到36值,同理通过yunjisuan key可以获取28值
  • 全内存缓存,效率高。Memcached管理内存的方式非常高效,即全部的数据都存放于Memcached服务事先分配好的内存中,无持久化存储的设计,和系统的物理内存一样,当重启系统或Memcached服务时,Memcached内存中的数据就会丢失。
  • 如果希望重启后,数据依然能保留,那么就可以采用redis这样的持久性内存缓存系统。
  • 当内存中缓存的数据容量达到服务启动时设定的内存值时,就会自动使用LRU算法(最近最少被使用的)删除过期的缓存数据。也可以在存放数据时对存储的数据设置过期时间,这样过期后数据就自动被清除,Memcached服务本身不会监控数据过期,而是在访问的时候查看key的时间戳判断是否过期。
  • 可支持分布式集群 
    Memcached没有像MySQL那样的主从复制方式,分布式Memcached集群的不同服务器之间是互不通信的,每一个节点都独立存取数据,并且数据内容也不一样。通过对Web应用端的程序设计或者通过支持hash算法的负载均衡软件,可以让Memcached支持大规模海量分布式缓存集群应用。 
    下面是利用Web端程序实现Memcached分布式的简单代码:
 
  1. "memcached_servers" ==>array(
  2. '10.4.4.4:11211',
  3. '10.4.4.5:11211',
  4. '10.4.4.6:11211',

下面使用Tengine反向代理负载均衡的一致性哈希算法实现分布式Memcached的配置。

 
  1. http {
  2. upstream test {
  3. consistent_hash $request_uri;
  4. server 127.0.0.1:11211 id=1001 weight=3;
  5. server 127.0.0.1:11212 id=1002 weight=10;
  6. server 127.0.0.1:11213 id=1003 weight=20;
  7. }
  8. }

提示: Tengine是淘宝网开源的Nginx的分支,上述代码来自: 
http://tengine.taobao.org/document_cn/http_upstream_consistent_hash_cn.html

 

3.2 Memcached工作原理与机制

 

3.2.1 Memcached工作原理

Memcached是一套类似C/S模式架构的软件,在服务器端启动Memcached服务守护进程,可以指定监听本地的IP地址,端口号,并发访问连接数,以及分配了多少内存来处理客户端请求。

 

3.2.2 Socket事件处理机制

Memcached软件是由C语言来实现的,全部代码仅有2000多行,采用的是异步epoll/kqueue非阻塞I/O网络模型,其实现方式是基于异步的libevent事件单进程,单线程模式。使用libevent作为事件通知机制,应用程序端通过指定服务器的IP地址及端口,就可以连接Memcached服务进行通信。

 

3.2.3 数据存储机制

  • 需要被缓存的数据以key/value键值对的形式保存在服务器端预分配的内存区中,每个被缓存的数据都有唯一的标识key,操作Memcached中的数据就是通过这个唯一标识的key进行的。缓存到Memcached中的数据仅放置在Memcached服务预分配的内存中,而非存储在Memcached服务器所在的磁盘上,因此存取速度非常快。
  • 由于Memcached服务自身没有对缓存的数据进行持久化存储的涉及,因此,在服务器端的Memcached服务进程重启之后,存储在内存中的这些数据就会丢失。且当内存中缓存的数据容量达到启动时设定的内存值时,也会自动使用LRU算法删除过期的数据。
  • 开发Memcached的初衷仅是通过内存缓存提升访问效率,并没有过多考虑数据的永久存储问题。因此,如果使用Memcached作为缓存数据服务,要考虑数据丢失后带来的问题,例如:是否可以重新生成数据,还有,在高并发场合下缓存宕机或重启会不会导致大量请求直接到数据库,导致数据库无法承受,最终导致网站架构雪崩等。
 

3.2.4 内存管理机制

Memcached采用了如下机制:

  • 采用slab内存分配机制
  • 采用LRU对象清除机制
  • 采用hash机制快速检索item
 

3.2.5 多线程处理机制

多线程处理时采用的是pthread(POSIX)线程模式。

若要激活多线程,可在编译时指定:./configure --enable-threads

锁机制不够完善

负载过重时,可以开启多线程(-t 线程数为CPU核数)

 

3.3 Memcached预热理念及集群节点的正确重启方法

 

3.3.1 Memcached预热理念

  • 当需要大面积重启Memcached时,首先要在前端控制网站入口的访问流量,然后,重启Memcached集群并进行数据预热,所有数据都预热完毕之后,再逐步放开前端网站入口的流量。
  • 为了满足Memcached服务数据可以持久化存储的需求,在较早时期,新浪网基于Memcached服务开发了一款NoSQL软件,名字为MemcacheDB,实现了在缓存的基础上增加了持久存储的特性,不过目前逐步被更优秀的Redis软件取代了。
 

3.3.2 如何正确开启网站集群服务器

如果由于机房断电或者搬迁服务器集群到新机房,那么启动集群服务器时,一定要从网站集群的后端依次往前端开启,特别是开启Memcached缓存服务器时要提前预热。

 

四,Memcached内存管理

 

4.1 Memcached内存管理机制深入剖析

(1)Malloc内存管理机制

在讲解Memcached内存管理机制前,先来了解malloc

  • malloc的全称是memory allocation,中文名称动态内存分配,当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态分配内存。
  • 早期的Memcached内存管理是通过malloc分配的内存实现的1,使用完后通过free来回收内存。这种方式容易产生内存碎片并降低操作系统对内存的管理效率。因此,也会加重操作系统内存管理器的负担,最坏的情况下,会导致操作系统比Memcached进程本身还慢,为了解决上述问题,Slab Allocator内存分配机制就诞生了。

(2)Slab内存管理机制 
现在的Memcached是利用Slab Allocation机制来分配和管理内存的,过程如下: 
1)提前将大内存分配大小为1MB的若干个slab,然后针对每个slab再进行小对象填充,这个小对象称为chunk,避免大量重复的初始化和清理,减轻了内存管理器的负担。 
Slab Allocation内存分配的原理是按照预先规定的大小,将分配给Memcached服务的内存预先分割成特定长度的内存块(chunk),再把尺寸相同的内存块(chunk)分成组(chunks slab class),这些内存块不会释放,可以重复利用,如下图所示。 
image_1cuh4mm661eff3sbae8km1r7akf.png-88.5kB

2)新增数据对象存储时。因Memcached服务器中保存着slab内空闲chunk的列表,他会根据该列表选择chunk,然后将数据缓存于其中。当有数据存入时,Memcached根据接收到的数据大小,选择最适合数据大小的slab分配一个能存下这个数据的最小内存块(chunk)。例如:有100字节的一个数据,就会被分配存入下面112字节的一个内存块中,这样会有12字节被浪费,这部分空间就不能被使用了,这也是Slab Allocator机制的一个缺点。

image_1cuh4ne3g17iciq113f81h6h9a5lc.png-40.5kB

Slab Allocator还可重复使用已分配的内存,即分配到的内存不释放,而是重复利用。

(3)Slab Allocation的主要术语

image_1cuh4o78t1ki9uo0mi717671o2nlp.png-81.5kB

(4)Slab 内存管理机制特点 
提前分配大内存Slab 1MB,再进行小对象填充chunk。 
避免大量重复的初始化和清理,减轻内存管理器负担。 
避免频繁malloc/free内存分配导致的碎片

下面对Mc的内存管理机制进行一个小结

Mc的早期内存管理机制为malloc(动态内存分配) 
malloc(动态内存分配)产生内存碎片,导致操作系统性能急剧下降。 
Slab内存分配机制可以解决内存碎片的问题 
Memcached服务的内存预先分割成特定长度的内存块,称为chunk,用于缓存数据的内存空间或内存块,相当于磁盘的block,只不过磁盘的每一个block都是相等的,而chunk只有在同一个Slab Class内才是相等的。 
Slab Class指特定大小(1MB)的包含多个chunk的集合或组,一个Memcached包含多个Slab Class,每个Slab Class包含多个相同大小的chunk。 
Slab机制也有缺点,例如,Chunk的空间会有浪费等

 

4.2 Memcached Slab Allocator内存管理机制的缺点

(1)chunk存储item浪费空间

Slab 
Allocator解决了当初的内存碎片问题,但新的机制也给Memcached带来了新的问题。这个问题就是,由于分配的是特定长度的内存,因此无法有效利用分配的内存。例如,将100字节的数据缓存到128字节的chunk中,剩余的28字节就浪费了,如下图所示:

image_1cuh4ptmo1h840dhk10in85pm6.png-25.9kB

避免浪费内存的办法是,预先计算出应用存入的数据大小,或把同一业务类型的数据存入一个Memcached服务器中,确保存入的数据大小相对均匀,这样就可以减少内存的浪费。 
还有一种办法是,在启动时指定“-f”参数,能在某种程度上控制内存组之间的大小差异。在应用中使用Memcached时,通常可以不重新设置这个参数,即使用默认值1.25进行部署即可。如果想优化Memcached对内存的使用,可以考虑重新计算数据的预期平均长度,调整这个参数来获得合适的设置值,命令如下:

-f <factor>chunk size growth factor (default:1.25)!

(2)Slab尾部剩余空间

  • 假设在classid=40中,两个chunk占用了1009384byte,那么就有1048576-1009384=39192byte会被浪费掉。解决办法:规划slab大小=chunk大小*n整数倍。
 

4.3 使用Growth Factor对Slab Allocator内存管理机制调优

在启动Memcached时指定Growth Factor因子(通过 -f 
选项),就可以在某种程度上控制每组Slab之间的差异。默认值1.25。但是,在该选项出现之前,这个因子曾经被固定为2,称为2“powers 
of 2”策略。让我们用以前的设置,以verbose模式启动Memcached试试看:

#memcached -f 2 w

下面是启动后的verbose输出:

 
  1. slab class 1:chunk size 128 perslab 8192
  2. slab class 2:chunk size 256 perslab 4096
  3. slab class 3:chunk size 512 perslab 2048
  4. slab class 4:chunk size 1024 perslab 1024
  5. slab class 5:chunk size 2048 perslab 512
  6. slab class 6:chunk size 4096 perslab 256
  7. slab class 7:chunk size 8192 perslab 128
  8. slab class 8:chunk size 16384 perslab 64
  9. slab class 9:chunk size 32768 perslab 32
  10. slab class 10:chunk size 65536 perslab 16
  11. slab class 11:chunk size 131072 perslab 8
  12. slab class 12:chunk size 262144 perslab 4
  13. slab class 13:chunk size 524288 perslab 2

可见,从128字节的组开始,组的大小依次增大为原来的2倍。这样设置的问题是,Slab之间的差别比较大,有些情况下就相当浪费内存。因此,为尽量减少内存浪费,两年前追加了growth factor这个选项。

来看看现在的默认设置(f=1.25)时的输出:

 
  1. slab class 1:chunk size 88 perslab 11915 <---88*11915=1048520
  2. slab class 2:chunk size 112 perslab 9362
  3. slab class 3:chunk size 144 perslab 7281
  4. slab class 4:chunk size 184 perslab 5698
  5. slab class 5:chunk size 232 perslab 4519
  6. slab class 6:chunk size 296 perslab 3542
  7. slab class 7:chunk size 376 perslab 2788
  8. slab class 8:chunk size 472 perslab 2221
  9. slab class 9:chunk size 592 perslab 1771
  10. slab class 10:chunk size 744 perslab 1409 <---744*1409=1048520
  • 此时每个Slab的大小是一样的,即1048520,1MB。组间的差距比因子为2时小得多,可见,这个值越小,Slab中chunk size的差距就越小,内存浪费也就越小。可见,默认值1.25更适合缓存几百字节的对象。从上面的输出结果来看,可能会觉得有些计算误差,这些误差是为了保持字节数的对齐而故意设置的。
  • 当使用Memcached或是直接使用默认值进行部署时,最好是重新计算一下数据的预期平均长度,调整growth factor,以获得最恰当的设置。内存是珍贵的资源,浪费就太可惜了。
 

4.4 Memcached的检测过期与删除机制

(1)Memcached懒惰检测对象过期机制

  • 首先要知道,Memcached不会主动检测item对象是否过期,而是在进行get操作时检查item对象是否过期以及是否应该删除!
  • 因为不会主动检测item对象是否过期,自然也就不会释放已分配给对象的内存空间了,除非为添加的数据设定过期时间或内存缓存满了,在数据过期后,客户端不能通过key取出它的值,其存储空间将被重新利用。
  • Memcached使用的这种策略为懒惰检测对象过期策略,即自己不监控存入的key/value对是否过期,而是在获取key值时查看记录的时间戳(sed key flag exptime bytes),从而检查key/value对空间是否过期。这种策略不会在过期检测上浪费CPU资源。

(2)Memcached懒惰删除对象机制

  • 当删除item对象时,一般不会释放内存空间,而是做删除标记,将指针放入slot回收插槽,下次分配的时候直接使用。
  • Memcached在分配空间时,会优先使用已经过期的key/value对空间;若分配的内存空间占满,Memcached就会使用LRU算法来分配空间,删除最近最少使用的key/value对,从而将其空间分配给新的key/value对。在某些情况下(完整缓存),如果不想使用LRU算法,那么可以通过“-M”参数来启动Memcached,这样,Memcached在内存耗尽时,会返回一个报错信息,如下:

    -M rerurn error on memory exhausted(rather than removing items)

下面针对Memcached删除机制进行一个小结

  1. 不主动检测item对象是否过期,而是在get时才会检查item对象是否过期以及是否应该删除。
  2. 当删除item对象时,一般不释放内存空间,而是做删除标记,将指针放入slot回收插槽,下次分配的时候直接使用。
  3. 当内存空间满的时候,将会根据LRU算法把最近最少使用的item对象删除。
  4. 数据存入可以设定过期时间,但是数据过期后不会被立即删除,而是在get时检查item对象是否过期以及是否应该删除。
  5. 如果不希望系统使用LRU算法清除数据,可以用使用-M参数。

五,Memcached服务安装

Memcached的安装比较简单,支持Memcached的平台常见的有Linux,FreeBSD,Solaris,Windows。这里以Centos6.5为例进行讲解。

 
memcached所需软件包如下:

链接:https://pan.baidu.com/s/1Y50XQJN4PBJXwklqGarp8g
提取码:h7qr

 

5.1 安装libevent及连接Memcached工具nc

系统安装环境如下: 
image_1cugju31c1671iti18noceg1el39.png-19.4kB

安装Memcached前需要先安装libevent,有关libevent的内容在前文已经介绍,此处用yum命令安装libevent。操作命令如下:

 
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-6.repo
wget -O /etc/yum.repos.d/163.repo http://mirrors.163.com/.help/CentOS6-Base-163.repo
yum -y install libevent libevent-devel nc --->自带光盘里没有,需要公网yum源
rpm -qa libevent libevent-devel nc

 

image_1cugkq6lehjae21vfm1dcu10u19.png-17.1kB

 

5.2 安装Memcached

操作命令如下:

 
  1. yum -y install memcached #此处可通过光盘安装

提示: 
形如“memcache-2.2.7.tgz”文件名的软件为客户端源代码软件,而形如“memcached-1.4.24.tar.gz”的文件为服务器端的源代码软件。

image_1cugkvhrq128116dk1brm6bbni019.png-11.2kB

 

六,Memcached服务的基本管理

 

6.1 启动Memcached

命令如下:

 
which memcached --->查看Memcached命令路径
memcached -m 16m -p 11211 -d -u root -c 8192 #启动第一个实例
memcached -m 16m -p 11212 -d -u root -c 8192 #启动第二个实例
# -m 启动16兆内存 -p 默认端口11211 -d 启动守护进程 -u 指定用户 -c 指定连接数

 

微信图片_20181212153840.png-15.9kB 
image_1cugl9c6pn9i1nkl1ggm1r8uj283m.png-17.9kB

 

6.2 Memcached启动命令相关参数说明

微信图片_20181212154038.png-36.9kB

微信图片_20181212154135.png-23.7kB

其他选项可通过“memcached -h”命令来显示。

 

6.3 向Memcached中写入数据并检查

 

6.3.1 Memcached中的数据形式及与MySQL相关语句对比

向Memcached中添加数据时,注意添加的数据一般为键值对的形式,例如:key1-->values1,key2-->values2

这里把Memcached添加,查询,删除等的命令和MySQL数据库做一个基本类比,见下表: 
image_1cuglu5at16vhgc3qcv1rin15mu7r.png-18.2kB

 

6.3.2 向Memcached中写入数据实践

 
[root@zsq~Memcached ~]# printf "set key1 0 0 5\r\nbenet\r\n" | nc 127.0.0.1 11211
STORED #出现STORED表示成功添加key1及对应的数据
#如果set命令的字节是6,那么后面就要6个字符(字节)。否则插入数据就会不成功。示例如下:
[root@zsq~Memcached ~]# printf "set key1 0 0 4\r\nbenet\r\n" | nc 127.0.0.1 11211
CLIENT_ERROR bad data chunk
ERROR
#通过printf配置nc从Memcached中读取数据,命令如下:
[root@zsq~Memcached ~]# printf "get key1\r\n" | nc 127.0.0.1 11211
VALUE key1 0 5
benet #这就是读取到的key1对应额值
END
#通过printf配合nc从Memcached中删除数据,命令如下:
[root@zsq~Memcached ~]# printf "delete key1\r\n" | nc 127.0.0.1 11211
DELETED
[root@zsq~Memcached ~]# printf "get key1\r\n" | nc 127.0.0.1 11211
END

 

提示: 推荐使用上述方法测试操作Memcached

(2)通过telnet命令写入数据时,具体步骤如下:

 
[root@zsq~Memcached ~]# telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
set user01 0 0 7
welcome
STORED
get user01
VALUE user01 0 7
welcome
END
delete user01
DELETED
get user01
END
quit
Connection closed by foreign host.

 

image_1cugmjprts3c1p6fk2d1jg84vl88.png-44.1kB

 

6.4 操作Memcached相关命令的语法

以下为操作Memcached的相关命令基本语法:

 
  1. set key1 0 0 6 \r\n benet \r\n
  2. <command name><key><flags><exptime><bytes><datablock><string><datablock>
  3. STORED
  4. <status>

下表为操作Memcached相关命令的详细说明:

image_1cugmo2rdp478jt6quvv1101l8l.png-158.3kB

 

6.5 关闭Memcached

单实例关闭Memcached的方法如下:

 
  1. killall memcachedpkill memcached

若启动了多个实例Memcached,使用killall或pkill方式就会同时关闭这些实例!因此最好在启动时增加-P参数指定固定的pid文件,这样便于管理不同的实例。示例如下:

 
[root@zsq~Memcached ~]# memcached -m 16m -p 11211 -d -u root -c 8192 -P /var/run/11211.pid
[root@zsq~Memcached ~]# memcached -m 16m -p 11212 -d -u root -c 8192 -P /var/run/11212.pid
[root@zsq~Memcached ~]# ps -ef | grep memcached | grep -v grep
root 1188 1 0 00:00 ? 00:00:00 memcached -m 16m -p 11211 -d -u root -c 8192 -P /var/run/11211.pid
root 1195 1 0 00:00 ? 00:00:00 memcached -m 16m -p 11212 -d -u root -c 8192 -P /var/run/11212.pid
[root@zsq~Memcached ~]# kill `cat /var/run/11211.pid` #只杀掉11211这个进程
[root@zsq~Memcached ~]# netstat -antup | grep 11211
[root@zsq~Memcached ~]# netstat -antup | grep 11212 #查看11212还是存在的
tcp 0 0 0.0.0.0:11212 0.0.0.0:* LISTEN 1195/memcached
tcp 0 0 :::11212 :::* LISTEN 1195/memcached
udp 0 0 0.0.0.0:11212 0.0.0.0:* 1195/memcached
udp 0 0 :::11212 :::* 1195/memcached

 

image_1cugmveud19s815krhlh1rrta989i.png-55.6kB

 

6.6 企业工作场景中如何配置Memcached

      • 在企业实际工作中,一般是开发人员提出需求,说要部署一个Memcached数据缓存。运维人员在接到这个不确定的需求后,需要和开发人员深入沟通,进而确定要将内存指定为多大,或者和开发人员商量如何根据具体业务来指定内存缓存的大小。此外,还要确定业务的重要性,进而决定是否采取负载均衡,分布式缓存集群等架构,最后确定要使用多大的并发连接数等。

      • 对于运维人员,部署Memcached一般就是安装Memcached服务器端,把服务启动起来,做好监控,配好开机自启动,基本就OK了,客户端的PHP程序环境一般在安装LNMP环境时都会提前安装Memcached客户端插件,Java程序环境下,开发人员会用第三方的JAR包直接连接Memcached服务。

七,安装Memcached客户端

7.1 LNMP PHP环境准备

# lnmp搭建脚本
#!/bin/bash
function Mysql () {
cd ~
groupadd mysql
useradd -s /sbin/nolongin -g mysql -M mysql
cd /root
tar xf mysql-5.5.32-linux2.6-x86_64.tar.gz -C /usr/local/
cd /usr/local/
ln -s mysql-5.5.32-linux2.6-x86_64 mysql
/bin/cp mysql/support-files/my-small.cnf /etc/my.cnf
chown -R mysql.mysql /usr/local/mysql
yum -y install libaio
/usr/local/mysql/scripts/mysql_install_db --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data --user=mysql
if [ $? -eq 0 ];then
        echo "mysql数据库安装成功"
else
        exit 0
fi
cd /usr/local/mysql
cp support-files/mysql.server /etc/init.d/mysqld
chmod +x /etc/init.d/mysqld
/etc/init.d/mysqld start
ln -s /usr/local/mysql/bin/* /usr/local/bin/
mysqladmin -u root password '123123'
}
function Nginx () {
yum install -y pcre-devel openssl-devel &>/dev/null
cd /root/
tar xf nginx-1.10.2.tar.gz -C /usr/src/
useradd -s /sbin/nologin -M www
cd /usr/src/nginx-1.10.2/
./configure --user=www --group=www --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module &>/dev/null
make && make install &>/dev/null
/usr/local/nginx/sbin/nginx
exit 0
}
function Php () {
yum -y install zlib-devel libxml2-devel libjpeg-devel libjpeg-turbo-devel libiconv-devel freetype-devel libpng-devel gd libcurl-devel libxslt-devel &>/dev/null
cd /root
tar xf libiconv-1.14.tar.gz -C /usr/src/
cd /usr/src/libiconv-1.14/
./configure --prefix=/usr/local/libiconv && make && make install
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-6.repo
yum -y install libmcrypt-devel mhash mcrypt &>/dev/null
cd /root
tar xf php-5.3.28.tar.gz -C /usr/src/
cd /usr/src/php-5.3.28 && ./configure --prefix=/usr/local/php5.3.28 --with-mysql=/usr/local/mysql --with-iconv-dir=/usr/local/libiconv --with-freetype-dir --with-jpeg-dir --with-png-dir --with-zlib --with-libxml-dir=/usr --enable-xml --disable-rpath --enable-safe-mode --enable-bcmath --enable-shmop --enable-sysvsem --enable-inline-optimization --with-curl --with-curlwrappers --enable-mbregex --enable-fpm --enable-mbstring --with-mcrypt --with-gd --enable-gd-native-ttf --with-openssl --with-mhash --enable-pcntl --enable-sockets --with-xmlrpc --enable-zip --enable-soap --enable-short-tags --enable-zend-multibyte --enable-static --with-xsl --with-fpm-user=www --with-fpm-group=www --enable-ftp
if [ $? -eq 0 ];then
        echo "php预配置成功"
else
        echo "php预配置失败"
fi
ln -s /usr/local/mysql/lib/libmysqlclient.so.18 /usr/lib64/
touch ext/phar/phar.phar
make &>/dev/null
make install &>/dev/null
ln -s /usr/local/php5.3.28/ /usr/local/php
cd /usr/src/php-5.3.28/
cp php.ini-production /usr/local/php/lib/php.ini
cd /usr/local/php/etc/
cp php-fpm.conf.default php-fpm.conf
/usr/local/php/sbin/php-fpm
}
Mysql
Nginx
Php

 

安装完了lnmp环境开始搭建: 

7.1.1、配置nginx的配置文件如下:

[root@zsq~Memcached blog]# cat /usr/local/nginx/conf/nginx.conf
worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    include extra/blog.conf;
}
[root@zsq~Memcached extra]# pwd
/usr/local/nginx/conf/extra
[root@zsq~Memcached extra]# ls
blog.conf
[root@zsq~Memcached extra]# cat blog.conf 
server {
        listen       80;
        server_name  blog.yunjisuan.com;
        location / {
            root   html/blog;
            index  index.html index.htm;
        }
    location ~ .*\.(php|php5)?$ {
            root    html/blog;
            fastcgi_pass    127.0.0.1:9000;
            fastcgi_index   index.php;
            include     fastcgi.conf;
        }
}

7.1.2、配置php

[root@zsq~Memcached blog]# pwd
/usr/local/nginx/html/blog
[root@zsq~Memcached blog]# cat test_info.php
<?php phpinfo(); ?>

 

调整Windows下的host解析

 

7.2 Memcached缓存PHP扩展插件安装

前面已经提过,Memcached分为服务器端软件和客户端插件两部分,这里是Memcached客户端PHP的扩展插件(memcache-2.2.7.tgz)在PHP环境中的安装,用于访问Memcached服务器端数据。 
PHP的Memcached扩展插件下载地址为:http://pecl.php.net/package/memcache

命令如下:

 
wget -q http://pecl.php.net/get/memcache-2.2.7.tgz
tar xf memcache-2.2.7.tgz -C /usr/src/
cd /usr/src/memcache-2.2.7/
/usr/local/php/bin/phpize
./configure --enable-memcache --with-php-config=/usr/local/php/bin/php-config
make && make install
ls -l /usr/local/php/lib/php/extensions/no-debug-non-zts-20090626/

 

微信图片_20181212164151.png-13.9kB

 

7.3 配置Memcache客户端,使其生效

修改PHP的配置文件php.ini,加入Memcache客户端的配置,命令如下:

 
[root@zsq~Memcached ~]# cd /usr/local/php/lib/
[root@zsq~Memcached lib]# vim php.ini
[root@zsq~Memcached lib]# tail -2 php.ini
#添加如下两行内容到php.ini文件结尾
extension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20090626/"
extension=memcache.so

 

 

7.4 重启php-fpm服务使PHP的配置修改生效

1)检查php-fpm语法:

 
  1. [root@zsq~Memcached lib]# /usr/local/php/sbin/php-fpm -t
  2. [13-Dec-2018 00:47:44] NOTICE: configuration file /usr/local/php5.3.28/etc/php-fpm.conf test is successful #配置文件/usr/local/php5.3.28/etc/php-fpm.conf测试成功

2)重启fpm,命令如下:

 
[root@zsq~Memcached lib]# pkill php-fpm
[root@zsq~Memcached lib]# ss -antup | grep 9000
[root@zsq~Memcached lib]# /usr/local/php/sbin/php-fpm
[root@zsq~Memcached lib]# ss -antup | grep 9000
tcp LISTEN 0 128 127.0.0.1:9000 *:* users:(("php-fpm",656,7),("php-fpm",659,0),("php-fpm",660,0))

 

3)打开浏览器访问phpinfo页面,若出现如下图所示内容,则表示Memcache客户端安装成功

微信图片_20181212172028.png-40.7kB

 

7.5 编写测试Memcached服务的PHP脚本

下面为简单的PHP程序连接Memcached测试脚本

[root@zsq~Memcached blog]# vim op_mem.php
[root@zsq~Memcached blog]# cat op_mem.php 
<?php
$memcache = new Memcache;
$memcacche->connect('192.168.200.166','11211') or die ("Could not connect Mc server");
$memcache->set('key','yunjisuan book');
$get=$memcache->get('key');
echo $get;
?>
[root@zsq~Memcached blog]# pwd
/usr/local/nginx/html/blog

配置详解

 
  1. <?php #PHP开始标识
  2. $memcache = new Memcache; #创建一个Memcache对象
  3. $memcache->connect('192.168.0.240','11211') or die ("Could not connect Mc server"); #连接Memcached服务器
  4. $memcache->set('key','yunjisuan book'); #设置一个变量到内存中
  5. $get=$memcache->get('key'); #从内存中取出key
  6. echo $get; #输出key值到屏幕
  7. ?> #PHP结束标识

Linux本地测试: 
image_1cugukksn1iud1lgpkpt1b8spqr1b.png-10.4kB

windows访问测试: 
image_1cuguk2du1lff1gu31s8i1vg41hjou.png-17.4kB

 

出现上述测试结果,就表示LNMP环境连接Memcached服务成功。

 

八,Memcached应用管理

 

8.1 通过命令管理Memcached

运维人员一般可通过在命令行执行“telnet ip port”的方式登陆到Memcached,然后执行一些管理的命令,除了telnet外,nc命令也是一个不错的管理Memcached服务的命令! 
有关“telnet ip port”命令前文已经介绍过了,下面将重点讲解通过nc管理及监控Memcached的一些常见操作。

以下通过脚本模拟用过户插入及删除数据来监控Memcached服务是否正常的示例

 

8.1.1 检查Memcached服务是否异常的监控脚本

脚本内容如下:

[root@zsq~Memcached scripts]# cat mon_mc.sh 
#!/bin/bash
export MemcachedIp=$1
export MemcachedPort=$2
export NcCmd="nc $MemcachedIp $MemcachedPort"
export MD5="3fe396c01f03425cb5e2da8186eb090d"
USAGE(){
echo "$0 MemcachedIp MemcachedPort"
exit 3
}
[ $# -ne 2 ] && USAGE
printf "set $MD5 0 0 9\r\nyunjisuan\r\n" | $NcCmd >/dev/null 2>&1 
if [ $? -eq 0 ];then
    if [ `printf "get $MD5\r\n"|$NcCmd|grep yunjisuan|wc -l` -eq 1 ];then
        echo "Memcached status is ok"
        printf "delete $MD5\r\n"|$NcCmd >/dev/null 2>&1
        exit 0
    else
        echo "Memcached status is error"
        exit 2
    fi
else
    echo "Could not connect Mc server"
    exit 2
fi
[root@zsq~Memcached scripts]# pwd
/server/scripts

image_1cugvfdcv1j611j8j1jnb1aej1osn28.png-17.4kB

image_1cugvi1gtgnu11smm728j915sh2l.png-21.3kB

image_1cugvjeqaakpjlm1q2p122i11cl32.png-41.8kB

通过上面的测试,我们发现使用监控脚本监控Memcached服务是否异常的运行是正确的。

 

8.1.2 通过nc命令查看Memcached服务的运行状态信息

查看Memcached服务运行状态信息的nc命令如下:

[root@zsq~Memcached scripts]# printf "stats\r\n"|nc 127.0.0.1 11211
STAT pid 975
STAT uptime 89
STAT time 1544639614
STAT version 1.4.4
STAT pointer_size 64
STAT rusage_user 0.003999
STAT rusage_system 0.000000
STAT curr_connections 10
STAT total_connections 14
STAT connection_structures 11
STAT cmd_get 1
STAT cmd_set 1
STAT cmd_flush 0
STAT get_hits 1
STAT get_misses 0
STAT delete_misses 0
STAT delete_hits 1
STAT incr_misses 0
STAT incr_hits 0
STAT 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 141
STAT bytes_written 77
STAT limit_maxbytes 16777216
STAT accepting_conns 1
STAT listen_disabled_num 0
STAT threads 4
STAT conn_yields 0
STAT bytes 0
STAT curr_items 0
STAT total_items 1
STAT evictions 0
END

除了上述输出的全部状态以外,Memcached还支持通过以下命令的输入来查看Memcached的部分运行状态信息。目前管理Memcached的命令见下表:

image_1cugvmp5h13b8tgqi4113fc15qq3f.png-29.1kB

例如,要查看Memcached的统计信息,可先执行“telnet ip 监听端口”命令,登陆成功之后执行stats命令,具体过程如下:

[root@zsq~Memcached scripts]# which telnet
/usr/bin/telnet
[root@zsq~Memcached scripts]# telnet 192.168.200.166 11211
Trying 192.168.200.166...
Connected to 192.168.200.166.
Escape character is '^]'.
stats
STAT pid 975 #启动的进程id
STAT uptime 289 #到目前1位置启动了多少秒
STAT time 1544639814 
STAT version 1.4.4  #Memcached的版本信息
STAT pointer_size 64
STAT rusage_user 0.011998
STAT rusage_system 0.000000
STAT curr_connections 10 #当前的并发连接数
STAT total_connections 15 #总的连接数 
STAT connection_structures 11
STAT cmd_get 1 #执行的get命令的次数
STAT cmd_set 1 #执行的set命令的次数
STAT cmd_flush 0 #执行flush命令的次数 
STAT get_hits 1 #get的命中数
STAT get_misses 0 #get的非命中数 
STAT delete_misses 0
STAT delete_hits 1
STAT incr_misses 0
STAT incr_hits 0
STAT 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 148
STAT bytes_written 846
STAT limit_maxbytes 16777216  #允许使用的最大内存容量
STAT accepting_conns 1
STAT listen_disabled_num 0
STAT threads 4
STAT conn_yields 0
STAT bytes 0
STAT curr_items 0
STAT total_items 1
STAT evictions 0
END

使用printf及nc命令获取状态信息更佳,因为无需交互,可以使用以下脚本批量操作

printf "stats\r\n"|nc 192.168.200.166 11211 

实例如下

[root@zsq~Memcached ~]# printf "stats\r\n"|nc 192.168.200.166 11211
STAT pid 975
STAT uptime 546
STAT time 1544640071
STAT version 1.4.4
STAT pointer_size 64
STAT rusage_user 0.022996
STAT rusage_system 0.000000
STAT curr_connections 10
STAT total_connections 16
STAT connection_structures 11
STAT cmd_get 1
STAT cmd_set 1
STAT cmd_flush 0
STAT get_hits 1
STAT get_misses 0
STAT delete_misses 0
STAT delete_hits 1
STAT incr_misses 0
STAT incr_hits 0
STAT 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 188
STAT bytes_written 1645
STAT limit_maxbytes 16777216
STAT accepting_conns 1
STAT listen_disabled_num 0
STAT threads 4
STAT conn_yields 0
STAT bytes 0
STAT curr_items 0
STAT total_items 1
STAT evictions 0
END

8.2 Memcached状态信息详细说明

Memcached状态信息的详细说明,大家可以通过该表来了解相关信息:

image_1cuh074iq9g6pm2feh6411njf4f.png-68.6kB

image_1cuh08f31kri2t82953or1ogu71.png-255.1kB

image_1cuh08t9u1a88h7ckv91loj1udc7e.png-131kB

 

8.3 通过memadmin php 工具展示Memcached状态信息

运维人员除了通过命令行管理Memcached以外,还可以使用很多的第三方开源管理工具,例如:通过memadmin工具管理及监控Memcached状态,软件的名称为“memadmin-1.0.12.tar.gz”,这个软件的部署十分简单,功能非常强大,但是依赖于PHP环境,推荐大家初学Memcached时使用。

 

8.3.1 部署memadmin php工具

因为这个软件是基于PHP程序的,因此,需要有PHP的环境才行,本章已经准备好了LNMP的环境,因此,可以简单地将上述“memadmin-1.0.12.tar.gz”解压到虚拟主机站点目录下,这里还是以bbs虚拟主机为例进行讲解。

部署的关键命令如下:

 
[root@zsq~Memcached ~]# ls -l memadmin-1.0.12.tar.gz
-rw-r--r--. 1 root root 196734 Dec 12 06:02 memadmin-1.0.12.tar.gz
[root@zsq~Memcached ~]# tar xf memadmin-1.0.12.tar.gz
[root@zsq~Memcached ~]# mv memadmin /usr/local/nginx/html/blog/

 

 

8.3.2 采用IP或者解析好的域名进行访问

LNMP服务器的nginx.conf配置文件内容通上方搭建LNMP配置文件一致

打开浏览器输入域名:http://blog.yunjisuan.com/memadmin/index.php

image_1cuh0nudbpoag2g17lo1mpecus8l.png-36.1kB

登录后如下图:

image_1cuh0rvlu1smk1lr7gbt1kr64i6a2.png-30.2kB

然后点击开始管理,进入下图:

image_1cuh10iik1eikludge71hdav94bf.png-78.9kB

 

九,Memcached在集群中session共享案例

 

9.1 Memcached在集群中的session共享存储实战

以下是PHP Web环境集群的session共享存储设置。

 
#修改为如下设置:
[root@zsq~Memcached extra]# vim /usr/local/php/lib/php.ini
[root@zsq~Memcached extra]# sed -n '1461p;1490p' /usr/local/php/lib/php.inisession.save_handler = memcache
session.save_path = "tcp://192.168.200.166:11211"

 

image_1cuh3kcab1vj31dhg1t89e3a1gr3f4.png-29.7kB

说明: 
192.168.200.166:11211为Memcached数据库缓存的IP及端口 
上述配置适合LNMP/LAMP环境 
Memcached服务器也可以是多台,并且可通过hash算法调度 
配置完毕通过phpinfo页面查看效果如下图所示:

image_1cuh3ouhbn1tskg1cq01984c88g1.png-119.4kB

 

9.2 Memcached在集群中的session共享存储的优缺点

优点:

1)读写速度上会比普通files速度快很多 
2)可以解决多个服务器共用session的难题

缺点:

1)session数据都保存在memory中,持久化方面有所欠缺,但对session数据来说不是问题。 
2)一般是单台,如果部署多台,多台之间无法数据同步。通过hash算法分配依然有session丢失的问题。

对于上面的缺点,解决思路如下:

1)可以用其他的持久化系统存储sessions,例如:redis,ttserver来替代Memcached 
2)高性能高并发场景,cookies效率比session要好很多,因此,大网站都会用cookies解决会话共享问题 
3)有的已经就业了的同学通过牺牲LB的负载均衡的策略实现,例如:lvs-p,nginx ip_hash等,但这些不是好的方法。

 

 

posted @ 2018-12-12 20:17  mendermi  阅读(213)  评论(0编辑  收藏  举报