Redis
开源的基于内存的数据存储系统:数据库,缓存和消息队列等
为了提供高速、低延迟的数据访问
以键值对的形式
区分大小写的
默认使用字符串存储数据,且二进制安全
键和值默认是不支持中文,因为键值以二进制形式存储
默认redis在输出结果时会进行美化,如字符串类型的值会自动添加引号,换行符等
–raw redis会直接输出命令的原始结果
–csv :以 CSV 格式输出结果显示
–bigkeys :分析 Redis 数据库中最大的键
–help :查看 redis-cli 的完整帮助文档
type 键 查看键的类型

基本数据类型
字符串
set 键 值
get 键
返回值

del 键
删除一个或多个键值对

exists 键
判断是否存在

keys *
查看所有键

clear
清除会话页面
flushall
删除所有键

redis-cli --raw 可以输出中文
中文乱码是终端问题

ttl 键
查看过期时间 -1没有设置
expire 键 时间
设置过期时间

setex 键 时间 值
设置键值对的过期时间,单位是s

psetex 键 时间 值
设置键值对的过期时间,单位是ms

setnx 键 值
只有键不存在的时候才能设置该键值对

getset 键 值
将当前键设置为对应值,并返回之前的旧值,若没有则返回nil

append 键 值
若键存在并且值是⼀个字符串,则把值追加到现有值的末尾,若键不存在,则添加该键值对,返回现有值的长度

setrange 键 索引(偏移量) 值
从索引开始用值覆盖键存储的原字符串,返回修改后的字符串长度

getrange 键 索引1 索引2
返回指定范围内的子串

incr/decr key
存储的值可以被解释为整型加1或减1

键值对不存,则先初始化0,再加1或减1

存储的值无法被解释为整型,则报错


incrby/decrby 键 增量值/减量值
值为小数,或无法被解释为整型会报错


存储的值可以被解释为整型,则加上一个增量值,或减去一个减量值


存储的值无法被解释为整型,则会报错


incrbyfloat 键 增量值
存储的值可以被解释为浮点型,则加上一个增量

不存在的键值对,先初始化为0,再加上增量

存储的值或增量值无法被解释为浮点数值,则报错

mset 键 值
同时设置一个或多个键值对

mget 键
返回一个或多个键对应的值

msetnx 键 值
当一个或多个键都不存在时,才会设置对应的键值对

strlen 键
返回对应值的字节长度
redis是utf-8编码,所以中文字符串,一个字符占3个字节

列表List
元素可以重复
当列表中元素全部弹出或移除,对应的键也会被自动删除
lpush 列表名 值
从头部开始依次存储元素
lrange 列表名 范围(从0开始,-1 代表末尾)

rpush 列表名 值
从尾部依次添加元素

键存在,但不是列表类型,会报错

lpushx/rpushx 键 值
当键存在且类型是列表时,才会依次插入对应值


lpop 列表名
从头部删除一个值,并返回该值
rpop 列表名
从尾部删除一个值,并返回该值

lpop 列表名 数量
删除对应数量的值

rpoplpush 源列表键 目标列表键
将源列表中尾部弹出,插入到目标列表中,并输出该元素

目标列表不存在会创建该列表,并将元素插入
源列表不存在没有任何实际意义


llen 列表名
返回列表里的元素数量


ltrim 列表名 范围
删除指定范围外的元素

lrem 列表名 数量 元素
移除该列表中指定元素的对应数量,返回被移除元素的数量

当列表中该元素的数量小于指定数量时,只移除列表中对应的数量


当数量设置为0时,会移除列表中所有指定的元素

当指定元素不存在时,返回0

lindex 列表名 索引
返回列表中该索引对应的元素(从0开始)

linsert 列表名 before/after 元素 目标元素
before 将目标元素插入到元素之前
after 将目标元素插入到元素之后
返回列表元素的数量

当元素不存在时无法插入,同时返回-1

lset 列表名 索引 元素
将指定索引位置设置为该元素
正数的索引值从0开始,从头部开始数

负数的索引从-1开始,从尾部开始数


超出范围会报错


blpop 列表名 超时时间(s)
当一个或多个列表中没有任何元素时阻塞直到超时或发现可弹出头部元素为止
列表不存在会在超时时间内等待创建弹出



存在的列表会直接弹出头部元素

列表中有存在的列表,会直接弹出,不会等待创建不存在列表

多个列表中一次只会弹出一个列表中的一个元素

brpop 列表名 超时时间(s)
当一个或多个列表中没有任何元素时阻塞直到超时或发现可弹出尾部元素为止

brpoplpush 源列表名 目标列表名
当源列表中没有任何元素时阻塞,直到超时或发现新的可弹出元素为止
有尾部元素则直弹出并插入

没有则等待创建后继续执行

集合set
元素不能重复,且无顺序
sadd 集合名 值
smembers 集合名
查看集合元素

sismember 集合名 值
查看集合中是否有该元素

srem 集合名 值
删除该元素

spop 集合名 (数量)
移除并返回集合中随机一个或多个元素

当指定数量超过集合元素数量,只会删除集合剩余个数的元素

报错

srandmember 集合名 (数量)
返回集合中随机一个或多个元素

正数时,不会返回重复的元素,当数量超过集合元素数量时,只返回集合内元素

负数时,会返回重复元素,当数量超过集合元素数量时,会返回指定数量的元素

要是整型

