简要概述:
Memcached是一款开源、高性能、分布式内存对象缓存系统,可应用各种需要缓存的场景,其主要目的是通过降低对Database的访问来加速web应用程序。它是一个基于内存的“键值对”存储,用于存储数据库调用、API调用或页面引用结果的直接数据(不做编码),如字符串、对象等。
为什么是直接数据?因为数据在网络中传输是要经过流式化的,数据传递到对方那里,它如何知道这是什么类型的数据呢,所以就需要在原有数据之外在增加其他数据来修饰数据。所以直接数据就是不加任何修饰的数据。
比如在读写分离一主多从的模式下,为了加速查询从服务器通常开启缓存,特别是会发生多表连接查询的时候。但是由于多个从服务器前端有一个调度器来进行负载均,所以每次同一个请求未必发送到同一个从服务器上,这样缓存就用不了了。为了解决这个问题,就引入了公共缓存,也就是Memached
Memached是键值数据库,键就是其实就是搜索条件的HASH码,值就是数据。查找过程就是对比HASH码,所以只要HASH码不冲突,那么它的查找时间无论你有多少HASH码时间都是一样的,也就O1的查找速度。
Memecached是一个开发工具,它不是一个代码加速器,也不是数据库中间件。为什么它不是数据库中间件呢,因为中间件是你请求它,如果它没有数据,它会替你去向后查找,而Memached则不是,所以这就是为什么说Memcached的功能一半依赖于客户端的开发,一半依赖于服务器。
它的特点如下:
- 简单的KEY/VALUE存储
- 功能实现一半依赖于客户端,一半依赖于服务器
- 各个Memcached服务器之间没有联系
- O1的执行效率
- 过期数据处理,默认情况下采用懒惰模式,因为内存数据库实际上就是对内存的操作,每增加一个数据就要发起系统调用去申请内存,减少一个数据也要发起系统调用去回收内存,所以如果频繁的这种操作会影响性能。
在Linux中内核使用Glibc库中的malloc()来实现内存分配,使用free()来实现内存释放,所以C\C++程序员可以手动管理内存,这是优势也是劣势,如果是一个经验少的程序员,会经常忘记申请或者释放内存,那么这个程序性能就很低下。所以Memached使用了内存管理器中的slap allocator分配器来实现内存申请和回收。你一启动Memached就要指定分配多大内存空间,无论用还是不用,然后使用slap allocator分配器来对这一段内存做分割,也就是切成小块,这些小块的大小不一样,主要就是为了适应不同大小的数据,也就是尽量避免内存的反复申请和释放。假设申请了一堆1K的2K的和4K的内存,如果来了一个3K的数据呢?它会使用最接近且大于它的内存块,也就是4K的,那剩余的1K也就浪费了,这是无法避免的。它不会使用2K+1K的内存块,因为数据不能再分割了,不是说技术上不允许而是从性能上来说的。所以内存页到底分配多大,用户可以自己设定分配因子。
内存术语:
Page:内存页,也就是让slab用于切割的内存空间。默认的页面大小为1M,然后slab对这些页面做切割。
Chunk:slab把页切割后的就叫做Chunk,用于缓存记录的空间。
Slab class:同一个种大小的chunk的被称为slabclass
Memcached协议有两种,文本格式和二进制格式。
如果1台Memcached不够用,可以增加,但是因为Memcached之间没有任何联系,所以你前端就需要有一个调度器或者负载均衡器,这样有带来一个问题就是缓存命中率的事情,你的缓存在哪里下次也要访问哪里。所以加负载均衡不是问题,而是命令率是重要的事情。为了保证命中率,就有了几个算法:
HASH取余:最简单的办法根据HASH值对后端服务器数量取余。不过这种方式有一个致命的缺点就是增减服务器,那么余将会改变,有可能导致所有缓存都全局失效。
一致性HASH:也就是一个虚拟的环,2的32次方。根据服务器名称计算HASH值,然后用这个HASH值对2的32次方取余,其结果一定是在2的32次方之内,所以这就是服务器的位置,假设你有3个服务器,落在了这个虚拟环的3个不同位置,用户请求来了,换句话说用户请求的KEY,计算KEY的哈希值,然后也对2的32次方取余,这样也有一个结果,也会落到这个环上,然后根据顺时针原则去找服务器。这种算法对于服务器的增减所带来的影响比较小。但是缺点是不均衡。尤其是当节点比较少的时候,每个节点在环的位置上不一样有些彼此离得近有些却远,离得远的显然处理的请求会更多。所以可以使用虚拟节点来增加节点数量,来减少不均衡的请求。
我们之前提到Memcached的功能一半是在客户端上,一半是在服务器上,所以上面说的均衡算法是在客户端上实现的或者是在Memcached前端的负载均衡器上实现的。