分布式锁:

  Redis可以实现分布式锁,只是讨论Redis的实现思路,相对来说,Zookeeper实现分布式锁可能更加可靠

为什么使用分布式锁:

  单机环境下只存在多线程,通过同步操作就可以实现对并发环境的安全操作,但是多机环境就变成多进程、多线程,这时候同步、加锁已经无

法保证原子性

实现分布式可靠性的条件:

  1、互斥性。在任意时刻,只有一个客户端能持有锁

  2、不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁

  3、具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁

  4、加锁和解锁必须是同一个客户端

实现分布式锁的方式:

  1、基于DB的唯一索引。

  2、基于ZK的临时有序节点。

  3、基于Redis的NX、EX参数。

代码实现:

public static final String LOCK_SUCCESS = "OK";//加锁成功

public static final String SET_IF_NOT_EXIST = "NX";

public static final String SET_WITH_EXPIRE_TIME = "PX";

public static final Long RELEASE_SUCCESS = 1L;
public class RedisUtils {

    @Autowired
JedisPool jedisPool; /**
* 尝试获取分布式锁
* @param lockKey
* @param requestId
* @param expireTime
* @return
*/
public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
Jedis jedis = jedisPool.getResource();
String result = jedis.set(lockKey, requestId, RedisConstant.SET_IF_NOT_EXIST, RedisConstant.SET_WITH_EXPIRE_TIME, expireTime);
if (StringUtils.equals(result, RedisConstant.LOCK_SUCCESS))
return true;
return false;
} /**
* 释放分布式锁
* @param jedis
* @param lockKey
* @param requestId
* @return
*/
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); if (RedisConstant.RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
}

加锁:

  lockKey:唯一的key

  requestId:每个客户端的唯一ID

  NX:保证key不存在才会set

  PX:key具有过期时间

  expireTime:key的具体过期时间

解锁:
  通过lua代码传到jedis.eval()方法里,并使参数KEYS[1]赋值为lockKey,ARGV[1]赋值为requestId。eval()方法是将Lua代码交给Redis服务

端执行。

  首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)。那么为什么要使用Lua语言来实现呢?因为要确保上述

操作是原子性的。

  以上只是针对单机部署Redis,如果Redis是多机部署的,可以采用Redisson实现分布式锁

PS:上面的set方法需要RedisV2.6+支持

无法避免的问题:

  如在 key 超时之后业务并没有执行完毕但却自动释放锁了,这样就会导致并发问题。

  就算 Redis 是集群部署的,如果每个节点都只是 master 没有 slave,那么 master 宕机时该节点上的所有 key 在那一时刻都相当于是释放

锁了,这样也会出现并发问题。就算是有 slave 节点,但如果在数据同步到 salve 之前 master 宕机也是会出现上面的问题。

  Redis分布式锁内容参考:https://xiaozhuanlan.com/topic/4672859130https://redis.io/topics/distlock

基于Redis实现分布式ID:

  因为Redis是单线程的,所以可以用来生成全部唯一ID,通过incr、incrby实现

  生产环境可能是Redis集群,假如有5个Redis实例,每个Redis的初始值是1,2,3,4,5,然后增长都是5

各个Redis生成的ID为:

A:1,6,11,16,21
B:2,7,12,17,22
C:3,8,13,18,23
D:4,9,14,19,24
E:5,10,15,20,25

这样的话,无论请求打到那个Redis上面,都可以获得不同的ID

优点:

  1、不依赖于数据库,灵活方便,且性能优于数据库。

  2、数字ID天然排序,对分页或者需要排序的结果很有帮助。

缺点:

  1、如果系统里没有Redis,就比较操蛋了

  2、编码、配置工作量大一点

分布式ID推荐一篇文章:https://blog.csdn.net/hengyunabc/article/details/44244951

流水号:

  Redis同样可以生成每天的流水号,日期+自增长序号,进行incr

面试题:如何从Redis查询出前缀为id的key?

  首先这个问题应该要明确数据量,如果数据量很小,可以直接使用keys id*,keys命令直接返回所有的key,如果是海量数据,keys命令肯定

不行了,所以要跟面试官明确这个问题。海量数据环境下,例如1亿条数据,可以使用scan命令

scan是基于游标的迭代器,每次使用都要基于上一次的游标延续之前的迭代过程

格式:scan cursor [MATCH pattern] [COUNT count]

cursor以0开始,到0结束,scan 0 match id* count 10,从0开始,匹配以id开头的key,每次返回10条

返回结果有两部分:

1) "0"
2) 1) "id1"
2) "id2"
.......

  1)为返回的游标,返回0证明迭代结束。这里希望返回10条,并不是一定返回10条,可能只是返回5条数据(一次返回的数量不可控,大概率符合

count),如果返回cursor不是0,证明迭代没有结束,可以继续查询,知道返回cursor为0,效率低于keys,但是不会阻塞Redis

