Memcached介绍(一)—基础介绍

第1章 Memcached介绍

1.1 Memcached与常用同类软件对比

1.1.1 什么是Memcached

Memcached是一个开源的、支持高性能、高并发的分布式纯内存缓存系统,由C语言编写,总共2000多行代码。从软件名称上看,“Mem”是内存的意思,“cache”是缓存的意思,“d”是daemon的意思,代表是服务器端守护进程模式服务。Memcache是项目的名字,诞生于2003年。

  • Memcached服务是C/S架构的,分为服务器端和客户端:
  1. 客户端软件命名:memcache
  2. 服务端软件命名:memcached

1.1.2 Memcached的作用

传统场景中,多数web应用都将数据保存到关系型数据库中(MySQL),web服务器从中读取数据并在浏览器中显示。但随着数据量的增大、访问的集中,关系型数据库的负担就会出现加重、响应缓慢、导致网站打开延迟等问题,影响用户体验。

使用Memcached的主要目的是,通过在自身内存中缓存关系型数据库的查询结果,减少数据库被访问的次数,以提高动态web应用的速度、提高网站架构的并发能力和可扩展性。

Memcached服务的运行原理是通过在事先规划好的系统内存空间中临时缓存数据库中的各类数据,以达到减少前端业务服务对数据库的直接高并发访问,从而提升大规模网站集群中动态服务的并发访问能力。

生产场景的Memcached服务一般被用来保存网站中经常被读取的对象或数据,就行我们的客户端浏览器也会把经常访问的网页缓存起来一样,通过内存缓存来存取对象或数据要比磁盘存取快很多,因为磁盘是机械的,因此在当今的IT企业中,Memcached的应用范围很广泛。

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

软件 类型 主要作用 缓存的数据
Memcached 纯内存型 常用于缓存网站后端的各类数据,例如:数据库中的数据 主要缓存用户重复请求的动态内容,例如:BLOG的博文、BBS的帖子等内容以及用户的session会话信息
Redis、Memcachedb 可持久化存储,即使用内存,也会使用磁盘存储 缓存后端数据库的查询数据

作为关系数据库的重要补充

作为缓存时,主要缓存用户重复请求的动态内容,例如:BLOG的博文、BBS的帖子等内容

作为数据库的有效补充时,例如:好友关注、粉丝统计、业务统计等功能可以用持久化存储

Squid、Nginx 内存或内存加磁盘缓存 主要用于缓存Web前端的服务内容 主要用于静态数据缓存,例如:图片、附件(压缩包)及JS、CSS、html静态代码等,此部分功能大多数企业会选择专业的CDN分公司,如:网宿、蓝汛

第2章 Memcached的用途与应用场景

2.1 Memcached常见用途工作流程

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

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

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

  • 使用Memcached缓存查询的数据来减少数据库压力的具体工作流程如下:
  1. web程序首先检查客户端请求的数据是否在Memcached缓存中存在,如果存在直接把请求的数据返回给客户端,此时不再请求后端数据库。
  2. 如果请求的数据在Memcachaed缓存中不存在,则程序会去请求数据库服务,把从数据库中取到的数据返回给客户端,同时把新取到的数据缓存一份到Memcached缓存中。

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

  • 具体流程如下:
  1. 当程序更新或删除数据时,会首先处理后端数据库中的数据。
  2. 在处理后端数据库中数据的同时也会通知Memcached,告诉它对应的旧数据失败,从而保证Memcached中缓存的数据始终和数据库中一致,这个数据一致性非常重要,也是大型网站分布式缓存集群最头疼的问题所在。
  3. 如果是在高并发读写场合,除了要程序通知Memcached过期的缓存失效外,还可能要通过相关机制,例如在数据库上部署相关程序(如在数据库中设置触发器使用UDFs),实现当数据库有更新时就把数据更新到Memcached服务中,这样一来客户端在访问新数据时,因预先把更新过的数据库数据复制到Memcached中缓存起来了,所以可以减少第一次查询数据库带来的访问压力,提升Memcached中缓存的命中率,甚至新浪门户还会把持久化存储Redis做成MySQL数据库的从库,实现真正的主从复制。
