Redis 知识整理
2021-07-19 18:01:27 16 举报
AI智能生成
Redis 知识整理
作者其他创作
大纲/内容
数据过期与内存淘汰机制
过期字典:
Redis 通过一个叫做过期字典(可以看做是 Hash 表)来保存数据过期时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。
Redis 通过一个叫做过期字典(可以看做是 Hash 表)来保存数据过期时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。
过期数据删除策略
惰性删除:
只会在去除 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。
只会在去除 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。
定期删除:
每个一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。
每个一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。
内存淘汰机制
no-eviction:当内存不足以容纳新写入数据时,新写入操作会报错。
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key (这个是最常用的)
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key。
allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key 。
(4.0 版本后增加)
(4.0 版本后增加)
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key。
volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key。
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。
volatile-lfu:当内存不足以容纳新写入数据时,从已设置过期时间的键空间中挑选最不经常使用的数据淘汰。
(4.0 版本后增加)
(4.0 版本后增加)
持久化机制
快照(snapshotting)持久化(RDB)
通过创建快照老获得存储在内存里面的数据在某个时间点上的副本。Redis 创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis 主从结构,主要用来提交 Redis 性能),还可以将快照留在原地以便重启服务器的时候使用。
快照持久化是 Redis 默认采用的持久化方式,在 Redis.confg 配置:
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
AOF(append-only file)持久化
与快照持久化相比,AOF 持久化的实时性更好,因此已成为主流的持久化方案。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启:appendonly yes
开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入硬盘中的 .aof 文件。AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的,默认的文件名是 appendonly.aof.
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no #让操作系统决定何时进行同步
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no #让操作系统决定何时进行同步
事务
通过 MULTI、EXEC、DISCARD 和 WATCH 等命令来实现事务功能
1. 开始事务(MULTI)。
2. 命令入队(批量操作 Redis 的命令,先进先出(FIFO)的顺序执行)。
3. 执行事务(EXEC)。
2. 命令入队(批量操作 Redis 的命令,先进先出(FIFO)的顺序执行)。
3. 执行事务(EXEC)。
可以使用 DISCARD 命令取消一个事务,它会清空事务队列中保存的所有命令。
WATCH 命令用于监听指定的键,当调用 EXEC 命令执行事务时,如果被一个 WATCH 命令监视的键被修改的话,这个事务都不会被执行,直接返回失败。
ACID
Redis 是不支持 rollback 的,因而不满足原子性的操作(而且不满足持久性)。
Redis 事务提供了一种将多个命令请求打包的功能,然后,再按照顺序执行打包的所有命令,并且不会被中途打断。
缓存穿透
缓存穿透说简单点就是大量请求的 key 不存在与缓存中,导致请求直接到了数据库上,没有经过缓存这一层。
缓存无效 key :如果缓存和数据库都查不到某个 key 的数据就会写一个到 Redis 中去并设置过期时间。这种方式可以解决请求的 key 变化频繁的情况,如果黑客恶意攻击,每次构建不同的请求 key ,会导致 Redis 中缓存大量无效的 key 。这种方案不能从根本上结局次问题。
布隆过滤器
一个元素加入布隆过滤器的过程:
1. 使用布隆过滤器中的哈希函数对元素值进行计算,的到哈希值(有几个哈希函数就得到几个哈希值)。
2. 根据得到的哈希值,在位数组中把对应下标的值置为 1 。
1. 使用布隆过滤器中的哈希函数对元素值进行计算,的到哈希值(有几个哈希函数就得到几个哈希值)。
2. 根据得到的哈希值,在位数组中把对应下标的值置为 1 。
判断元素是否存在于布隆过滤器:
1. 对给定元素再次进行相同的哈希计算。
2. 得到值之后判断位数组中的每个元素是否都为 1 ,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1 ,说明该元素不再布隆过滤器中。
1. 对给定元素再次进行相同的哈希计算。
2. 得到值之后判断位数组中的每个元素是否都为 1 ,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1 ,说明该元素不再布隆过滤器中。
缓存雪崩
缓存在同一时间大面积失效,后面的请求都直接落到了数据库上,造成数据库短时间内承受大量请求压力。
采用 Redis 集群,避免单机出现问题整个缓存服务没有版本使用。
限流,避免同时处理大量的请求。
如何保证缓存和数据库的一致性
缓存失效时间变短(不推荐)
增加 cache 更新重试机制(常用):
如果 cache 服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己设定。如果措辞重试还是失败的话,我们可以把当前更新失败的 key 存入列队中,等缓存服务可用之后,再将缓存中的 key 删除即可。
如果 cache 服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己设定。如果措辞重试还是失败的话,我们可以把当前更新失败的 key 存入列队中,等缓存服务可用之后,再将缓存中的 key 删除即可。
优点
读写性能优异,Redis 能读的速度是 110000 次/s,写的速度是 81000 次/s
支持数据持久化,支持 AOF 和 RDB 两种持久化方式
支持事务,Redis的所有操作都是原子性的,同时 Redis 还支持对几个操作合并后的原子性执行。
数据结构丰富,除了支持 stirng 类型外,还支持 hash、set、zset、list 等数据结构
支持主从复制,主机会自动将数据同步到从机,可以进行读写分离
缺点
数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
数据类型
string
string 数据结构是简单的 key-value 类型。虽然 Redis 是用 C 语言写的,但是 Redis 并没有使用 C 的字符串表示,而是自己构建了一种 简单动态字符串(simple dynamic string,SDS)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的 SDS API 是安全的,不会造成缓冲区溢出。
常用命令: set,get,strlen,exists,decr,incr,setex 等等。
list
list 即是 链表。链表是一种非常常见的数据结构,特点是易于数据元素的插入和删除并且且可以灵活调整链表长度,但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 LinkedList,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 list 的实现为一个 双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
常用命令: rpush,lpop,lpush,rpop,lrange、llen 等。
应用场景: 发布与订阅或者说消息队列、慢查询。
hash
hash 类似于 JDK1.8 前的 HashMap,内部实现也差不多(数组 + 链表)。不过,Redis 的 hash 做了更多优化。另外,hash 是一个 string 类型的 field 和 value 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息,商品信息等等。
常用命令: hset,hmset,hexists,hget,hgetall,hkeys,hvals 等。
应用场景: 系统中对象数据的存储。
set
set 类似于 Java 中的 HashSet 。Redis 中的 set 类型是一种无序集合,集合中的元素没有先后顺序。当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提供的。可以基于 set 轻易实现交集、并集、差集的操作。比如:你可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程。
常用命令: sadd,spop,smembers,sismember,scard,sinterstore,sunion 等。
应用场景: 需要存放的数据不能重复以及需要获取多个数据源交集和并集等场景
sorted set
和 set 相比,sorted set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。
常用命令: zadd,zcard,zscore,zrange,zrevrange,zrem 等。
应用场景: 需要对数据根据某个权重进行排序的场景。比如在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息。
bitmap
bitmap 存储的是连续的二进制数字(0 和 1),通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 bitmap 本身会极大的节省储存空间。
常用命令: setbit 、getbit 、bitcount、bitop
应用场景: 适合需要保存状态信息(比如是否签到、是否登录...)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。
单线程模型
文件事件处理器(file event handler)(基于 Reactor 模式设计了一套高效的事件处理模型)
多个 sockert (客户端连接)
IO 多路复用程序(支持多个客户端连接的关键)
文件事件分派器(将 socket 关联到相应的事件处理器)
事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
文件事件处理器通过 IO 多路复用监听来自客户端的大量连接(I/O 多路复用技术的使用让 Redis 不需要额外创建多余的线程来监听客户端的大量连接,降低了资源的消耗(和 NIO 中的 Select 组件很像))
多线程设计
4.0 之后版本加入了多线程的支持,但是6.0之前主要还是多线程处理
6.0 引入多线程主要是为了提高网络 IO 读写性能(这个属于 Redis 的一个性能瓶颈)
多线程默认是禁止使用,只是用主线程。
开启配置(redis.config):
io-threads-do-reads yes # 开启多线程
io-threads 4 #设置线程核心数。官网建议 4 核的机器建议设置为 2 或 3 个线程
开启配置(redis.config):
io-threads-do-reads yes # 开启多线程
io-threads 4 #设置线程核心数。官网建议 4 核的机器建议设置为 2 或 3 个线程
0 条评论
下一页