1. Redis主从复制的常见拓扑结构有哪些
- 一主多从:这是最基本的拓扑结构,包含一个主节点和多个从节点,所有写操作都在主节点上执行,而读操作可以在从节点上进行,以提高读取速度和负载均衡。
- 树状主从结构:从节点也可以作为其他从节点的主节点,这样形成了一个层次结构,主节点负责写操作,而从节点负责读操作,并将数据再次复制到更下一级的从节点。
- 主主结构(双主或者多主)
在这种拓扑中,有两个或者多个主节点,他们之间相互复制数据,这种结构提高了系统的写能力和容错性。
2. List介绍
- 常见命令
lpush:将一个或者多个值插入到列表头部,列表不存在,一个新的列表会被创建。
rpush:将一个或者多个值插入到列表尾部
lpop:移除并返回列表头部的元素
rpop:移除并返回列表尾部的元素
lrange:获取列表指定范围内的元素
lindex:通过索引获取列表中的元素
llen:获取列表长度
lset:将列表中指定索引的元素设置为另一个值
lrem:移除列表中与参数匹配的元素 lrem mylist 1 a # 从mylist中移除第一个a
ltrim:修剪一个已存在的list,使其只包含指定范围的元素。 ltrim mylist 1 2 # 保留mylist中索引从1到2的元素。
- 列表的存储结构
Redis List类型的底层实现有两种数据结构,Redis会根据列表的长度和每个元素的大小自动选择使用哪种结构。- 双向链表:当列表元素较多时,使用双向链表实现,便于从两端快速添加和删除。
- 压缩列表:当列表元素较少时且总大小较小时,使用内存更节省的压缩列表实现。
- List使用场景
- 消息队列:Redis的LPUSH和RPOP组合可以用来实现生产者——消费者模型,将LPUSH用于生产者,RPOP用于消费者,这种方式可以实现简单的消息队列。
- 任务调度:可以使用LPUSH和BRPOP来实现任务调度,将任务放入队列中,消费者通过阻塞方式从队列中取任务进行处理
- 聊天记录:可以使用Redis List存储用户聊天记录。
- 列表操作的性能问题
- 大列表操作:当列表非常大时,lrange,lrem可能会导致Redis阻塞,因为Redis是单线程的,因此,对大列表的操作应该尽量避免。
- 列表裁剪:使用ltrim命令对列表进行裁剪,以控制列表的大小,避免列表无限增长导致内存占用过高。例如,ltrim mylist 0 99,只保留前100个元素。
3. Redis中ZipList和Quicklist的特点是什么
Ziplist
- 简单、紧凑、连续存储,适用于小数据量场景,但是对大量数据或者频繁的修改操作不太友好。
- 适合小数据量存储,例如 短列表、小哈希表等,因为他的内存紧凑,可以大幅减少内存使用
Quicklist
- 通过将链表和ziplist结合,既实现了链表的灵活操作,又能节省内存。Redis 3.2之后成为List的默认实现。
- Quicklist是为了替代纯链表设计的,
适用于需要频繁的对列表进行插入、删除、查找等操作的场景,并且数据量可能较大,他在存储多个元素时,既保留了链表的灵活性,又具备压缩列表的内存优势。
4. Redis复制延迟的原因有哪些
Redis的复制延迟是指从节点同步主节点数据时可能出现时间延迟。在读写分离场景,这个延迟会导致写入了数据,但去从节点查询的时候无法查到。
可能原因
- 网络原因
- 主节点负载过高:主节点接收到大量的写操作,在处理客户端请求的同时,还需向从节点发送复制数据。如果主节点负载较高时,来不及处理从节点复制的请求,就会导致复制延迟。
- 复制缓冲区溢出
- 主节点持久化,无法及时响应复制请求:生成RDB快照或者AOF文件重写都会占用大量的CPU和IO资源,可能会影响复制的速度。
5. Redis Cluster模式和Sentinel模式的区别
Redis Cluster是Redis集群,提供自动分片功能
,将数据自动分布在多个节点上,支持自动故障转移。如果一个节点失败,集群会自动重新配置和平衡,不需要外部介入。- Sentinel是哨兵,用于管理多个Redis服务器实例来提高数据的高可用性。
当主节点宕机,哨兵会将从节点提升为主节点,他不提供数据分片功能。
如果需要处理大量数据并进行数据分片,应该选择Redis Cluster,他支持水平扩展,适用于大规模数据、高吞吐量场景。
如果只是为了提高Redis实例的可用性,并不需要数据分片,应该选择主从+sentinel,他主要关注故障转移和实例高可用。适用于高可用性、读写分离场景。
6. 在Redis集群中,如何根据键定位到对应的节点
Redis集群将数据分布到16384个哈希槽(slots)中,每个键通过哈希函数计算出一个槽位编号,然后根据槽位编号定位到具体的节点,具体是使用CRC16哈希函数计算键的哈希值,然后对16384取模,得到哈希槽编号。
1. Redis集群节点的通信
Redis集群节点在一开始的时候,只知道自己的槽位,不知道其他节点的槽位。他们之间会通过Gossip协议使用ping命令和pong响应进行通信,彼此之间交换槽位的信息。这样之后,Redis集群的节点之间就互相知道彼此的槽位。
2. 访问Redis集群时,如何得到key所对应的节点
Redis客户端通过key访问数据的时候,可以将请求打在Redis集群中的任意一个节点
- Redis客户端使用CRC16算法计算出key的哈希值,然后将哈希值对16384取模,从而计算key最终要落到的槽位。
- 一般客户端在启动时会从集群中获取哈希槽到节点的关系映射,因此,可以选择对应的节点。
- 如果节点上有数据则直接返回,如果访问的key不在连接的节点上时,Redis会返回一个重定向命令(Moved或者ASK)。
- 当客户端收到Moved响应时,表示key所在的哈希槽已经被移动到另一个节点,客户端需要更新哈希槽映射并重试操作。
- 当客户端收到ASK响应时,表示Redis集群进行伸缩。
- Redis客户端根据Moved/ASK指令重定向到正确的Redis节点。
3. ASK重定向的工作原理
- 客户端请求:客户端发送一个命令来访问某个key。
- 如果该key所在的哈希槽正在从源节点迁移到目标节点,源节点会返回一个ASK重定向指令。
- 客户端收到ASK重定向后,首先发送一个ASKING命令到目标节点,随后重新发送原始命令到目标节点。
为什么客户端需要先发送一个ASKING命令到目标节点,然后再发送实际的请求?
因为集群扩容还未完成,所以理论新的节点还未完全拥有这个槽,而ASKING命令其实是一个临时授权,告诉目标节点即使该节点还没有正式拥有该哈希槽,也要暂时处理这个请求。如果没有先发送ASKING命令,目标节点可能会因为还没有正式接管哈希槽而拒绝处理请求。