smove 源集合名 目标集合名 元素
将指定元素从源集合移动到目标集合
移动成功返回1,失败返回0



scard 集合名
返回集合中元素数量

scan cursor(游标) (match 过滤键名) (count 键数量) (type 键类型)
cursor 从0开始,用于追踪迭代的进度
macth 匹配想要遍历的键,可以使用通配符*
count 表示一次迭代要扫描的元素数量,默认是10
type 表示要查询的键类型
渐进式遍历数据库中的键,非阻塞的迭代方式,Keys命令和Smembers命令类似于数据库中的全表扫描,会造成线程堵塞
基于游标(指针)的迭代器
起始值是0,每次调用会将游标进行更新,可以通过下次扫描使用该游标继续之前的迭代过程,当再次返回的游标是0时,表示迭代已经结束,数据集完整遍历

当提供的游标值是0,会重新开始扫描

若提供的既不是0,也不是上次返回的游标值,会尝试从该游标值指定的位置开始扫描
-
重复扫描某些键:如果游标值小于上一次返回的游标值,可能会重新扫描之前已经扫描过的键

-
跳过某些键:如果游标值大于上一次返回的游标值,可能会跳过某些键

-
返回错误的游标值:如果提供的游标值无效,Redis 可能会返回错误的游标值,甚至直接返回游标值 0,表示迭代完成,但实际可能并未完成
sinter 集合名
一个集合返回全部元素

多个集合返回交集

sinterstore 目标集合名 源集合名
会将sinter 输出的结果集保存在目标集合中,若目标集合存在则会覆盖里面的元素,目标集合也可以是源集合
返回目标集合的元素个数


sunion 集合名
一个集合返回所有元素

多个集合返回并集

sunionstore 目标集合名 源集合名
会将sunion 输出的结果集保存在目标集合中,若目标集合存在则会覆盖里面的元素,目标集合也可以是源集合
返回目标集合的元素个数

sdiff 集合名
一个集合返回所有元素

多个集合返回差集
以第一个表为主体,返回去除和其他表的交集部分剩余的元素

sdiffstore 目标集合名 源集合名
会将sdiff 输出的结果集保存在目标集合中,若目标集合存在则会覆盖里面的元素,目标集合也可以是源集合
返回目标集合的元素个数

有序集合 SortedSet(ZSet)
有序集合的每个元素会关联一个浮点类型的数值,根据该数值从小到大排序
元素是唯一的,但是小数可以重复

成员是唯一的,一个成员对应一个分数
但分数可以重复记录,通过zrange可以查询到多个分数

zadd 集合名 分数 成员
分数必须可以被解释为浮点数

nx
只有当成员不存在时才会插入




xx
只有成员存在时,才会将对应分数修改为指定分数


gt
只有指定分数比成员对应分数大时,才会修改分数

lt
只有在成员分数比指定分数大时,才会修改分数



ch
当插入或更新成功后返回实际修改的成员数量(包括分数更新)




incr
将成员对应分数和指定分数相加,更新分数

当成员不存在时,会创建该成员并将分数初始化0和指定分数相加,插入集合

zrem 集合名 成员
将一个或多个成员移除,当成员不存在时会被忽略

zremrangebyrank 集合名 范围
删除指定范围内的成员 (从小到大)


zremrangebyscore 集合名 分数1 分数2
移除指定分数区间内的成员

zremrangebylex 集合名 字符序
字典序是逐字符进行比较的


zrange 集合名 范围
zrange 集合名 范围 withscores
同时输出成员和分数

byscore
按照分数范围返回成员

rev
逆序返回成员,默认情况从低到高

bylex
根据字典序返回成员
-表示最小字典序
+表示最大字典序
不能和withscores搭配

