redis分布式锁如何实现原理


本文摘自PHP中文网,作者(*-*)浩,侵删。

分布式锁,是控制分布式系统之间同步访问共享资源的一种方式。

在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。

使用setnx、getset、expire、del这4个redis命令实现 (推荐学习:Redis视频教程)

setnx 是『SET if Not eXists』(如果不存在,则 SET)的简写。 命令格式:SETNX key value;使用:只在键 key 不存在的情况下,将键 key 的值设置为 value 。若键 key 已经存在, 则 SETNX 命令不做任何动作。返回值:命令在设置成功时返回 1 ,设置失败时返回 0 。

getset 命令格式:GETSET key value,将键 key 的值设为 value ,并返回键 key 在被设置之前的旧的value。返回值:如果键 key 没有旧值, 也即是说, 键 key 在被设置之前并不存在, 那么命令返回 nil 。当键 key 存在但不是字符串类型时,命令返回一个错误。

expire 命令格式:EXPIRE key seconds,使用:为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。返回值:设置成功返回 1 。 当 key 不存在或者不能为 key 设置生存时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新 key 的生存时间),返回 0 。

del 命令格式:DEL key [key …],使用:删除给定的一个或多个 key ,不存在的 key 会被忽略。返回值:被删除 key 的数量。

Redis实现分布式锁的原理:

1.通过setnx(lock_timeout)实现,如果设置了锁返回1, 已经有值没有设置成功返回0

2.死锁问题:通过实践来判断是否过期,如果已经过期,获取到过期时间get(lockKey),然后getset(lock_timeout)判断是否和get相同,相同则证明已经加锁成功,因为可能导致多线程同时执行getset(lock_timeout)方法,这可能导致多线程都只需getset后,对于判断加锁成功的线程, 再加expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS)过期时间,防止多个线程同时叠加时间,导致锁时效时间翻倍

redis-8.jpg代码:

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

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

/**

 * @author yaoxin

 * @date 2018/8/13下午5:04

 */

public class RedisLockTest {

  

    public static final String url = "jdbc:mysql://127.0.0.1:3306/ly?characterEncoding=UTF-8";

    public static final String name = "com.mysql.jdbc.Driver";

    public static final String user = "root";

    public static final String password = "";

  

    public static void main(String[] args) {

  

        Integer count = 50;

        while (count > 0) {

            count--;

            new Thread(new Runnable() {

                @Override

                public void run() {

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

                    jedis.auth("1234");

                    String lock = lock(jedis);

                    if (lock != null) {

                        Statement statement = null;

                        Connection conn = null;

                        ResultSet resultSet = null;

                        try {

                            Class.forName(name);// 指定连接类型

                            conn = DriverManager.getConnection(url, user, password);// 获取连接

                            statement = conn.createStatement();// 准备执行语句

                            String querySql = "SELECT id,name,count FROM production WHERE id=2";

                            resultSet = statement.executeQuery(querySql);

                            int count = 0;

                            while (resultSet.next()) {

                                System.out.println(Thread.currentThread().getName() + "抢到了锁 id: " + resultSet.getString("id")

                                        + " name: " + resultSet.getString("name")

                                        + " count: " + resultSet.getString("count"));

                                count = Integer.valueOf(resultSet.getString("count"));

                            }

                            String updateSql = "UPDATE production SET count=" + (count - 1)

                                    + " WHERE id=2";

                            int rows = statement.executeUpdate(updateSql);

                            if (rows > 0) {

                                System.out.println("更新成功" + Thread.currentThread().getName() + "  库存剩余:" + (count - 1));

                                System.out.println(Thread.currentThread().getName() + " === > >开始解锁");

                                boolean unlock = unlock(jedis, lock);

                                if (unlock)

                                    System.out.println(Thread.currentThread().getName() + " === > >解锁成功");

                            } else {

                                System.out.println("更新失败" + Thread.currentThread().getName());

                            }

                        } catch (Exception e) {

                            e.printStackTrace();

                        } finally {

                            try {

                                if (conn != null)

                                    conn.close();

                                if (statement != null)

                                    statement.close();

                                if (resultSet != null)

                                    resultSet.close();

                            } catch (Exception e) {

                                e.printStackTrace();

                            }

                        }

                    }

                }

            }, "线程" + count).start();

        }

    }

  

    public static String lock(Jedis jedis) {

        try {

            while (true) {

                String lockTime = Long.valueOf(jedis.time().get(0)) + 5 + "";

                if (jedis.setnx("lock", lockTime) == 1) {

                    jedis.expire("lock", 5);

                    return lockTime;

                }

                String lock = jedis.get("lock");

                if (!StringUtils.isEmpty(lock) && Long.valueOf(lock) < Long.valueOf(jedis.time().get(0))) {

                    String oldLockTime = jedis.getSet("lock", lockTime);

                    if (!StringUtils.isEmpty(oldLockTime) && oldLockTime.equals(lock)) {

                        return lockTime;

                    }

                }

                Thread.sleep(100);

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

        return null;

    }

  

    public static boolean unlock(Jedis jedis, String lockTag) {

        if (lockTag.equals(jedis.get("lock"))) {

            jedis.del("lock");

            return true;

        }

        return false;

    }

  

}

运行结果如下图:

redis-9.jpg

更多Redis相关技术文章,请访问Redis入门教程栏目进行学习!

以上就是redis分布式锁如何实现原理的详细内容,更多文章请关注木庄网络博客

相关阅读 >>

Redis中的几种常用基础对象介绍

Redis自定义systemctl管理服务

Redis除了缓存还能做什么

Redis怎么做mysql缓存

.net中使用Redis的方法介绍

如何解决Redis连接超时问题

Redis集群如何分配哈希槽

Redis和mysql一般怎么配合

Redis分布式锁的正确实现方式介绍

怎么查看Redis集群信息

更多相关阅读请进入《Redis》频道 >>


数据库系统概念 第6版
书籍

数据库系统概念 第6版

机械工业出版社

本书主要讲述了数据模型、基于对象的数据库和XML、数据存储和查询、事务管理、体系结构等方面的内容。



打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...