一、Redis数据操作
1. 数据库操作
# 选择数据库,一共有16个库,i 的范围 0 ~ 15,默认为 0 号库
select i
# 查询数据库容量
dbsize
# 清除当前库
flushdb
# 清除所有库
flushall
2. 数据操作
1)Redis-Key
# 查询数据库中所有key
keys *
# 设置key
set key value
# 判断key是否存在,存在返回1,不存在返回0
exists key
# 移除当前库中的key到指定的库中,如下面的命令将key移动到库1中
move key 1
# 设置key的过期时间i,单位秒,返回-1表示没有过期时间,-2表示key不存在(可能已经过期)
expire key i
# 设置key的同时设置过期时间
set key value ex i
# 查询key的剩余有效原因
ttl key
# 查看key的类型
type key
2)String(字符串)
# 设置值
set key value
# 获取值
get key
# 给key追加字符串,若不存在,则新增一个
append key append_value
# 获取字符串长度
strlen key
######################################################################
#------------步长操作------------
# key自增/自减1
incr/decr key
# 设置增量i,即在 key的值 + i,当 i < 0时即为减量
incrby key i
# 设置减量i, 即key_value - i,当
decrby key i
######################################################################
#---------字符串范围--------------
# 截取字符串[begin, end],0为第一个字符,-1表示最后一个字符
# 当end的值大于字符串的长度时,截取到末尾结束
# getrange key 0 -1 等价于 get key
getrange key begin end
#替换指定位置开始的字符串
#get key abcdef
#setrange key 1 xx axxdef
setrange key index value
######################################################################
# setex(set with expire) 设置过期时间
# setnx(set if not exist) 不存在就设置(在分布式锁中会常常使用)
# 设置过期时间,等价于前面说的:set key value ex i
setex key i value
# 若key存在,则创建失败
setnx key value
######################################################################
mset # 同时设置多个值【k1 v1 k2 v2......】
mget # 同时获取多个值【mget k1 k2...】
msetnx # 原子性操作,多个(key,value)中,若有一个已经存在,则该操作会失败,即使其它key不存在
#对象
set user:1 {name:zhangshan,age;3} # 设置一个user:1对象,值为json字符串来保存对象
# key的巧用:object:{id}:{filed}
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 20
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "20"
getset # 先get后set
127.0.0.1:6379> getset db redis # 不存在,返回nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb # 如果存在,返回原来的值,并设置新值
"redis"
127.0.0.1:6379> get db
"mongodb"
value除了是字符串,还可以是数字:
- 计数器
- 统计数量
3)List(列表)
redis中,可以把List当栈、队列、阻塞队列等使用
所有的List命令以L开头
######################################################################
127.0.0.1:6379> lpush list one # 将一个或多个值放到列表头部(左边)
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1 # 获取指定区间的值(从0开始)
1) "three"
2) "two"
127.0.0.1:6379> rpush list right
(integer) 4
127.0.0.1:6379> lrange list 0 -1 # 将一个或多个值插入到列表尾部(右边)
1) "three"
2) "two"
3) "one"
4) "right"
######################################################################
lpop
rpop
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> lpop list
"three"
127.0.0.1:6379> lrange list 0 -1 # 移除列表第一个(头部、左边)值
1) "two"
2) "one"
3) "right"
127.0.0.1:6379> rpop list # 移除错后一个(尾部、右边)值
"right"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
#移除key中的cnt个element元素
# cnt > 0 从头到尾删除cnt个element元素
# cnt < 0 从尾到头删除|cnt|个element元素
# cnt = = 删除全部值为element元素
lrem key cnt element
######################################################################
lindex # 根据下标获取值
127.0.0.1:6379> lindex list 1
"one"
127.0.0.1:6379> lindex list 0
"two"
######################################################################
llen
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> llen list # 返回列表长度
(integer) 2
# 截取[begin,end]范围的值,并将其赋值给key
ltrim key begin end
# 移除列表最后一个元素,并放到新列表中
rpoplpush # 6.2.0被弃用
# 可以使用lmove source destination right left来代替使用
127.0.0.1:6379> lrange list 0 -1
1) "-1"
2) "0"
3) "hello2"
4) "hello1"
5) "hello"
127.0.0.1:6379> rpoplpush list mylist # 移除列表最后一个元素,并放到新列表中
"hello"
127.0.0.1:6379> lrange list 0 -1 # 原列表中无被移除的元素
1) "-1"
2) "0"
3) "hello2"
4) "hello1"
127.0.0.1:6379> lrange mylist 0 -1 # 新列表中存在新值
1) "hello"
######################################################################
lset key index value # 更新指定下标的值,若不存在指定下标,这报错
# element插入到指定的pivot的前/后面
linsert key before/after pivot element
4)Set(集合)
无序无重复集合
# 往Set集合中添加成员
sadd key member[...member]
# 查看指定key的成员
smembers key
# 等价于只有一个key的sinter交集操作
sinter key
# 判断集合key中是否存在成员member
sismember key member
# 判断集合key中的多个成员member是否存在
smismember key member [...member]
# 查询集合成员个数
scard key
# 移除key中指定的成员mem
srem key mem
# 随机选择cnt个成员,默认一个
srandmember key 【cnt】
# 随机删除成员
spop key
# 将指定的成员mem移动到另外的SET集合key2中
smove key1 key2 mem
# 数学集合
127.0.0.1:6379> sadd key1 a b c
(integer) 3
127.0.0.1:6379> sadd key2 c d e
(integer) 3
127.0.0.1:6379> sdiff key1 key2 # 差集(key1 - key2)
1) "b"
2) "a"
127.0.0.1:6379> sinter key1 key2 # 交集
1) "c"
127.0.0.1:6379> sunion key1 key2 # 并集
1) "b"
2) "a"
3) "c"
4) "d"
5) "e"
5)Hash(哈希)
即Map集合,<key, map>
本质与String没区别,唯一的差异是vlaue从一个String变成了 key value 的map对象
Hash更适合存储对象信息,key就是对象名,field就是对象属性,value就是属性对应的值。
###################################################################################
# 设置一个或多个hash,其中key是mykey,value是一个map对象<field, value>
hset mykey field value [...field value]
# 获取key中指定的字段值
hget key field
# 获取key中指定的多个字段值
hmget key field [...field]
# 获取key中所有字段
hgetall key
# 删除key中指定字段field
hdel key field [...field]
127.0.0.1:6379> hset mykey f1 v1 f2 czm f3 v3 f4 ob # 设置hash
(integer) 4
127.0.0.1:6379> hget mykey f2 # 获取hash中指定field的value
"czm"
127.0.0.1:6379> hmget mykey f2 f4 # 获取hash中指定的多个field的value
1) "czm"
2) "ob"
127.0.0.1:6379> hgetall mykey # 获取指定hash的所有field和value
1) "f1"
2) "v1"
3) "f2"
4) "czm"
5) "f3"
6) "v3"
7) "f4"
8) "ob"
127.0.0.1:6379> hdel mykey f3 # 删除一个指定field
(integer) 1
127.0.0.1:6379> hgetall mykey
1) "f1"
2) "v1"
3) "f2"
4) "czm"
5) "f4"
6) "ob"
127.0.0.1:6379> hdel mykey f2 f4 # 删除多个指定field
(integer) 2
127.0.0.1:6379> hgetall mykey
1) "f1"
2) "v1"
###################################################################################
# 获取hash长度
hlen key
# 获取hash中指定field的长度
hstrlen key field
# 判断hash中的指定字段是否存在
hexists key field
# 只获取所有的字段
hkeys key
#只获取所有的字段值
hvals key
###################################################################################
# 为key中的字段值加上增量increment,increment < 0时表示减量
hincrby key field increment
# 为key中的字段值减上减量incr,incr > 0 时表示增量
# 当key中存在指定的key时,不操作,不存在则新增
hsetnx key field value
6)ZSet/SortedSet(有序集合)
在Set的基础上新增了一个score,并根据器进行排序,默认升序
###################################################################################
127.0.0.1:6379> zadd salary 2500 xh # 添加单个成员
(integer) 1
127.0.0.1:6379> zadd salary 2000 zs 500 ks #添加对各成员
(integer) 2
127.0.0.1:6379> zrange salary 0 -1 # 查询所有成员
1) "ks"
2) "zs"
3) "xh"
127.0.0.1:6379> zrangebyscore salary -inf +inf # 默认就是升序排序
1) "ks"
2) "zs"
3) "xh"
127.0.0.1:6379> zrevrange salary 0 -1 # zrevrange 从大到小排序
1) "xh"
2) "zs"
3) "ks"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores # 查询所有成员,并带着score
1) "ks"
2) "500"
3) "zs"
4) "2000"
5) "xh"
6) "2500"
###################################################################################
# 移除集合中的成员
zrem key member
# 获取集合中的个数
zcard key
# 获取集合中指定范围[min, max]的成员个数
zcount key min max
二、Redis持久化
三、Redis发布订阅
Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。如微信、微博、各种关注系统!
redis客户端可以订阅任意数量的频道。
发布订阅消息图:
第一个:消息发送者
第二个:频道
第三个:消息订阅者
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fp1KhW2F-1648989377753)(D:\Install\Typora\typora-user-images\image-20210611112300495.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RQTMdZLT-1648989377754)(D:\Install\Typora\typora-user-images\image-20210611112432368.png)]
命令
# 订阅给定的一个或多个频道的信息
subscribe channel [...channel]
# 将消息message发送到指定的频道channel
publish channel message
# 退订指定频道,若未指定,则退订所有频道
unsubscribe [channel ...]
# 订阅一个或多个符合给定模式的频道
psubscribe pattern [...pattern]
# 退订所有给定模式的频道
punsubscribe pattern
# 查看发布订阅系统状态
pubsub <subcommand>
# 列出当前活跃频道
pubsub channels
测试
订阅端:
127.0.0.1:6379> subscribe omaster # 订阅消息一个频道 omaster
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "omaster"
3) (integer) 1
# 等待读取频道推送的消息
1) "message" # 类型;消息
2) "omaster" # 消息来源:哪个频道
3) "hello omaster" # 消息的具体内容
1) "message"
2) "omaster"
3) "hello_redis"
发送端:
127.0.0.1:6379> publish omaster "hello omaster" # 发布者发布消息到频道
(integer) 1
127.0.0.1:6379> publish omaster hello_redis # 发布者发布消息到频道
(integer) 1
原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bj4DAY79-1648989377755)(D:\Install\Typora\typora-user-images\image-20210611134222969.png)]
使用场景
1、实时消息系统
2、实时聊天系统(频道当作聊天室,将信息发送给所有人)
3、订阅、关注等
稍复杂的系统使用消息队列中间件
四、Redis主从复制/哨兵模式
1. 主从复制(一主二从)
主从复制,读写分离,主机写,从机读,80%的情况下都是读操作,两从机,减缓主服务器压力。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cO0N5j9q-1648989377755)(D:\Install\Typora\typora-user-images\image-20210611144413707.png)]
环境配置
redis默认为主库,顾只需配置从库即可!
127.0.0.1:6379> info replication # 查看当前库信息
# Replication
role:master # 角色; masater
connected_slaves:0 # 从机数量
master_failover_state:no-failover
master_replid:65d7189800a0e326fe09a230d709e0c19efde02c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
将配置文件辅助多份(想配几个从机就复制几份),修改相应配置:
1、端口
2、pid名称
3、log文件名
4、dump.rdb文件名
5、若是主机配置了密码,则还需配置主机密码:masterauth password
,pasword为主机的密码
==默认情况下,每台Redis服务器都是主节点;==一般情况下配置从机就行。
127.0.0.1:6380> slaveof 127.0.0.1 6379 # slaveof host port 设置主机ip和端口
OK
127.0.0.1:6380> info replication
# Replication
role:slave # 当前角色:从机
master_host:127.0.0.1 # 主机相关信息
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:14
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:eb4bc712d6f547a5cd353a7bc1d5d297199320ac
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:14
#主机中信息
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1 # 从机数量
slave0:ip=127.0.0.1,port=6380,state=online,offset=14,lag=1 # 从机信息
master_failover_state:no-failover
master_replid:eb4bc712d6f547a5cd353a7bc1d5d297199320ac
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:14
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=1602121,lag=1
slave1:ip=::1,port=6381,state=online,offset=1602121,lag=1
master_failover_state:no-failover
master_replid:eb4bc712d6f547a5cd353a7bc1d5d297199320ac
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1602121
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:553546
repl_backlog_histlen:1048576
实际环境中,通过配置文件配置实现永久配置,使用命令配置是暂时的。
细节
主机可以写,也可以读,从机不能写,只能读。
设置为从机后,从机会自动将主机中所有数据复制过来,这是全量复制
之后,主机写入新的数据,从机会继续更新这些数据,这叫==增量复制
主机断开连接,如何操作?
可以使用slaveof no one
将从机变为主机,其他从机连到该主机即可。
2. 哨兵模式
测试
1、配置哨兵配置文件sentinel.conf
# 监控名称 监控的主机 端口 主机宕机后是否投票产生主机
sentinel monitor myredis 127.0.0.1 6379 1
2、启动
redis-sentinel conf/sentinel.conf
[root@172-18-232-233 bin]# redis-sentinel conf/sentinel.conf
11605:X 11 Jun 2021 17:31:47.766 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
11605:X 11 Jun 2021 17:31:47.766 # Redis version=6.2.4, bits=64, commit=00000000, modified=0, pid=11605, just started
11605:X 11 Jun 2021 17:31:47.766 # Configuration loaded
11605:X 11 Jun 2021 17:31:47.768 * monotonic clock: POSIX clock_gettime
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 6.2.4 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 11605
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | https://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
11605:X 11 Jun 2021 17:31:47.769 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
11605:X 11 Jun 2021 17:31:47.787 # Sentinel ID is d0806228171b60b3bb19912699597aeb91e7ff49
11605:X 11 Jun 2021 17:31:47.787 # +monitor master myredis 127.0.0.1 6379 quorum 1
11605:X 11 Jun 2021 17:31:47.788 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:31:47.797 * +slave slave [::1]:6381 ::1 6381 @ myredis 127.0.0.1 6379
3、主机宕机后,哨兵投票从从机中选出一个主机
11605:X 11 Jun 2021 17:32:47.106 # +sdown master myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:32:47.106 # +odown master myredis 127.0.0.1 6379 #quorum 1/1
11605:X 11 Jun 2021 17:32:47.106 # +new-epoch 1
11605:X 11 Jun 2021 17:32:47.106 # +try-failover master myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:32:47.115 # +vote-for-leader d0806228171b60b3bb19912699597aeb91e7ff49 1
11605:X 11 Jun 2021 17:32:47.115 # +elected-leader master myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:32:47.115 # +failover-state-select-slave master myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:32:47.174 # +selected-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:32:47.174 * +failover-state-send-slaveof-noone slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:32:47.240 * +failover-state-wait-promotion slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:32:48.269 # +promoted-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:32:48.269 # +failover-state-reconf-slaves master myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:32:48.270 * +slave-reconf-sent slave [::1]:6381 ::1 6381 @ myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:32:48.286 * +slave-reconf-inprog slave [::1]:6381 ::1 6381 @ myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:32:49.323 * +slave-reconf-done slave [::1]:6381 ::1 6381 @ myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:32:49.413 # +failover-end master myredis 127.0.0.1 6379
11605:X 11 Jun 2021 17:32:49.413 # +switch-master myredis 127.0.0.1 6379 127.0.0.1 6380
11605:X 11 Jun 2021 17:32:49.414 * +slave slave [::1]:6381 ::1 6381 @ myredis 127.0.0.1 6380
11605:X 11 Jun 2021 17:32:49.414 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6380
11605:X 11 Jun 2021 17:32:50.002 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6380
11605:X 11 Jun 2021 17:33:19.421 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6380
主机恢复后,则自动为新主机的从机。
哨兵模式
优点:
1、哨兵集群,基于主从复制,拥有全部的主从配置
2、主从自动切换,故障可以转移、系统可用性强
3、哨兵模式是主从配置的升级,从手动到自动,健壮性好
缺点:
1、Redis不方便在线扩容,集群数量达到上限,扩容十分麻烦
2、哨兵模式的配置很复杂
哨兵模式配置文件
五、Redis缓存穿透、击穿、雪崩
参考文章:Redis缓存穿透、击穿、雪崩详解
1. 缓存穿透(查不到)
概念
缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。
解决方案
1、布隆过滤器
首先也是对所有可能查询的参数以hash形式存储,当用户想要查询的时候,使用布隆过滤器发现不在集合中,就直接丢弃,不再对持久层查询。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-usppl0gV-1648989377755)(D:\Install\Typora\typora-user-images\image-20210611205917042.png)]
2、缓存空对象
当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2W1ZeeCP-1648989377756)(D:\Install\Typora\typora-user-images\image-20210611205930655.png)]
存在的两个问题:
1)如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;
2)即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。
2. 缓存击穿(量太大,缓存过期)
概念
缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
解决方案
1、热点数据永不过期
比如我们可以将某个 key 的缓存时间设置为 25 小时,然后后台有个 JOB 每隔 24 小时就去批量刷新一下热点数据。就可以解决这个问题了。
2、使用互斥锁
容易影响吞吐量,大部分项目设置热点 key 永不过期就妥妥的了。
3. 缓存雪崩
概念
在某个时间段,大量缓存数据到期失效,导致大量请求到数据库,可以理解这个时间段内缓存宕机失效。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rgvNMLtG-1648989377756)(D:\Install\Typora\typora-user-images\image-20210611211303071.png)]
解决方案
1、 Redis高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。
多提供几台服务器,做好集群。(异地多活)
2、限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
3、数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
4、数据永不过期