
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文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。