其他资料
每日速记10道java面试题16-优快云博客
每日速记10道java面试题17-优快云博客
目录
9.Redis 中原生批处理命令(MSET、MGET)与 Pipeline 的区别是什么?
1.Redis主从同步中的增量和完全同步怎么实现?
完全同步:
- slave节点请求增量同步
- master节点判断runid,发现不一致,判断为第一次连接,拒绝增量同步,进行全量同步
- master调用bgsave命令将完整内存数据生成RDB,发送RDB到slave
- slave清空本地数据,加载master的RDB
- master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave
- slave执行接收到的命令,保持与master之间的同步
增量同步:
从节点也是发送psync命令给主节点,同时带上主节点的runid以及offset,主节点判断从节点传递过来的runid是否和主节点一致;如果一致,则根据ofset判断数据是否还在repl backlog buffer中,如果在,则通过offset进行定位,将offset之后的命令数据写入到复制缓冲区中;然后通过复制缓冲中区发给从节点,进行同步。如果offset判断对应的数据在repl backlog buffer不存在了,则进行完全同步。
2.要是主从架构中的主节点宕机了怎么办?
Redis的主从架构中实现了读写分离,在从节点读,在主节点写,然后数据同步到从节点,当主节点宕机了之后,就需要在从节点中选出一个节点作为主节点,这个过程可以由Redis的哨兵机制实现。
哨兵(Sentinel)其实是一个运行在特殊模式下的Redis进程,是属于一个观察者节点,观察的是主从节点,哨兵节点主要负责三件事:监控、选举、通知。
监控:
这里的监控就是哨兵会每隔一秒发送一个ping指令给所有节点,如果没收到对应节点的pong回应,那么这个节点就会被哨兵认为是主观下线。这时候这个哨兵节点会询问其他哨兵节点,如果哨兵集群中超过一定数量的哨兵都判断该节点是主观下线,那么此时这个节点就是客观下线。
如果客观下线的redis节点是从节点或者是Sentinel节点,则操作到此为止,没有后续的操作了;如果客观下线的redis节点为主节点,则开始故障转移,从从节点中选举一个节点升级为主节点。
选举:
Sentinel Leader选举:
判断主节点主观下线的Sentinel节点为候选人,此时他想成为Leader。如果有多个Sentinel 节点判断主节点主观下线,那么就有多个候选人。候选人会给自己先投一票,其他Sentinel节点会给候选人投一票,投票涉及到raft算法。为了避免平票,Sentinel 节点的数量一般会设置为奇数。
Redis主节点选举:
选出Sentinel Leader之后,由Sentinel Leader去从节点中选出主节点:
1.过滤故障的节点
2.选择优先级高(slave-priority最小)的从节点作为主节点,如不存在则继续
3.选择复制偏移量(数据写入量的字节,记录写了多少数据。主服务器会把偏移量同步给从服务器,当主从的偏移量一致,则数据是完全同步)最大的从节点作为主节点,如不存在则继续
4.选择runid(redis每次启动的时候生成随机的runid作为redis的标识)最小的从节点作为主节点
总结来说就是:先剔除下线的从节点,然后从从节点中选出优先级高、复制偏移量大、runid小的从节点作为主节点。
通知:
选好主节点之后,哨兵 leader 会让其他从节点全部成为新 master 节点的 slave 节点。最后利用 redis 的发布/订阅机制,把新主节点的 IP 和端口信息推送给客户端,此时主从切换就结束了
延伸→那“苏醒”过来的旧主节点怎么办?
会成为新主节点的从节点,并开始进行全量同步。
3.Redis集群的模式了解吗?讲一下?
主从和哨兵已经解决高可用、高并发读的问题,但仍然有海量数据存储和高并发写的问题没有解决,因此采用Redis集群作为这写问题的解决方案。
Redis集群中有多个master,而每个master又连接多个slave节点,master之间通过ping来检测彼此健康状态,客户端请求可以访问集群任意节点,最终都会被转发到正确节点去。
分片集群引入了哈希槽的概念,一共有16384个哈希槽,平均分配给不同的redis实例,读写的时候,将key值根据CRC 16算法计算得到一个16bit的值,将值向16384取余,得出来的结果就是该key在槽位中的位置。
优缺点如下:
优点:
高可用性:节点之间采用主从复制,即使集群中一个节点挂掉了,仍然不影响集群的工作。
高性能:redis集群读写效率高,当数据比较大的时候就可以考虑搭建集群。
缺点:
部署和维护起来比较复杂,要考虑很多配置。
集群同步问题:通过某个节点出现故障或者网络故障,集群中数据同步的问题也会出现,随着节点数量越来越多,会带来一定的读写延迟。
4.redis应用场景是什么?
数据库、缓存、排行版、分布式锁、计数器、轻量级消息队列。
具体内容请看我的另一篇文章:最适合使用Redis作为解决方案的几个应用场景-优快云博客
5.Redis支持并发操作吗?
单个 Redis 命令的原子性:Redis 的单个命令是原子性的,这意味着一个命令要么完全执行成功,要么完全不执行,确保操作的一致性。这对于并发操作非常重要。
多个操作的事务:Redis 支持事务,可以将一系列的操作放在一个事务中执行,使用 MULTI、EXEC、DISCARD 和 WATCH 等命令来管理事务。这样可以确保一系列操作的原子性。
6.什么是big key?
Redis大key问题指的是某个key对应的value值所占的内存空间比较大,导致Redis的性能下降、内存不足、数据不均衡以及主从同步延迟等问题。
到底多大的数据量才算是大key?
没有固定的判别标准,通常认为字符串类型的key对应的value值占用空间大于1M,,或者集合类型的元素数量超过1万个,就算是大key。
延伸→big key带来的缺点是什么?
- 内存占用过高。大Key占用过多的内存空间,可能导致可用内存不足,从而触发内存淘汰策略。在极端情况下,可能导致内存耗尽,Redis实例崩溃,影响系统的稳定性。
- 性能下降。大Key会占用大量内存空间,对于大Key的操作,如读取、写入、删除等,都会消耗更多的CPU时间和内存资源,进一步降低系统性能。
- 阻塞其他操作。某些对大Key的操作可能会导致Redis实例阻塞。例如,使用DEL命令删除一个大Key时,可能会导致Redis实例在一段时间内无法响应其他客户端请求,从而影响系统的响应时间和吞吐量。
- 网络拥塞。每次获取大key产生的网络流量较大,可能造成机器或局域网的带宽被打满,同时波及其他服务。例如:一个大key占用空间是1MB,每秒访问1000次,就有1000MB的流量。
延伸→怎么解决big key?
- 对大Key进行拆分。例如将含有数万成员的一个HASH Key拆分为多个HASH Key,并确保每个Key的成员数量在合理范围。在Redis集群架构中,拆分大Key能对数据分片间的内存平衡起到显著作用。
- 对大Key进行清理。将不适用Redis能力的数据存至其它存储,并在Redis中删除此类数据。注意,要使用异步删除。
7.Redis 中的缓存击穿、缓存穿透和缓存雪崩是什么?
缓存击穿:指某个热点数据在缓存中失效,导致大量请求直接访问数据库。此时,由于瞬间的高并发,可能导致数据库崩溃。
缓存穿透:指查询一个不存在的数据,缓存中没有相应的记录,每次请求都会去数据库查询,造成数据库负担加重。
缓存雪崩:指多个缓存数据在同一时间过期,导致大量请求同时访问数据库,从而造成数据库瞬间负载激增。
缓存击穿解决方案:
- 互斥锁方案,保证同一时间只有一个业务线程更新缓存,未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。
- 不给热点数据设置过期时间,由后台异步更新缓存,或者在热点数据准备要过期前,提前通知后台线程更新缓存以及重新设置过期时间;
缓存穿透解决方案:
- 非法请求的限制:当有大量恶意请求访问不存在的数据的时候,也会发生缓存穿透,因此在 API 入口处我们要判断求请求参数是否合理,请求参数是否含有非法值、请求字段是否存在,如果判断出是恶意请求就直接返回错误,避免进一步访问缓存和数据库。
- 缓存空值或者默认值:当我们线上业务发现缓存穿透的现象时,可以针对查询的数据,在缓存中设置个空值或者默认值,这样后续请求就可以从缓存中读取到空值或者默认值,返回给应用,而不会继续查询数据库。
- 布隆过滤器:我们可以在写入数据库数据时,使用布隆过滤器做个标记,然后在用户请求到来时,业务线程确认缓存失效后,可以通过查询布隆过滤器快速判断数据是否存在,如果不存在,就不用通过查询数据库来判断数据是否存在。即使发生了缓存穿透,大量请求只会查询 Redis 和布隆过滤器,而不会查询数据库,保证了数据库能正常运行,Redis 自身也是支持布隆过滤器的。
缓存雪崩解决方案:
- 均匀设置过期时间:如果要给缓存数据设置过期时间,应该避免将大量的数据设置成同一个过期时间。给这些数据的过期时间加上一个随机数,这样就保证数据不会我们可以在对缓存数据设置过期时间时,在同一时间过期。
- 互斥锁:当业务线程在处理用户请求时,如果发现访问的数据不在 Redis 里,就加个互斥锁,保证同一时间内只有一个请求来构建缓存(从数据库读取数据,再将数据更新到 Redis 里),当缓存构建完成后,再释放锁。未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。实现互斥锁的时候,最好设置超时时间,不然第一个请求拿到了锁,然后这个请求发生了某种意外而一直阻塞,一直不释放锁,这时其他请求也一直拿不到锁,整个系统就会出现无响应的现象。
- 后台更新缓存:业务线程不再负责更新缓存,缓存也不设置有效期,而是让缓存“永久有效”,并将更新缓存的工作交由后台线程定时更新。
8.Redis 中如何保证缓存与数据库的数据一致性?
- 先更新数据库,再删除缓存,后续等查询把数据库的数据回种到缓存中
- 缓存双删策略:更新数据库之前,删除一次缓存,更新完数据库后,再进行一次延迟删除
- 使用 Binlog 异步更新缓存,监听数据库的 Binlog 变化,通过异步方式更新 Redis 缓存
- 如果是要考虑实时一致性的话,先写 MySQL,再删除 Redis 应该是较为优的方案,虽然短期内数据可能不一致,不过其能尽量保证数据的一致性。
- 如果考虑最终一致性的话,推荐的是使用 binlog +消息队列的方式,这个方案其有重试和顺序消费,能够最大限度地保证缓存与数据库的最终一致性。
9.Redis 中原生批处理命令(MSET、MGET)与 Pipeline 的区别是什么?
mset和mget是原生命令,用于一次set和get多个键值对。是单个命令。原子性。
pipeline是一种机制,用于在一次网络连接中,发送多个命令到服务端执行。是机制。能减少网络往返时间。非原子性。
10.如何在 Redis 中实现队列和栈数据结构?
可以通过 List 类型 来实现 队列 和 栈
实现队列(FIFO)
- 队列是一种 先进先出(FIFO)的数据结构。在 Redis中,可以使用 LPUSH和 RPOP命令组合来实现队列。
- LPUSH 向列表的左侧推入元素,而 RPOP从列表的右侧弹出元素,这样可以保证最先进入的元素最先被弹出。
实现栈(LIFO):
- 栈是一种 后进先出(LIFO)的数据结构。在Redis中,可以使用LPUSH和 RPOP命令组合来实现栈.
- LPUSH 向列表的左侧推入元素,而 LPOP从列表的左侧弹出元素,这样可以保证最后进入的元素最先被弹出。