Java面试——缓存
一、什么是缓存
【1】缓存就是数据交换的缓冲区(称作:Cache),当某一硬件要读取数据时,会首先从缓存中查询数据,有则直接执行,不存在时从磁盘中获取。由于缓存的数据比磁盘快的多,所以缓存的作用就是帮助硬件更快的运行。
【2】缓存往往使用的是RAM(断电既掉的非永久存储),所以在用完后还是会把文件送到硬盘等存储器中永久存储。电脑中最大缓存就是内存条,硬盘上也有16M或者32M的缓存。
【3】高速缓存是用来协调CPU与主存之间存取速度的差异而设置的。一般CPU工作速度高,但内存的工作速度相对较低,为了解决这个问题,通常使用高速缓存,高速缓存的存取速度介于CPU与主存之间。系统将一些CPU在最近几个时间段经常访问的内容存在高速缓存,这样就在一定程度上缓解了由于主存速度低造成的CPU“停工待料”的情况。
【4】缓存就是把一些外存上的数据保存在内存上而已,为什么保存在内存上,我们运行的所有程序里面的变量都是存放在内存中的,所以如果想将值放入内存上,可以通过变量的方式存储。在JAVA中一些缓存一般都是通过Map集合来实现的。
▁▂▃▅▆ :缓存在不同的场景下,作用是不一样的具体举例说明:
✔ 操作系统磁盘缓存 ——> 减少磁盘机械操作。
✔ 数据库缓存——>减少文件系统IO。
✔ 应用程序缓存——>减少对数据库的查询。
✔ Web服务器缓存——>减少应用服务器请求。
✔ 客户端浏览器缓存——>减少对网站的访问。
二、常见的缓存策略有哪些
【1】由于不同系统的数据访问模式不同,同一种缓存策略很难在不同的数据访问模式下取得满意的性能,研究人员提出不同缓存策略以适应不同的需求。缓存策略的分类:
1)、基于访问的时间:此类算法按各缓存项被访问时间来组织缓存队列,决定替换对象。如 LRU;
2)、基于访问频率:此类算法用缓存项的被访问频率来组织缓存。如 LFU、LRU2、2Q、LIRS;
3)、访问时间与频率兼顾:通过兼顾访问时间和频率。使得数据模式在变化时缓存策略仍有较好性能。如 FBR、LRUF、ALRFU。多数此类算法具有一个可调或自适应参数,通过该参数的调节使缓存策略在基于访问时间与频率间取得一个平衡;
4)、基于访问模式:某些应用有较明确的数据访问特点,进而产生与其相适应的缓存策略。如专用的 VoD 系统设计的A&L缓存策略,同时适应随机、顺序两种访问模式的 SARC策略;
【2】数据不一致性产生的原因: 1)、先操作缓存,再写数据库成功之前,如果有读请求发生,可能导致旧数据入缓存,引发数据不一致。在分布式环境下,数据的读写都是并发的,一个服务多机器部署,对同一个数据进行读写,在数据库层面并不能保证完成顺序,就有可能后读的操作先完成(读取到的是脏数据),如果不采用给缓存设置过期时间策略,该数据永远都是脏数据。
【解决办法】:①、可采用更新前后双删除缓存策略;②、可以通过“串行化”解决,保证同一个数据的读写落在同一个后端服务上;
2)、先操作数据库,再清除缓存。如果删缓存失败了,就会出现数据不一致问题。
【方案一】:将删除失败的 key 值存入队列中重复删除,如下图:
三、如何防止缓存穿透、缓存击穿、缓存雪崩和缓存刷新。
【1】缓存穿透:缓存穿透是说收到一个请求,但是该请求缓存中不存在,只能去数据库中查询,然后放进缓存。但当有好多请求同时访问同一个数据时,业务系统把这些请求全发到了数据库;或者恶意构造一个逻辑上不存在的数据,然后大量发送这个请求,这样每次都会被发送到数据库,最终导致数据库挂掉。
【解决的办法】:对于恶意访问,一种思路是先做校验,对恶意数据直接过滤掉,不要发送至数据库层;第二种思路是缓存空结果,就是对查询不存在的数据也记录在缓存中,这样就可以有效的减少查询数据库的次数。非恶意访问,结合缓存击穿说明。
【2】缓存击穿:上面提到的某个数据没有,然后好多请求查询数据库,可以归为缓存击穿的范畴:对于热点数据,当缓存失效的一瞬间,所有的请求都被下放到数据库去请求更新缓存,数据库被压垮。
【解决的办法】:防范此类问题,一种思路是加全局锁,就是所有访问某个数据的请求都共享一个锁,获得锁的那个才有资格去访问数据库,其他线程必须等待。但现在大部分系统都是分布式的,本地锁无法控制其他服务器也等待,所以要用到全局锁,比如 Redis的 setnx实现全局锁。另一种思想是对即将过期的数据进行主动刷新,比如新起一个线程轮询数据,或者比如把所有的数据划分为不同的缓存区间,定期分区间刷新数据。第二个思路与缓存雪崩有点关系。
【3】缓存雪崩:缓存雪崩是指当我们给所有的缓存设置了同样的过期时间,当某一时刻,整个缓存的数据全部过期了,然后瞬间所有的请求都被抛向了数据库,数据库就崩掉了。
【解决的办法】:解决思路要么是分治,划分更小的缓存区间,按区间过期;要么给每个 key的过期时间加一个随机值,避免同时过期,达到错峰刷新缓存的目的。
对于 Redis 挂掉了,请求全部走数据库,也属于缓存雪崩,我们可以有以下思路进行解决:
事发前:实现 Redis 的高可用(主从架构+Sentinel 或者 Redis Cluster),尽可能避免 Redis 挂掉这种情况。
事发中:万一 Redis 真的挂了,我们可以设置本地缓存(ehcache)+ 限流(hystrix),尽量避免我们的数据库被干掉。
事发后:Redis 持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。
【4】缓存刷新:既清空缓存 ,一般在 Insert、Update、Delete 操作后就需要刷新缓存,如果不执行就会出现脏数据。但当缓存请求的系统蹦掉后,返回给缓存的值为null。
四、Redis内存用完会发生什么
如果达到设置的上限,Redis 的写命令会返回错误信息(但是读命令还是可以正常返回),或者将 Redis 当缓存使用,配置缓存淘汰机制,当 Redis 达到内存的上线时会冲掉旧的数据。也会将value的数据通过 swap机制同步到磁盘进行存储。但是 key的之不能使用 swap机制。如果key也很多的话,可以通过压缩记性存储。
五、Redis 的 List 结构相关的操作
【1】PUSH操作:是从队列头部和尾部增加节点的操作。
①、RPUSH KEY VALUE [VALUE ...] :从队列的右端入队一个或者多个数据,如果 key值不存在,会自动创建一个空的列表。如果对应的 key不是一个List,则会返回一个错误。
②、LPUSH KEY VALUE [VALUE...] :从队列的左边入队一个或多个元素。复杂度O(1)。
③、RPUSHX KEY VALUE:从队列的右边入队一个元素,仅队列存在时有效,当队列不存在时,不进行任何操作。
④、LPUSHX KEY VALUE:从队列的左边入队一个元素,仅队列存在时有效。当队列不存在时,不进行任何操作。
【2】POP操作:获取并删除头尾节点的操作。
①、LPOP KEY:从队列左边出队一个元素,复杂度O(1)。如果list为空,则返回nil。
②、RPOP KEY:从队列的右边出队一个元素,复杂度O(1)。如果list为空,则返回nil。
③、BLPOP KEY[KEY...] TIMEOUT:删除&获取KEY中最左边的第一个元素,当队列为空时,阻塞TIMEOUT时间,单位是秒(这个时间内尝试获取KEY中的数据),超过TIMEOUT后如果仍未数据则返回(nil)。
1 redis> BLPOP queue 1 2 (nil) 3 (1.10s)
④、BRPOP KEY[KEY...] TIMEOUT:删除&获取KEY中最后一个元素,或阻塞TIMEOUT。如上↑
【3】POP and PUSH
①、RPOPLPUSH KEY1 KEY2:删除KEY1中最后一个元素,将其追加到KEY2的最左端。
②、BRPOPLPUSH KEY1 KEY2 TIMEOUT:弹出KEY1列表的值,将它推到KEY2列表,并返回它;或阻塞TIMEOUT时间,直到有一个可用。
【4】其他
①、LLEN KEY:获取队列(List)的长度。
②、LRANG KEY START STOP:从列表中获取指定(START-STOP)长度的元素。负数表示从右向左数。需要注意的是,超出范围的下标不会产生错误:如果start>end,会得到空列表,如果end超过队尾,则Redis会将其当做列表的最后一个元素。
④、LSET KEY INDEX:重置队列中 INDEX位置的值。当 index越界的时候,这里会报异常。
⑤、LREM KEY COUNT VALUE:从列表中删除COUNT个VALUE元素。COUNT参数有三种情况:
☛ count > 0: 表示从头向尾(左到右)移除值为value的元素。
☛ count < 0: 表示从尾向头(右向左)移除值为value的元素。
☛ count = 0: 表示移除所有值为value的元素。
⑥、LTRIM KEY START STOP:修剪到指定范围内的清单,相当与截取,只保留START-STOP之间的数据。
六、Redis的数据结构都有哪些
【1】String:可以是字符串,整数或者浮点数,对整个字符串或者字符串中的一部分执行操作,对整个整数或者浮点执行自增(increment)或者自减(decrement)操作。
【2】List:一个链表,链表上的每个节点都包含了一个字符串,链表的两端推入或者弹出元素,根据偏移量对链表进行修剪(trim),读取单个或者多个元素,根据值查找或者移除元素。可参考5
【3】Set:包含字符串的无序收集器(unordered collection)、并且被包含的每个字符串都是独一无二的。添加,获取,移除单个元素,检查一个元素是否存在于集合中,计算交集(sinter),并集(suion),差集(sdiff),从集合里面随机获取元素。
【4】SortSet:是一个排好序的 Set,它在 Set 的基础上增加了一个顺序属性 score,这个属性在添加修改元素时可以指定,每次指定后,SortSet 会自动重新按新的值排序。sorted set 的内部使用 HashMap 和跳跃表(SkipList)来保证数据的存储和有序,HashMap 里放的是成员到 score 的映射,而跳跃表里存放的是所有的成员,排序依据是 HashMap 里存的 score。
使用阶段我们从数据存储和数据获取两个方面来说明开发时的注意事项:
【1】数据存储:因为内存空间的局限性,注定了能存储的数据量有限,如何在有限的空间内存储更多的数据信息是我们应该关注的。Redis内存储的都是键值对,那么如何减小键值对所占据的内存空间就是空间优化的本质。在能清晰表达业务含义的基础上尽可能缩减 Key的字符长度,比如一个键是user:{id}:logintime ,可以使用业务属性的简写来u:{id}:lgt,只要能清晰表达业务意义,使用简写形式是有其必要性的。在不影响使用的情况下,缩减Value的数据大小。如果Value是较大的数据信息,比如图片,大文本等,可以使用压缩工具压缩过后再存入Redis;如果Value是对象序列化或者gson信息,可以考虑去除非必要的业务属性。
减少键值对的数量,对于大量的String类型的小对象,可以尝试使用Hash的形式组合他们,在Hash对象内Field数量少于1000,且Value的字符长度小于40时,内部使用ziplist的编码形式,能够极大的降低小对象占据的内存空间。
Redis内维护了一个[0-9999]的整数对象池,类似Java内的运行时常量池,只创建一个常量,使用时都去引用这个常量,所以当存储的value是这个范围内的数字时均是引向一个内存地址,所以能够降低一些内存空间耗费。但是共享对象池和maxmemory+LRU的内存回收策略冲突,因为共享Value对象的lru值也共享,难以通过lru知道哪个Key的最后引用时间,所以永远也不能回收内存。如果多次数据操作要求原子性,可使用Multi来实现Redis的事务。
【2】数据查询:Redis 是一种数据库,和其他数据库一样,操作时也需要有连接对象,连接对象的创建和销毁也需要耗费资源,复用连接对象很有必要,所以推荐使用连接池来管理连接。Redis数据存储在内存中,查询很快,但不代表连接也很快。一次Redis查询可能IO部分占据了请求时间的绝大部分比例,缩短IO时间是开发过程中很需要注意的一点。对于一个业务内的多次查询,考虑使用Pipeline,将多次查询合并为一次查询,命令会被执行多次,但是只有一个IO传输,能够有效的提高响应速度。
对于多次String类型的查询,使用mget,将多次请求合并为一次,同时命令和会被合并为一次,能有效提高响应速度,对于Hash内多个Field查询,使用hmget,起到和mget同样的效果。Redis是单线程执行的,也就是说同一时间只能执行一条命令,如果一条命令执行的时间较长,其他线程在此期间均会被阻塞,所以在操作Redis时要注意操作指令的涉及的数据量,尽量降低单次操作的执行时间。
【持久化方式】:RDB 时间点快照 AOF 记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。可参考14深度解析
【内存设置】:maxmemory used_memory
【虚拟内存】: vm-enabled yes
【集群的应用和优劣势】:参考9
【内存优化】:链接
【淘汰策略】:链接
八、
九、
十、Memcache 的原理
首先要说明一点,MemCache 的数据存放在内存中,存放在内存中个人认为意味着几点:
【1】访问数据的速度比传统的关系型数据库要快,因为 Oracle、MySQL 这些传统的关系型数据库为了保持数据的持久性,数据存放在硬盘中,IO操作速度慢;
【2】MemCache 的数据存放在内存中同时意味着只要 MemCache 重启了,数据就会消失;
【3】既然 MemCache 的数据存放在内存中,那么势必受到机器位数的限制,这个之前的文章写过很多次了,32位机器最多只能使用2GB的内存空间,64位机器可以认为没有上限。
MemCache 的原理:MemCache 最重要的莫不是内存分配的内容了,MemCache 采用的内存分配方式是固定空间分配,还是自己画一张图说明:
十一、Redis 和 Memcached 的内存管理的区别
在 Redis中,并不是所有的数据都一直存储在内存中的。这是和 Memcached相比一个最大的区别。当物理内存用完时,Redis可以将一些很久没用到的 value交换到磁盘,Redis只会缓存所有的 key的信息。如果 Redis发现内存的使用量超过了某一个阀值,将触发 swap的操作,Redis根据 “swappability = age*log(size_in_memory)”计算出哪些 key对应的 value需要 swap到磁盘。然后再将这些 key对应的 value持久化到磁盘中,同时在内存中清除。这种特性使得 Redis可以保持超过其机器本身内存大小的数据。当然,机器本身的内存必须要能够保持所有的key,毕竟这些数据是不会进行 swap操作的。同时由于 Redis将内存中的数据swap到磁盘中的时候,提供服务的主线程和进行 swap操作的子线程会共享这部分内存,所以如果更新需要 swap的数据,Redis将阻塞这个操作,直到子线程完成 swap操作后才可以进行修改。当从 Redis中读取数据的时候,如果读取的 key对应的 value不在内存中,那么 Redis就需要从 swap文件中加载相应数据,然后再返回给请求方。 这里就存在一个 I/O线程池的问题。在默认的情况下,Redis会出现阻塞,即完成所有的 swap文件加载后才会相应。这种策略在客户端的数量较小,进行批量操作的时候比较合适。但是如果将 Redis应用在一个大型的网站应用程序中,这显然是无法满足大并发的情况的。所以 Redis运行我们设置 I/O线程池的大小,对需要从 swap文件中加载相应数据的读取请求进行并发操作,减少阻塞的时间。
对于像 Redis和 Memcached这种基于内存的数据库系统来说,内存管理的效率高低是影响系统性能的关键因素。传统C语言中的malloc/free函数是最常用的分配和释放内存的方法,但是这种方法存在着很大的缺陷:首先,对于开发人员来说不匹配的malloc和free容易造成内存泄露;其次频繁调用会造成大量内存碎片无法回收重新利用,降低内存利用率;最后作为系统调用,其系统开销远远大于一般函数调用。所以,为了提高内存的管理效率,高效的内存管理方案都不会直接使用malloc/free调用。Redis和Memcached均使用了自身设计的内存管理机制,但是实现方法存在很大的差异,下面将会对两者的内存管理机制分别进行介绍。
Memcached默认使用 Slab Allocation机制管理内存,其主要思想是按照预先规定的大小,将分配的内存分割成特定长度的块以存储相应长度的key-value数据记录,以完全解决内存碎片问题。Slab Allocation机制只为存储外部数据而设计,也就是说所有的key-value数据都存储在Slab Allocation系统里,而 Memcached的其它内存请求则通过普通的 malloc/free来申请,因为这些请求的数量和频率决定了它们不会对整个系统的性能造成影响 Slab Allocation的原理相当简单。 如图所示,它首先从操作系统申请一大块内存,并将其分割成各种尺寸的块Chunk,并把尺寸相同的块分成组Slab Class。其中,Chunk就是用来存储key-value数据的最小单位。每个Slab Class的大小,可以在 Memcached启动的时候通过制定 Growth Factor来控制。假定图中 Growth Factor的取值为1.25,如果第一组 Chunk的大小为88个字节,第二组 Chunk的大小就为112个字节,依此类推。
当Memcached接收到客户端发送过来的数据时首先会根据收到数据的大小选择一个最合适的Slab Class,然后通过查询Memcached保存着的该Slab Class内空闲Chunk的列表就可以找到一个可用于存储数据的Chunk。当一条数据库过期或者丢弃时,该记录所占用的Chunk就可以回收,重新添加到空闲列表中。从以上过程我们可以看出Memcached的内存管理制效率高,而且不会造成内存碎片,但是它最大的缺点就是会导致空间浪费。因为每个Chunk都分配了特定长度的内存空间,所以变长数据无法充分利用这些空间。如图 所示,将100个字节的数据缓存到128个字节的Chunk中,剩余的28个字节就浪费掉了。
十二、Redis 的并发竞争问题如何解决,了解 Redis 事务的 CAS 操作吗
Redis 为单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis 本身没有锁的概念,Redis 对于多个客户端连接并不存在竞争,但是在 Jedis 客户端对 Redis 进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客户端连接混乱造成。对此有2种解决方法:
【1】客户端角度,为保证每个客户端间正常有序与 Redis 进行通信,对连接进行池化,同时对客户端读写 Redis 操作采用内部锁 synchronized。
【2】服务器角度,利用 setnx 实现锁。MULTI,EXEC,DISCARD,WATCH 四个命令是 Redis 事务的四个基础命令。其中:
☆ MULTI,告诉 Redis 服务器开启一个事务。注意,只是开启,而不是执行
☆ EXEC,告诉 Redis 开始执行事务
☆ DISCARD,告诉 Redis 取消事务
☆ WATCH,监视某一个键值对,它的作用是在事务执行之前如果监视的键值被修改,事务会被取消。
【Redis 事务机制】:链接
【CAS 操作】:链接
十三、Redis 的选举算法和流程是怎样的
Raft 采用心跳机制触发 Leader 选举。系统启动后,全部节点初始化为 Follower,term 为0。节点如果收到了 RequestVote 或者AppendEntries,就会保持自己的 Follower 身份。如果一段时间内没收到 AppendEntries 消息直到选举超时,说明在该节点的超时时间内还没发现 Leader,Follower 就会转换成 Candidate,自己开始竞选 Leader。一旦转化为 Candidate,该节点立即开始下面几件事情:
1)、增加自己的term。
2)、启动一个新的定时器。
3)、给自己投一票。
4)、向所有其他节点发送RequestVote,并等待其他节点的回复。
✔ 如果在这过程中收到了其他节点发送的AppendEntries,就说明已经有 Leader产生,自己就转换成Follower,选举结束。
✔ 如果在计时器超时前,节点收到多数节点的同意投票,就转换成Leader。同时向所有其他节点发送 AppendEntries,告知自己成为了Leader。
✔ 每个节点在一个 term内只能投一票,采取先到先得的策略,Candidate前面说到已经投给了自己,Follower会投给第一个收到 RequestVote的节点。每个 Follower有一个计时器,在计时器超时时仍然没有接受到来自 Leader的心跳RPC, 则自己转换为Candidate, 开始请求投票,就是上面的的竞选 Leader步骤。
✔ 如果多个Candidate发起投票,每个Candidate都没拿到多数的投票(Split Vote),那么就会等到计时器超时后重新成为Candidate,重复前面竞选Leader步骤。
✔ Raft协议的定时器采取随机超时时间,这是选举Leader的关键。每个节点定时器的超时时间随机设置,随机选取配置时间的1倍到2倍之间。由于随机配置,所以各个Follower同时转成Candidate的时间一般不一样,在同一个term内,先转为Candidate的节点会先发起投票,从而获得多数票。多个节点同时转换为Candidate的可能性很小。即使几个Candidate同时发起投票,在该term内有几个节点获得一样高的票数,只是这个term无法选出Leader。由于各个节点定时器的超时时间随机生成,那么最先进入下一个term的节点,将更有机会成为Leader。连续多次发生在一个term内节点获得一样高票数在理论上几率很小,实际上可以认为完全不可能发生。一般1-2个term类,Leader就会被选出来。
【Sentinel 的选举流程】:Sentinel 集群正常运行的时候每个节点 epoch 相同,当需要故障转移的时候会在集群中选出 Leader执行故障转移操作。Sentinel采 用了Raft 协议实现了 Sentinel 间选举 Leader 的算法,不过也不完全跟论文描述的步骤一致。Sentinel 集群运行过程中故障转移完成,所有 Sentinel 又会恢复平等。Leader 仅仅是故障转移操作出现的角色。
【选举流程】:1)、某个 Sentinel 认定 master 客观下线的节点后,该 Sentinel 会先看看自己有没有投过票,如果自己已经投过票给其他 Sentinel 了,在2倍故障转移的超时时间自己就不会成为 Leader。相当于它是一个 Follower。
2)、如果该 Sentinel 还没投过票,那么它就成为 Candidate。
3)、和 Raft 协议描述的一样,成为 Candidate,Sentinel 需要完成几件事情。
【1】更新故障转移状态为start
【2】当前epoch加1,相当于进入一个新term,在Sentinel中epoch就是Raft协议中的term。
【3】更新自己的超时时间为当前时间随机加上一段时间,随机时间为1s内的随机毫秒数。
【4】向其他节点发送is-master-down-by-addr命令请求投票。命令会带上自己的epoch。
【5】给自己投一票,在 Sentinel 中,投票的方式是把自己 master 结构体里的 leader 和 leader_epoch 改成投给的 Sentinel 和它的 epoch。
4)、其他Sentinel会收到Candidate的is-master-down-by-addr命令。如果Sentinel当前epoch和Candidate传给他的epoch一样,说明他已经把自己master结构体里的leader和leader_epoch改成其他Candidate,相当于把票投给了其他Candidate。投过票给别的Sentinel后,在当前epoch内自己就只能成为Follower。
5)、Candidate会不断的统计自己的票数,直到他发现认同他成为Leader的票数超过一半而且超过它配置的quorum(quorum可以参考《redis sentinel设计与实现》)。Sentinel比Raft协议增加了quorum,这样一个Sentinel能否当选Leader还取决于它配置的quorum。
6)、如果在一个选举时间内,Candidate没有获得超过一半且超过它配置的quorum的票数,自己的这次选举就失败了。
7)、如果在一个epoch内,没有一个Candidate获得更多的票数。那么等待超过2倍故障转移的超时时间后,Candidate增加epoch重新投票。
8)、如果某个Candidate获得超过一半且超过它配置的quorum的票数,那么它就成为了Leader。
9)、与Raft协议不同,Leader并不会把自己成为Leader的消息发给其他Sentinel。其他Sentinel等待Leader从slave选出master后,检测到新的master正常工作后,就会去掉客观下线的标识,从而不需要进入故障转移流程。
十四、Redis 的持久化的机制,AOF和RDB的区别
Redis的持久化机制:Redis 提供两种方式进行持久化,一种是RDB持久化(原理是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化),另外一种是AOF(append only file)持久化(原理是将Reids的操作日志以追加的方式写入文件)。
AOF和RDB的区别:RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。rdb这个就更有些 eventually consistent的意思了。
十五、缓存预热
新的缓存系统没有任何数据,在缓存重建数据的过程中,系统性能和数据负载都不太好,所以最好在系统上线之前就把缓存的热点数据加载到缓存中,这种缓存预加载手段就是缓存预热。
十六、缓存热备
缓存热备既当一个缓存服务器不可用时能实时切换到备用缓存服务器,不影响缓存使用。集群模式下,每个主节点都会有一个或多个从节点备用,一旦主节点挂掉,从节点会被哨兵提升为主节点使用。
十七、Redis 集群同步数据
【参考博客】:链接
十八、知道哪些 Redis 的优化操作
【参考博客】:链接
十九、Reids 的主从复制机制原理
【参考博客】:链接
二十、Redis 的线程模型是什么
【参考博客】:链接
二十一、本地缓存与分布式缓存区别
本地缓存的优势是没有网络开销,在大并发量时用好本地缓存很重要;分布式缓存比如Redis 优势是能够无限扩容量和多个系统公用缓存数据,结合这个去在业务中使用缓存是很重要的。
本地缓存的缺点是会占用堆内存,影响垃圾回收、影响系统性能。分布式缓存两大开销(网络延迟和对象序列化)会导致其慢于本地缓存,同时也需要搭建分布式缓存系统。
建议:进程内缓存适用于较小且频率可见的访问场景,尤其适用于不变对象,对于较大且不可预见的访问,最好采用分布式缓存。