导图社区 Redis详解
Redis详解,详细介绍redis相关知识点,涵盖基础原理、命令、运维、尤其相关命令的方法的详解。
编辑于2021-11-19 16:39:56Redis
数据类型
Redis的发布订阅
Redis的五大数据类型
string(字符串)
String(字符串) string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。 string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。 string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512M
hash(哈希,类似java里的Map)
Hash(哈希) Redis hash 是一个键值对集合。 Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 类似Java里面的Map
list(列表)
List(列表) Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)。 它的底层实际是个链表
set(集合)
Set(集合) Redis的Set是string类型的无序集合。它是通过HashTable实现实现的,
zset(sorted set:有序集合)
zset(sorted set:有序集合) Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。 不同的是每个元素都会关联一个double类型的分数。 redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
bitmaps
HyperLogLog
GEO
pipeline
功能
bitmap
hyperLogLog
geospatial
pub/sub
pipeline
lua脚本
事务
持久化
RDB(Redis DataBase)
触发机制
手动触发
save
阻塞redis服务器,直到持久化完成
bgsave
fork一个子进程,由子进程完成持久化
自动触发
满足save n m条件 就会自动触发,即m秒内数据集发生n次改变
debug reload
流程说明
1、执行bgsave命令,没有子进程在执行,才会创建成功
2、父进程执行fork操作创建子子进程,fork过程会阻塞,通过info stats命令查看最佳一个fork耗时
3、父进程fork完后,bgsave命令返回background saving started信息,并不在阻塞父进程,响应其他命令
4、子进程创建RDB文件,生成临时快照文件,替换原来的文件
5、进程发送信号给父进程表示完成,父进程更新统计信息
AOF(Append Only File)
开启配置:appendonly yes;默认不开启 Aof保存的是appendonly.aof文件
流程
1、所有写入命令会追加到aof_buf缓冲区中
2、AOF缓冲区根据对应的策略向硬盘做同步操作
3、AOF超过配置最大值,会进行重写
4、redis重启时,会加载AOF文件进行数据恢复
重写机制
为了防止文件过大
手动触发
bgrewriteaof
自动触发
auto-aof-rewrite-min-size和auto-aof-rewrite-percentage
流程
1、执行AOF重写请求
2、父进程执行fork创建子进程
3、父进程fork完后继续响应其他命令
4、子进程根据内存快照,按照命令合并规则写入到新的AOF文件
5、新AOF文件写入完成后,子进程发送信号给父进程,父进程更新统计信息,父进程把AOF重写缓冲区的数据写入到新的AOF文件,使用新AOF文件替换老文件,完成AOF重写
重启加载
流程
1、AOF持久化开启且存在AOF文件时,优先加载AOF文件
2、AOF关闭或者AOF文件不存在时,加载RDB文件
3、加载AOF/RDB文件成功后,redis启动成功
4、AOF/RDB文件存在错误时,redis启动失败并打印错误信息
4.0/5.0新特性
命令
http://doc.redisfans.com/
键 key
del
删除
可指定多个
返回被删除的数量
exists
是否存在
若 key 存在,返回 1 ,否则返回 0
expire
更新有效时间
单位秒
成功返回1,否则0
pexpire
和 EXPIRE 命令的作用类似
以毫秒为单位设置 key 的生存时间
expireat
和 EXPIRE 类似
不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)
EXPIREAT cache 1355292000
pexpireat
和 EXPIREAT 命令类似
以毫秒为单位设置 key 的过期 unix 时间戳
persist
移除给定 key 的生存时间
即设置为永不过期
成功返回1,否则0
ttl
查看剩余有效时间
单位秒
pttl
查看剩余有效时间
单位毫秒
keys
根据表达式批量获取key
migreate
不同实例之间迁移数据
move
将当前数据库的 key 移动到给定的数据库 db 当中
object
允许从内部察看给定 key 的 Redis 对象
子命令
REFCOUNT
返回给定 key 引用所储存的值的次数。此命令主要用于除错
ENCODING
返回给定 key 锁储存的值所使用的内部表示(representation)
IDLETIME
返回给定 key 自储存以来的空转时间(idle, 没有被读取也没有被写入),以秒为单位
randomkey
从当前数据库中随机返回(不删除)一个 key
RENAMENX
RESTORE
dump
序列化
sort
返回或保存给定列表、集合、有序集合 key 中经过排序的元素
排序默认以数字作为对象,值被解释为双精度浮点数,然后进行比较
rename
key重新命名
成功返回1,否则0
type
返回 key 所储存的值的类型
类型
none
(key不存在)
string
(字符串)
list
(列表)
set
(集合)
zset
(有序集)
hash
(哈希表)
SCAN
字符串 String
append
如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾
如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样
返回执行后字符串的总长度
bit
setbit
getbit
对 key 所储存的字符串值,获取指定偏移量上的位(bit)
bitcount
给定字符串中,被设置为1的比特位的数量
bitop
BITOP operation destkey key [key ...]
操作类型
AND
OR
NOT
XOR
数值增减
增
incr
incrby
incrbyfloat
为 key 中所储存的值加上浮点数增量 increment
如果 key 不存在,那么 INCRBYFLOAT 会先将 key 的值设为 0 ,再执行加法操作
减
decr
key 中储存的数字值减一
不存在的key先初始化为0,然后减1
decrey
基于decr指定缩减长度
get
getrange
返回 key 中字符串值的子字符串,字符串的截取范围由 start 和 end 两个偏移量决定(包括 start 和 end 在内)
-1 表示最后一个字符, -2 表示倒数第二个,以此类推
getset
将给定 key 的值设为 value ,并返回 key 的旧值(old value)
mget
返回所有(一个或多个)给定 key 的值
mset
同时设置一个或多个 key-value 对
msetnx
同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
即使只有一个给定 key 已存在, MSETNX 也会拒绝执行所有给定 key 的设置操作
MSETNX 是原子性的
psetex
和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间
set
将字符串值 value 关联到 key
特别注意
如果 key 已经持有其他值, SET 就覆写旧值,无视类型
参数
EX
过期时间
秒
PX
过期时间
毫秒
NX
只有key不存在才设置
XX
key存在才操作
setex
将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)
是一个原子性(atomic)操作
setnx
将 key 的值设为 value ,当且仅当 key 不存在
setrange
用 value 参数覆写(overwrite)给定 key 所储存的字符串值
strlen
返回 key 所储存的字符串值的长度
哈希 Hash
hdel
hexists
hget
hgetall
hincrby
为哈希表 key 中的域 field 的值加上增量 increment
hincrbyfloat
hkeys
返回哈希表 key 中的所有域
hlen
返回哈希表 key 中域的数量
hmget
hmset
hset
hsetnx
hvals
返回哈希表 key 中所有域的值
hscan
列表 List
LPOP
移除并返回列表 key 的头元素
BLPOP
列表的阻塞式(blocking)弹出原语
给定 key 都不存在或包含空列表,那么 BLPOP 命令将阻塞连接,直到等待超时,或有另一个客户端对给定 key 的任意一个执行 LPUSH 或 RPUSH 命令为止
给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素
RPOP
移除并返回列表 key 的尾元素
BRPOP
BRPOPLPUSH
RPOPLPUSH source destination
执行
将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端
将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素
RPOPLPUSH
LINDEX
返回列表 key 中,下标为 index 的元素
-1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素
LINSERT
LINSERT key BEFORE|AFTER pivot value
将值 value 插入到列表 key 当中,位于值 pivot 之前或之后
当 pivot 不存在于列表 key 时,不执行任何操作
当 key 不存在时, key 被视为空列表,不执行任何操作
LLEN
返回列表 key 的长度
LPUSH
将一个或多个值 value 插入到列表 key 的表头
LPUSHX
将值 value 插入到列表 key 的表头,当且仅当 key 存在并且是一个列表
当 key 不存在时, 什么也不做
RPUSH
将一个或多个值 value 插入到列表 key 的表
RPUSHX
将值 value 插入到列表 key 的表尾,当且仅当 key 存在并且是一个列表
当 key 不存在时, 什么也不做
LRANGE
返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定
LREM
根据参数 count 的值,移除列表中与参数 value 相等的元素
count类型
count > 0
从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count
count < 0
从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值
count = 0
移除表中所有与 value 相等的值
返回
被移除元素的数
LSET
将列表 key 下标为 index 的元素的值设置为 value
当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误
LTRIM
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除
LTRIM key start stop
集合 Set
sadd
将一个或多个 member 元素加入到集合 key 当中
特别注意
已经存在于集合的值将被忽略
scard
返回集合 key 的基数(集合中元素的数量)
sdiff
SDIFF key [key ...]
返回多个集合的差值
即多个集合中值不相等的元素
sdiffstore
SDIFFSTORE destination key [key ...]
类似SDIFF
将结果保存到destination
若destination已存在,则将其覆盖
destination 可以是 key 本身
sinter
SINTER key [key ...]
返回给定集合的交集
sinterstore
SINTERSTORE destination key [key ...]
类似SINTER
将结果保存到destination
若destination已存在,则将其覆盖
destination 可以是 key 本身
sismember
SISMEMBER key member
判断 member 元素是否集合 key 的成员
smembers
SMEMBERS key
返回集合 key 中的所有成员
不存在的 key 被视为空集合
smove
SMOVE source destination member
spop
SPOP key
移除并返回集合中的一个随机元素
srandmember
SRANDMEMBER key [count]
参数
只提供key
返回集合的一个随机元素
不移除
count
正数
小于集合基数
返回count个随机元素
大于集合基数
返回全部
负数
类似正数
元素可以重复
srem
SREM key member [member ...]
移除集合中的一个或多个元素
sunion
SUNION key [key ...]
返回多个集合的并集
是否去重?
sunionstore
SUNIONSTORE destination key [key ...]
类似sunion
将结果存储在destination
存在则覆盖
destination 可以是 key 本身
SSCAN
有序集合 SortedSet
zadd
ZADD key score member [[score member] [score member] ...]
score主要用于排序
返回
被成功添加的新成员的数量
不包括那些被更新的、已经存在的成员
zcard
返回
集合的元素数量
zcount
ZCOUNT key min max
返回
有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量
zincrby
ZINCRBY key increment member
increment可为正数、负数
为有序集 key 的成员 member 的 score 值加上增量 increment
zrange
ZRANGE key start stop [WITHSCORES]
如果你需要成员按 score 值递减(从大到小)来排列,请使用 ZREVRANGE 命令
返回
有序集 key 中,指定区间内的成员
成员的位置按 score 值递增(从小到大)来排序
zrangebyscore
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列
zrank
ZRANK key member
返回有序集 key 中成员 member 的排名
有序集成员按 score 值递增(从小到大)顺序排列
ZREM
ZREM key member [member ...]
移除有序集 key 中的一个或多个成员,不存在的成员将被忽略
ZREMRANGEBYRANK
ZREMRANGEBYSCORE
ZREVRANGE
ZREVRANGEBYSCORE
ZREVRANK
ZSCORE
ZUNIONSTORE
ZINTERSTORE
ZSCAN
Pub/Sub
PSUBSCRIBE
PUBLISH
PUBSUB
PUNSUBSCRIBE
SUBSCRIBE
UNSUBSCRIBE
Transaction
DISCARD
EXEC
MULTI
UNWATCH
WATCH
单线程理解
命令执行过程
建立网络链接
连接池解决
客户端发送命令
服务端接收命令
每一条到达服务端的命令不会立刻执行,所有的命令都会进入一个队列中,然后逐个被执行
服务端执行命令
单线程处理
服务端返回结果
为什么快
绝大部分请求是纯粹的内存操作
采用单线程,避免了不必要的上下文切换和竞争条件
非阻塞IO - IO多路复用
应用场景
redis可以做什么
缓存
排行榜系统
计数器应用
社交网络
消息队列系统
redis不可以做什么
存储大数据成本高
冷数据放redis浪费资源
运维
部署架构
单机模式
主从复制模式
流程
1. slave启动后,向master发送SYNC命令,master接收到SYNC命令后通过bgsave保存快照(即上文所介绍的RDB持久化),并使用缓冲区记录保存快照这段时间内执行的写命令
2. master将保存的快照文件发送给slave,并继续记录执行的写命令
3. slave接收到快照文件后,加载快照文件,载入数据
4. master快照发送完后开始向slave发送缓冲区的写命令,slave接收命令并执行,完成复制初始化
5. 此后master每次执行一个写命令都会同步发送给slave,保持master与slave之间数据的一致性
注意
不能自动做故障转移
包含一个主数据库实例(master)与一个或多个从数据库实例(slave)
master挂了以后,不影响slave的读,但redis不再提供写服务,master重启后redis将重新对外提供写服务
master挂了以后,不会在slave节点中重新选一个master
一个master可以拥有多个slave,但是一个slave只能对应一个master
弊端就是不具备高可用性
Sentinel哨兵
机制
每个sentinel以每秒钟一次的频率向它所知的master,slave以及其他sentinel实例发送一个 PING 命令
如果一个实例距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被sentinel标记为主观下线。
如果一个master被标记为主观下线,则正在监视这个master的所有sentinel要以每秒一次的频率确认master的确进入了主观下线状态
当有足够数量的sentinel(大于等于配置文件指定的值)在指定的时间范围内确认master的确进入了主观下线状态, 则master会被标记为客观下线
在一般情况下, 每个sentinel会以每 10 秒一次的频率向它已知的所有master,slave发送 INFO 命令
当master被sentinel标记为客观下线时,sentinel向下线的master的所有slave发送 INFO 命令的频率会从 10 秒一次改为 1 秒一次
若没有足够数量的sentinel同意master已经下线,master的客观下线状态就会被移除;
若master重新向sentinel的 PING 命令返回有效回复,master的主观下线状态就会被移除
注意
探测所有实例状态
master宕机后切换slave为master
master重新启动后,它将不再是master而是做为slave接收新的master的同步数据
sentinel因为也是一个进程有挂掉的可能,所以sentinel也会启动多个形成一个sentinel集群
多sentinel配置的时候,sentinel之间也会自动监控
一个sentinel或sentinel集群可以管理多个主从Redis,多个sentinel也可以监控同一个redis
sentinel最好不要和Redis部署在同一台机器,不然Redis的服务器挂了以后,sentinel也挂了
Cluster模式
sentinel和主从模式的结合体
注意
所有的节点都是一主一从(也可以是一主多从),其中从不提供服务,仅作为备用
不支持同时处理多个key(如MSET/MGET),因为redis需要把key均匀分布在各个节点上,并发量很高的情况下同时创建key-value会降低性能并导致不可预测的行为
支持在线增加、删除节点
客户端可以连接任何一个主节点进行读写
Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接
当主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉
生产环境
配置
daemonize
是否守护进程(默认否)
pidfile
PID文件
默认:/var/run/redis.pid
多个实例时,需要指定不同文件
port
监听端口,默认为6379
bind
指定请求来源白名单
默认接受所有请求
timeout
设置客户端连接时的超时时间,单位为秒
当客户端在这段时间内没有发出任何指令,那么关闭该连接
tcp-keepalive
指定TCP连接是否为长连接,"侦探"信号有server端维护
默认为0.表示禁用
loglevel
日志级别
等级
debug
verbose
notice(默认)
warning
logfile
log 文件地址
默认使用标准输出,即打印在命令行终端的窗口上,修改为日志文件目录
databases
设置数据库的个数
save 900 1 save 300 10 save 60 10000
保存数据快照的频率
即将数据持久化到dump.rdb文件中的频度
解释
在60 秒之内有10000 个keys 发生变化时
在300 秒之内有10 个keys 发生了变化
在900 秒之内有1 个keys 发生了变化
stop-writes-on-bgsave-error(yes/no)
当持久化出现错误时,是否依然继续进行工作,是否终止所有的客户端write请求。默认设置"yes"表示终止,一旦snapshot数据保存故障,那么此server为只读服务。如果为"no",那么此次snapshot将失败,但下一次snapshot不会受到影响,不过如果出现故障,数据只能恢复到"最近一个成功点"
当持久化出现错误时,是否依然继续进行工作
rdbcompression(yes/no)
在进行数据镜像备份时,是否启用rdb文件压缩手段,默认为yes。压缩可能需要额外的cpu开支,不过这能够有效的减小rdb文件的大,有利于存储/备份/传输/数据恢复
rdbchecksum(yes/no)
是否进行校验和
dbfilename dump.rdb
镜像备份文件的文件名
dir
数据库镜像备份的文件rdb/AOF文件放置的路径
slaveof <masterip> <masterport>
设置该数据库为其他数据库的从数据库,并为其指定master信息
masterauth
当主数据库连接需要密码验证时,在这里指定
slave-serve-stale-data yes
当主master服务器挂机或主从复制在进行时,是否依然可以允许客户访问可能过期的数据。在"yes"情况下,slave继续向客户端提供只读服务,有可能此时的数据已经过期;在"no"情况下,任何向此server发送的数据请求服务(包括客户端和此server的slave)都将被告知"error"
slave-read-only
slave-read-only yes
slave是否为"只读",强烈建议为"yes"
repl-ping-slave-period 10
slave向指定的master发送ping消息的时间间隔(秒),默认为10
整合Springboot
默认使用的是lettuce作为redis客户端
单实例
lettuce与Jedis区别
Jedis是同步的,不支持异步,Jedis客户端实例不是线程安全的,需要每个线程一个Jedis实例,所以一般通过连接池来使用Jedis
Lettuce是基于Netty框架的事件驱动的Redis客户端,其方法调用是异步的,Lettuce的API也是线程安全的,所以多个线程可以操作单个Lettuce连接来完成各种操作,同时Lettuce也支持连接池
引入依赖
默认
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
排除lettuce
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
配置
spring: redis: host: 192.168.40.201 port: 6379 password: passw0rd database: 0 # 数据库索引,默认0 timeout: 5000 # 连接超时,单位ms jedis: # 或lettuce, 连接池配置,springboot2.0中使用jedis或者lettuce配置连接池,默认为lettuce连接池 pool: max-active: 8 # 连接池最大连接数(使用负值表示没有限制) max-wait: -1 # 连接池分配连接最大阻塞等待时间(阻塞时间到,抛出异常。使用负值表示无限期阻塞) max-idle: 8 # 连接池中的最大空闲连接数 min-idle: 0 # 连接池中的最小空闲连接数
配置类
配置类注入了自定义的RedisTemplate<String, Object>, 替换RedisAutoConfiguration中自动配置的RedisTemplate<Object, Object>类(RedisAutoConfiguration另外还自动配置了StringRedisTemplate) @Configuration @EnableCaching public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); // 使用Jackson2JsonRedisSerialize 替换默认的jdkSerializeable序列化 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key采用String的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash的key也采用String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value序列化方式采用jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash的value序列化方式采用jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } }
引入哨兵模式
其他与单实例一样
spring: redis: sentinel: master: mymaster nodes: 192.168.40.201:26379,192.168.40.201:36379,192.168.40.201:46379 # 哨兵的IP:Port列表
引入Cluster模式
其他与单实例一样
spring: redis: cluster: nodes: 192.168.40.201:7100,192.168.40.201:7200,192.168.40.201:7300,192.168.40.201:7400,192.168.40.201:7500,192.168.40.201:7600 max-redirects: 3 # 重定向的最大次数
常见问题
java客户端
Jedis
Redis的Java实现的客户端
是比较底层的暴露的Redis的API
Jedis中的Java方法基本和Redis的API保持着一致
Redisson
实现了分布式和可扩展的Java数据结构
提供很多分布式相关操作服务
分布式锁
分布式集合
延迟队列
和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性
Lettuce
高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。目前springboot默认使用的客户端
面试
Redis为什么这么快?