redis原理与应用

命令

1.插入键值对

1.1.set key value

返回值(OK:成功)

若已经存在相同的key值 -> 覆盖原值

set 命令可加选项

  • NX
    • 加上此选项时,则与setnx功能相同
    • 成功则返回OK
    • 失败时(当已经存在key值的键值对时),返回nil。
  • PX miliseconds
    • 指定过期时间,指定该键值对的生存时间为 millisecond 毫秒
    • 与 set key value + expire key seconds 功能一样

1.2.setnx key value

返回值(1:成功;0:失败)

  • 1:成功;
  • 0:失败,若key值已存在则不允许重复赋值

因为不允许用户创建key值重复的键值对,所以可用于实现分布式锁,只要结果返回“1”,说明本对象获取了分布锁。
若key值已经存在,则用户利用setnx 命令尝试创建相同key值的操作将会失败。

2.get key

返回值(value)

根据key值获取value值

3.getset key newValue

返回值(被newValue覆盖的原value)

根据key值获取当前value值,并将value值覆盖为新value值。

4.del key

返回值

  • 1:成功;
  • 0:失败,若key值不存在则会返回失败

删除键值对(key,value)

5.exists key

返回值

  • 1:存在;
  • 0:不存在

判断键值对(key,value)是否存在

6.TTL key

返回值

  • -2:key值不存在;
  • -1:key存在但没有设置过期时间;
  • 剩余生存时间(存在过期时间)

获取键值对(key,value)的剩余生存时间(单位:毫秒)

7.expire key seconds

返回值

  • 1:成功;
  • 0:失败,若key值不存在则会返回失败

对键值对(key,value)设置过期时间(number单位:秒),过期后该键值对会被自动删除;
还有其他设置过期时间的命令,只是number单位不同。

8.persist key

返回值

  • 1:成功;
  • 0:失败

将键值对设置为永不过期。

如何启动redis服务

1.启动redis 服务端

redis-server.exe redis.windows.conf

只需要存在.exe文件和conf配置文件这两个,即可成功的启动一个实例(linux需要.sh文件)

2.启动redis客户端,需要指定连接的redis服务端提供的ip地址

redis-cli.exe -h [host-address] -p [port] -a [password]

若不输入 -a [password],可以在连接上服务端后,使用 auth [password] 命令,进行校验。

//举例:
redis-cli.exe -h 127.0.0.1 -p 6379 -a redis

3.redis集群部署

重启实例会自动部署到集群中,因为存在配置文件、aop\rdb文件。所以,只有在删除这些文件后,才需要重新部署集群。

A.首先开启若干个redis的server服务(至少三个才能部署有效集群);
B.其次输入语句,调用redis-trib.rb文件,对服务实例进行集群部署。

redis-trib.rb create --replicas [raplica’s number] [serverip:port] [serverip:port] [serverip:port] …

参数 --replicas 的作用是指明主节点的从节点数量。若为0个,则redis集群不存在从节点备份主节点–>容易导致主节点挂掉引起的数据丢失和不可用,因此建议至少有一个从节点。

//举例
redis-trib.rb create --replicas 1 127.0.0.1:6631 127.0.0.1:6632 127.0.0.1:6633 127.0.0.1:6634 127.0.0.1:6635 127.0.0.1:6636
C.测试是否部署成功

启动redis客户端,登陆到任意一个服务实例;
使用 get set命令,查看结果。

分布式锁的实现

面试再被问到分布式锁,把这篇文章甩给他

实现原理

通过对某个key值的键值对的写操作结果成功与否,判断进行写操作的用户是否成功获取了分布式锁。

成功获取锁的条件:

  • 锁对象:key值符合
  • 锁对象状态:过期(键值对的生存时间超过了应该生存的最长时间)—— 避免死锁
  • 写(或写覆盖)结果:成功

因此,一个key,就是一个锁。锁的获取与释放生存周期就是该key键值对的创建到删除的过程。

锁的获取与释放:

  • 获取锁 : set key value NX PX miliseconds
    • 不能使用 setnx key value 或 set key value NX,组合expire key seconds 的方式获取锁,因为加锁过程应该为原子操作,若redis在创建key锁后设置过期时间前宕机,则该key锁将永远存在(造成死锁)。
  • 释放锁 : del key
    • 注意释放锁前先判断锁是自己创建的锁(不能破坏被锁状态);
    • 简单方法是保证value唯一,通过value值判断待删除键值对是否为自己创建的

一组用户都希望向键值固定为key的键值对中写入自己的value,“写入成功”就称为“成功获取了分布式锁key”。不过为了解决死锁的问题,引入写操作成功的另外一个条件:只有对已经超过过期时间的value进行成功覆盖写的用户,才是获取了分布式锁的用户。

