memcache:入门

是什么

  • memcache是一个自由、源码开放、高性能、分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高了网站访问的速度。
  • memcache是一种基于内存的key-value存储,用来存储小块的任意数据(字符串、对象)。这些数据可以是数据库调用、API调用或者是页面渲染的结果。
  • memcache简洁而强大。它的简洁设计便于快速开发,减轻开发难度,解决了大数据量缓存的很多问题。它的API兼容大部分流行的开发语言。

许多语言都实现了连接memcached的客户端,其中以Perl、PHP为主。仅仅memcached网站上列出的有:

  • Perl
  • PHP
  • Python
  • Ruby
  • C#
  • C/C++
  • Lua
  • 等等

本质上,它是一个简洁的key-value存储系统。

memcache和memcached的区别:

  • memcache是项目的名称
  • memcached是memcache服务器端可以执行文件的名称

官网

特征

memcache作为高速运行的分布式缓存服务器,具有如下特点

  • 协议简单
  • 基于libevent的事件处理
  • 内置内存存储方式
  • memcached互不通信的分布式

应用场景

  • 访问频繁的字典数据。
  • 大量的hot数据。
  • 页面缓存。
  • 频繁的查询条件和结果。
  • 临时处理的数据。

一般的使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度、提高可扩展性。

  • memcache可以应对任意多个连接,使用非阻塞的网络IO。由于它的工作机制是在内存中开辟一块空间,然后建立一个HashTable,memcached自管理这些HashTable。
  • 使用memcache的网络一般流量都是比较大的,为了缓解数据库的压力,让memcache作为一个缓存区域,把部分数据保存在内存中,在前端能够快速的进行存取。那么一般的焦点就是集中在如何分担数据库压力和进行分布式,毕竟单台memcache的内存容量的有限的。

memcache的操作流程

在这里插入图片描述

  • 检查客户端的请求数据是否在memcached中,如有,直接把请求数据返回,不再对数据库进行任何操作,路径操作为①②③⑦。
  • 如果请求的数据不在memcached中,就去查数据库,把从数据库中获取的数据返回给客户端,同时把数据缓存一份到memcached中(memcached客户端不负责,需要程序明确实现),路径操作为①②④⑤⑦⑥。
  • 每次更新数据库的同时更新memcached中的数据,保证一致性。
  • 当分配给memcache内存空间用完之后,会使用LRU(Least Recently Used,最近最少使用)策略加上到期失效策略,失效数据首先被替换,然后再替换掉最近未使用的数据

memcache访问模型

在这里插入图片描述
特别澄清一个问题,memcache虽然被称为“分布式缓存”,但是memcache本身完全不具备分布式的功能。memcache集群之间不会相互通信(与之形成对比的,比如JBoss Cache,某台服务器有缓存数据更新时,会通知集群中其他机器更新缓存或者清除缓存数据),所谓“分布式”,完全依赖于客户端程序的实现。就像上面这张图的流程一样。

同时基于这张图,我们理一下memcache一次写缓存的流程

  • 应用程序输入需要写缓存的数据
  • API将Key输入路由算法模块,路由算法根据Key和MemCache集群服务器列表得到一台服务器编号
  • 由服务器编号得到MemCache及其的ip地址和端口号
  • API调用通信模块和指定编号的服务器通信,将数据写入该服务器,完成一次分布式缓存的写操作

读缓存和写缓存一样,只要使用相同的路由算法和服务器列表,只要应用程序查询的是相同的Key,MemCache客户端总是访问相同的客户端去读取数据,只要服务器中还缓存着该数据,就能保证缓存命中。

这种MemCache集群的方式也是从分区容错性的方面考虑的,假如Node2宕机了,那么Node2上面存储的数据都不可用了,此时由于集群中Node0和Node1还存在,下一次请求Node2中存储的Key值的时候,肯定是没有命中的,这时先从数据库中拿到要缓存的数据,然后路由算法模块根据Key值在Node0和Node1中选取一个节点,把对应的数据放进去,这样下一次就又可以走缓存了,这种集群的做法很好,但是缺点是成本比较大。