提示:生产环境中,推荐使用Memcached网站作为缓存应用更新数据的方案。

2.1.3 网站读取和更新数据时的工作流程图

图片[1]|Memcached介绍(一)—基础介绍|leon的博客

2.2 Memcached在企业中的应用场景

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

将数据存储在内存中,只有当磁盘胜任不了的时候,才会启用缓存。当数据库(mysql)承受不了大并发的请求时,可以将数据缓存到内存中(缓存数据库),然后就可以解决。

缺点:断电后数据丢失(双电),用缓存存储数据的目的只是为了应付大并发的业务,至于数据存储及可靠性需要其他软件支持。

扩展:关系型数据库和非关系型数据库区别:
1、关系型数据库:能够保证数据一致性,保证数据不丢失,但是因为功能太多导致性能不高,如MySQL,可以做数据仓库
2、非关系型数据库:性能极高,但是不能保证数据完整性,如memcache/redis,他们是业务的数据提供者
  • 完整数据缓存:

例如:电商的商品分类功能不是经常变动的,因此可以事先放到Memcached里,然后再对外提供数据访问,这个过程被称为“数据预热”。

此时只需读取缓存,无需读取数据库就能得到Memcached缓存里的所有商品分类数据了,所以数据库的访问压力就会大大降低。

如果商品分类数据做成静态化文件,然后通过前端web缓存或者使用CDN加速效果更好。

  • 热点数据缓存:

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

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

如果碰到电商的双十一、秒杀高并发的业务场景,必须要事先预热各种缓存,包括前端的web缓存和后端的数据库缓存。

也就是先把数据放入内存预热,然后逐步动态更新。此时会先读取缓存,如果缓存里没有对应的数据再去读取数据库,然后把读到的数据放入缓存。如果数据库里的数据更新,需要同时触发缓存更新,防止给用户过期的数据,当然对于百万级别并发还有很多其他的工作要做。

绝大多数的网站动态数据都是保存在数据库当中的,每次频繁地存取数据库会导致数据库性能急剧下降,无法同时服务更多的用户(比如MySQL特别频繁的锁表就存在次问题),那么就可以让Memcached来分担数据库的压力。增加Memcached服务的好处除了可以分担数据库的压力以外,还包括无须改动整个网站架构,只需简单地修改下程序逻辑,让程序先读取Memcached缓存查询数据即可,同时更新数据时也要更新Memcached缓存。

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

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

图片[2]|Memcached介绍(一)—基础介绍|leon的博客

  • 使用session会话共享的原因:

因为服务器在用户登录完的那一刻,在用户浏览器写入一个cookies文件,这个文件就存储用户的账号信息等个人信息,cookies类似身份证,但是直接将个人数据存储在cookies非常不安全,所以将个人数据存储在服务端的文件中,然后这个文件叫做session。如果session存放在磁盘的文件中,多台web同时提供访问时,会出现session文件找不到的情况,所以可以使用session会话共享解决这个问题。

  • 使用session存储会话时cookie和session存储的内容:
  1. 浏览器COOKIE:存储session id
  2. 服务器session:存储session id和session值(个人数据),可以通过session id查找session值
  • session共享的解决方案:
  1. session文件通过NFS共享
  2. session文件通过Rsync、SCP共享
  3. 将session的内容存放在数据库(mysql)中,所有机器都可以通过ip:port读取
  4. 将session的内容存放在缓存数据库中,所有机器都可以通过ip:port读取; 好处是可以利用断电、重启丢失数据的特性定时清理数据,提高并发

第3章 Memcached的特点与工作机制

3.1 Memcached的特点

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

  • 协议简单

