Redis系列(二)--分布式锁、分布式ID简单实现及思路
分布式锁:
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/4672859130,https://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简单实现及思路的更多相关文章
- Redis系列(二):Redis的数据类型及命令操作
原文链接(转载请注明出处):Redis系列(二):Redis的数据类型及命令操作 Redis 中常用命令 Redis 官方的文档是英文版的,当然网上也有大量的中文翻译版,例如:Redis 命令参考.这 ...
- Redis 分布式锁 - 分布式锁的正确实现方式
前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...
- php+redis 学习 二 悲观锁
<?php header('content-type:text/html;chaeset=utf-8'); /** * redis实战 * * 实现悲观锁机制 * */ $timeout = 5 ...
- Redis系列(二):Redis的5种数据结构及其常用命令
上一篇博客,我们讲解了什么是Redis以及在Windows和Linux环境下安装Redis的方法, 没看过的同学可以点击以下链接查看: Redis系列(一):Redis简介及环境安装. 本篇博客我们来 ...
- Redis系列二:reids介绍
一.什么是redis.redis有哪些特性.redis有哪些应用场景.redis的版本 1. 什么是redis redis是一种基于键值对(key-value)数据库,其中value可以为string ...
- Redis系列(二)-Hredis客户端设计及开源
接上篇c#实现redis客户端(一),重新整理些了下. 阅读目录: 项目说明 Hredis设计图 单元测试场景 总结 项目说明 背景:因为有地方要用,而又没找到对sentinel良好支持的Net客户端 ...
- Redis系列二之事务及消息通知
一.事务 Redis中的事务是一组命令的集合.一个事务中的命令要么都执行,要么都不执行. 1.事务简介 事务的原理是先将一个事务的命令发送给Redis,然后再让Redis依次执行这些命令.下面看一个示 ...
- Redis系列二(yum切换为网易163)
这个可能和Redis没有直接的关系... 是我在yum install的时候发现centos的yum实在是太慢,上网查了下.网易163有个yum镜像,为了让CentOS6使用速度更快的YUM更新源,可 ...
- redis系列二: linux下安装redis
下面介绍在Linux环境下,Redis的安装与配置 一. 安装 1.首先上官网下载Redis 压缩包,地址:http://redis.io/download 下载稳定版3.0即可. 2.通过远程管理工 ...
随机推荐
- the largest value you actually can transmit between the client and server is determined by the amount of available memory and the size of the communications buffers.
the largest value you actually can transmit between the client and server is determined by the amoun ...
- Bing必应地图中国API-显示兴趣点 (转)
Bing必应地图中国API-显示兴趣点 2011-05-24 14:29:55| 分类: Bing&Google|字号 订阅 在地图上显示一个兴趣点,这个应用可以说是最简单但是最广泛 ...
- MFC ListControl技巧汇总
转自:http://hi.baidu.com/qi_xian/blog/item/1971aa22da89ada24723e856.html 以下未经说明,listctrl默认view 风格为repo ...
- 洛谷 P1570【NOIP2013】花匠
题目描述 花匠栋栋种了一排花,每株花都有自己的高度.花儿越长越大,也越来越挤.栋栋决定 把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希 望剩下的花排列得比较别致. 具 ...
- Size Classes with Xcode 6:为所有的尺寸准备一个Storyboard
我最喜欢的Xcode6的特性是新的size classes 概念,有了它,我们解决了“我如何能够快速的为那么多不同尺寸屏幕以及方向的设备写app”.他们也让在一个storyboard中创建一个通用的a ...
- BZOJ_4800_[Ceoi2015]Ice Hockey World Championship_双指针
BZOJ_4800_[Ceoi2015]Ice Hockey World Championship_双指针 Description 有n个物品,m块钱,给定每个物品的价格,求买物品的方案数. Inpu ...
- Windows路径
绝对路径 是从盘符开始的路径,形如 C:\windows\system32\cmd.exe 相对路径 是从当前目录开始的路径,假如当前目录为C:\windows 要描述上述路径,只需输入 system ...
- [luogu2620]虫洞
https://www.zybuluo.com/ysner/note/1284536 题面 给一个一维坐标系,出发点为\(0\),目标点为\(w\). 每\(1\)秒可以往后移不超过\(s\)个单位距 ...
- bzoj1509
树的直径 我先开始以为是个图,想想并不知道什么求图的直径的方法,结果是棵树 那么直觉告诉我们是在直径上面,实际上就是直径+min(i->u,i->v),扫一遍就行了 #include< ...
- spring-boot-configuration-processor的作用
spring默认使用yml中的配置,但有时候要用传统的xml或properties配置,就需要使用spring-boot-configuration-processor了 先引入pom依赖 <d ...