一致性hash算法

从上面的图中,可以看出一个很重要的问题,就是对服务器集群的管理,路由算法至关重要,就和负载均衡算法一样,路由算法决定着究竟该如何访问集群中的哪台服务器,先看一个路由算法

余数Hash

比方说,字符串str对应的HashCode是50、服务器的数目是3,取余数得到2,str对应节点Node2,所以路由算法把str路由到Node2服务器上。由于HashCode随机性比较强,所以使用余数Hash路由算法就可以保证缓存数据在整个MemCache服务器集群中有比较均衡的分布。

如果不考虑服务器集群的伸缩性,那么余数Hash算法几乎可以满足绝大多数的缓存路由需求,但是当分布式缓存集群需要扩容的时候,就难办了。

就假设MemCache服务器集群由3台变为4台吧,更改服务器列表,仍然使用余数Hash,50对4的余数是2,对应Node2,但是str原来是存在Node1上的,这就导致了缓存没有命中。如果这么说不够明白,那么不妨举个例子,原来有HashCode为0~19的20个数据,那么:
在这里插入图片描述
现在我扩容到4台,加粗标红的表示命中:
在这里插入图片描述
如果我扩容到20+的台数,只有前三个HashCode对应的Key是命中的,也就是15%。当然这只是个简单例子,现实情况肯定比这个复杂得多,不过足以说明,使用余数Hash的路由算法,在扩容的时候会造成大量的数据无法正确命中(其实不仅仅是无法命中,那些大量的无法命中的数据还在原缓存中在被移除前占据着内存)。这个结果显然是无法接受的,在网站业务中,大部分的业务数据度操作请求上事实上是通过缓存获取的,只有少量读操作会访问数据库,因此数据库的负载能力是以有缓存为前提而设计的。当大部分被缓存了的数据因为服务器扩容而不能正确读取时,这些数据访问的压力就落在了数据库的身上,这将大大超过数据库的负载能力,严重的可能会导致数据库宕机。

这个问题有解决方案
(1)在网站访问量低谷,通常是深夜,技术团队加班,扩容、重启服务器

(2)通过模拟请求的方式逐渐预热缓存,使缓存服务器中的数据重新分布

一致性Hash算法

一致性Hash算法通过一个叫做一致性Hash环的数据结构实现Key到缓存服务器的Hash映射
在这里插入图片描述
具体算法过程为:先构造一个长度为 2 32 2^{32} 232的整数环(这个环叫做一致性hash环),根据节点名称的Hash值(其分布为 [ 0 , 2 32 − 1 ] [0, 2^{32}-1] [0,2321])将缓存服务器节点放置在这个hash环上,然后根据需要缓存的数据的key值计算得到hash值(其分布也为 [ 0 , 2 32 − 1 ] [0, 2^{32}-1] [0,2321]),,然后在Hash环上顺时针查找距离这个Key值的Hash值最近的服务器节点,完成Key到服务器的映射查找。

就如同图上所示,三个Node点分别位于Hash环上的三个位置,然后Key值根据其HashCode,在Hash环上有一个固定位置,位置固定下之后,Key就会顺时针去寻找离它最近的一个Node,把数据存储在这个Node的MemCache服务器中。使用Hash环如果加了一个节点会怎么样,看一下:
在这里插入图片描述
看到我加了一个Node4节点,只影响到了一个Key值的数据,本来这个Key值应该是在Node1服务器上的,现在要去Node4了。采用一致性Hash算法,的确也会影响到整个集群,但是影响的只是加粗的那一段而已,相比余数Hash算法影响了远超一半的影响率,这种影响要小得多。更重要的是,集群中缓存服务器节点越多,增加节点带来的影响越小,很好理解。换句话说,随着集群规模的增大,继续命中原有缓存数据的概率会越来越大,虽然仍然有小部分数据缓存在服务器中不能被读到,但是这个比例足够小,即使访问数据库,也不会对数据库造成致命的负载压力。

至于具体应用,这个长度为 2 32 2^{32} 232的一致性Hash环通常使用二叉查找树实现

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值