Memcached 是一个简单纯粹的高性能的分布式内存对象缓存系统,主要用于缓存中间件最原始的场景:缓存数据和对象来减少数据库负载。Memcached只提供最简单的k-v形式的数据存储能力,不像redis那样有丰富的数据结构,今天我们就来着重聊一聊Memcached。
1. 数据库缓存
Memcached最核心、最常用的功能就是数据库缓存,对于热点数据,我们可以在缓存中存一份,由于缓存数据库性能往往都比db好得多,也能支持更高的请求量,因此可以极大的提高整个系统的荷载。
2. 数据一致性问题
数据库缓存适用于热点数据以及读多写少的场景,因为过于频繁的数据更新问题,就涉及到复杂的缓存与DB的数据一致性问题。
缓存的本质是数据的冗余存储,而所有冗余的数据,都有一致性问题,最常说的缓存一致性方案是延迟双删:
- 数据发生更新
- 先删除缓存 -> 防止并发的查询操作查到脏数据
- 再更新数据库
- 一段时间后再删除缓存 -> 防止上图中已经进入网络传输的老的操作4把缓存更新为藏数据
但是,延迟双删其实是一个玄之又玄的方案,比如说延迟,究竟应该延迟多长时间?在逻辑上,这个方案是没办法保证必然的数据一致性的。并且,这个方案确实非常复杂。
对于高频修改的数据,设置缓存本身就不是一个明智之选,而对于低频修改的数据,我们直接保证数据先写入数据库,再按照数据库数据更新缓存两步操作的事务就可以了。至于说更新完数据库,还未更新缓存时,并发查询操作查到的是脏数据的问题,我认为是可以接受的,因为就算没有缓存,当两个客户端对DB发起并发的读写操作时,我们也没办法确保读到的数据是修改前还是修改后的。
3. Memcached内存模型
3.1. 内存数据结构
Memcached是一个纯内存操作的缓存中间件,其底层结构就是一张存在于内存中的巨大的哈希表。另外,Memcached 使用 LRU(Least Recently Used,最近最少使用)算法来管理缓存,当内存不足时会自动过期回收旧的数据。
3.2. Slab机制
Memcached使用slab机制进行物理内存的管理,slab机制在内存管理中非常常见,linux、Go语言的内存管理中都有Slab的影子,这里,我们简单介绍,帮住大家快速了解什么是Slab。
Slab的思想和常量池、线程池非常相似,当我们需要分配一块内存空间时,如果即时创建,就不可避免的要有实实例化的耗时,因此,我们可以像线程池一样,把需要的内存空间准备出来,需要分配的时候开箱即用,直接分配,就能大大缩短这一时间开销,这就是Slab机制。
我们把内存空间分为一个个固定大小的chunked,需要使用时,按需选择适合的chunked直接分配就可以了。当小chunked不够用时,可以将大chunked切割为一个个小chunked,在进行分配。另外,Slab机制中往往有一个线程进行碎片chunked的整理,将小chunked适时合并为大的chunked。
4. 线程模型
Memcached使用的是多线程模型,这一点和redis有很大的不同,我们都知道,redis的主线程是单线程的,由于redis是纯内存操作,速度很快,就算是单线程处理,性能瓶颈也不在CPU而是网络IO,并且单线程还避免了锁操作的额外开销,那么,为什么Memcached还要使用多线程模型呢?
其实这个问题我也暂时还没找到答案,但是我的思考是这样的,相较于强大的,提供了多种数据结构的redis,Memcached的定位还是更纯粹一点,更多的还是当作数据库DB前的数据缓存,前面我们也提到了,这种应用场景就需要我们的业务具有读多写少的特征,而在这种场景下,发生锁争抢的可能本身就是很小的,因此,通过多线程的方式进一步提升数据并发读取的性能,也不失为一种选择。
Memcached与redis相比,还有一个不同,Memcached没有数据持久化的能力,而redis有RDB和AOF两种持久化机制。