注:如有侵权,请联系删除。
注:文章内含链接都是本人看过相关内容觉得写的不错又契合相关内容的博客,本文本身是一种相关内容的简介及引申,便于查阅相关资料或信息。
官方文档链接:Develop with Redis | Docs
中文文档连接:Redis 入门 - Redis 中文文档 (tkcnn.com)
一、Redis概述
Redis,英文全称是Remote Dictionary Server(远程字典服务),是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
与MySQL数据库不同的是,Redis的数据是存在内存中的。它的读写速度非常快,每秒可以处理超过10万次读写操作。因此redis被广泛应用于缓存,另外,Redis也经常用来做分布式锁。除此之外,Redis支持事务、持久化、LUA 脚本、LRU 驱动事件、多种集群方案。
二、常用数据结构
String(字符串)
Hash(哈希)
List(列表)
Set(集合)
-
zset(有序集合)
以上五种使用样例及使用场景可参考如下:redis专题:redis的常用数据结构及使用场景_redis数据结构使用场景-CSDN博客
Geospatial
Hyperloglog (使用样例:Redis HyperLogLog 是什么?这些场景使用它,让我枪出如龙,一笑破苍穹-腾讯云开发者社区-腾讯云 (tencent.com))
Bitmap
三、支持部署方案
单点、主从、哨兵、集群
Redis四种部署模式(原理、优缺点及解决方案)_redis部署-CSDN博客
四、持久化方案
RDS、AOF、RDS与AOF混合持久化
五、Redis缓存穿透/击穿/雪崩原理及其解决方案
详解Redis缓存穿透/击穿/雪崩原理及其解决方案Redis脚本之家 (jb51.net)
穿透:通过接口访问一个缓存和数据库都不存在的数据。
因为服务出于容错考虑,当请求从持久层查不到数据则不写入缓存,这将导致请求这个不存在的数据每次都要到持久层去查询,失去了缓存的意义。
此时,缓存起不到保护后端持久层的意义,就像被穿透了一样。导致数据库存在被打挂的风险。
击穿:某个热点 key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,导致数据库存在被打挂的风险。
雪崩:大量的热点数据过期时间相同,导致数据在同一时刻集体失效。造成瞬时数据库请求量大、压力骤增,引起雪崩,导致数据库存在被打挂的风险。
六、分布式锁实现
关于分布式锁的推荐阅读:Redis、ZooKeeper、Etcd,谁有最好用的分布式锁? (qq.com)
基于 Redis 实现的分布式锁,一个严谨的的流程如下:
加锁:SET ... EX ... NX
操作共享资源:没操作完之前,开启守护线程,定期给锁续期;
释放锁:Lua 脚本,先 GET 判断锁是否归属自己,再 DEL 释放锁。
七、Redis其他知识点
1、Redis为什么快?
Redis基于内存
Redis使用IO多路复用和单线程循环事件处理
Redis使用更有效率的数据结构
2、常见缓存读写策略
(1)Cache Aside Pattern(旁路缓存模式)
写:
先更新 db
然后直接删除 cache 。
读 :
从 cache 中读取数据,读取到就直接返回
cache 中读取不到的话,就从 db 中读取数据返回
再把数据放到 cache 中。
Cache Aside Pattern 的缺陷。
缺陷 1:首次请求数据一定不在 cache 的问题
解决办法:可以将热点数据可以提前放入 cache 中。
缺陷 2:写操作比较频繁的话导致 cache 中的数据会被频繁被删除,这样会影响缓存命中率 。
解决办法:
数据库和缓存数据强一致场景:更新 db 的时候同样更新 cache,不过我们需要加一个锁/分布式锁来保证更新 cache 的时候不存在线程安全问题。
可以短暂地允许数据库和缓存数据不一致的场景:更新 db 的时候同样更新 cache,但是给缓存加一个比较短的过期时间,这样的话就可以保证即使数据不一致的话影响也比较小。
(2)Read/Write Through Pattern(读写穿透)
写(Write Through):
先查 cache,cache 中不存在,直接更新 db。
cache 中存在,则先更新 cache,然后 cache 服务自己更新 db(同步更新 cache 和 db)。
读(Read Through):
从 cache 中读取数据,读取到就直接返回 。
读取不到的话,先从 db 加载,写入到 cache 后返回响应。
(3)Write Behind Pattern(异步缓存写入)
写(Write Through):
- 更新 cache,然后 cache 服务自己异步更新 db。
读(Read Through):
从 cache 中读取数据,读取到就直接返回 。
读取不到的话,先从 db 加载,写入到 cache 后返回响应。
注:上述三种缓存读写策略也只是市面上常见的,在实际缓存读写过程中需要结合自身业务需要和数据库与缓存数据不一致的容忍程度来设计相关的读写策略,不能完全照搬和把自己局限住。
3、Redis 除了做缓存,还能做什么?
分布式锁:通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。关于 Redis 实现分布式锁,通过setnx 或带过期时间的set ex nx。
限流:一般是通过 Redis + Lua 脚本的方式来实现限流。如果不想自己写 Lua 脚本的话,也可以直接利用 Redisson 中的
RRateLimiter
来实现分布式限流,其底层实现就是基于 Lua 代码+令牌桶算法。消息队列:Redis 自带的 List 数据结构可以作为一个简单的队列使用。Redis 5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka,有主题和消费组的概念,支持消息持久化以及 ACK 机制。
延时队列:Redisson 内置了延时队列(基于 Sorted Set 实现的)。
分布式 Session :利用 String 或者 Hash 数据类型保存 Session 数据,所有的服务器都可以访问。
复杂业务场景:通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 Bitmap 统计活跃用户、通过 Sorted Set 维护排行榜。
……
4、Redis 做搜索引擎的优缺点
Redis 是可以实现全文搜索引擎功能的,需要借助 RediSearch ,这是一个基于 Redis 的搜索引擎模块。
RediSearch 支持中文分词、聚合统计、停用词、同义词、拼写检查、标签查询、向量相似度查询、多关键词搜索、分页搜索等功能,算是一个功能比较完善的全文搜索引擎了。
相比较于 Elasticsearch 来说,RediSearch 主要在下面两点上表现更优异一些:
性能更优秀:依赖 Redis 自身的高性能,基于内存操作(Elasticsearch 基于磁盘)。
较低内存占用实现快速索引:RediSearch 内部使用压缩的倒排索引,所以可以用较低的内存占用来实现索引的快速构建。
对于小型项目的简单搜索场景来说,使用 RediSearch 来作为搜索引擎还是没有问题的(搭配 RedisJSON 使用)。
对于比较复杂或者数据规模较大的搜索场景还是不太建议使用 RediSearch 来作为搜索引擎,主要是因为下面这些限制和问题:
数据量限制:Elasticsearch 可以支持 PB 级别的数据量,可以轻松扩展到多个节点,利用分片机制提高可用性和性能。RedisSearch 是基于 Redis 实现的,其能存储的数据量受限于 Redis 的内存容量,不太适合存储大规模的数据(内存昂贵,扩展能力较差)。
分布式能力较差:Elasticsearch 是为分布式环境设计的,可以轻松扩展到多个节点。虽然 RedisSearch 支持分布式部署,但在实际应用中可能会面临一些挑战,如数据分片、节点间通信、数据一致性等问题。
聚合功能较弱:Elasticsearch 提供了丰富的聚合功能,而 RediSearch 的聚合功能相对较弱,只支持简单的聚合操作。
生态较差:Elasticsearch 可以轻松和常见的一些系统/软件集成比如 Hadoop、Spark、Kibana,而 RedisSearch 则不具备该优势。
Elasticsearch 适用于全文搜索、复杂查询、实时数据分析和聚合的场景,而 RediSearch 适用于快速数据存储、缓存和简单查询的场景。
5、Redis 内存淘汰策略
volatile-lru(least recently used):从已设置过期时间的数据集(
server.db[i].expires
)中挑选最近最少使用的数据淘汰。volatile-ttl:从已设置过期时间的数据集(
server.db[i].expires
)中挑选将要过期的数据淘汰。volatile-random:从已设置过期时间的数据集(
server.db[i].expires
)中任意选择数据淘汰。allkeys-lru(least recently used):从数据集(
server.db[i].dict
)中移除最近最少使用的数据淘汰。allkeys-random:从数据集(
server.db[i].dict
)中任意选择数据淘汰。no-eviction(默认内存淘汰策略):禁止驱逐数据,当内存不足以容纳新写入数据时,新写入操作会报错。
4.0 版本后增加以下两种:
volatile-lfu(least frequently used):从已设置过期时间的数据集(
server.db[i].expires
)中挑选最不经常使用的数据淘汰。allkeys-lfu(least frequently used):从数据集(
server.db[i].dict
)中移除最不经常使用的数据淘汰。
6、Redis性能优化
你的Redis真的变慢了吗?性能优化如何做 (qq.com)
(1)Redis常见阻塞原因总结
O(n)命令执行
SAVE命令创建快照
AOF
大key
清空数据库
集群扩容
Swap(内存交换)
CPU竞争
网络问题
(2)优化
慢查询优化
集中过期优化
实例内存达到上限优化
...
7、redis 事务
- Redis 事务在运行错误的情况下,除了执行过程中出现错误的命令外,其他命令都能正常执行。并且,Redis 事务是不支持回滚(roll back)操作的。因此,Redis 事务其实是不满足原子性的。
- 一段 Lua 脚本可以视作一条命令执行,一段 Lua 脚本执行过程中不会有其他脚本或 Redis 命令同时执行,保证了操作不会被其他指令插入或打扰。不过,如果 Lua 脚本运行时出错并中途结束,出错之后的命令是不会被执行的。并且,出错之前执行的命令是无法被撤销的,无法实现类似关系型数据库执行失败可以回滚的那种原子性效果。因此, **严格来说的话,通过 Lua 脚本来批量执行 Redis 命令实际也是不完全满足原子性的。
注意两者执行错误指令的效果略有区别