memcache是互联网分层架构中,使用最多的的KV缓存。面试的过程中,memcache相关的问题几乎是必问的,关于memcache的面试提问,你能回答到哪一个层次呢? 第一类问题:知道不知道这一类问题,考察用没用过,知不知道,相对比较好回答。 关于memcache一些基础特性,使用过的小伙伴基本都能回答出来: 面对这类封闭性的问题,一定要斩钉截铁,毫无犹豫的给出回答。 第二类问题:为什么(why),什么(what)这一类问题,考察对于一个工具,只停留在使用层面,还是有原理性的思考。 memcache为什么不支持复杂数据结构?为什么不支持持久化?业务决定技术方案,mc的诞生,以“以服务的方式,而不是库的方式管理KV内存”为设计目标,它颠覆的是,KV内存管理组件库,复杂数据结构与持久化并不是它的初衷。 当然,用“颠覆”这个词未必不合适,库和服务各有使用场景,只是在分布式的环境下,服务的使用范围更广。设计目标,诞生背景很重要,这一定程度上决定了实现方案,就如redis的出现,是为了有一个更好用,更多功能的缓存服务。 memcache是用什么技术实现key过期的?懒淘汰(lazy expiration)。 memcache为什么能保证运行性能,且很少会出现内存碎片?提前分配内存。 memcache为什么要使用非阻塞IO复用网络模型,使用监听线程/工作线程的多线程模型,有什么优缺点?目的是提高吞吐量。 面对这类半开放的问题,有些并没有标准答案,一定要回答出自己的思考和见解。 第三类问题:怎么做(how) | 文本刚开始这一类问题,探测候选人理解得有多透,掌握得有多细,对技术有多刨根究底。 memcache是什么实现内存管理,以减小内存碎片,是怎么实现分配内存的?开讲之前,先解释几个非常重要的概念: 如上图所示,一系列slab,分别管理128B,256B,512B…的chunk内存单元。 将上图中管理128B的slab0放大:
假如用户要存储一个100B的item,是如何找到对应的可用chunk的呢?
### 为什么不会出现内存碎片呢? 拿到一个128B的chunk,去存储一个100B的item,余下的28B不会再被其他的item所使用,即:实际上浪费了存储空间,来减少内存碎片,保证访问的速度。 memcache通过slab,chunk,free_chunk_list来快速分配内存,存储用户的item,那它又是如何快速实现key的查找的呢?
用最朴素的方式,实现key的快速查找。 随着item的个数不断增多,hash冲突越来越大,hash表如何保证查询效率呢?当item总数达到hash表长度的1.5倍时,hash表会动态扩容,rehash将数据重新分布,以保证查找效率不会不断降低。 扩展hash表之后,同一个key在新旧hash表内的位置会发生变化,如何保证数据的一致性,以及如何保证迁移过程服务的可用性呢(肯定不能加一把大锁,迁移完成数据,再重新服务吧)? 哈希表扩展,数据迁移是一个耗时的操作,会有一个专门的线程来实施,为了避免大锁,采用的是“分段迁移”的策略。 当item数量达到阈值时,迁移线程会分段迁移,对hash表中的一部分桶进行加锁,迁移数据,解锁:
新的问题来了,对于已经存在与旧hash表中的item,可以通过上述方式迁移,那么在item迁移的过程中,如果有新的item插入,是应该插入旧hash表还是新hash表呢?
为什么要这么做呢,不能直接插入新hash表吗? memcache是怎么实现key过期的,懒淘汰(lazy expiration)具体是怎么玩的?实现“超时”和“过期”,最常见的两种方法是:
mc的查询业务非常简单,只会返回cache hit与cache miss两种结果,这种场景下,非常适合使用懒淘汰的方式。 懒淘汰的核心是:
举个例子,假如set了一个key,有效期100s:
这种方式的实现代价很小,消耗资源非常低:
内存总是有限的,chunk数量有限的情况下,能够存储的item个数是有限的,假如chunk被用完了,该怎么办? 仍然是上面的例子,假如128B的chunk都用完了,用户又set了一个100B的item,要不要挤掉已有的item? 这里的启示是: 挤掉哪一个item?怎么挤?这里涉及LRU淘汰机制。 如果操作系统的内存管理,最常见的淘汰算法是FIFO和LRU:
使用LRU算法挤掉item,需要增加两个属性:
思路比结论重要。 来源:https://www./content-4-754051.html |
|