Memcached的协议实现很简单,采用的是基于文本行的协议,能通过telnet/nc等命令直接操作Memcached服务存取数据。

  • 支持epol/kqueue异步I/O模型,使用Libevent作为事件处理通知机制。

简单的说,libevent是一套利用C开发的程序库,它将BSD系统的kqueue、Linux系统的epoll等事件处理功能封装成一个接口,确保即使服务器端的连接数增加也能发挥很好的性能。Memcached就是利用这个libevent库进行异步事件处理的。

  • 采用key/value键值类型

被缓存的数据以key/value键值形式存在,例如:

A->B,key=A,value=B

通过A的key可以获取到B的值

  • 全内存缓存,效率高

Memcached管理内存的方式非常高效,即全部的数据都存放于Memcached服务事先分配好的内存中,无持久化存储的设计和系统的物理内存一样,当重启系统或Memcached服务时Memcached内存中的数据就会丢失。

如果希望重启后数据依然能保留,那么就可以采用Redis这样的持久性内存缓存系统,更多的开源软件见http://oldboy.blog.51cto.com/2561410/775056

当内存中缓存的数据容量达到服务启动时设定的内存值时,就会自动使用LRU算法删除过期的缓存数据,也可以再存放数据时对存储的数据设置过期时间,这样过期后数据就会自动被清除,Memcached服务本身不会监控数据过期,而是在访问的时候查看key的时间戳判断是否过期。

  • 可支持分布式集群

Memcached没有像MySQL那样的主从复制方式,分布式Memcached集群的不同服务器之间是互不通信的,每一个节点都独立存取数据,并且数据内容也不一样。通过对web应用端的程序设计或者通过支持hash算法的负载均衡软件,可以让Memcached支持大规模海量分布式缓存集群应用。

3.2 Memcached工作原理与机制

3.2.1 Memcached工作原理

Memcached是一套类似C/S模式架构的软件,在服务器端启动Memcached服务守护进程,可以指定监听本地的IP地址、端口号、并发访问连接数,以及分配了多少内存来处理客户端请求。在启动Memcached时,根据指定的内存大小参数,会被分配一个内存空间,当用户读取数据库的各类业务数据后,数据会同时放入Memcached缓存中,当下一次用户请求同样的数据,程序直接去Memcached取数据返回给用户。

  • Memcached的优点:
  1. 对于用户来讲,用户访问网站更快了,体验更好了。
  2. 对网站来说,数据库压力降低了,只有当内存没有数据或第一次写入数据时会请求数据库,一般公司没有预热,只有当用户读取过数据库才会放到Memcached中。
  3. 提升了网站的并发访问,减少服务器数量。

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软件,名字为MemcachedDB,实现了在缓存的基础上增加了持久存储的特性,不过目前逐步被更优秀的Redis软件取代了。

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

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

第4章 Memcached内存管理

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

4.1.1 Malloc内存管理机制

malloc的全称是memory allocation(动态内存分配),当无法知道内存具体位置的时候,想要绑定真正的内存空间就需要用到动态分配内存。

早期的Memcached内存管理是通过malloc分配的内存实现的,使用完后通过free来回收内存。这种方式容易产生内存碎片并降低操作系统对内存的管理效率。因此也会加重操作系统内存管理器的负担,最坏的情况下会导致操作系统比Memcached进程本身还慢,为了解决上述问题,Slab Allocator 内存分配机制就诞生了。

4.1.2 Slab内存管理机制