( 不包括指定值
[ 包括指定值

limit 偏移量 数量
分页 从偏移量开始查询指定数量的成员,不足指定数量则返回剩余的
偏移量是从通过范围获取到的结果集开始排序
需要和byscore或bylex搭配
可以选择搭配withscores


zrevrange 集合名 范围
从大到小,返回范围内的成员

zrangebyscore 集合名 分数1 分数2 (withscore) (limit 偏移量 数量)
从小到大

zrevrangebyscore 集合名 分数1 分数2 (withscore) (limit 偏移量 数量)
从大到小
+inf 正无穷
-inf 负无穷
[ 包括指定值 默认
( 不包括指定值


zrangebylex 集合名 字典序
返回指定字典序区间的成员

zscore 集合名 元素
查看成员对应的数值
zrank 集合名 元素
查看成员,按照从小到大索引对应的排名

zrevrank 集合名 成员
返回成员的排名 从大到小

不存在的成员返回nil或空值

zincrby 集合名 增量值 成员
将成员的分数加上一个指定的增量值(可以是任意数值)

当成员不存在时会初始化为0,再加上一个增量值

小数和负数

zcard 集合名
返回该集合的成员数量

zcount 集合名 分数1(min) 分数2(max)
返回集合中分数在范围内的成员数量(包括两端)

zlexcount 集合名 分数1 分数2
返回字典序指定区间的成员数量

zscan 集合名 游标 (match 匹配成员) (count 数量) 参考scan


count
指定的数值是建议,不一定会执行
数据量小
zunionstore 目标集合名 参与操作集合数量 源集合名
将一个或多个集合的并集存储到目标集合中 ,返回目标集合中成员数量
相同的成员对应的分数会相加


weights(权重) 浮点数
将对应集合的分数和指定数值相乘后,再继续操作

aggregate
min
并集时保留最小值

max
并集时保留最大值

zinterstore 目标集合名 参与操作集合数量 源集合名
将一个或多个集合的交集存储到目标集合中 ,返回目标集合中成员数量
相同的成员对应的分数会相加




哈希 Hash
字符类型的字段和值的映射表,键值对的哈希表,适合用来存储对象
hset 表名 字段名 值
hget 表名 字段名

hsetnx 表名 字段名 值
当字段名不存在时,插入对应的字段名和值

hgetall 表名
输出表内所有的键值对

hdel 表名 字段名
hexists 表名 字段名
判断是否存在

hkeys 表名
输出表内所有的字段名
hlen 表名
输出表内的键值对个数

hstrlen 表名字段名
返回指定字段对应的值字节长度

hincrby 表名 字段名 增量值(整数)
对应值加上一个增量值
对应字段下的值要能被解释为数值


hincrbyfloat 表名 字段名 增量值
对应值加上一个增量值


hmset 表名 字段名 值
设置一个或多个键值对

hmget 表名 字段名
返回一个或多个字段名对应的值

hvals 表名
返回表中所有的值

hscan 表名 游标 (macth 匹配字段) (count 数量) 参考scan


count 数量是建议不会严格遵守

高级数据类型
地理空间 Geospatial
geoadd 空间名 经度 纬度 地名
nx
在地名不存在时插入

xx
在地名存在时更新位置

ch
返回实际修改的地名数量(包括插入和更新)
返回3 添加旧金山,niuyue 地址被修改两次


geopos 空间名 地名
查看经纬度


geodist 空间名 地名1 地名2
返回两个地方的直线距离,默认单位是m (m|km|ft|mi)

指定单位

范围可以是成员位置或指定经纬度为中心按照圆形或矩形的范围搜索
frommember 地方名
fromlonlat 地理信息
byradius 半径搜索
bybox 矩形搜索
asc 从远到近排序
desc 从近到远排序
withcoord 返回地名的经纬度
withdist 返回与中心点的距离
withhash 返回地名的哈希字符串
count 数量 指定返回地名的数量
geosearch 空间名 frommember 地方名 byradius 半径长度 单位


geosearchstore
和geosearch一样搜索,将结果集存储到目标空间

gohash 空间名 地名
以哈希字符串的形式返回成员的地理信息

georadiusbymember 空间名 地名 半径 单位(m|km|ft|mi)
返回以该地方为中心,指定半径的范围内的地名

georadius 空间名 经度 纬度 半径 单位
返回以指定地理信息为中心,指定半径范围内的地名

HyperLogLog
做基数统计的算法
基数:集合中每个元素都是唯一且不重复的,这个集合的基数就是元素的个数
pfadd 集合名 元素
pfcount 集合名
输出该集合的基数

pfmerge 结果集名 集合名1 集合名2
合并两个集合到结果集中

pfdebug 命令 键名 [additional arguments]
- help:显示关于 PFDEBUG 命令的帮助信息
- digest:返回指定 HLL 的内部哈希摘要。
- encoding:返回指定 HLL 的编码方式(通常是 dense 或 sparse)
- regdump:返回指定 HLL 的寄存器(register)值
- cardinality:返回指定 HLL 的基数估计值。
- merge:将多个 HLL 合并为一个,并返回合并后的 HLL 基数估计值
- clear:清空指定 HLL
位图Bitmap
字符串类型的扩展
可以使用一个string类型模拟一个bit数组,数组下标就是偏移量,值只有0和1,也支持一些位运算

setbit 键名 偏移量 值
设置某个偏移量的值
getbit 键名 偏移量
输出对应的值

利用字符串命令设置,利用十六进制来表示二进制的数

bitcount 键名 统计有多少数值是1
bitpos 键名 0/1 返回第一次出现0或1的位置

bitop 操作命令 目标键名 源键名
- AND:对一个或多个键执行逻辑与操作
- OR:对一个或多个键执行逻辑或操作
- XOR:对一个或多个键执行逻辑异或操作
- NOT:对单个键执行逻辑非操作
对一个或多个保存二进位的字符串进行位操作,并将结果存储到指定键中


因为结果是
\x07\x0d\x0c\x06\x04\x14(这些是不可见的控制字符)


位域 Bitfield
将很多小的整数存储到较大位图中
bitfield 键名 set 类型 位置 值
u8:8位无符号整数
#0:第一个位置
get 键名
返回对应的十六进制形式的值

bitfield 键名 get 类型 位置
返回对应的值


incrby
增加

消息队列 Stream
轻量级的消息队列
由于发布订阅无法实现消息持久化
Stream提供消息持久化和主备复制功能
xadd 流名 * 字段名 值
返回消息id,一次可以发送多个消息
"* " 表示自动生成一个消息id
消息内容可以重复
maxlen 数量
确保该流最多只能保存指定数量的消息

~
近似修剪,可能会保留一些额外的消息,但效率高
=
精确修剪,严格按照要求,删除信息,默认

nomkstream
当流不存在时,不会创建流
省略时,流不存在会创建

手动指定消息 ID,必须确保它大于流中当前的最新消息 ID
minid id
删除比指定id小的消息


limit 数量
限制删除的最大数量
但它只能和~搭配使用


xlen 流名
返回消息个数

时间戳-序列号 手动指定消息id,和自动一样要保证时间戳递增

xrange 流名 范围
- + 表示所有

起始id 结束id count 数量
查找指定范围内指定数量的消息

xdel 流名 消息id
删除id对应的消息

xread count 数量 block 时间(ms) streams 流名 id
从频道的指定id开始读取指定数量的消息,读取不到阻塞指定时间


xread count 数量 block 时间 stream 流名 $
获取从当前时间后的最新消息


同时读取多个流

xinfo stream 流名
返回流的基本信息

full 返回流的全部信息

count 数量
限制返回消息的数量

xgroup create 流名 组名 id
创建消费者组
$
消费者组,从最新的消息开始读取


mkstream
当指定流不存在时,强制创建

entriesread 数量
作用在恢复消费者组状态的时,记录消费者组已经读取过的消息数量,在下次读取时会直接跳过指定数量的消息



xinfo groups 流名
查看消费者组的信息
组名
消费者数量
待处理消息数量等

xinfo consumers 流名 组名
返回消费者组中消费者列表


xgroup createconsumer 流名 组名 消费者名
在指定消费者组中创建一个消费者


xgroup delconsumer 流名 组名 消费者名
在指定消费者组中删除指定消费者

xgroup destroy 流名 组名
删除指定消费者组

xgroup createconsumer 消息名 组名 消费者名
添加一个消费者到组内

xgroup setid 流名 组名 id
将消费者组的起始id设置为指定id,便于消费者从指定位置开始读取消息


$
从最新的消息后开始

entriesread 数量
设置消费者组已经读取消息的数量


xreadgroup group 组名 消费者名 stream 流名 >
> 表示从消费者组的last-delivered-id(游标) 之后的消息开始读取
启动消费者并读取消息

xreadgroup group 组名 消费者名 count 数量 block 时间 streams 消息名 >
一次读取指定数量的消息,读取不到阻塞指定时间,> 从该消息中读取最新消息

xack 流名 组名 消息id
消费者确认消息,移出待处理,返回确认成功的数量

确认多条消息
已经确认或不在待处理列的消息,不会被计入

xclaim 流名 组名 最小空闲时间(ms) 消息id
只有超过最小空闲时间的消息才会被重新获取
重新获取待处理消息的所有权,处理消息获取失败或消息重新分配

- IDLE ms(可选):指定消息的空闲时间,用于过滤消息
- TIME unix-time-milliseconds(可选):指定消息的相对时间,用于计算空闲时间
- RETRYCOUNT count(可选):指定消息的重试次数
- FORCE(可选):强制重新获取消息,即使消息的空闲时间不足
- JUSTID(可选):只返回消息 ID,而不返回消息内容
- LASTID lastid(可选):指定最后一个消息 ID,用于分页获取消息
xtrim 流名 maxlen 0
maxlen 数量
当流的长度超过指定数量,则修剪该流
删除所有消息

minid id
当消息id低于指定id,则删除该消息

=
精确修剪,删除不符合要求的消息,默认
~
近似修剪,允许不符合要求消息的存在
limit 数量
限制删除消息的数量


发布订阅模式
无法消息持久化,无法记录历史消息
基于频道
subscribe 频道名称 等待接收消息,可以多个客户端订阅一个频道

publish 频道名称 消息 发送消息

基于模式
psubscribe
通过模式和频道匹配,发布订阅
? 表示一个占位符,单字符匹配
* 表示任意占位符


事务
在一次请求中执行多个命令
redis中命令是原子性的,而事务没有原子性
在事务执行的过程中,其他客户端提交的命令不会插入到事务执行命令的序列中
在队列缓存中命令并不会被执行
- 原子性:Redis 事务在某些情况下可以保证原子性,但不支持回滚机制,因此在命令语法错误或服务器宕机的情况下无法完全保证原子性
- 一致性:Redis 事务在不发生宕机的情况下可以保证一致性,但在服务器宕机的情况下无法保证
- 隔离性:Redis 事务可以完全保证隔离性,因为 Redis 使用单线程模式执行命令
- 持久性:Redis 事务的持久性取决于持久化配置,使用 AOF 持久化并配置为每次写入都刷盘时可以保证持久性
multi 开启事务
exec 提交事务

事务中运行时失败的命令不会影响其他命令的执行

事务中命令编译时失败,会影响事务提交,所有命令无法执行

discard
取消事务

watch 键名
使事务具有回滚的能力
监视指定的一个或多个键值对,决定是否回滚
当监视的键值对发生变化,则不执行事务中的任何命令,回滚事务
当监视的键值对没有发生变化,则执行命令,提交事务
事务外的命令不会受到影响

unwatch
取消所有键值对的监测


持久化
Redis 会优先载入 AOF 文件来恢复数据,而不是 RDB 文件
AOF 文件通常包含了更完整的操作记录,从而能够恢复更完整的数据状态
RDB 文件是定时生成的数据快照,所以它可能没有记录到最后一次快照之后发生的所有更改
使用 AOF 文件恢复数据可以提供更高的数据完整性
RDB
在指定时间间隔内将内存中的数据快照写入磁盘,某个时间点上数据的完整副本
自动触发
配置redis.conf
save 时间(s) 被修改键值对的数量
在指定时间内超过指定数量的键值对被修改,就生成rdb

stop-writes-on-bgsave-error
yes:后台保存数据出现错误时,停止所有写入操作,确保在可能的磁盘问题或其他故障时,不再接受可能导致数据丢失的写操作
no:即使后台保存数据出现错误,仍然接受写入操作

rdbcompression
yes:启用,在保存数据时先进行压缩,减少存储空间的使用

rdbchecksum
yes:在rdb文件末尾添加crc64校验和,每次保存或加载时,都会计算该校验和,确保数据完整性

sanitize-dump-payload
yes:会检查所有数据,确保安全性,但会更慢
client:只有从客户端发来的数据会被检查,同步的数据和rdb不会

rdb-del-sync-files
在主从复制中,主节点需要生成rdb文件发送给从节点进行数据同步
yes:主节点没有启动rdb和aof的情况下,会自动删除这些和复制相关的rdb文件
no:在同步后不会自动删除

rdb文件名

rdb文件存放路径

可以通过save 命令手动触发快照


bgsave
创建(fork)一个子进程将内存中的数据写入硬盘中
子进程会获得父进程内存中的数据副本,但操作系统使用写时复制技术,任何在父进程上的写操作不会影响到子进程中的数据
子进程遍历所有数据集,写入一个新的临时rdb文件,线性操作:数据在磁盘中是连续写入的
完成新的rdb文件写入后,会替换掉旧的rdb文件,并发送信号到父进程,退出子进程

AOF
持续保存服务器上所有修改操作,对于会改变数据的命令,会将该命令写入aof文件中
恢复数据时执行文件中的命令
追加文件,在执行写命令时,不仅将命令写入到内存中,同时将命令写入到追加的文件中

- 命令追加: 将 redis 写操作命令追加到 aof_buf 缓冲区
- 文件写入: 周期性地将 aof_buf 缓冲区的命令写入 AOF 文件的内核缓冲区
- 文件同步:根据配置同步策略,将 AOF 文件缓冲区的内容同步到磁盘
同步策略
- always:每次命令写入立即同步,最高的数据安全性,但效率最低
- everysec:每秒同步一次
- no:由操作系统决定同步时间,效率最高
配置redis.conf
appandonly yes
启用aof

aof文件名

appendfsync
同步频率

no-appendfsync-on-rewrite
yes:避免主进程fsync延迟

自动触发aof重写
auto-aof-rewrite-percentage 文件增长百分比
100 当文件大小是上次重写后的两倍时,会考虑触发
auto-aof-rewrite-min-size 文件大小门槛
超过该门槛,即使增长百分比小也会触发重写

aof-load-truncated
当aof文件在末尾被截断,redis会尝试加载并启动,同时发出日志警告

AOF重写
减少AOF文件大小,去除冗余和不必要的命令
- redis 主进程 fork 子进程来进行 AOF 的重写,生成 AOF 文件
- 在子进程进行 AOF 重写的同时,redis 主进程将新的写操作命令写入 AOF重写缓冲区
- 主进程将 AOF 重写缓冲区的内容写入到新的 AOF 文件中
- 使用新的 AOF 文件替换旧的 AOF 文件
通过读取当前内存数据进行重写,只需查看当前键值对的状态,生成一个简短命令,避免冗余操作

混合持久化
RDB 的快速恢复和 AOF 的数据完整性的优点
先以 RDB 格式保存当前数据状态,然后继续以 AOF 格式记录新的写操作
AOF 重写之前,RDB 和 AOF 都是按照它们各自的持久化策略工作
AOF 重写被触发时,将当前的数据集会首先以RDB 格式写入新 AOF 文件的顶部,然后再追加新的命令到文件的末尾
配置redis.conf
aof-use-rdb-preamble
同时启用rdb和aof

子进程进行 AOF 重写:
- 首先创建新的 AOF 文件 appendonly.rdb
- 将 Redis 当前的数据生成 RDB 快照写入 appendonly.rdb 文件的开始部分
主进程:
- 主进程先将新的写操作命令写入 AOF 重写缓冲区
- 主进程将 AOF 重写缓冲区的内容追加到 appendonly.rdb文件的 RDB 数据的末尾
- 使用 appendonly.rdb 文件替换旧的 AOF 文件

主从复制
- 数据冗余:主从复制实现了数据的热备份
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载,提高Redis服务器的并发量。
- 高可用基石:主从复制还是哨兵和集群能够实施的基础
将主节点redis数据复制到其他从节点
一个主节点可以有多个从节点,一个从节点只能有一个主节点
数据复制只能从主节点到从节点
一般主节点负责写操作,从节点负责读操作
主节点将自己数据的变化通过异步的方式发送到从节点,从节点接收到数据后更新自己的数据
默认就是主节点配置

通过配置redis.conf 实现主从复制
port , pidfile和 dbfilename 配置该为对应端口号



replicaof 配置主节点

从节点

主节点


5.0之前
以下操作需要先开启一个不是6379端口的redis服务
只需要配置port,其他文件名看个人需求
通过在redis实例的命令行配置主从节点




通启动redis服务的参数,配置主从节点


在配置文件中加入slaveof 主节点IP 主节点端口


slaveof命令执行之后,从服务器会根据设置的ip和端口,向主服务器简历socket连接
发送ping命令,检查连接是否可用
- 返回pong:说明socket连接正常,且主节点当前可以处理请求,复制过程继续
- 超时:一定时间后从节点仍未收到主节点的回复,说明socket连接不可用,则从节点断开socket连接,并重连
- 返回pong以外的结果:如果主节点返回其他结果,如正在处理超时运行的脚本,说明主节点当前无法处理命令,则从节点断开socket连接,并重连

有密码需要一步身份验证(很不建议了)
全量同步
从节点初次连接到主节点时,会进行全量同步。主节点会生成一个 RDB 文件,并将其发送给从节点。从节点接收到 RDB 文件后,会加载该文件以更新自己的数据
增量同步
主节点会将后续的写操作命令发送给从节点,从节点会执行这些命令以保持与主节点的数据一致

slave-read-only yes
在从节点的redis.conf中配置,使从节点只能读

全量复制
初次复制或其他无法进行部分复制的情况,将主节点中的所有数据都发送给从节点
- 主节点收到全量复制的命令后,执行bgsave,在后台生成RDB文件,并使用一个缓冲区(称为复制缓冲区)记录从现在开始执行的所有写命令
- 主节点的bgsave执行完成后,将RDB文件发送给从节点;从节点首先清除自己的旧数据,然后载入接收的RDB文件,将数据库状态更新至主节点执行bgsave时的数据库状态
- 主节点将前述复制缓冲区中的所有写命令发送给从节点,从节点执行这些写命令,将数据库状态更新至主节点的最新状态

部分复制
网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点
若网络中断时间过长,导致主节点没能够完整保存中断期间执行的写命令,仍需全量复制
psync 运行id 偏移量
通过info replication查看运行id
偏移量 从节点已经接收到的数据偏移量
分别是
从节点偏移量
运行id
主节点偏移量
主从节点偏移量差值越大表示从节点缺失的数据越多

- 主节点返回 fullresync {runid} {offset}回复,表示主节点要求与从节点进行数据的完整全量复制,其中runid表示主节点的运行ID,offset表示当前主节点的复制偏移量
- 主节点返回 +continue,表示主节点与从节点会进行部分数据的同步操作,将从节点缺失的数据复制过来即可
- 主节点返回 -err,表示主节点的Redis版本低于2.8,无法识别psync命令,此时从节点会向主节点发送sync命令,进行完整的数据全量复制
偏移量
主节点每次向从节点同步了N字节数据后,将修改自己的复制偏移量offset+N
从节点每次从主节点同步了N字节数据后,将修改自己的复制偏移量offset+N
offset用于判断主从节点的数据库状态是否一致:
offset相同,则一致
offset不同,则不一致,此时可以根据两个offset找出从节点缺少的那部分数据
复制积压缓冲区
主节点维护一个固长,先进先出队列,默认大小1mb
主节点传播命令,会先将命令写入复制积压缓冲区
在缓冲区内保存的是主节点最近执行的写命令
时间较早的写命令会被挤出缓冲区
当主从节点偏移量过大,超出缓冲区长度时,则只能全量复制
- 当偏移量之后的数据在缓冲区,则部分复制
- 当偏移量之后的数据不在缓冲区,则全量复制
repl-backlog-size
可以根据需求配置缓冲区大小

哨兵模式
一个分布式系统,为Redis提供高可用性解决方案
可以在一个架构中运行多个 Sentinel 进程(progress), 这些进程使用流言协议 (gossip protocols) 来接收关于主服务器是否下线的信息, 并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个从服务器作为新的主服务器
解决当主节点宕机时需要手动将另一台从节点提升为主节点
以一个独立的进程运行在redis集群中
通过不断发送命令监测各个节点是否运行正常
通知:发现某个节点出现问题,通过发布订阅模式通知其他节点
自动故障转移:当主节点不能正常工作时,开始一个自动故障转移的操作,将一个从节点升级为新的主节点,然后再将其他从节点指向新的主节点
客观下线:当有 N 个哨兵实例时,要有 N/2 + 1 个实例判断 master 为「主观下线」,才能最终判定 Master 为「客观下线」,其实就是过半机制
创建一个sentinel.conf 添加如下配置
# 配置主节点IP 设置1个哨兵
sentinel monitor master 127.0.0.1 6379 1
redis-sentinel sentinel.conf

主节点宕机后



更换过主节点的配置文件

info sentinel
可以查看哨兵的状态
过期策略
定时删除
在设置key过期时间的同时,为key创建一个定时器,让定时器在key的过期时间来临时,对key及进行删除
- 优点:
保证内存被尽快释放 - 缺点:
若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key
定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器产生),性能影响严重
惰性删除
key过期时不删除,每次从数据库中获取key的时候检查是否过期,过期则删除,返回null
- 优点:
删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步 - 缺点:
若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)
定期删除
每隔一段时间执行一次删除(redis.conf 配置文件设置 hz,1s 刷新的频率)过期key操作
- 优点:
- 通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用–处理"定时删除"的缺点
- 定期删除过期key–处理"惰性删除"的缺点
- 缺点:
- 在内存友好方面,不如"定时删除"
- 在CPU时间友好方面,不如"惰性删除"
- 合理设置删除操作的执行时长和执行频率
- 配置
redis.conf的hz选项,默认为10 (即1秒执行10次,100ms一次,值越大说明刷新频率越快,最Redis性能损耗也越大) - 配置
redis.conf的maxmemory最大值,当已用内存超过maxmemory限定时,就会触发主动清理策略
- 配置
- 删除机制
遍历每个数据库(就是redis.conf中配置的"database"数量,默认为16)- 检查当前库中的指定个数个key(默认是每个库检查20个key,注意相当于该循环执行20次,循环体时下边的描述)
- 如果当前库中没有一个key设置了过期时间,直接执行下一个库的遍历
- 随机获取一个设置了过期时间的key,检查该key是否过期,如果过期,删除key
- 判断定期删除操作是否已经达到指定时长,若已经达到,直接退出定期删除
- 检查当前库中的指定个数个key(默认是每个库检查20个key,注意相当于该循环执行20次,循环体时下边的描述)
RDB处理过期key
- 从数据库持久化到RDB文件之前,检查key是否过期,过期不写入RDB文件
- 加载到数据库时,对key进行过期检查,过期则不导入数据库
AOF处理过期key
- 当 key 过期但还没有被删除时,如果此时进行 AOF 持久化操作,这个过期的 key 不会进入 AOF 文件
- 因为 AOF 持久化是基于命令记录的,只有对 key 进行了写操作(如 SET、LPUSH 等修改数据的命令)才会记录到 AOF 文件
- 过期这个状态本身并没有对应的写操作命令来记录到 AOF 中,除非执行了删除操作
- 当 key 过期后,在发生删除操作时,比如 Redis 的定期删除机制或者惰性删除机制触发了对该过期 key 的删除操作,程序会向 AOF 文件追加一条 DEL 命令
- 在将来通过 AOF 文件恢复数据时,能够准确地恢复数据库的状态,即这个过期的 key 已经被删除了
- 在进行 AOF 重写时,Redis 会先判断 key 是否过期。如果 key 已经过期,那么它不会被重写到 AOF 文件中
- AOF 重写是为了生成一个更小、更高效的 AOF 文件,避免包含已经过期并且应该被删除的 key 的记录
- 可以减少 AOF 文件的大小,提高恢复数据时的效率,同时也能保证数据的一致性,因为当从重写后的 AOF 文件恢复数据时,过期的 key 不会被重新写入内存数据库
淘汰策略
主要分为主动淘汰和被动淘汰
策略主要由 maxmemory-policy 参数配置,决定了当内存不足时如何选择要删除的键
-
noeviction
不淘汰任何数据。当内存不足时,新写入操作会返回错误(OOM,Out Of Memory)- 适用场景:适用于数据绝对不能丢失的场景,例如金融系统中的关键数据
- 特点:内存满后,写操作失败,读操作不受影响
-
allkeys-lru
对所有键使用 LRU(Least Recently Used,最近最少使用) 算法,优先淘汰最近最少使用的键- 适用场景:适用于热点数据频繁访问的场景,例如缓存系统
- 特点:通过访问时间判断键的“活跃度”,优先删除不活跃的键
-
allkeys-lfu
对所有键使用 LFU(Least Frequently Used,最不经常使用) 算法,优先淘汰访问频率最低的键- 适用场景:适合数据访问频率差异较大的场景,优先保留高频访问的数据
- 特点:基于访问频率而非单一时间戳,适合长期运行的系统
-
allkeys-random
从所有键中随机选择键进行淘汰- 适用场景:适用于对数据无明显访问模式的场景,或者测试环境
- 特点:实现简单,性能开销低,但可能误删高价值数据
-
volatile-lru
仅对设置了过期时间(TTL)的键使用 LRU 算法进行淘汰- 适用场景:适用于部分键有明确生命周期的场景,例如临时缓存
- 特点:不会影响未设置过期时间的键,适合混合数据场景
-
volatile-lfu
仅对设置了过期时间的键使用 LFU 算法进行淘汰- 适用场景:与 volatile-lru 类似,但更注重访问频率
- 特点:结合了 TTL 和访问频率的双重筛选
-
volatile-random
从设置了过期时间的键中随机选择键进行淘汰- 适用场景:适用于设置了 TTL 的键无明显访问模式的场景
- 特点:简单高效,但随机性可能导致重要数据被删除
-
volatile-ttl
从设置了过期时间的键中,优先淘汰剩余存活时间(TTL)最短的键- 适用场景:适用于希望优先清理即将过期的键的场景
- 特点:基于过期时间排序,逻辑清晰,适合短生命周期数据
Fast和Slow模式
Redis在执行内存淘汰时的算法运行模式,决定淘汰算法的执行效率和精度,由maxmemory-eviction-tenacity 参数控制
maxmemory-eviction-tenacity- 0:始终使用 Fast 模式。
- 100:始终使用 Slow 模式。
- 中间值(如 10、50):根据内存压力动态选择 Fast 或 Slow 模式,值越大越倾向于 Slow 模式
- Fast 模式:
- 特点:快速执行,采样较少的数据(通常是少量随机样本)
- 优点:性能开销低,适合高并发场景
- 缺点:精度较低,可能导致次优的淘汰选择
- 适用场景:对淘汰精度要求不高,但需要快速响应的场景
- Slow 模式:
- 特点:采样更多的数据,执行更精确的算法(如 LRU/LFU 的完整计算)
- 优点:淘汰选择更准确,能更好地保留高价值数据
- 缺点:性能开销较高,可能影响响应速度
- 适用场景:对数据保留精度要求高的场景
三类问题

缓存雪崩
指在某一时刻,大量缓存同时失效,导致大量请求直接打到数据库层,造成数据库压力骤增,甚至可能导致数据库崩溃、系统不可用的情况
- 缓存集中失效: 通常情况下,缓存的失效时间(TTL)是设置好的,但如果大量缓存键设定了相同或接近的过期时间点,那么在这些缓存集中失效时,会造成大量的请求无法从缓存中读取数据,只能直接访问数据库
- 缓存服务器宕机: 如果 Redis 服务器集群出现宕机或故障,那么所有缓存数据会瞬间不可用,大量请求直接涌向数据库
解决方案:
- 缓存过期时间分散化
为不同的缓存键设置不同的失效时间,使缓存的过期时间均匀分布,避免大量缓存同时失效
// 设置缓存时,加一个随机时间,防止集中过期
int randomTTL = ttl + new Random().nextInt(100);
- 缓存预热
在系统上线前,提前将热点数据加载到缓存中避免大量请求同时触发缓存未命中 - 降级策略
在雪崩时采取限流、降级等策略:在缓存失效时,直接返回默认值或缓存过期的旧数据,避免数据库短时间内处理大量请求 - 多级缓存架构
使用本地缓存+分布式缓存的方式,部分热点数据先放入本地缓存,降低redis和数据库压力 - Redis高可用
部署redis主从集群,使用哨兵模式或redis cluster实现高可用,避免缓存服务器单点故障
缓存击穿
指缓存中存储的某个热点数据在某一时刻失效,大量并发请求同时去访问这个热点数据,导致所有请求打到数据库,造成数据库压力骤增的情况
- 热点缓存失效: 当某个热点数据的缓存过期时,大量请求涌入到数据库层,而此时数据库需要处理所有的请求,造成数据库的瞬时压力增大
解决方案
- 热点数据永不过期
对于特别重要的热点数据,可以考虑不设置缓存过期时间,让数据一直保存在缓存中,通过定时热内手动更新缓存中的数据避免数据过期问题 - 互斥锁机制
通过加锁,保证同一时刻只有一个线程能访问数据库,其他线程需要等待该线程将新数据写入缓存后,再读取缓存
String value = redisTemplate.opsForValue().get(key);
if (value == null) {
// 获取分布式锁
if (redisTemplate.opsForValue().setIfAbsent(lockKey, "lock", 10, TimeUnit.SECONDS)) {
try {
// Double-check
value = redisTemplate.opsForValue().get(key);
if (value == null) {
// 查询数据库
value = database.get(key);
// 将结果写入缓存
redisTemplate.opsForValue().set(key, value, ttl, TimeUnit.SECONDS);
}
} finally {
// 释放锁
redisTemplate.delete(lockKey);
}
} else {
// 等待锁释放后,再从缓存中读取数据
Thread.sleep(100); // 自行调整等待时间
value = redisTemplate.opsForValue().get(key);
}
}
- 预防性缓存更新
在热点数据即将过期时,提前异步刷新缓存,通过检测热点数据的访问频率,当即将过期时自动触发更新操作,避免过期瞬间的击穿问题 - 双缓存机制
一个主要缓存层负责缓存大部分数据,另一个次缓存层保存上次的缓存数据。在主要缓存失效时,可以直接从次缓存层读取数据,避免直接打到数据库
缓存穿透
指恶意用户或程序请求查询的数据在缓存和数据库中都不存在,导致每次请求都会直接打到数据库,绕过缓存
由于缓存没有存储该请求的结果,所有这类请求都会绕过缓存,直接访问数据库,从而导致数据库承受巨大的压力
- 恶意攻击: 有意构造大量不存在的数据请求,如查询不存在的用户 ID 或商品 ID,缓存中没有这些数据,因此直接请求数据库
- 查询不存在的键: 一些业务逻辑上无法避免查询不存在的数据,例如用户查询某些过时或错误的请求参数,数据库中也没有相应的记录
解决方案
- 缓存空结果
查询的某个键在数据库中不存在,则将该键的查询结果(如 null 或空值)缓存起来,并设定一个较短的过期时间,防止该键反复查询打到数据库
// 查询缓存
String value = redisTemplate.opsForValue().get(key);
if (value == null) {
// 查询数据库
value = database.get(key);
if (value == null) {
// 缓存空结果,避免缓存穿透
redisTemplate.opsForValue().set(key, "null", 5, TimeUnit.MINUTES);
} else {
// 将数据库中的值写入缓存
redisTemplate.opsForValue().set(key, value, ttl, TimeUnit.SECONDS);
}
}
- 布隆过滤器
使用布隆过滤器对所有可能存在的数据进行标记,所有请求先经过布隆过滤器进行校验,只有布隆过滤器认为存在的数据,才会去查询缓存或数据库 - 参数校验
在查询请求进入系统前,进行严格的参数校验和过滤,避免不合法的请求进入系统
布隆过滤器
一种数据结构,分配一块内存空间做为bit数组,是由一串很长的二进制向量组成,可以将其看成一个二进制数组,初始值默认为0
- 优点:二进制组成的数组,占用内存极少,并且插入和查询速度都足够快
- 缺点:随着数据的增加,误判率会增加;还有无法判断数据一定存在;另外还有一个重要缺点,无法删除数据(删除意味着需要对应的k个bit设为0,但可能是其他元素对应位)
当布隆过滤器认为,某个数据存在时,这个数据可能不存在
当布隆过滤器认为,某个数据不存在时,那么这个数据一定不存在
- 加入元素时,采用 k 个相互独立的 Hash 函数计算,然后将元素 Hash 映射的 K 个位置全部设置为 1
- 检测 key 是否存在,仍然用这 k 个 Hash 函数计算出 k 个位置,如果位置全部为 1,则表明 key 存在,否则不存在

Redis核心功能与高级特性解析
13万+

被折叠的 条评论
为什么被折叠?



