redis 知识点整理
Laiyong Wang Lv5

redis基础 : http://laiyong.wang/2024/05/14/baseRedis/

集群模式

  • 主从模式
    主节点挂了,手动将一个从节点升为主节点
  • 哨兵模式
    主节点挂了,会选举一个新的
  • Cluster模式
    数据分片,基于哈希槽

Redis为什么这么快

其实主要是因为i/o多路复用+基于内存操作,别的也有影响

  • 基于内存:Redis 是一种基于内存的数据库,数据存储在内存中,数据的读写速度非常快,因为内存访问速度比硬盘访问速度快得多。
  • 单线程模型:Redis 使用单线程模型,这意味着它的所有操作都是在一个线程内完成的,不需要进行线程切换和上下文切换。这大大提高了 Redis 的运行效率和响应速度。
  • 多路复用 I/O 模型:Redis 在单线程的基础上,采用了I/O 多路复用技术,实现了单个线程同时处理多个客户端连接的能力,从而提高了 Redis 的并发性能。
  • 高效的数据结构:Redis 提供了多种高效的数据结构,如哈希表、有序集合、列表等,这些数据结构都被实现得非常高效,能够在 O(1) 的时间复杂度内完成数据读写操作,这也是 Redis 能够快速处理数据请求的重要因素之一。
  • 多线程的引入:在Redis 6.0中,为了进一步提升IO的性能,引入了多线程的机制。采用多线程,使得网络处理的请求并发进行,就可以大大的提升性能。多线程除了可以减少由于网络 I/O 等待造成的影响,还可以充分利用 CPU 的多核优势。

Redis 的数据类型

常用的:

  • 字符串(String)
    缓存,锁,计数等
  • 哈希(Hash)
    缓存(类似于购物车这种多层级的)
  • 列表(List)
    队列
  • 集合(Set)
    交集可以商品、好友推荐
  • 有序集合(Sorted Set)
    各种排行榜
  • 高级的数据类型
  • Streams
    消息队列
  • Bitmap
    差异,每日是否登录等
  • Geospatial
    距离计算
  • HyperLogLog
    基数统计

Zset是怎么实现

有多种结构,大类的话有两种,分别是ziplist(压缩列表)和skiplist(跳跃表),但是这只是以前,在Redis 5.0中新增了一个listpack(紧凑列表)的数据结构,这种数据结构就是为了替代ziplist的,而在之后Redis 7.0的发布中,在Zset的实现中,已经彻底不再使用zipList了。

当ZSet的元素数量比较少时,Redis会采用ZipList(ListPack)来存储ZSet的数据。ZipList(ListPack)是一种紧凑的列表结构,它通过连续存储元素来节约内存空间。当ZSet的元素数量增多时,Redis会自动将ZipList(ListPack)转换为SkipList,以保持元素的有序性和支持范围查询操作

跳表个人理解:

  • 类似于多层级的有序链表
  • 正常的链表每个节点只保存下一个指针,当每个节点保存多个层级的下一个指针即可实现
  • 其实还可以扩展下,不仅只保存下一个,还保存下N个指针,那就可以快速搜索,降低时间复杂度。这应该算是步长的概念

Redis为什么被设计成是单线程的

最开始是网络和数据操作是单线程,现在只有数据操作模块是单线程的,别的如持久化存储模块、集群支撑模块等是多线程

多线程的目的,是通过并发的方式来提升I/O的利用率和CPU的利用率。但是Redis的操作基本都是基于内存的,CPU资源不是Redis的性能瓶颈

setnx命令为什么是原子性的

因为数据操作模块是单线程的

过期策略 、

  • 过期策略
    定期删除+惰性删除
  • 内存淘汰策略
  • noeviction:不会淘汰任何键值对,而是直接返回错误信息。
  • allkeys-lru:从所有 key 中选择最近最少使用的那个 key 并删除。
  • volatile-lru:从设置了过期时间的 key 中选择最近最少使用的那个 key 并删除。
  • allkeys-random:从所有 key 中随机选择一个 key 并删除。
  • volatile-random:从设置了过期时间的 key 中随机选择一个 key 并删除。
  • volatile-ttl:从设置了过期时间的 key 中选择剩余时间最短的 key 并删除。
  • volatile-lfu:淘汰的对象是带有过期时间的键值对中,访问频率最低的那个。
  • allkeys-lfu:淘汰的对象则是所有键值对中,访问频率最低的那个。

热Key问题

同一个时间点上,Redis中的同一个key被大量访问,就会导致流量过于集中,使得很多物理资源无法支撑

拆分分散到不同服务器即可,多级缓存也行

大Key问题

影响(其实就是占位置、搜索慢、迁移慢,就是慢)

  • 影响性能
  • 占用内存
  • 内存空间不均匀。
  • 影响Redis备份和恢复
  • 搜索困难
  • 迁移困难
  • 过期执行耗时

处理

  • 有选择地删除Big Key:针对Big Key,我们可以针对一些访问频率低的进行有选择性的删除,删除Big Key来优化内存占用。

  • key拆分

  • 在业务代码中,将一个big key有意的进行拆分,比如根据日期或者用户尾号之类的进行拆分。使用小键替代大键可以有效减小存储空间,从而避免影响系统性能

  • 使用Cluster集群模式,以将大 key 分散到不同服务器上,以加快响应速度。

redis和数据库一致性问题

常见方案:

  • 先更新数据库, 再删除缓存。
    先操作数据库是因为reids操作快,为啥啊是删除不是更新,因为更新通常意味着查询、更新,删除简单更快,简单意味着失败概率小
  • 延迟双删:先删除缓存,再更新数据库,再删除一次缓存
    第一次删除是因为不是原子操作,第二次删除是为了解决并发导致的不一致
  • cache-aside:更新数据库,基于 binlog 监听进行缓存删除
    这个有试错机制

基于Redis实现滑动窗口限流

基于ZSET来实现

  • 定义滑动窗口的时间范围,例如,窗口大小为60秒。
  • 每次收到一个请求时,我们就定义出一个zset然后存储到redis中。
  • 然后再通过ZREMRANGEBYSCORE命令来删除分值小于窗口起始时间戳(当前时间戳-60s)的数据。
  • 最后,再使用ZCARD命令来获取有序集合中的成员数量,即在窗口内的请求量。

遍历所有key

  • KEYS命令
    用于查找所有符合给定模式的键,例如KEYS *会返回所有键。它在小数据库中使用时非常快,但在包含大量键的数据库中使用可能会阻塞服务器,因为它一次性检索并返回所有匹配的键。

  • SCAN命令
    提供了一种更安全的遍历键的方式,它以游标为基础分批次迭代键集合,每次调用返回一部分匹配的键

写回策略

  • RDB 的写回策略

  • save 900 1 # 如果900秒内至少有1个键发生变化,则保存快照

  • save 300 10 # 如果300秒内至少有10个键发生变化,则保存快照

  • save 60 10000 # 如果60秒内至少有10000个键发生变化,则保存快照

  • AOF的写回策略

  • Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;

  • Everysec,每秒写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;

  • No,操作系统控制的写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。