Redis知识点整理
2021-10-27 14:15:59 27 举报
AI智能生成
Redis知识点整理
作者其他创作
大纲/内容
速度快的原因
完全基于内存
数据结构简单,操作也简单
采用单线程设计,避免上下文切换和竞争条件
使用多路I/O复用模型,非阻塞IO
使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,
Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
通信协议
使用RESP协议
纯文本协议,虽然换行符较多,但是其足够简单,解析也极其容易
数据结构
String
[key|value(string/int/float)]
set
设置置顶key的值
set key value
get
获取指定key的值
get key
incr
将key中储存的数字值增一
incr key
decr key
将key中储存的数字值减一
decr key
incrby
key 所储存的值增加给定的减量值(decrement)
incrby key decrement
decrby
key 所储存的值减去给定的减量值(decrement)
decrby key decrement
append
如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。
append key value
setnx
只有在 key 不存在时设置 key 的值
setnx key value
mget
获取所有(一个或多个)给定 key 的值。
mget key [key...]
mset
同时设置一个或多个 key-value 对
mget key value [key value ...]
getset
将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
getset key value
setex
将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。
setex key seconds value
strlen
返回 key 所储存的字符串值的长度
strlen key
del
删除键
del key
Hash
key-> field1 value(string/int/float)
field2 value(string/int/float)
field3 value(string/int/float)
field2 value(string/int/float)
field3 value(string/int/float)
hset
将哈希表 key 中的字段 field 的值设为 value
srem key field value
hmset
同时将多个 field-value (域-值)对设置到哈希表 key 中
hmset key field1 value1 [field2 value2]
hsetnx
只有在字段 field 不存在时,设置哈希表字段的值
hsetnx key field value
hget
获取存储在哈希表中指定字段的值
hget key field
hmget
获取所有给定字段的值
hget key field1 [field2]
hgetall
获取在哈希表中指定 key 的所有字段和值
hgetall key
hvals
获取哈希表中所有值
hvals key
hlen
获取哈希表中字段的数量
hlen key
hkeys
获取所有哈希表中的字段
hkeys key
hdel
删除一个或多个哈希表字段
hdel key field1 [field2]
hexitst
查看哈希表 key 中,指定的字段是否存在
hexitst key field
List
[key => value1 | 自
value2 | 左
value3 | 而
value4 | 右
]
value2 | 左
value3 | 而
value4 | 右
]
push
lpush
将一个或多个值插入到列表头部
lpush key value1 [value2....]
rpush
将一个或多个值插入到列表尾部
rpush key value1 [value2....]
pop
lpop
移出并获取列表的第一个元素
lpop key
rpop
移出并获取列表的最后一个元素
rpop key
lrange
获取列表指定范围内的元素
lrange key start stop
llen
获取列表长度
llen key
lindex
通过索引获取列表中的元素
lindex key index
lrem
移除列表元素
lrem key count value
count > 0
从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT
count < 0
从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值
count = 0
移除表中所有与 VALUE 相等的值
lset key index value
通过索引设置列表元素的值
lset key index value
Set
key->[ value1
value2
value3
value4
]
value2
value3
value4
]
sadd
向集合添加一个或多个成员(存在则返回0)
sdd key member1 [member2]
scard
获取集合的成员数
scard key
sinter
返回给定所有集合的交集
sinter key1 [key2]
sismember
判断 member 元素是否是集合 key 的成员
sismember key member
smembers
返回集合中的所有成员
smembers key
srandmember
返回集合中一个或多个随机数
srandmember key [count]
srem
移除集合中一个或多个成员
srem key member1 [member2]
ZSet
key-> score(10.1) value(string/int/float) rank:1
score(9.1) value(string/int/float) rank:0
score(11.2) value(string/int/float) rank:2
score(9.1) value(string/int/float) rank:0
score(11.2) value(string/int/float) rank:2
zadd
向有序集合添加一个或多个成员,或者更新已存在成员的分数
zadd key score1 member1 [score2 member2....]
zcard
获取有序集合的成员数
zcard key
zcount
计算在有序集合中指定区间分数的成员数
zcount key min max
zincrby
有序集合中对指定成员的分数加上增量 increment
zincrby key increment member
zrange
通过索引区间返回有序集合成指定区间内的成员
zrange key start stop [withscores]
zrank
返回有序集合中指定成员的索引
zrank key member
zrem
移除有序集合中的一个或多个成员
zrem key member1 [member2....]
zrevrange
返回有序集中指定区间内的成员,通过索引,分数从高到底
zrevrange key start stop [withscores]
zscore
返回有序集中,成员的分数值
zscore key member
功能
bitmap
Bitmap 位图是支持按 bit 位来存储信息,可以用来实现 BloomFilter
hyperLogLog
HyperLogLog 提供不精确的去重计数功能,比较适合用来做大规模数据的去重统计,例如统计 UV;
geospatital
Geospatial 可以用来保存地理位置,并作位置距离计算或者根据半径计算位置等
pub/sub
pub/sub 功能是订阅发布功能,可以用作简单的消息队列
pipeline
Pipeline可以批量执行一组指令,一次性返回全部结果,可以减少频繁的请求应答
减少RTT
lua脚本
支持提交 Lua 脚本来执行一系列的功能
事务
Redis 提供的不是严格的事务,Redis 只保证串行执行命令,并且能保证全部执行,但是执行命令失败时并不会回滚,而是会继续执行下去
4.0、5.0新特性
Module
Stream
一些多读的消息队列
PSYNC2.0
混合RDB-AOF持久化格式
推荐阅读
Redis设计与实现
Redis实战
应用
分布式锁
先用setnx ex来获取锁,并设置过期时间,过期时间主要用来防止业务代码异常,锁没有释放的情况导致死锁的情况
redis分布式锁过期时间到了业务没执行完问题
redssion 中的 watch dog自动延期机制
默认设置过期时间为30秒,只要客户端成功获得锁,就会启动watch dog。每10秒检查一次客户端是否还持有锁,如果有,则延长过期时间
主从异步复制导致的问题
客户端1对某个redis master实例,写入了锁,此时会异步复制给对应的master slave实例
但是这个过程中一旦发生redis master宕机,主备切换,redis slave变为了redis master
接着就会导致,客户端2来尝试加锁的时候,在新的redis master上完成了加锁,而客户端1也以为自己成功加了锁。
此时就会导致多个客户端对一个分布式锁完成了加锁
RedLock算法:https://www.jianshu.com/p/fba7dd6dcef5
计数器
分库分表的自增id
INCR指令实现简单限流
缓存:热点数据
异步队列
list中还有另一个指令,BLPOP key [keys ...] timeout:阻塞直到队列有消息或者超时,当 timeout 为 0 是表示阻塞时间无限制,会导致空闲连接问题,服务器会主动断开,抛出异常,客户端需要捕获处理,或重试,并且只能供一个消费者消费
list结构作为队列,使用rpush/lpush操作入队列,使用lpop 和 rpop来出队列。缺点:没有等待队列里有值就直接消费,可以通过在应用层引入sleep机制来降低请求频率
pub/sub:主题订阅者模式 实现1:N的消息队列,但是消费者下线的时候会导致消息丢失,所以当redis宕机的时候,pub/sub的数据不会持久化
Redis5.0后提供 Stream 解决了pub/sub消息不能持久化的问题。https://blog.csdn.net/enmotech/article/details/81230531
延时队列
使用zset结构,将消息序列化成一个字符串作为 zset 的value,这个消息的到期处理时间作为score
用多个线程轮询 zset 获取到期的任务进行处理,多个线程是为了保障可用性,万一挂了一个线程还有其它线程可以继续处理
解决服务器分布式部署时,由于负载均衡策略,分发用户请求到不同的服务器,导致session不一致的问题
持久化
RDB
RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。对redis中的数据执行周期性的全量持久化到磁盘,恢复快
RDB生成快照可自动触发,也可以使用命令手动触发,以下是redis触发执行快照条件
客户端执行命令save和bgsave会生成快照
save操作阻塞主进程,会导致服务一段时间不可用
bgsave 是fork子进程来执行save操作,仅在fork子进程的时候发生阻塞
根据配置文件save m n规则进行自动快照
主从复制时,从库全量复制同步主库数据,此时主库会执行bgsave命令进行快照
客户端执行数据库清空命令FLUSHALL时候,触发快照
客户端执行shutdown关闭redis时,触发快照
RDB持久化机制的工作流程
1、fork一个子进程出来,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来
2、子进程尝试将数据dump到临时的rdb快照文件中
3、完成rdb快照文件的生成之后,就替换之前的旧的快照文件
优点
- RDB文件数据是被压缩写入的,因此RDB文件的体积要比整个实例内存要小
- 当实例宕机恢复时,加载RDB文件的速度很快,能够在很短时间内迅速恢复文件中的数据
缺点
- 由于是某一时刻的数据快照,因此它的数据并不全
- 生成RDB文件的代价是比较大的,它会消耗大量的CPU和内存资源
适用场景
- 主从全量同步数据
- 数据库备份(冷备)
- 对于丢失数据不敏感的业务场景,实例宕机后快速恢复数据
AOF
AOF 追加 更新指令 到本地文件,达到一定的量后会对文件进行瘦身
AOF持久化配置
always: 每次写入一条数据,同步追加到aof文件,性能非常非常差,吞吐量很低; 确保说redis里的数据一条都不丢失
everysec: 每秒将os cache中的数据追加到aof文件。(默认)
no: 仅仅redis负责将数据写入os cache就撒手不管了,然后后面os自己会时不时有自己的策略将数据刷入磁盘,不可控制(即redis不负责更新aof文件,何时更新由宿主机自己决定,Linux64位大概是30秒一次)
AOF 重写
aof文件是追加的形式增量递增的 ,因为内存的容量是有限的,且redis的有些数据会有过期的概念,和淘汰机制的存在,会导致一开始存储的指令没有实际对应的数据。
当aof文件增长到一定量(auto-aof-rewrite-percentage 100 增长的百分比 ,auto-aof-rewrite-min-size 64mb 触发重写的最小阈值 )的时候会对其进行重写
重写工作流程
redis fork一个子进程
redis主进程,接收到client新的写操作之后,在内存中写入日志,同时新的日志也继续写入旧的AOF文件
子进程写完新的日志文件之后,redis主进程将内存中的新日志再次追加到新的AOF文件中,最后替换旧的文件
优点
该机制可以带来更高的数据安全性,即数据持久性。Redis 中提供了 3 种同步策略,即每秒同步、每修改(执行一个命令)同步和不同步
日志文件的写入操作采用的是 append 模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容
AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,
只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据
只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据
子进程基于当前内存中的数据,构建日志,开始往一个新的临时的AOF文件中写入日志
缺点
对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大
根据同步策略的不同,AOF 在运行效率上往往会慢于 RDB
持久化的意义:故障恢复
持久化恢复数据的时候
AOF,存放的指令日志,做数据恢复的时候,其实是要回放和执行所有的指令日志,来恢复出来内存中的所有数据的
RDB,就是一份数据文件,恢复的时候,直接加载到内存中即可
持久化方案:主从的时候 主节点只做aof (避免aof重写?) 从节点使用aof+rdb混合持久化
key失效机制
主动删除
定期主动删除
Redis内部维护一个定时任务,默认每隔100毫秒会从过期字典中随机取出20个key,删除过期的key,如果过期key的比例超过了25%,则继续获取20个key,删除过期的key,循环往复,直到过期key的比例下降到25%或者这次任务的执行耗时超过了25毫秒,才会退出循环
被动删除
访问时触发被动删除
只有当访问某个key时,才判断这个key是否已过期,如果已经过期,则从实例中删除
淘汰机制
必要性
当 Redis 内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换 (swap);
交换会让 Redis 的性能急剧下降,对于访问量比较频繁的 Redis 来说,这样龟速的存取效率基本上等于不可用;
为了限制最大使用内存,Redis 提供了配置参数 maxmemory 来限制内存超出期望大小 ,超出后执行淘汰策略;
交换会让 Redis 的性能急剧下降,对于访问量比较频繁的 Redis 来说,这样龟速的存取效率基本上等于不可用;
为了限制最大使用内存,Redis 提供了配置参数 maxmemory 来限制内存超出期望大小 ,超出后执行淘汰策略;
淘汰策略
noeviction:不淘汰key(默认)
volatile-lru : 淘汰过期集合的key中最少使用
volatile-ttl : 淘汰过期集合的key中,剩余寿命最短
volatile-lfu:从所有配置了过期时间的键中淘汰最近没有使用的key (4.0新增)
volatile-random : 随机淘汰过期集合的key
allkeys-lru : 淘汰全部key中最少使用
allkeys-lfu:从所有键中淘汰最近没有使用的key (4.0新增)
allkeys-random 随机淘汰全部key
集群方案
基于客户端分片
描述
Redis Sharding是Redis Cluster出来之前,业界普遍使用的多Redis实例集群方法。其主要思想是基于哈希算法,根据Redis数据的key的哈希值对数据进行分片,将数据映射到各自节点上
优点
实现简单
缺点
当Redis集群调整,每个客户端都需要更新调整
基于代理服务器分片
描述
客户端发送请求到独立部署代理组件,代理组件解析客户端的数据,并将请求转发至正确的节点,最后将结果回复给客户端
优点
透明接入,容易集群扩展
缺点
多了一层代理转发,性能有损耗
Redis Sentinel(哨兵)
描述
Redis Sentinel是官方从Redis 2.6版本提供的高可用方案,在Redis主从复制集群的基础上,增加Sentinel集群监控整个Redis集群。当Redis集群master节点发生故障时,Sentinel进行故障切换,选举出新的master,同时Sentinel本身支持高可用集群部署
优点
支持集群高可用,高性能写
缺点
没有实现数据分片,每个节点需要承载完整数据集,负载能力受当个Redis服务器限制,仅支持通过增加机器内存实现垂直扩容,不支持水平扩展
主节点只有一个,写入称为性能瓶颈
主从同步
全量同步
刚建立主从关系后,会触发全量同步
主节点bgsave,将内存中数据生成rdb文件,传输给从节点
在bgsave的过程中,新生成的数据会放到环形buffer里,rdb传输完成后,传输buffer中的数据
如果在bgsave过程中,buffer超出限制并覆盖,则会再次触发全量同步,所以buffer大小需要合理设置
增量同步
传输buffer中的数据
buffer落后超过限制后,触发全量同步
无磁盘复制
2.8之后支持,纯内存复制
哨兵sentinel
主节点挂了后,从节点升级为主节点
哨兵需要保证高可用
哨兵之间采用raft协议保证分布式一致性
在平常,哨兵之间是平等的,只有在检测到redis节点超过一定时间没有回复,才会使用raft协议进行选主
一个哨兵认为server节点下线标记该server为主观下线,超过配置的哨兵数认为server节点下线,则该server节点为客观下线,需要进行主从切换
主从切换对配置文件也会进行修改
raft动图
http://thesecretlivesofdata.com/raft/
Redis Cluster
描述
Redis Cluster 是 在 3.0 版本正式推出的高可用集群方案,相比Redis Sentinel,Redis Cluster方案不需要额外部署Sentinel集群,而是通过集群内部通信实现集群监控,故障时主从切换;同时,支持内部基于哈希实现数据分片,支持动态水平扩容
集群中有多个主节点,每个主节点有多个从节点,主从节点间数据一致
最少3个master节点(投票需要超过半数,防止脑裂)
数据分片
将整个数据集按照一定规则分配到多个节点上,称为数据分片,Redis Cluster采用的分片方案是哈希分片
Redis Cluster首先定义了编号0 ~ 16383的区间,称为槽,所有的键根据哈希函数映射到0 ~ 16383整数槽内,计算公式:slot=CRC16(key)&16383。
每一个节点负责维护一部分槽以及槽所映射的键值数据
每一个节点负责维护一部分槽以及槽所映射的键值数据
槽是 Redis 集群管理数据的基本单位,集群扩容收缩就是槽和数据在节点之间的移动
CAP取舍
CAP包括:一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)
系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须在C和A之间做出选择
系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须在C和A之间做出选择
Redis Cluster选择了AP架构,为了保证可用性,Redis并不保证强一致性,在特定条件下会出现数据不一致甚至丢失写操作
原因
为了在性能和一致性上做出权衡,主从节点间数据同步是异步复制的,当客户端成功写入master节点,master返回成功,master节点才将写操作异步复制给slave节点
当集群发送网络分区,集群可能会分为两部分:多数派和少数派,假如masterA节点位于少数派,如果网络分区发生时间较短,那么集群将会继续正常运作;如果分区的时间足够长,让多数派中选举为新的master替代matsterA,那么分区期间写入masterA的数据就丢失了
在网络分区期间, 客户端可以向matsterA发送写命令的最大时间是有限制的, 这一时间限制称为节点超时时间(cluster-node-timeout),是 Redis集群的一个重要的配置选项
在网络分区期间, 客户端可以向matsterA发送写命令的最大时间是有限制的, 这一时间限制称为节点超时时间(cluster-node-timeout),是 Redis集群的一个重要的配置选项
常见问题
缓存更新方式
数据源
DB
同步更新
先删后更
监听binlog
service
失效更新
同步更新
异步更新
失效时先不清除数据,继续使用旧数据,异步线程去更新缓存
定时更新
定时分批更新
数据不一致
产生原因
主动更新失败:更新DB后redis超时
异步更新失败:远程service不可用
解决办法
缓存数据存活时间长短
长
服务是否对耗时敏感
是
异步补偿,保证及时响应
否
尝试重试,尽量保证缓存成功
短
短期数据不一致不会影响业务稳定,那么等下次更新成功
缓存穿透
产生原因
访问DB中不存在的数据,导致请求穿过缓存一直触达DB
代码错误
恶意请求
解决办法
缓存中保存空对象进行标记,但可能导致缓存中出现大量无效空对象
使用 BloomFilter 过滤器,BloomFilter 的特点是存在性检测,如果 BloomFilter 中不存在,那么数据一定不存在;如果 BloomFilter 中存在,实际数据也有可能会不存在。非常适合解决这类的问题。
缓存击穿
产生原因
热点数据缓存过期,大量读请求触达DB
热点数据在更新时,因为一致性的考量,采用先删缓存再更新的方式,导致在更新时,大量读请求触达DB
热点数据没有做热备处理
解决办法
针对多个热点key同时失效:缓存时间加上一个随机时间,避免大量热点数据同一个时间失效
互斥锁更新,保证数据更新时,不会有大量的读请求触达DB
随机退避,失效时随机sleep一个很短的时间,再次查询后如果缓存还没数据,执行读DB更新缓存操作
热点数据的发现和隔离
缓存雪崩
产生原因
缓存挂掉,所有请求触达DB
解决办法
保证高可用
快速失败:熔断策略,减少 DB 瞬间压力
Q$A
如何做大量数据插入
Redis2.6开始redis-cli支持一种新的被称之为pipe mode的新模式用于执行大量数据插入工作
分布式Redis是前期做还是后期规模上来了再做好?为什么?
既然Redis是如此的轻量(单实例只使用1M内存),为防止以后的扩容,最好的办法就是一开始就启动较多实例。即便你只有一台服务器,你也可以一开始就让Redis以分布式的方式运行,使用分区,在同一台服务器上启动多个实例。
一开始就多设置几个Redis实例,例如32或者64个实例,对大多数用户来说这操作起来可能比较麻烦,但是从长久来看做这点牺牲是值得的。
这样的话,当你的数据不断增长,需要更多的Redis服务器时,你需要做的就是仅仅将Redis实例从一台服务迁移到另外一台服务器而已(而不用考虑重新分区的问题)。一旦你添加了另一台服务器,你需要将你一半的Redis实例从第一台机器迁移到第二台机器。
一开始就多设置几个Redis实例,例如32或者64个实例,对大多数用户来说这操作起来可能比较麻烦,但是从长久来看做这点牺牲是值得的。
这样的话,当你的数据不断增长,需要更多的Redis服务器时,你需要做的就是仅仅将Redis实例从一台服务迁移到另外一台服务器而已(而不用考虑重新分区的问题)。一旦你添加了另一台服务器,你需要将你一半的Redis实例从第一台机器迁移到第二台机器。
如何降低内存使用
少用string,多用剩余的做数据聚合
多用hash
0 条评论
下一页