memcache是互联网分层架构中,使用最多的的KV缓存。面试的过程当中,memcache相关的问题几乎是必问的,关于memcache的面试提问,你能回答到哪个层次呢?
画外音:极可能关乎,你拿到offer的薪酬档位。面试
这一类问题,考察用没用过,知不知道,相对比较好回答。redis
关于memcache一些基础特性,使用过的小伙伴基本都能回答出来:
(1)mc的核心职能是KV内存管理,value存储最大为1M,它不支持复杂数据结构(哈希、列表、集合、有序集合等);
(2)mc不支持持久化;
(3)mc支持key过时;
(4)mc持续运行不多会出现内存碎片,速度不会随着服务运行时间下降;
(5)mc使用非阻塞IO复用网络模型,使用监听线程/工做线程的多线程模型;算法
面对这类封闭性的问题,必定要斩钉截铁,毫无犹豫的给出回答。缓存
这一类问题,考察对于一个工具,只停留在使用层面,仍是有原理性的思考。网络
业务决定技术方案,mc的诞生,以“以服务的方式,而不是库的方式管理KV内存”为设计目标,它颠覆的是,KV内存管理组件库,复杂数据结构与持久化并非它的初衷。数据结构
固然,用“颠覆”这个词未必不合适,库和服务各有使用场景,只是在分布式的环境下,服务的使用范围更广。设计目标,诞生背景很重要,这必定程度上决定了实现方案,就如redis的出现,是为了有一个更好用,更多功能的缓存服务。
画外音:我很喜欢问这个问题,大部分候选人面对这个没有标准答案的问题,状态多是蒙圈。多线程
懒淘汰(lazy expiration)。架构
提早分配内存。分布式
目的是提升吞吐量。
多线程可以充分的利用多核,但会带来一些锁冲突。ide
面对这类半开放的问题,有些并无标准答案,必定要回答出本身的思考和看法。
这一类问题,探测候选人理解得有多透,掌握得有多细,对技术有多刨根究底。
画外音:所谓“好奇心”,真的很重要,只想要“一份工做”的技术人很难有这种好奇心。
开讲以前,先解释几个很是重要的概念:
chunk:它是将内存分配给用户使用的最小单元。
item:用户要存储的数据,包含key和value,最终都存储在chunk里。
slab:它会管理一个固定chunk size的若干个chunk,而mc的内存管理,由若干个slab组成。
画外音:为了不复杂性,本文先不引入page的概念了。
如上图所示,一系列slab,分别管理128B,256B,512B…的chunk内存单元。
将上图中管理128B的slab0放大:
可以发现slab中的一些核心数据结构是:
会从最接近item大小的slab的chunk[]中,经过free_chunk_list快速找到对应的chunk,如上图所示,与item大小最接近的chunk是128B。
### 为何不会出现内存碎片呢?
拿到一个128B的chunk,去存储一个100B的item,余下的28B不会再被其余的item所使用,即:实际上浪费了存储空间,来减小内存碎片,保证访问的速度。
画外音:理论上,内存碎片几乎不存在。
memcache经过slab,chunk,free_chunk_list来快速分配内存,存储用户的item,那它又是如何快速实现key的查找的呢?
没有什么特别算法:
用最朴素的方式,实现key的快速查找。
当item总数达到hash表长度的1.5倍时,hash表会动态扩容,rehash将数据从新分布,以保证查找效率不会不断下降。
扩展hash表以后,同一个key在新旧hash表内的位置会发生变化,如何保证数据的一致性,以及如何保证迁移过程服务的可用性呢(确定不能加一把大锁,迁移完成数据,再从新服务吧)?
哈希表扩展,数据迁移是一个耗时的操做,会有一个专门的线程来实施,为了不大锁,采用的是“分段迁移”的策略。
当item数量达到阈值时,迁移线程会分段迁移,对hash表中的一部分桶进行加锁,迁移数据,解锁:
新的问题来了,对于已经存在与旧hash表中的item,能够经过上述方式迁移,那么在item迁移的过程当中,若是有新的item插入,是应该插入旧hash表仍是新hash表呢?
memcache的作法是,判断旧hash表中,item应该插入的桶,是否已经迁移至新表中:
为何要这么作呢,不能直接插入新hash表吗?
memcache没有给出官方的解释,楼主揣测,这种方法可以保证一个桶内的数据,只在一个hash表中(要么新表,要么旧表),任何场景下都不会出现,旧表新表查询两次,以提高查询速度。
实现“超时”和“过时”,最多见的两种方法是:
mc的查询业务很是简单,只会返回cache hit与cache miss两种结果,这种场景下,很是适合使用懒淘汰的方式。
懒淘汰的核心是:
举个例子,假如set了一个key,有效期100s:
这种方式的实现代价很小,消耗资源很是低:
内存老是有限的,chunk数量有限的状况下,可以存储的item个数是有限的,假如chunk被用完了,该怎么办?
仍然是上面的例子,假如128B的chunk都用完了,用户又set了一个100B的item,要不要挤掉已有的item?
要。
这里的启示是:
(1)即便item的有效期设置为“永久”,也可能被淘汰;
(2)若是要作全量数据缓存,必定要仔细评估,cache的内存大小,必须大于,全量数据的总大小,不然很容易采坑;
这里涉及LRU淘汰机制。
若是操做系统的内存管理,最多见的淘汰算法是FIFO和LRU:
使用LRU算法挤掉item,须要增长两个属性:
思路比结论重要。
架构师之路-分享技术思路
文章较长,如有收获,帮忙转发+再看一下。
调研:面试memcache内核,你在哪一个档位?