PS:scan返回的游标可能后一次比前一次更小,所以可能会出现重复数据,需要外部程序进行去重

Redis系列(二)--分布式锁、分布式ID简单实现及思路的更多相关文章

  1. Redis系列(二):Redis的数据类型及命令操作

    原文链接(转载请注明出处):Redis系列(二):Redis的数据类型及命令操作 Redis 中常用命令 Redis 官方的文档是英文版的,当然网上也有大量的中文翻译版,例如:Redis 命令参考.这 ...

  2. Redis 分布式锁 - 分布式锁的正确实现方式

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...

  3. php+redis 学习 二 悲观锁

    <?php header('content-type:text/html;chaeset=utf-8'); /** * redis实战 * * 实现悲观锁机制 * */ $timeout = 5 ...

  4. Redis系列(二):Redis的5种数据结构及其常用命令

    上一篇博客,我们讲解了什么是Redis以及在Windows和Linux环境下安装Redis的方法, 没看过的同学可以点击以下链接查看: Redis系列(一):Redis简介及环境安装. 本篇博客我们来 ...

  5. Redis系列二:reids介绍

    一.什么是redis.redis有哪些特性.redis有哪些应用场景.redis的版本 1. 什么是redis redis是一种基于键值对(key-value)数据库,其中value可以为string ...

  6. Redis系列(二)-Hredis客户端设计及开源

    接上篇c#实现redis客户端(一),重新整理些了下. 阅读目录: 项目说明 Hredis设计图 单元测试场景 总结 项目说明 背景:因为有地方要用,而又没找到对sentinel良好支持的Net客户端 ...

  7. Redis系列二之事务及消息通知

    一.事务 Redis中的事务是一组命令的集合.一个事务中的命令要么都执行,要么都不执行. 1.事务简介 事务的原理是先将一个事务的命令发送给Redis,然后再让Redis依次执行这些命令.下面看一个示 ...

  8. Redis系列二(yum切换为网易163)

    这个可能和Redis没有直接的关系... 是我在yum install的时候发现centos的yum实在是太慢,上网查了下.网易163有个yum镜像,为了让CentOS6使用速度更快的YUM更新源,可 ...

  9. redis系列二: linux下安装redis

    下面介绍在Linux环境下,Redis的安装与配置 一. 安装 1.首先上官网下载Redis 压缩包,地址:http://redis.io/download 下载稳定版3.0即可. 2.通过远程管理工 ...

随机推荐

  1. Mali GPU OpenGL ES 应用性能优化--基本方法

    1. 经常使用优化工具 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXlBcnJvdw==/font/5a6L5L2T/fontsize/400/fil ...

  2. hadoop分布式安装部署具体视频教程(网盘附配好环境的CentOS虚拟机文件/hadoop配置文件)

    參考资源下载:http://pan.baidu.com/s/1ntwUij3视频安装教程:hadoop安装.flvVirtualBox虚拟机:hadoop.part1-part5.rarhadoop文 ...

  3. NSoup解析处理Html

    以前在做网页静态生成的时候,使用正则表达式分析提取网页链接.最近搜索了解到java有个Jsoup解析网页,对应.net有个nsoup.处理网页非常好用. Document doc = NSoupCli ...

  4. createTextNode和innerHTML什么区别

    今天写代码,用到createTextNode,发现好像功能和innerHTML是一样的,于是查阅了网上的资料了. 一.createTextNode 例如: var element = document ...

  5. go4--break,continue + 标签

    package main /* 指针 Go虽然保留了指针,但与其它编程语言不同的是,在Go当中不 支持指针运算以及”->”运算符,而直接采用”.”选择符来操作指针 目标对象的成员 操作符”&am ...

  6. virtual (C# Reference)

    https://msdn.microsoft.com/en-us/library/9fkccyh4.aspx The virtual keyword is used to modify a metho ...

  7. mysqldump 导出数据表,和数据

    目录 导出数据库表与数据 导出数据表数据 导出多个表数据 只导出数据 只导出创建表的数据 导出数据库表与数据 mysqldump -uroot -p caomall>tmp.sql 导出数据表数 ...

  8. 学习Flash Builder编程的准备工作

    1. 下载教科书,Essential ActionScript 3.0或者Programming ActionScript 3.0.这将是你的很好的老师.英语不灵的买一本中文版本. 2. 安装Flas ...

  9. 【USACO 2008FEB】 旅馆

    [题目链接] 点击打开链接 [算法] 线段树 对于一个节点,记录它从左端点延伸的最多的空房间的个数,从右端点延伸的最多的空房间个数,和该区间最多的连续 空房间个数 [代码] #include<b ...

  10. 第八周 Leetcode 44. Wildcard Matching 水题 (HARD)

    Leetcode 44 实现一种类似正则表达式的字符串匹配功能. 复杂度要求不高, 调代码稍微费点劲.. 好像跟贪心也不太沾边, 总之 *把待匹配串分成若干个子串, 每一个子串尽量在模式串中靠前的部分 ...