目录
介绍一下什么是 Redis,有什么特点
Redis 支持哪些数据类型
Redis 数据类型底层的数据结构/编码方式是什么
ZSet 为什么使用跳表,而不是使用红黑树来实现
Redis 的常见应用场景有哪些
怎样测试 Redis 服务器的连通性
如何设置 key 的过期时间
Redis 为什么是单线程模型
Redis ⾥的 IO 多路复⽤是怎么回事
Redis 的持久化机制有哪些
RDB 的持久化触发条件是怎样的
AOF 的⽂件同步策略有哪些
AOF 的重写机制是怎样的
Redis 的 key 的过期删除策略是怎样的
如果⼤量的 key 在同⼀时间点过期,会产⽣什么问题? 如何处理?
Redis 的淘汰策略是怎样的
Redis 如果内存⽤完了, 会出现什么情况
Redis 为什么把数据放到内存中
Redis 的主从同步/主从复制是怎么回事
Redis 的 pipeline (流⽔线/管道) 是什么
介绍下 Redis 哨兵
Redis 哨兵如果发现主节点宕机了, 接下来会做哪些事情
Redis 的集群是⼲什么的
Redis 的哈希槽是怎么回事
Redis 集群的最⼤节点个数是多少
Redis 集群会有些操作丢失吗
Redis 集群如何选择数据库
介绍下⼀致性哈希算法
如何理解 Redis 的事务? 和 MySQL 的事务有啥区别?
Redis 和事务相关的命令有哪些
为什么在⽣产环境上不应该使⽤ keys * 命令
如何使⽤ Redis 作为消息队列
如何使⽤ Redis 实现分布式锁
什么是热 key 问题? 如何解决?
如何实现 Redis ⾼可⽤
Redis 和 MySQL 如何保证双写⼀致性
⽣成 RDB 期间, Redis 是否可以处理写请求
Redis 的常⽤管理命令有哪些
Redis ⽤到的⽹络通讯协议是怎样的
Redis 如何遍历 key
Redis 如何实现 "查找附近的⼈"
什么是 Redis 的 "bigkey" 问题? 如何解决?
redis%20%E6%BA%90%E7%A0%81-toc" name="tableOfContents" style="margin-left:80px">阅读 redis 源码
redis学习🥳
介绍一下什么是 Redis,有什么特点
Redis 是一个高性能的 key-value 内存数据库.
不同于传统的 MySQL 这样的关系型数据库,Redis 主要使用内存存储数据 (当然也支持持久化存储到
硬盘上),并非是使用 "表" 这样的结构来组织数据,而是使用键值对的方式.
Redis 没有关系型数据库的复杂查询以及约束等功能,换来的是简单易用和更高的性能.
特点:
- 使用内存存储 (高性能).
- 支持多种数据结构(string、list、hash、set、zset等).
- 支持持久化(rdb 和 aof).
- 单线程处理请求.
- 支持主从复制.
- 支持哨兵模式.
- 支持集群模式.
- 支持事务.
- 支持多语言客户端.
- ......
参考资料:https://redis.io/docs/about/
Redis 支持哪些数据类型
五种最核心的类型:
- String
- List
- Hash
- Set
- ZSet
在后续版本中也逐渐新增了一些新的数据类型:
- Bitmap:通过二进制位表示某个数字是否存在.
- Bitfield:把字符串当做位图,并进行位操作.
- Hyperloglog:基于位图的结构实现 "计数" 效果(统计某个数字出现几次, 比 hashmap节省空间).
- Geospatial:地理信息,存储经纬度. 并且可以进行一些空间计算 (比如找出某个点附近 1km 内都有哪些点).
- Stream:消息队列.
这几个类型都属于特定场景中才会使用的类型. 不像前面五个类型通用性那么强. 因此我们课堂中没有介绍. 大家感兴趣可以自行了解.
参考资料: https://redis.io/docs/data-types/
Redis 数据类型底层的数据结构/编码方式是什么
- embstr:是针对短字符串的优化实现. 小于等于 39 字节的字符串使用 embstr, 大于则使用 raw.
- ziplist:压缩列表. 本质上是个字节数组. 可以节省内存空间. 当有序集合、哈希表、列表元素少,并且元素都是短字符串的时候,会使用这个.
- linklist:链表. 需要额外内存开销,容易引入内存碎片.
- skiplist:跳表,能够 O(logN) 的复杂度进行查找元素的复杂链表.
- quicklist:是个链表,每个元素是一个 ziplist.
- listpack:从 Redis 7 开始,引入了新的数据结构 listpack, 用来代替 ziplist
使用哪种编码,是 redis 内部自动决定的,使用 object encoding key 来查看编码.
ZSet 为什么使用跳表,而不是使用红黑树来实现
跳表的插入 / 查询 / 删除时间复杂度都是 O(logN),和红黑树是一样的.
但是跳表实现起来更简单. 而且不需要重新平衡这样的操作.
Redis 的常见应用场景有哪些
- 缓存:一些经常被访问到的热点数据,可以使用 Redis 进行缓存,降低查询数据库的次数.
- 计数器:统计点击次数 / 访问次数 / 收藏次数等常见需求.
- 排行榜:可以基于 Redis 的 ZSet 轻松实现.
- 分布式会话:使用 Redis 存储会话信息,可以使用户访问到系统的不同模块时都使用同一个公共的会话.
- 分布式锁:对于分布式系统的并发访问控制,可以基于 Redis 来实现分布式锁,避免并发的竞争问题(类似于线程安全问题).
- 消息队列:Redis 自身支持 Stream 数据类型,可以作为简单的消息队列使用.
- ......
怎样测试 Redis 服务器的连通性
使用 ping 命令即可. 如果连通,服务器会返回 pong
如何设置 key 的过期时间
可以在 set 的时候通过 EX 选项指定过期时间. 也可以通过 expire 命令单独指定.
Redis 为什么是单线程模型
Redis 内部的逻辑比较简单,一般的性能瓶颈都是出现在 内存 或者 IO 上,很少是 CPU . 因此使用多
线程并没有太大的收益,反而可能会引入线程安全问题.
从 6.0 开始,Redis 引入多线程. 此时只是使用多个线程去处理 网络请求+协议解析,真正执行仍然是
单线程完成的. 这样做可以进一步提高 IO 的处理效率.
Redis ⾥的 IO 多路复⽤是怎么回事
Redis 主要是基于了 Linux 提供的 epoll 机制来完成 IO 多路复⽤.
简单的说,所谓的 "IO多路复⽤" 就是⽤⼀个线程,来管理多个 socket (⽂件描述符), 并按需激活线程.
虽然⼀个 Redis 服务器要处理很多个客⼾端的请求, 但是这些客⼾端的请求并⾮是 "同⼀时刻" 到达的.
在⼀个单位时间内, 可能只有两三个客⼾端的请求到来了. 因此如果使⽤传统的每个线程处理⼀个连接的⽅式实现, ⼤部分线程其实都是挂起的(不活跃的).
因此与其搞了⼀堆线程, 但是线程在摸⻥, 还不如就⽤⼀个线程来处理了.上述这样的需求, 被操作系统内
核封装好了. 就是 IO 多路复⽤.
Linux 中, IO 多路复⽤有三种实现:
- select
- poll
- epoll
其中 epoll 是最新的版本 (2005 年左右引⼊的), 也是最⾼效的版本.
epoll 在内核中维护了⼀个红⿊树, 来管理所有的 socket, 并且每个节点都关联了⼀个事件回调. 当系统内核感知到⽹卡收到数据了, 进⼀步判定这个数据是给哪个 socket 的, 随之调⽤对应的回调, 进⼀步唤醒⽤⼾线程, 来处理这个收到的数据.
此时这个⽤⼾线程是 "忙碌" 的, 要不停的处理来⾃各个客⼾端的请求数据.
Redis 的持久化机制有哪些
RDB 和 AOF:参考⽂档前⾯的章节 "第四章 持久化".
RDB 的持久化触发条件是怎样的
• ⼿动触发:使⽤ save 或者 bgsave 即可触发持久化.
• ⾃动触发:
- 在配置⽂件中通过 save m n 即可设定 m 秒内发⽣ n 次修改, 就触发持久化
- 从节点进⾏全量复制操作, 触发持久化.
- 执⾏ shutdown 命令, 关闭 redis server, 触发持久化.
AOF 的⽂件同步策略有哪些
可配置值 | 说明 |
always | 命令写⼊ aof_buf 后调⽤ fsync 同步,完成后返回 |
everysec | 命令写⼊aof_buf 后只执⾏ write 操作,不进⾏ fsync。每秒由同步线程进⾏ fsync。 |
no | 命令写⼊ aof_buf 后只执⾏ write 操作,由 OS 控制 fsync 频率。 |
AOF 的重写机制是怎样的
参考⽂档前⾯的章节 "第四章 持久化".
Redis 的 key 的过期删除策略是怎样的
惰性过期
只有当访问⼀个key时,才会判断该key是否已过期,过期则清除。该策略可以最⼤化地节省CPU资源,却对内存⾮常不友好。极端情况可能出现⼤量的过期key没有再次被访问,从⽽不会被清除,占⽤⼤量内存。
定期过期
每隔⼀定的时间,会扫描⼀定数量的数据库的expires字典中⼀定数量的key,并清除其中已过期的 key。该策略是前两者的⼀个折中⽅案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表⽰的过期时间。键空间是指该Redis集群中保存的所有键。
Redis 中同时使⽤了惰性过期和定期过期两种过期策略。
- 每隔 100ms 就随机抽取⼀定数量的key来检查和删除的。
- 在获取某个key的时候,redis 会检查⼀下,这个 key 如果设置了过期时间并且已经过期了,此时就会删除。
如果⼤量的 key 在同⼀时间点过期,会产⽣什么问题? 如何处理?
如果⼤量的 key 过期时间设置的过于集中,到过期的那个时间点,Redis 可能会出现短暂的卡顿现象。为何会出现卡顿呢? Redis 针对过期 key 的删除, 采取 定期采样删除 + 惰性删除 两种⽅式结合.
对于定期采样删除来说, Redis 会注册⼀个定时器, 每隔⼀定时间触发⼀次采样删除.在删除的时候会先根据事先统计好的过期 key 的个数来决定后续策略.
如果过期 key 的数⽬超过总 key 数⽬的 25% 以上, 就会使 Redis 持续删除过期 key 直到最⼤时间删除时间 (默认是 25ms).
之所以限制这个最⼤时间, 就是为了防⽌ Redis 被卡住太久
但是即使如此, 有些对性能要求较⾼的场景仍然会因为阻塞 25ms 导致性能下降严重.
解决⽅案: 可以在过期时间上加⼀个随机值,使得过期时间分散⼀些.
很多时候过期时间并不⼀定⾮得卡的那么精准. ⽐如设定 2s 之后过期, 不⼀定⾮要正好的 2000ms, 2001, 2002, 1999, 1998 甚⾄ 2010 这些时间都是问题不⼤的.
因此让过期时间通过随机值分散, 就可以避免同⼀时刻过期的 key 太多, 从⽽降低触发 25% 这个阈值的可能性.
Redis 的淘汰策略是怎样的
当 Redis 内存不⾜时, 如果继续尝试添加新的 key, 就会触发淘汰策略, 把之前的旧 key 给⾃动删除掉.
Redis 提供的淘汰策略有以下⼏种:
• volatile-lru 当内存不足以容纳新写入数据时,从设置了过期时间的key中使用LRU(最近最少使用)算法进行淘汰
• allkeys-lru 当内存不足以容纳新写入数据时,从所有key中使用LRU(最近最少使用)算法进行淘汰.
• volatile-lfu 4.0版本新增,当内存不足以容纳新写入数据时,在过期的key中,使用LFU算法进行删除key.
• allkeys-lfu 4.0版本新增,当内存不足以容纳新写入数据时,从所有key中使用LFU算法进行淘汰.
• volatile-random 当内存不足以容纳新写入数据时,从设置了过期时间的key中,随机淘汰数据.
• allkeys-random 当内存不足以容纳新写入数据时,从所有key中随机淘汰数据.
• volatile-ttl 在设置了过期时间的key中,根据过期时间进行淘汰,越早过期的优先被淘汰.(相当于 FIFO, 只不过是局限于过期的 key)
• noeviction 默认策略,当内存不足以容纳新写入数据时,新写入操作会报错.
🦄 关于 LRU 和 LFU
LRU:Least Recently Used 最近最久使用算法. 哪个 key 上次访问时间最长,就干掉.
LFU:Least Frequently Used 最近最常使用算法. 哪个 key 在最近一段时间里使用次数最少,就干掉.
在上述策略中, 默认策略⼀般来说都是⽐较好的选择.
- 部署 Redis 的机器正常来说不应该消耗到内存耗尽才做处理. 应该有完善的监控报警体系, 当内存接近上限的时候提前通知程序猿, 尽早进⾏扩容.
- 如果确实出现内存耗尽了, 相⽐于 Redis 偷偷的删除⼀部分 key, 带伤运⾏(当然, volatile 前缀系列的策略不算带伤), 不如尽早显式的把问题暴露出来, 及时处理.
- 除⾮是 Redis 中的数据完全不关键, 否则不应该使⽤ allkeys 系列的策略.
Redis 如果内存⽤完了, 会出现什么情况
会触发 Redis 的淘汰策略, 把⼀些 key ⾃动删除掉.参考上个问题.
Redis 为什么把数据放到内存中
效率.
访问内存的速度⽐访问硬盘的速度快 3-4 个数量级.
如下表可以看到:
层级 | 速度 |
L1 cache reference | 0.5 ns |
Branch mispredict | 5 ns |
L2 cache reference | 7 ns |
Mutex lock/unlock | 25 ns |
Main memory reference | 100 ns |
Compress 1 K bytes with Zippy | 3 000 ns |
Send 2 K bytes over 1 Gbps network | 20 000 ns |
Read 1 MB sequentially from Memory | 250 000 ns |
Round trip within same datacenter | 500 000 ns |
Disk seek | 10 000 000 ns |
Read 1 MB sequentially from disk | 20 000 000 ns |
Send packet CA -> Netherlands -> CA | 150 000 000 ns |
进⾏⼀次内存的随机访问操作是 100ns
进⾏⼀次硬盘的随机访问操作是 10 000 000 ns
这个差距还是⾮常明显的.
因此使⽤内存存储数据让 Redis 相⽐于 MySQL 等数据库, 具有了⾮常显著的优势. 当然, 使⽤内存存储的劣势也是⽐较明显的:
- 内存的存储空间⽐硬盘⼩很多. 不过随着硬件的发展, ⼤内存的服务器越来越便宜了, 市⾯上甚⾄可以看到 1TB 级别的内存的机器了.
- 内存的数据掉电后会丢失. 因此 Redis 引⼊持久化的⽅式, 把数据在硬盘上也备份⼀遍(当然, 增删改查仍然是以内存为主), 速度不影响的前提下, 保证重启后数据不丢失.
Redis 的主从同步/主从复制是怎么回事
解决的问题:
- 提⾼ Redis 的可⽤性, 避免 Redis 机器/进程 挂了之后⽆法提供服务
- 降低 Redis 服务器的压⼒, 从节点读, 主节点写, 让单个节点承担的压⼒更⼩.
需要有多个服务器, 部署多个 Redis 服务器程序. 其中⼀个作为主节点, 其他作为从节点.
在从节点 Redis 启动的时候, 通过配置⽂件或者命令参数 --slaveof 指定当前节点的主节点是哪个.
当从节点启动之后, 就会清空⾃⾝数据, 并把主节点的所有数据都复制过来. 并且主节点的数据后续如果
进⾏修改, 从节点也能⾃动随之更新.
主节点可以写数据也可以读数据. 从节点只能读数据.
这样的话, 主节点的数据就多了⼏个 "备份", 当主节点挂了仍然可以通过从节点来读取数据.另⼀⽅⾯访
问 Redis ⼤多还是读操作, 通过从节点就可以分担主节点的读操作的压⼒.
💡 比如一个 Redis 每单位时间要处理 100 次写请求,和 10w 次读请求.
如果使用主从的方式部署,比如一个主节点,三个从节点,此时主节点负责处理 100 次写请求,主节点和从节点再负责处理 2.5w 次读请求.
这样主节点的压力就降低到了原来的 1/4了.
Redis 的 pipeline (流⽔线/管道) 是什么
在 Redis 客⼾端执⾏ N 个命令, ⼤概的过程如下:
发送命令-> 命令排队-> 命令执行-> 返回结果
发送命令-> 命令排队-> 命令执行-> 返回结果
发送命令-> 命令排队-> 命令执行-> 返回结果
发送命令-> 命令排队-> 命令执行-> 返回结果
......
这个过程称为 Round trip time (简称RTT, 往返时间) .
如果我们需要执⾏多个操作, 就需要多个这样的 RTT . 其中⼤量的时间都消耗在 发送命令/返回结果 的
⽹络时间上了.
使⽤ pipeline, 就可以把多个 redis 命令, 合并到⼀个请求中, 节省⽹络开销.
发送命令-> 命令排队-> 命令排队-> 命令排队-> 命令执行 -> 命令执行-> 命令执行-> 返回结果
🏕 注意! pipeline 中的这 N 个命令的执行并非是原子的! 他们只是同乘一趟地铁的陌路人,
彼此之间没啥关联关系.
通过 Redis 客⼾端使⽤管道, 可以基于 Linux 的管道实现. ⽐如把要执⾏的 N 个命令写⼊ cmd.txt ⽂本⽂件中, 每个命令⼀⾏.
然后执⾏
cat cmd.txt | redis-cli --pipe
通过 pipe 参数即可让 redis-cli 读取执⾏ cmd.txt 中的内容.
如果通过管道执⾏的命令太多, 可能会使服务器被阻塞住.
介绍下 Redis 哨兵
参考⽂档前⾯的章节 "第七章 哨兵"
Redis 哨兵如果发现主节点宕机了, 接下来会做哪些事情
参考⽂档前⾯的章节 "第七章 哨兵 - 7.4 选举原理"
Redis 的集群是⼲什么的
参考⽂档前⾯章节 "第⼋章 集群"
Redis 的哈希槽是怎么回事
参考⽂档前⾯章节 "第⼋章 集群 - 8.2 数据分⽚算法"
Redis 集群的最⼤节点个数是多少
作者建议不要超过 1000 个.
参考链接: https://github.com/antirez/redis/issues/2576
Redis 集群会有些操作丢失吗
Redis 并不能保证数据的强⼀致性,这意味这在实际中集群在特定的条件下可能会丢失写操作.
⽐如在写成功⼀个 key 之后, 正好主节点宕机, 此时由于这个数据还没有来得及同步到从节点上, 也没来得及 AOF 写⼊⽇志, 就丢失了.
Redis 集群如何选择数据库
Redis 集群不⽀持选择数据库. 只能使⽤默认的数据库 0 .
介绍下⼀致性哈希算法
参考⽂档前⾯章节 "第⼋章 集群 - 8.2 数据分⽚算法"
如何理解 Redis 的事务? 和 MySQL 的事务有啥区别?
参考⽂档前⾯章节 "第五章 事务"
Redis 和事务相关的命令有哪些
MULTI、EXEC、DISCARD、WATCH
为什么在⽣产环境上不应该使⽤ keys * 命令
keys * 类似于 sql 的 select * , 如果存储的数据量特别⼤, 那么这个操作就会耗时⾮常⻓, 甚⾄阻塞住 Redis, 使 Redis 难以处理其他请求, 造成⽣产环境故障.
如何使⽤ Redis 作为消息队列
有三种典型的做法.
1)直接使⽤ List. Redis 中提供了 BLPOP 和 BRPOP, 可以阻塞式的获取元素. 就可以让消费者通过 List来进⾏阻塞式取元素, ⽣产者通过 RPUSH 或者 LPUSH 即可完成⽣产操作.
2)使⽤ PUB / SUB 命令.
3)使⽤ Redis 5 提供的 Stream 类型.
整体来说, Stream 这个⽅案提供的功能相对更加完整, 但是即使如此, 更多的时候还是使⽤专业的消息
队列更合适⼀些.
如果确实某个场景对于 MQ 的功能需求不⾼, 且不想引⼊额外的 mq 组件依赖, Redis 也可以使⽤.
如何使⽤ Redis 实现分布式锁
参考⽂档上⽅章节
什么是缓存穿透, 缓存雪崩, 缓存击穿
参考⽂档上⽅章节
什么是热 key 问题? 如何解决?
某些 key 的访问频率⾮常⾼. 称为 "热 key".
有些热 key 可能达到⾮常热的情况, 以⼀⼰之⼒就能把 redis 打挂.
💡虽然 Redis 通过集群部署的方式,能够分散请求的压力.
但是由于热 key 是同一个 key, 对应访问的机器也是同一组机器. 这就会导致其他组的机器虽然硬件资源富裕,但是也无法帮上忙.
如何解决:
- 进⼀步扩⼤ Redis 集群的规模. 尤其是针对热 key 所属分⽚, 部署更多的 slave 节点分担读压⼒.
- 应⽤服务器对热 key 识别出来, 并单独的进⾏⼆次哈希, 也就相当于是把⼀个 key 分散到多个 Redis分⽚上存储.
- 应⽤服务器对热 key 识别出来, 把热 key 使⽤单独的 Redis 集群部署, 并赋予更多的机器.
- 使⽤应⽤程序本地缓存, 降低 Redis 的压⼒.
如何实现 Redis ⾼可⽤
主从 + 哨兵 + 集群.参考⽂档上⽅章节.
Redis 和 MySQL 如何保证双写⼀致性
什么是 "双写⼀致性"
当⽤⼾修改数据的时候, 需要修改数据库, 同时也要更新缓存中的数据.
否则再直接读缓存的数据就是 "脏数据" 了.
但是如果直接写数据库并且写 Redis, 此时万⼀有⼀⽅写⼊失败, 就容易出现数据不⼀致的情况.
如何解决
⽅案⼀: 延时双删.
"双删" 的⽬的是多⼀重保证. 如果第⼀次删除失败, 第⼆次删除仍然能够兜底.
📌 只要把 Redis 的数据删了,后续访问该数据的时候就会自动的从数据库读取,并且把结果也写入 Redis 了. 就可以保证 Redis 和 数据库一致.
如果直接修改 Redis, 由于修改 Redis 和 修改 MySQL 并非是原子的,多个应用服务器并发执行的时候,可能就会出现类似于 "线程安全" 的问题了.
服务器1 和 服务器2 同时都写 Redis 和 MySQL,其中服务器1写 Redis 的结果被 服务器2 写Redis 的结果覆盖了. 服务器 2 写 MySQL 的结果被服务器 1 写 MySQL 的结果覆盖了. 此时就不一致了.
因此,"数据一致" 问题就转换成了能成功删除 Redis 数据的问题.
问题来了, 是否可能第⼆次删除也失败了呢?答案是肯定的. 但是概率是⼤⼤降低了.
⽅案⼆: 删除缓存重试
先删除缓存数据, 如果删除失败, 则把失败的 key 放到⼀个 mq 中, 稍后进⾏重试.
只要删除不成功, 这个重试就会反复⼀直进⾏.
上述操作确实能更严格的保证删除效果. 但是代码实现起来⽐较复杂.
幸运的是, 有⼤佬把这个过程封装好了, 我们可以直接使⽤.
例如 阿⾥巴巴 提供了开源⼯具 canal , 可以⽐较⽅便的获取到 mysql 的 binlog, 并基于此实现上述逻辑.
⽣成 RDB 期间, Redis 是否可以处理写请求
⽣成 RDB 有两种⽅式.
- save. 这个操作会阻塞 Redis 的主线程, 此时⽆法处理外界的写请求.
- bgsave. 这个操作会让 Redis ⽣成⼦进程. ⼦进程负责⽣成 RDB, ⽗进程负责处理写请求.
此时新写的数据不⼀定会被写⼊ RDB ⽂件中. 需要触发下⼀轮 RDB 的时候才能真正确保持久化了.
Redis 的常⽤管理命令有哪些
# dbsize 返回当前数据库 key 的数量。
# info 返回当前 redis 服务器状态和一些统计信息。
# monitor 实时监听并返回redis服务器接收到的所有请求信息。
# shutdown 把数据同步保存到磁盘上,并关闭redis服务。
# config get parameter 获取一个 redis 配置参数信息。(个别参数可能无法获取)
# config set parameter value 设置一个 redis 配置参数信息。(个别参数可能无法获取)
# debug object key 获取一个 key 的调试信息。
# debug segfault 制造一次服务器当机。
# flushdb 删除当前数据库中所有 key,此方法不会失败。小心慎用
# flushall 删除全部数据库中所有 key,此方法不会失败。小心慎用
Redis ⽤到的⽹络通讯协议是怎样的
Redis Serialization Protocol (RSP)
这个是 Redis 专⻔实现的应⽤层协议. ⽤于 Redis 客⼾端和服务器之间的通信.
RSP 是⼀种纯⽂本协议. 协议规则参考 https://redis.io/docs/reference/protocol-spec/
Redis 如何遍历 key
使⽤ keys * 虽然能⼀次性获取到所有 key,但是这个操作开销可能⾮常⼤, 会把 Redis 卡死.
更靠谱的⽅法是使⽤ scan 命令.
SCAN cursor [MATCH pattern] [COUNT count]
每次 scan 时间复杂度 O(1).
每次 scan 都能返回⼀批 keys 同时告知我们下次应该从哪⾥开始进⾏ scan
需要使⽤多次 scan 才能完成整个遍历.
Cursor 表⽰遍历 key 的光标. 从 0 开始, 每次执⾏ scan 都会返回下次开始的光标. 当返回结果为 0, 则说明遍历结束.
通过 count 可以限制每次获取到的 key 的个数.
redis 127.0.0.1:6379> scan 0
1) "17"
2) 1) "key:12"2) "key:8"3) "key:4"4) "key:14"5) "key:16"6) "key:17" 7) "key:15"8) "key:10"9) "key:3"10) "key:7"11) "key:1"
redis 127.0.0.1:6379> scan 17
1) "0"
2) 1) "key:5"2) "key:18"3) "key:0"4) "key:2"5) "key:19"6) "key:13"7) "key:6"8) "key:9"9) "key:11"
🚅 Redis 是按照哈希 的方式来管理 keys 的,因此在遍历的时候得到的 keys 序列并非是 "有序"的.
每次调⽤ scan 会返回下⼀次 scan 的游标和本次的 keys.
下次再 scan 的时候根据这个游标继续遍历即可.
同理, 还提供了 hscan (哈希), sscan(set), zscan(zset), ⽤于遍历. ⽤法类似.
渐进式遍历解决了阻塞问题, 但是如果遍历过程中, 键存在变化, 则导致重复遍历或者遗漏.
Redis 如何实现 "查找附近的⼈"
可以使⽤ Geospatial 类型, 存储每个⼈的地理位置.
geoadd key [经度] [纬度] member [经度] [纬度] member .......
使⽤
georadius key [经度] [纬度] [距离]
查询以给定位置为圆⼼, 距离为半径, ⾥⾯有哪些 member 符合条件.
什么是 Redis 的 "bigkey" 问题? 如何解决?
bigkey 指的是某个 key 对应的 value 占据较多的存储空间.
⽐如 value 是字符串类型, 是⼀个⾮常⻓的字符串; 或者 value 是 hash 或者 set 类型, ⾥⾯的元素特别多.
这样的 bigkey 会导致读写的时候性能下降. 如果是集群分⽚部署, 也会引起不同分⽚的数据倾斜.
解决⽅案:
核⼼思路就是拆分, 把⼀个⼤的 key 拆成多个⼩的 key, 每个 key 对应 value 的⼀部分数据.
可以使⽤ redis-cli --bigkey 查找 bigkey
删除 bigkey 不要直接使⽤ del, 也可能阻塞 Redis. 使⽤ unlink 命令在后台删除更合适.
redis%20%E6%BA%90%E7%A0%81" name="%E9%98%85%E8%AF%BB%20redis%20%E6%BA%90%E7%A0%81">阅读 redis 源码
阅读代码,核心思路 三个字 “花时间” 抓住一些问题. 针对性的看
1. redis 启动的过程是怎样的.
2. redis 是如何解析各种配置项的.
3. redis 是如何接收请求,并且处理请求命令的.
4. redis 持久化数据格式是怎样的(rdb,二进制格式如何生成的?).
5. 持久化触发实际在代码中具体怎么实现的.
6. 多线程具体起到什么作用.
看是一方面,整理是一方面=>形成一些 流程图/结构图
redis学习打卡🥳