分布式死锁

1.什么是分布式死锁

若没有设置“锁”(即键值对)的过期时间,则当需要释放锁的对象在成功释放锁前崩溃,则该锁将永远无法被释放,造成其他尝试获取锁的对象一直以为该锁被占用,造成死锁现象。

一般释放锁的操作由持有锁的对象进行释放。但若持有锁的对象无法主动释放锁(如对象存在的机器在释放锁前崩溃了,无法在进行任何操作),则分布式锁中其他等待获取该锁的对象会以为该锁一直被占用着,进入无限等待(redis死锁)。

2.如何避免死锁

对锁设置过期时间,超过过期时间则主动“解锁”。

3.进行释放死锁的对象

3.1.redis 服务

在创建键值对时就为他设置过期时间,这样过期后锁会被自动释放(即键值对的存储值会在一定时间后被自动清除)。

setnx只有当原key键值对不存在时才能成功写入,这样等待锁的对象只需要利用setnx 不断尝试获取锁即可。

3.2.其他等待锁的对象

判断锁已经过期,主动删除并创建锁(覆盖原key值)

这样可能出现很多对象同时 ttl 发现这个锁过期了,并都对该键值对进行了写覆盖动作(getset 命令)。
只要redis服务正常:覆盖动作(getset)结果都是成功的,因此需要double-check
只有在double-check 成功后,才应该继续执行下面的任务。
任务执行完成后不要忘记释放锁。

3.3.比较

redis服务对于过期键的删除有多种策略1,若需要redis服务实现避免死锁,那么就只能选择“立即删除”或“惰性删除”的策略,但这些策略有一些缺点。

  • a).对于立即删除,比如若程序在当过期时间点恰好正在执行某个比较占内存占cpu的任务,那么若此时开始执行删除过期键值对的任务,则会增大系统压力(此时“错峰”会比较好,等系统不忙了再执行)。
  • b).对于立即删除,另外由于redis的事件执行器的特点,删除过期键这种时间事件的寻址是根据无序链表,时间复杂度为O(n),在缓存数据比较多的系统中,将是一个比较耗时的任务,这对cpu的消耗将会导致系统提供给其他应用的服务能力降低。因此,为每个键值对都进行立即删除并不是一个理想的策略。
  • c).对于惰性删除,会比较占内存,对于内存空间对性能影响较大的程序不友好。

因此,鉴于redis服务的一些缺点,可以选择两者结合的方式来释放锁。

4.利用double-check实现分布式锁:double-check 剩余生存时间避免死锁

当不依靠redis服务删除过期键值来规避死锁时,需要double-check
真实使用时,应该使用 set key value NX PX miliseconds 不断尝试获取锁 即可(保证锁操作的原子性)。

如何double-check:
  • 1).first-check:利用 ttl command 发现锁已经过期,第一次"check过期"成功,进行键值覆盖动作,
  • 2).second-check:在first-check 成功后,进行getset commmand 操作,然后利用 ttl command 进行sencond-check。
    • 失败 —— 没有获取锁
      • 被自己覆盖的键值是被其他对象覆盖的新值:返回值发现被覆盖值的生存剩余时间不为0(未达到过期时间),说明被自己覆盖的键值是被其他对象覆盖的新值,则继续陷入锁等待状态;
      • 第二次check过期失败,以返回的被覆盖值的真实过期时间为新的过期时间作为首次check依据(等待生存剩余时间后在进行尝试锁的过期检查)
    • 成功 —— 获取锁
      • 在获取锁的竞争中自己是第一个完成了键值覆盖的用户:若返回值发现被覆盖值的生存剩余时间已经为0,则获取了锁。

经过上述释放锁的过程可以看出,需要谨慎选择过期时间,避免持有锁对象正常执行时被剥夺锁的持有,这样就丧失了同步锁的意义。

注意

1、一定要用SET key value NX PX milliseconds 命令
如果不用,先设置了值,再设置过期时间,这个不是原子性操作,有可能在设置过期时间之前宕机,会造成死锁(key永久存在) 。
double-check只是获取锁的逻辑,要保证锁的特性,必须保证加\解锁操作具有原子性。

2、value要具有唯一性
这个是为了在解锁的时候,需要验证value是和加锁的一致才删除key。

这是避免了一种情况:假设A获取了锁,过期时间30s,此时35s之后,锁已经自动释放了,A去释放锁,但是此时可能B获取了锁。A客户端就不能删除B的锁了。


  1. redis过期删除策略:三种策略(立即删除,惰性删除,定时删除),redis采取惰性删除与定时删除的组合方式作为默认的过期删除策略。 ↩︎

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值