这本书中间还有个4,是es面试题。仔仔细细读了两遍就劝退我了,分开来每个字都差不多认识,连在一起完全看不明白,而且我也没实践经验,所以就不浪费时间了,直接跳到缓存这里。下面开始一个题一个题整理:
1. Memcached是什么,有什么作用?
首先,Memcached是一个开源的,高性能的内存缓存软件。从名称上看,Mem是内存的意思,cached是缓存、连起来就是内存缓存。
通过在事先规划好的内存空间内临时缓存数据库中的各类数据,以减少业务对数据库的高并发访问。提升数据库的访问性能,加速集群应用的反应能力。
Memcached的应用场景:
- 作为数据库的前端缓存应用
- 完整缓存,静态缓存
比如商品分类,商品信息。我们可以先放在内存中,再提供前端访问入口。我们把这种先放入内存再访问的,称之为预热。用户访问时可以只读取缓存,而不用访问数据库了。 - 执点缓存
即只存热点数据,也就是经常访问的数据。然后我们先预热,存在缓存中,如果缓存中没有再去查询数据库,其实这个我们是经常使用的,一般我们用的Memcached注解版就是这样一个作用。在查询的时候相同的方法参数走缓存查询,缓存中没有去查询数据库,然后存到缓存中并将结果返回。
当然了这个需要配套的一个功能就是:当数据发生更改时要相应的修改缓存中的数据,或者直接删除等待下次访问再重新查询。
- 完整缓存,静态缓存
电商秒杀:当如果遇到电商秒杀这种时间短,并发高的场景,一定要事先预热或者采用其他措施来解决。这个其他措施可以是获取资格而不是直接购买。
比如可以在数据库标记0/1.先标记出来再事后去下订单等操作(因为下单是一系列操作。而修改标记只是一个操作)。
2. Memcached服务分布式集群如何实现的?
和mysql不同,Memcached服务每一个都只存一部分数据,这些集群中所有数据加起来才是完整数据。其数据访问有两种方式:
- 前端实现。通过对key做hash(一致性hash算法)定位到具体的服务器。
- 负载均衡器。(其本质也是对key做hash,只不过一个是代码实现,一个是现有的工具实现)
一致hash算法不仅仅是为了每次访问只请求一个服务器,同时也是为了当某一台服务器宕机后,缓存服务器的更新重分配比例降到最低。
3. Memcached的服务特点及工作原理是什么?
- Memcached是完全基于内存缓存的。
- Memcached节点之间相互独立
- Memcached是C/S架构,C语言缩写的,全长2000行代码
- Memcached是异步I/O结构,使用libevent作为事件通知机制
- 被缓存的数据以KV键值对的形式存储
- 全部数据都放入内存中,无持久性存储的设计。重启服务器所有数据丢失
- 当内存中存储的数据容量达到启动时设定的内存值时,会自动使用LRU算法删除过期的缓存数据。
- 可以对存储的数据设定过期时间,这样过期的数据会自动被清楚。服务器本身不会监控过期,而是访问的时候查看key的时间戳,判断是否过期。
- memcached会在设定的内存中分块,再把块分组。然后再提供服务。(这一块的分块,分组是基于Memcached的内存管理机制,也就是Slab Allocation内存分配机制。不太懂的可以看下一道题)
4. 简述Memcached内存管理机制原理
早期的Memcached内存管理方式是通过malloc的分配的内存。使用完后通过free来回收内存。这种方式容易产生内存碎片。并降低操作系统对内存的管理效率。加重操作系统内存管理器的负担,最坏的情况下,会导致操作系统比memcached进程本身还慢。为了解决 这个问题,Slab Allocation内存分配机制就诞生了。
现在Memcached利用Slab Allocation机制来分配和管理内存。
Slab Allocation
Slab Allocation机制原理是按照预先规定的大小,将分配给memcached的内存分割成特定长度的内存块。再把尺寸相同的内存块分成组。Slab Allocation有重复使用已分配的内存的目的。也就是说分配到的内存不会释放,而是重复利用。
Slab Allocation的主要术语:
- Page:
分配给Slab的内存空间默认是1MB,分配给Slab之后根据slab的大小切分成chunk。 - Chunk
用户缓存记录的内存空间(也可以理解成块)。 - SlabClass
特定大小的chunk的组。
5. Memcached是怎么工作的?
神奇的两阶段哈希(two-stage hash)。
其实这个是个很有意思的设计。属于两次查询。我们之前说过了Memcached里存储的是KV对的hash表。通过key可以存储或查询任意的数据。
但是这里有个查询问题。在一百个数据中查询一个和在一万个数据中查询一个。肯定是一百个快啊。
所以Memcached的集群差不多如此效果:一万个数据分100个节点存储。一个节点存100个数组。
然後查询的时候先参考节点列表计算出key 的哈希值(阶段一哈希)。进而选中一个节点。
然後在memcached节点中通过一个内部的hash算法(阶段二哈希)查找真正的数据。
6. Memcached最大的优势是什么?
Memcached最大的好处就是它带来了极佳的水平扩展性。特别是在一个巨大的系统中。
由于客户端自己做了一个hash。我们很容易把心的memcached添加到集群中。
memcached之间没有相互通信,所以集群扩招也不会增加memcached的负载。没有多播协议也就不会网络通信量爆炸。
总而言之,这个memcached有点类似与mysql的分库(注意是分库而不是分表)。
7. memcached和MySQL的query cache相比,有什么优缺点?
我看的这本书上说Memcached引用复杂。但是现在2021年了,spring boot都到2了,其自带的Memcached已经配置好了。可以直接使用。注解版的话只需要添加几个注解就可以解决。所以不能说使用复杂了。
然后优点相比于MySQL的query cache有一下几点:
- 修改表的时候MySQL的query cache会立刻被刷新。当写操作很频繁的时候MySQL的query cache会经常让所有缓存数据失效。
- 多核CPU上,query cache会增加一个全局锁。所以当需要刷新的数据变多时,速度会变慢。
- MySQL的query cache只能存储SQL查询结果。而Memcached则可以存储任意的数据。
- query cache能利用的内存容量受到MySQL服务器的空闲内存空间的限制。而Memcached只要有空闲的内存都可以用来增加Memcached集群的规模。
简单总结下:Memcached存储不局限于sql语句,query cache只能存sql运行结果。query cache依赖MySQL服务器的空闲内存。Memcached不局限。Memcached更加灵活方便和便于使用。
8. Memcached的cache机制是怎么样的?
这个其实上文已经简单的提到了。主要的cache机制是LRU算法+超时失效。
存数据到Memcached的时候,可以指定该数据在缓存中待多久。然后当Memcached的内存不够用了优先把过期的slabs替换,如果过期都替换了还是不够用,就把最长时间未被使用的slabs替换。
9. Memcached如何实现冗余机制?
这个问题其实是个坑。因为Memcached根本不带任何冗余机制。Memcached应该是应用的缓存层。简单来说应该是一个有了更好,没有也可以的作用。如果就因为没有了Memcached就程序出问题了说明设计的有问题。。。
应该特别注意:我们的应用应该可以容忍任何节点的失效。也就是如果Memcached节点失去了所有的数据,也应该可以从数据源(比如数据库)再次获取到数据。
当然了如果担心节点失效会加大数据库的负担,可以选择别的方式:比如增加更多的节点。热备节点等。
10. Memcached如何处理容错的?
依然是坑。因为结果是不处理。在Memcached节点失效的情况下,集群没有必要做任何容错处理。如果发生了节点失效,应对的措施完全取决于用户。节点失效时,下面几种方案可以选择:
- 忽略他。down就down了。等修好了再说。反正只是一部分数据而已。数据库一点压力没问题的。
- 失效的节点从节点列表移除。 这个很严肃,因为默认情况下(余数式哈希算法)客户端添加或者删除节点会法制所有的缓存数据不可用。会从新分配所有的key的所在节点。
- 启动热备节点,就是当有down掉的接管失效节点所占用的ip。防止哈希紊乱。
- 两次hash。如果存数据发现第一个hash的节点down了,直接再做一次与第一次不同的hash,存储到另一个节点上。当然取的时候也先去第一个down的节点取,没有结果去第二个取。
需要注意这种方式的话千万别节点时好时坏。。不然有脏数据的风险。
11. 如何将Memcached中kv对批量导入导出?
这是一个不应该的操作。Memcached的特性(缓存在内存。非阻塞。)如果在导入导出期间数据发生了变化,就会产生脏数据了。
当然了,有些东西比如几乎从不变化,但是因为经常使用所以存在内存(比如省市区,轻易不变化。)我们确实可以在一开始把这些信息导入进缓存。但是注意:
Memcached是可以减轻数据库压力。但是不是说我们有了缓存就高枕无忧了,改优化查询还是要优化查询的!我们应该让Memcached实现的是如虎添翼,雪上添花的功能。而不能说没有了Memcached就整个程序崩溃了。
12. Memcached是如何做身份验证的?
注意这个题又是一个坑!因为Memcached是没有身份认证机制的。Memcached是运行在应用下层的软件。身份验证应该是应用上层的职责。
Memcached的客户端和服务端之所以是轻量级的,部分原因就是完全没有实现身份验证机制。这样可以很快的创建新连接。服务端也无需任何配置。
13. Memcached的多线程是什么?如何使用它们?
Memcached1.2及以上才有多线程模式。多线程允许Memcached能够充分利用多个cpu。并在CPU之间共享所有的缓存数据。Memcached使用一种简单的锁机制来保证数据更新操作的互斥。相比同一个物理机上运行多个Memcached实例。这种方式更能够有效的处理存取。
当然了,如果系统负载并不重的情况下,也许不需要启用多线程工作模式。
简单总结一下:命令解析可以运行在多线程的模式下。Memcached内部对数据的操作是基于很多全局锁的(因为这部分不是多线程)。未来对多线程的改进应该是移除大量全局锁,提高Memcached在负载极高的场景下的性能。
14. Memcached能接受的key的最大长度是多少?
key的最大长度是250个字符。需要注意的是250是memcached服务器端内部的限制。
如果您的客户端支持key的前缀或者类似特性,那么实际key=前缀+原始key。其实最大长度是可以超过250字符的。但是我们推荐使用短的key,因为较短的key可以节省内存和宽带。
15. Memcached对item的过期时间有什么限制?
过期时间最大可以达到30天。Memcached吧传入的过期时间解释成一个时间点(我们可以理解为时间戳)。一旦到了这个时间点,Memcached就把item设置为失效状态。
16. Memcached最大能存储多大的单个item?
1MB(为什么是1MB呢?内存分配器的算法就是这样的!),如果数据大于1MB,可以考虑在客户端压缩或者拆分到多个key中。
17. Memcached如何更有效的使用内存?
Memcached客户端仅仅根据哈希算法来决定将某个key存储在哪个节点上。而不考虑节点的内存大小。因为我们可以在不同的节点上使用大小不等的缓存。但是一般都是这么做的:在拥有较多内存的节点上可以运行多个Memcached示例。使得每个实例使用的内存跟其他节点上的实例相同(人为去控制)。
18. Memcached的内存分配器是如何工作的?为什么不适用malloc/free?为什么要使用slabs?
其实这个最开始的时候Memcached确实是使用malloc/free来管理内存的。然后在实际工作中发现这种方式不能很好的工作。因为反复的malloc/free会造成内存碎片。OS最终花费大量的时间去查找连续的内存块来满足malloc的请求而不是运行memcached的进行。
(这一块可以联想java的垃圾回收机制来理解-标记清除的过时原因。)
19. Memcached是原子的么?
所有被发送到Memcached的单个命令都是原子的。这个有点类似于redis。就是保证每一个命令的原子性。如果发送了一个命令组:先set,再get。他们不会影响多方。会被串行化,先后执行。
但是问题来了!!!如果你发送一个命令组:set/get。但是不敢保证你get到的是你set的数据。因为可能有别的线程在你set后有把这个值set成了新值。你get到的也就是新值了。(和redis一毛一样)
然後memcached1.2.5以上提供了一些解决命令序列的原子问题。有点类似CAS的版本号。就是每次修改都带个版本号。版本号不一致写会失败。
20. 如何实现集群中的session共享存储?
其实我觉得这个题有点跟Memcached没啥关系了。是设计架构上的 单点登录吧?
反正看到了就要记录一下。先说一下什么是单点登录:Session是运行在一台服务器上的。我们可以根据客户端传来的sessionId获取session。如果session不存在则说明用户没登陆或者登录过期了。这个时候需要重新登陆。
但是问题来了。如果是集群环境下有多台服务器,那么请求会由Nginx转发。用户登录时nginx把请求转发其中一台服务器上。这个服务器创建了session并返回给客户端sessionId。但是如果下次请求被负载到其余的服务器上,那么这个session是不存在的。所以要重新登录。这种情况多了用户体验就不好了。
解决办法其实也很多。简单说几个:
- 粘性session
Nginx将同一个用户请求转发至同一台服务器。(这种我个人觉得不好,而且失去了集群的意义。如果一台服务器挂了呢?并且每次转发也要多处理) - 服务器session复制
每次session发生变化(增删改)广播给所有集群中的服务器。使得所有服务器上session一致 - session共享
这个有点类似于第二种方式的进化版。想要共享每个客户端都存麻烦的很。直接把session提出来单独存,但是每个服务器都可以读取。这个用redis,memcached都可以。 - session持久化
这个其实目的和2,3一直。都是为了数据共享。只不过实现方式不同。都放到数据库,每个服务器都可以去读。
21. memcached和redis的区别?
这个是memcached的最后一道问答题。其实二者相似点很多,当然了肯定区别也是有的,下面一点点慢慢说
- redis不仅仅支持简单的k/v类型数据。还支持list。set,zset,hash等数据结构的存储。而memcached只支持简单的数据类型。需要客户端自己处理复杂对象
- redis支持数据的持久化(aof(文件追加)或者rdb(快照))
- 因为memcached不能持久化,所以down机所有数据丢失,redis则有一定的灾备机制。
- Memcached可以使用Magent在客户端进行一致性hash做分布式。而redis只能在服务端做分布式
- Memcached的限制就是简单的kv限制。比如key的最大长度250.时间最长30天。存储数据不能超过1MB(超过了要压缩或者自己分几个key存)这是典型的slab的最大值,不适合虚拟机使用。而redis的key最长可以支持到512k。
- Redis使用的是单线程模式。保证了数据按顺序提交。Memcached是使用cas保证数据一致性。
- cpu利用上:redis只是用单核。而Memcached可以使用多核。所以当数据小的时候一个核上的redis比多核的Memcached性能更高。但是在100k以上的数据中,Memcached性能要高于Redis。
-
Memcached的内存管理使用Slab Alloction。原理相当简单:预先分配一系列大小固定的组。然後根据数据大小选择最合适的块存储。避免了内存碎片。但是缺点是不能变长,会浪费一定的空间(我们可以想象饭店的包厢:4人房,8人房,12人房。20人房。进去一波客人根据自己的人数去选择房型。但是假如去了15个人也只能选20人房。这个房间就空了5个位置。算是浪费了。)Memcached默认情况下下一个slab的最大值为前一个的1.25倍。
而Redis的内存管理:Redis通过定义一个数组来记录所有的内存分配情况。Redis采用的是包装的malloc/free。相较于Memcached的内存管理方法来说要更简单。犹豫malloc首先以链表的方式搜索以管理的内存中可用的空间分配,可能导致内存碎片较多。(还是按照饭店来想象:这个没有包厢,就是一个大圆桌。贼大的圆桌。一起去吃饭的人去挨着坐就行了。但是问题来了。客人来来走走,如果出了偶尔有两个空位,偶尔有三个空位,偶尔有一个空位的情况,这个时候如果来了一波10个人。明明空位置还有,但是因为不挨着。所以这是五个人就不吃了!这个就是内存碎片多但是又无法合理的复用!)
大概Redis和Memcached的区别就这么些吧,主要是内存管理,持久化,cpu利用,存储的数据结构几个方面。
本篇笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注。也祝大家工作顺顺利利吧!愿所有的付出都有回报,愿所有的汗水都不白流!ps:最近正在求职,所以一直啃面试题。欢迎同样的小伙伴们加个好友互通有无哟!