Redis 分布式锁

一. 什么是分布式锁

    分布式锁其实可以理解为:控制分布式系统有序的去对共享资源进行操作,通过互斥来保持一致性。

    举个不太恰当的例子:假设共享的资源就是一个房子,里面有各种书,分布式系统就是要进屋看书的人,分布式锁就是保证这个房子只有一个门并且一次只有一个人可以进,而且门只有一把钥匙。然后许多人要去看书,可以,排队,第一个人拿着钥匙把门打开进屋看书并且把门锁上,然后第二个人没有钥匙,那就等着,等第一个出来,然后你在拿着钥匙进去,然后就是以此类推

二. 保证分布式锁满足的四个条件

     1.互斥性

     2.安全性

     3.避免死锁

     4.容错

三. 使用redis实现分布式锁

  • 使用redis命令 set key value NX EX max-lock-time 实现加锁

  • 使用redis命令 EVAL 实现解锁

加锁:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

Jedis jedis =  new  Jedis( "127.0.0.1" 6379 );

private  static  final  String SUCCESS =  "OK" ;

/**

* 加锁操作

* @param key 锁标识

* @param value 客户端标识

* @param timeOut 过期时间

*/

public  Boolean lock(String key,String value,Long timeOut){

String var1 = jedis.set(key,value, "NX" , "EX" ,timeOut);

if (LOCK_SUCCESS.equals(var1)){

return  true ;

}

return  false ;

}

解读:

  • 加锁操作:jedis.set(key,value,"NX","EX",timeOut)【保证加锁的原子操作】

  • key就是redis的key值作为锁的标识,value在这里作为客户端的标识,只有key-value都比配才有删除锁的权利【保证安全性】

  • 通过timeOut设置过期时间保证不会出现死锁【避免死锁】

  • NX,EX什么意思?

  • NX:只有这个key不存才的时候才会进行操作,if not exists;

  • EX:设置key的过期时间为秒,具体时间由第5个参数决定


解锁

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

Jedis jedis =  new  Jedis( "127.0.0.1" 6379 );

private  static  final  Long UNLOCK_SUCCESS = 1L;

/**

* 解锁操作

* @param key 锁标识

* @param value 客户端标识

* @return

*/

public  static  Boolean unLock(String key,String value){

String luaScript =  "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else  return 0 end" ;

Object var2 = jedis.eval(luaScript,Collections.singletonList(key), Collections.singletonList(value));

if  (UNLOCK_SUCCESS == var2) {

return  true ;

}

return  false ;

}

解读:

  • luaScript 这个字符串是个lua脚本,代表的意思是如果根据key拿到的value跟传入的value相同就执行del,否则就返回0【保证安全性】

  • jedis.eval(String,list,list);这个命令就是去执行lua脚本,KEYS的集合就是第二个参数,ARGV的集合就是第三参数【保证解锁的原子操作】

上述就实现了怎么使用redis去正确的实现分布式锁,但是有个小缺陷就是锁过期时间要设置为多少合适,这个其实还是需要去根据业务场景考量一下的


  上面那只是讲了加锁与解锁的操作,试想一下如果在业务中去拿锁如果没有拿到是应该阻塞着一直等待还是直接返回,这个问题其实可以写一个重试机制,根据重试次数和重试时间做一个循环去拿锁,当然这个重试的次数和时间设多少合适,是需要根据自身业务去衡量的

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

/**

* 重试机制

* @param key 锁标识

* @param value 客户端标识

* @param timeOut 过期时间

* @param retry 重试次数

* @param sleepTime 重试间隔时间

* @return

*/

public  Boolean lockRetry(String key,String value,Long timeOut,Integer retry,Long sleepTime){

Boolean flag =  false ;

try  {

for  ( int  i= 0 ;i<retry;i++){

flag = lock(key,value,timeOut);

if (flag){

break ;

}

Thread.sleep(sleepTime);

}

} catch  (Exception e){

e.printStackTrace();

}

return  flag;

}

到这,用redis实现分布式锁就写完了

转载自   https://www.cnblogs.com/cmyxn/p/9047848.html

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

KVM调整cpu和内存

一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid

Redis 分布式锁 - HelloWorld