学习情况
第一轮学习,狂神视频,学习到以下内容
- 了解string、map、set、zset、list五大基础类型;复杂类型地图geo的基础命令
- redis数据存储模式:rdb数据快照和aof命令库、主从复制、哨兵模式选举方式
- redis实现简易的发布订阅
- 缓存击穿和雪崩的几种情况和解决方法
常见的操作
redis不区分大小写命令
基础操作
官网命令库
https://redis.io/commands
常看所有的key
keys * // 查看所有的key
清空数据库
flushdb // 清空当前数据库
FLUSHALL // 清空全部数据库
切换数据库
select 0 // 切换到0号数据库
某key是否存在
exists A // A是否存在,存在会返回: (integer) 1
exists A1 // A1是否存在,不存在会返回: (integer) 0
设置一个key
set name sgw // 设置key为name,value为sgw的键值对
获得一个key的value
get name // 获取key为name键值对的value
删除key
del name // 删除name
查看key的类型
type name // 查看key为name的类型,如strig的结果: string
对某键设置自动过期并查看倒计时
expire name 10// name键值对10秒后过期
ttl name // 还剩多少秒过期time to live,可为负数(已过期xx时间),比如还剩4秒返回: (integer) 4
获取字符串的长度
strlen name // name的长度
截取字符串
getrange name 0 -1 // 从下标0截取到末尾截取键值对name
替换字符串
setrange name 1 xx // 从下标1后2个字符为xx // abcde -> axxde
列表设置
LPUSH mylist one // 将one插入到mylist的头部(左插)
RPUSH mylist right // 讲right 插入到mylist的尾部(右插)
LSET mylist 0 item // 将item存入mylist第0下标上(前提mylist存在,否则报错)
列表查询
LRANGE mylist 0 -1 // 查看mylist列表的所有元素
列表移除
LPOP mylist // 左弹出一个元素,返回被移除元素的值
RPOP mylist // 右弹出一个元素,返回被移除元素的值
lrem mylist 1 one // 移除列表mylist一个value为one的值
列表下标
LINDEX mylist 1 // 通过下标获得list的某一个值
列表长度
LLEN mylist // 列表长度
集合增删查改
sadd myset "one" # 添加元素
smembers myset # 查询set所有值
srem myset "one" # 移除元素one
spop myset # 随机删除myset一个元素
集合判断存在
sismember myset "hello" # 判断hello是否存在myset
集合数量
scard myset # 获取myset的元素个数
集合随机取数
srandmember myset # 随机抽出myset中一个元素
集合数据转移
smove myset myset2 "sgw" # 把myset中的sgw送给myset2
集合差集
SDIFF SET1 SET2 # 返回set1-(set1和set2的交集元素)
集合交集
sinter set1 set2 # 返回set1和set2的交集
集合并集
sunion set1 set2 # 返回set1和set2的并集
哈希增删查改
HSET myhash key1 a key2 b# 插入
HGET myhash key1 # 获取
hmget myhash key1 key2 # 批量获取
hgetall myhash # 获取全部
hdel myhash key1 # 删除指定key
hkeys myhash # 获取hash所有key
hvals myhash # 获取hash所有val
哈希长度
HLEN myhash
哈希判断存在
HEXIST MYHASH
zset增删查改
zadd myset 1 A 2 B
zrem myset A
zset排序
zrangebyscore myset -inf +inf #从小到大排序
zrevrange myset 0 -1 # 倒序
zset计数
zcount myset 1 3 # 计算区间[1,3]的元素数量
geo增删查改
geoadd china:city 116 39 beijing #设置一个key为china:city 位置beijing,坐标(116,39)的数据
geopos china:city beijing # 获取北京的坐标数据
返回两个位置距离
geodist china:city beijing shanghai km # 计算北京和上海之间的距离(单位:km)
半径范围搜索目标
georadius china:city 110 30 1000 km # 寻找坐标(110,30)为中心,半径1000km范围内的china:city内的坐标
genreaiusbymember china:city beijing 1000 km # 寻找beijing为中心,半径1000km范围内的china:city内的坐标
hyperloglog基数统计
pfadd mypf a b c d # 存储
pfadd mypf2 c d e
pfcount mypf # 计数
pfmerge mypf3 mypf mypf2 # 合并到mypf3(取并集,不重复)
组合的操作
设置且定时
setex name 30 "hello" // setex = set + expire,设置30秒存活的键值对name
不存在再设置
可用于分布式锁,session的设置
setnx name value // 如果之前已经设置了且存活,就不会生效,返回结果 (integer) 0
批量设置
mset k1 v1 k2 v2 k3 v3 // 空格分隔,设置3个键值对(k1,v1)(k2,v2)(k3,v3)
批量获取
mget k1 k2 k3 // 返回 v1 v2 v3
批量不存在再设置
原子性操作
msetx k1 v1 k4 v4 // k1和k4是原子性的,一起成功一起失败
取旧值设新值
先get后set,CAS原理(先比较,后设置)
gstset name value // 如果不存在返回null,并设置键值对;如果存在返回旧值并设置新值
列表弹出且存储
lpoprpush from to // 从列表from左弹一个元素,右插到列表to中
常见问题
Redis是单线程的吗?为什么单线程还这么快
Redis是单线程的,不是多线程。Redi是基于内存操作的,cpu不是redis性能瓶颈,redis瓶颈是根据机器的内存和带宽,(多线程其实就是多cpu操作,多线程的目的就是想快,那既然多线程无法提升redis的效率,那redis当然直接用单线程是实现拉)
- 误区1:高性能的服务器一定是多线程的?
- 误区2:多线程(CPU上下文会切换)一定比单线程效率高
对于内存系统来说,没有上下文切换效率最高
说一下redis的list?
redis的堆栈队列可以使用list实现,list的命令都是l开头的,list实际是一个双向链表,跟java中linkedlist同理
消息队列:Lpush、Rpop
栈:Lpush、Lpop
说一下redis的set?
无序集合,set的命令都是s开头
常见使用场景:共同好友、共同爱好、二度好友、推荐好友(六度分隔理论)
说一下redis的zset?
有序集合,zset的命令是z开头
zset除了需要存value还需要存score权重
说一下redis的hash?
hash命令h开头,Map集合,本质和string类型没太大区别,很多命令相似,只是对象变成键值对
说一下redis的geospatial?
geospatial地理位置是redis1的特殊数据结构,简称geo。两极无法直接添加
参数:key、经度、纬度、名称
有效的经度:-180+180;有效的纬度:-85+85
场景:打车距离、朋友定位、附近的人
说一下redis的hyperloglog?
hyperloglog是redis用于统计pv的工具,优点是占用内存超小,但又0.81%的错误率(其实对于很多大数是允许容错的),对于直播统计人气这种可以用
redis事务是原子性的吗?
redis事务不保证原子性。
redis事务没有隔离级别的概念:只有发起执行命令的时候才会执行
redis事务:
- 开启事务(命令:multi)
- 命令入队(命令还不会执行)
- 执行事务(命令一起执行,命令:exec)
- 取消事务(命令:discard)
事务中的如果某条命令出现RunTimeException,并不会全部不执行,而是有问题的那条不执行,其他会继续执行,这就证明了redis不保证原子性
redis的乐观锁和悲观锁?
使用watch命令实现redis乐观锁
set money 100
set out 0
watch money # 监控money对象,当事务执行期间,如果有另一个线程更新了money对象,整个事务一起失败
multi
decrby money 20
incrby out 20
exec
# 如果上述代码发生异常,那就要取消watch,重新监视
unwatch
watch money
... ...
redis的作用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QIlb2YP5-1624629495983)(https://i.loli.net/2021/06/21/aEKTWrOHc3YPpeR.png)]
redis的持久化
RDB(redis database)和AOF(append only if),redis暂存在内存,如果出现断电情况内存会丢失,所以需要持久化到数据库
RDB
概念:在指定的间隔内,将内存数据写入磁盘,也就是snapshot快照,当需要恢复数据时,即可将快照文件直接读取到内存。
默认文件名:dump.rdb
步骤:redis会单独创建fork一个子进程进行持久化,会将数据写入到临时文件A,当持久化结束会将此临时文件替换上一次持久化好的文件,整个过程不进行任何io操作,所以性能极好,但因为可能出现宕机前的那一次持久化还未结束就宕机了,所以最后一次持久化的数据可能不完整。
优点:
1 适合大规模的数据恢复。
2 如果业务对数据完整性和一致性要求不高,RDB是很好的选择。
缺点:
1 数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。
2 备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍哦),最后再将临时文件替换之前的备份文件。所以要考虑到大概两倍的数据膨胀性。
触发机制:
- save的规则满足配置条件时会触发
- 执行flushall会触发
- 退出redis会触发
如何恢复:
只要将rdb文件放在redis启动目录即可,启动目录可通过命令查看》 config get dir
AOF
概念:以日志的形式记录每个写操作,只允许追加文件不允许改写文件,redis启动时会读取该文件重新构件数据。默认不开启。
优点:数据完整性好
缺点:相对于数据文件,aof远远大于rdb,修复速度也比rdb慢
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W6XackX9-1624629495985)(https://i.loli.net/2021/06/24/mvnPEcB2L1MQt8V.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zKh2KRtH-1624629495986)(https://i.loli.net/2021/06/24/sMgQbItvGLWEXri.png)]
Redis发布订阅
概念:redis发布订阅是一种消息通信模式:发送者发送消息,消费者接受处理消息。
相似技术:jms、kafka、mq(一般专业的事情给专业的人做,redis的发布订阅只是临时方案,不过发布订阅模型可以学习)
模型:消息发送者、频道、消息订阅者
原理:
通过subscribe 命令订阅频道后,redis-server里维护了一个字典,字典的键就是一个个频道,字典的值是一个链表,链表中保存了所有订阅这个channel的客户端,subscribe源码操作就是将用户信息放到了链表中。
通过publish命令向订阅者发送消息,redis-server会向给定的频道作为键查出链,遍历链子发送消息。
案例:群聊,即时聊天,订阅关注系统
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WX4dFt1l-1624629495987)(https://i.loli.net/2021/06/24/minR2vzFlGKShjc.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kcgSWCXx-1624629495988)(https://i.loli.net/2021/06/24/S5qBNDwnPUMrcxb.png)]
Redis主从复制
概念:主从复制,指将一台redis服务器的数据复制到其他redis服务器。前者称为主节点(master、leader),后者称为从节点(slave、follower),数据的复制是单向的,只能主到子,master以写为主,slave以读为主
作用:
- 数据备份:当主节点出问题,可从子节点提供服务
- 负载均衡:主从读写分离,分担服务器负担,尤其是写少读多的场景,通过多个节点分担负载,可以大大提供redis的服务器并发量
- 高可用基石:主从复制是哨兵和集群能够实施的基础
一般来说,要将redis运行于服务,一台机器是不行的,至少一主二从,因为
- 从结构上讲,单个redis服务器会发生单点故障,并且一台服务器需要处理所有的请求压力较大
- 从容量上讲,单个redis最大使用的内存不应该超过20G
一主二从:
默认情况下,所有节点都是主节点。需要目标从机执行命令 》 slaveof 主节点Ip地址 主节点端口号 才可以跟所主节点。
主从设置好后,主从会拥有以下特性:从机只能读取数据,写操作只能主机执行。
复制原理:slave启动成功连接到master后,会发送一个sync同步命令,master接收到命令,启动后台存盘进程,收集所有接受到用于修改数据的命令,在后台执行完毕后,master将传送整个数据文件到slave,并完成一次完全同步。
全量复制:slave服务接收到数据库文件后,将其存盘并加载到内存中。
增量复制:master继续将新的所有收集到的命令依次传给slave,完成同步。
但是只要是重新连接master,一次全量复制将被执行
Redis的哨兵模式:
概念:如果没设置哨兵模式,当主机断开连接后,从机依然连接主机,但是会无法写操作,直到主机恢复,除非人工手动干预。而更好的方式是考虑哨兵模式sentinel。
后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。
原理:哨兵是一个独立的进程,独立运行。哨兵通过发送命令,等待redis服务器响应,从而监控运行的redis实例。当哨兵检测到master宕机,会自动将slave切换为master,然后通过发布订阅模式通知其他服务器,让他们切换leader。同理,哨兵也可能死,所以又衍生出多哨兵互相监控的模型。而如何判断主机是否宕机呢,假设哨兵1检测到master“挂了”(主观下线),其实仅仅是哨兵1的主观判定,需要当后面的哨兵也检测到master挂了并且监测到的哨兵数量到达一定数量,这时候哨兵就会发起一次投票,选举出下一任master(failover),然后才到发布订阅通知修改(客观下线),假设主机现在恢复了,也只能当小弟。
优点:主从可以切换,基于主从复制
缺点:redis不好在线扩容,如果集群容量达到上线,扩容麻烦。实现哨兵的设置其实很麻烦的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fbg2SF2M-1624629495989)(https://i.loli.net/2021/06/24/XM792ZexJKtr63m.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mQfYnMGN-1624629495990)(https://i.loli.net/2021/06/24/439zGoYqtw16rTQ.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wvznkw2L-1624629495991)(https://i.loli.net/2021/06/24/loKGd4ZtSqEu38Y.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fYOZozUn-1624629495991)(https://i.loli.net/2021/06/24/Yeq9bhSmyHfxZtK.png)]
Redis缓存穿透和雪崩
概念:用户想查一个数据,发现redis内存没有,也就是没命中缓存,于是向持久层数据库查询,发现也没有,于是本次查询失败,当用户很多时,缓存都没命中(秒杀),然后都去请求持久化数据库,这时候给持久层压力很大,这种现象就叫做缓存穿透。
解决方案:
方案1:布隆过滤器,布隆过滤器是一种数据结构,对所有可能的查询的参数以hash形式存储,在控制层进行校验,不符合的丢弃,从而避免了对底层存储系统的压力。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q3tEAqTo-1624629495992)(https://i.loli.net/2021/06/24/nv8FKRJsZor1AVM.png)]
方案2:缓存空对象,当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端的数据源
缺点:
- 如果空值能够被缓存,那就意味着缓存需要更多的键,无意义的存空值的键
- 即使对空值设置了过期,还是会存在缓存层和存储层数据有一段时间不一致,这对于需要保持持久化的业务有影响
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TYmPdBIs-1624629495993)(https://i.loli.net/2021/06/24/O16qmuMrja2PRcd.png)]
Redis的缓存击穿
概念:指一个key非常热点,大并发集中对一个点进行访问,当这个key失效的瞬间,持续的大并发全打到数据库,演变成缓存穿透
解决方案:
- 设置热点数据永不过期
- 加互斥锁:使用分布式锁setnx,保证对每个key只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可,这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大
缓存雪崩
概念:在某一段时间,缓存集中过期失效(宕机,断电,集体set缓存)。产生雪崩的原因之一是,比如马上到双十二零点,很快迎来一波抢购,这波商品比较集中的放入缓存,假设缓存一个小时,那么到了凌晨1点,这批缓存一起过期,而对这批商品的访问查询瞬间落到数据库,这个过程就叫做缓存雪崩。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dbEFJLeq-1624629495993)(https://i.loli.net/2021/06/24/MVbhmijkdD8FpG5.png)]
解决方案:
- 异地服务器
- 限流降级:缓存失效后,通过加锁或者控制数据库读写缓存的线程数量。比如对某个key只允许一个线程查询和写,其他线程等待
- 数据预热:在正式部署前,我们先把可能的数据先预先访问一遍,这样部分key先加载到缓存,并设置不同的过期时间,让缓存失效时间分布均匀点