Redis用于分布式锁以及各类场景情况(欢迎补充纠错)

2 篇文章 0 订阅
2 篇文章 0 订阅

Redis数据类型、常用命令以及应用场景

String

  • 常用命令:set/get/decr/incr/mget
  • 应用场景 :String是最常用的一种数据类型,普通的key/value存储都可以归为此类;
  • 实现方式:String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr、decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。

Hash

  • 常用命令 :hget/hset/hgetall等
  • 应用场景 :我们要存储一个用户信息对象数据,其中包括用户ID、用户姓名、年龄和生日,通过用户ID我们希望获取该用户的姓名或者年龄或者生日;
    实现方式:Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口。Key是用户ID,value是一个Map。这个Map的key是成员的属性名,value是属性值。这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field),也就是通过key(用户ID) + field(属性标签)就可以操作对应属性数据。当前HashMap的实现有两种方式:当HashMap的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,这时对应的value的redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时redisObject的encoding字段为int。

List

  • 常用命令 :lpush/rpush/lpop/rpop/lrange等;
  • 应用场景 :Redis list的应用场景
    非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现;
  • 实现方式:Redis
    list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。

Set

  • 常用命令 :sadd/spop/smembers/sunion等;
  • 应用场景 :set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的;
  • 实现方式:set 的内部实现是一个value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。

Sorted Set

  • 常用命令 :zadd/zrange/zrem/zcard等;
  • 应用场景 :Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted
    set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sortedset数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
  • 实现方式:Redis sorted
    set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。

分布式锁

分布式锁需要解决的问题:
1、互斥性
2、安全性
3、死锁
4、容错

Redis分布式锁实现的三个核心要素:

  • 加锁

最简单的方法是使用setnx命令。key是锁的唯一标识,按业务来决定命名,value为当前线程的线程ID。(注意redis cluster集群不支持 setNx)

比如想要给一种商品的秒杀活动加锁,可以给key命名为 “lock_sale_ID”
。而value设置成什么呢?我们可以姑且设置成1。加锁的伪代码如下:

setnx(key,1)当一个线程执行setnx返回1,说明key原本不存在,该线程成功得到了锁,当其他线程执行setnx返回0,说明key已经存在,该线程抢锁失败。

  • 解锁

有加锁就得有解锁。当得到锁的线程执行完任务,需要释放锁,以便其他线程可以进入。释放锁的最简单方式是执行del指令,伪代码如下:

del(key)释放锁之后,其他线程就可以继续执行setnx命令来获得锁。

  • 锁超时

锁超时是什么意思呢?如果一个得到锁的线程在执行任务的过程中挂掉,来不及显式地释放锁,这块资源将会永远被锁住,别的线程再也别想进来。

所以,setnx的key必须设置一个超时时间,以保证即使没有被显式释放,这把锁也要在一定时间后自动释放。setnx不支持超时参数,所以需要额外的指令。

极端场景A:获取到锁后和释放锁之前发生宕机,那么这个锁就永远无法释放了。
极端场景B:假如某线程成功得到了锁,并且设置的超时时间是30秒。如果某些原因导致线程A执行的很慢很慢,过了30秒都没执行完,这时候锁过期自动释放,线程B得到了锁。随后,线程A执行完了任务,线程A接着执行del指令来释放锁。但这时候线程B还没执行完,线程A实际上删除的是线程B加的锁。

避免A

可以通过SET key value [EX seconds] [PX milliseconds] [NX|XX] EX
seconds:设置键的过期时间多少秒;
PX milliseconds:设置键的过期时间为millisecond毫秒
NX:只在键不存在时,才对键进行设置操作 XX:只在键已经存在时,才对键进行设置操作; SET操作完成时,返回OK,否则返回nil

避免B

可以在del释放锁之前做一个判断,验证当前的锁是不是自己加的锁。
为了实现原子操作,可采用Lua脚本

场景题

  • MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?

voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据

  • 如何从redis从找出固定前缀的key或从海量key里面查询出某一固定前缀的Key

摸清数据规模,问清楚边界; 对线上的业务的影响 如果你直接回答keys k1就进入了套路了 所以回答之前先问清楚是否是生产环境和数据量
使用scan cursor [match pattern] [count count]
基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程; 以0作为游标开始一次新的迭代,知道命令返回游标0完成一次遍历;
不保证每次执行都返回某个给定数量的元素,支持模糊查询; 一次返回的数量不可控, 只能是大概率符合count参数;
怼:如果不是生产环境可以直接使用keys k1
,如果是生产环境我们就用 scan 0 match key1* count
10用sursor游标的方式去获取,虽然每次返回的数量不可控,可以去做一个排重,总体时间会长但是对于线上生产环境不会有影响;

  • Redis内存回收策略

LRU算法,当数据很长时间没有被访问就会移除掉; LFU:不在乎加入时间长短,剔除访问次数少的; LRU(least recently used,最近最少使用):根据数据的历史访问记录来进行淘汰数据;
核心思想是如果数据最近被访问过,那么将来被访问的几率也更高。通过对少量keys进行
取样(50%),然后回收其中一个最少的key,配置方式就是maxmemory-samples 5 LFU(least frequently used)根据数据的历史访问评率来淘汰数据 核心思想是如果数据过去被访问多次,那么将来被访问的频率也更高,Redis实现的是近似的实现,每次对key进行访问时,用基于概率的对数计数器来记录访问次数,同时这个计数 器会随着时间推移而减小。

  • 什么是缓存穿透?如何避免?

缓存穿透 一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫 做缓存穿透。 如何避免?
1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理 缓存。
2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过 该bitmap过滤。

  • 什么是缓存雪崩?何如避免?

缓存雪崩 当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压 力。导致系统崩溃。 如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线 程查询数据和写缓存,其他线程等待。
2:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为 短期,A2设置为长期
3:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大能猫猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值