Memcached利用Slab Allocation机制来分配和管理内存过程如下:

  1. 提前将大内存分配大小为1MB的若干个slab,然后针对每个slab再进行小对象填充,这个小对象称为chunk,避免大量重复的初始化和清理,减轻了内存管理器的负担。Slab Allocation内存分配的原理是按照预先规定的大小,将分配给Memcached服务的内存能与吸纳分割成特定长度的内存块(chunk),再把尺寸相同的内存块(chunk)分成组,这些内存块不会释放,可以重复利用。
  2. 新增数据对象存储时,因Memcached服务器中保存着slab内空闲chunk的列表,它会根据该列表选择chunk,然后将数据缓存与其中。当有数据存入时Memcached根据接收到的数据大小,选择最适合数据大小的slab分配一个能存下这个数据的最小内存块(chunk)。例如:有100字节的一个数据就会被分配存入下面112字节的一个内存块中,这样会有12字节被浪费,这部分空间就不能被使用了,这也是Slab Allocator机制的一个缺点。Slab Allocator还可重复使用已分配的内存,即分配的内存不释放,而是重复利用。

4.1.3 Slab Allocation的主要术语

Slab Allocation主要术语 注释说明
slab class 内存区类别(48byte-1MB)
slab 动态创建的实际内存区,即分配给Slab的内存空间,默认是1MB,分配给Slab之后根据slab的大小切分成chunk,slab大于1MB的数据会忽略
slab classid slab class的ID
chunk 数据区块固定大小,1.4版本chunk初始大小是48bytes
item 实际存储在chunk中的数据项

4.1.4 Slab内存管理机制特点

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

4.1.5 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内存管理机制的缺点

4.2.1 chunk存储item浪费空间

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

避免浪费内存的办法是预先计算出应用存入的数据大小,或把同一业务类型的数据存入一个Memcached服务器中,确保存入的数据大小相对均匀,这样就可以减少内存的浪费。

还有一种办法是在启动时指定“-f”参数,能在魔种程度上控制内存组之间的大小差异。在应用中使用Memcached时,通常可以不重新设置这个参数,即使用默认值1.25进行部署即可。如果想优化Memcached对内存的使用,可以考虑重新计算数据的预期平均长度,调整这个参数来获得合适的设置值,命令如下:

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

4.2.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,称为“powers of 2”策略。

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

4.4.1 Memcached懒惰检测对象过期机制

Memcached不会主动检测item对象是否过期,而是进行get操作时检查item对象是否过期以及是否应该删除。

因为不会主动检测item对象是否过期,自然也就不会释放已分配给对象的内存空间,除非为添加的数据设定过期时间或内存缓存满了,在数据过期后,客户端不能通过key取出它的值,其存储空间将被重新利用。

Memcached使用的这种策略为懒惰检测对象过期策略,即自己不监控存入的key/value对是否过期,而是在获取key值时查看记录的时间戳,从而检查key/value对空间是否过期。这种策略不会砸过期检测上浪费CPU资源。

4.4.2 Memcached懒惰删除对象机制

当删除item对象时,一般不会释放内存空间,而是做删除标记,将指针放入slot回收插槽,下次分配的时候直接使用。

Memcached在分配空间时会优先使用已经过期的key/value对空间;若分配的内存空间占满,Memcached就会使用LRU算法来分配空间,删除最近最少使用的key/value对,从而将其空间分配给新的key/value对。在某些情况下(完整缓存),如果不想使用LRU算法,那么可以通过“-M”参数来启动Memcached,这样Memcached在内存耗尽时,会返回一个报错信息,如下:

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

4.4.3 Memcached删除机制小结

  • 不主动检测item对象是否过期,而是在get时才会检查item对象是否过期以及是否应该删除。
  • 当删除item对象时,一般不释放内存空间,而是做删除标记,将指针放入slot回收插槽,下次分配的时候直接使用。
  • 当内存空间满的时候将会根据LRU算法把最近最少使用的item对象删除。
  • 数据存入可以设定过期时间,但是数据过期后不会被立即删除,而是在get时检查item对象是否过期以及是否过期是否应该删除。
  • 如果不希望系统使用LRU算法清除数据,可以使用-M参数。
温馨提示:本文最后更新于2022-12-20 20:57:54,已超过436天没有更新。某些文章具有时效性,若文章内容或图片资源有错误或已失效,请联系站长。谢谢!
转载请注明本文链接:https://blog.leonshadow.cn/763482/241.html
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享