当前位置: 首页 > news >正文

Redis 分布式锁的实现方式

一般来说,在对数据进行“加锁”时,程序首先需要通过获取(acquire)锁来得到对数据排他性访问的能力,然后才能对数据执行一系列操作,最后还要将锁释放(release)给其他程序。

对于能够被多个线程访问的共享内存数据结构(shared-memory data structure)来说,这种“先获取锁,然后执行操作,最后释放锁”的动作非常常见。Redis 使用 WATCH 命令来代替对数据进行加锁,因为 WATCH 只会在数据被其他客户端抢先修改了的情况下,才通知执行了这个命令的客户端,而不会阻止其他客户端对数据进行修改,所以被称为乐观锁(optimistic lock)。

分布式锁也有类似的“首先获取锁,然后执行操作,最后释放锁”动作,但这种锁既不是给同一个进程中的多个线程使用,也不是给同一台机器上的多个进程使用,而是由不同机器上的不同 Redis 客户端进行获取和释放的。何时使用以及是否使用 WATCH 或者锁取决于给定的应用程序:有的应用不需要使用锁就可以正确地运行,而有的应用只需要使用少量的锁,还有的应用需要在每个步骤都使用锁。

分布式锁在业务中非常常见,能够避免在分布式环境中同时对同一个数据进行操作,进而避免并发问题。

锁出现不正确行为的原因,以及锁在不正确运行时的症状

  • 持有锁的进程因为操作时间过长而导致锁被自动释放,但进程本身并不知晓这一点,甚至还可能会错误地释放掉了其他进程持有的锁
  • 一个持有锁并打算执行长时间操作的进程已经崩溃,但其他想要获取锁的进程并不知道哪个进程持有锁,也无法检测出持有锁的进程已经崩溃,只能白白地浪费时间,等待锁被释放
  • 在一个进程持有的锁过期后,其他多个进程同时尝试去获取锁,并且都获得了锁
  • 上面提到的第一种情况和第三种情况同时出现,导致有多个进程获得了锁,并且每个进程都以为自己是唯一一个获得锁的

以下代码省去了一些程序的上下文环境描述。

// io-redis获取redis所有key
async getKeys(match, count = 100) {const { app: { redis }, ctx: { helper: { _ } } } = this;let keys = [];const scanStream = await redis.scanStream({ match, count });const result = await new Promise(resolve => {scanStream.on('data', data => {keys = keys.concat(data);});scanStream.on('end', () => {resolve(_.uniq(keys));});});return result;
}
// 获取redis所有key
async getKeys(pattern, count = 100) {const { app: { redis }, ctx: { helper: { _ } } } = this;let result = [];const _getKeys = async (cursor = 0) => {// redis scan迭代获取, 默认取100const apiResult = await redis.scan(cursor, 'match', pattern, 'count', count);result = result.concat(apiResult[1]);// 新游标返回0则表示迭代已结束if (Number(apiResult[0]) === 0) {return;}// 继续迭代await _getKeys(apiResult[0]);}await _getKeys();// 去重return _.uniq(result);
}
// redis获取锁
async lock(key, count) {const { app: { redis }, config: { cacheKey } } = this;const result = await redis.set(key, count, 'EX', cacheKey.lock.expireTime, 'NX');return result === 'OK';
}// redis释放锁
async unlock(key, count) {const { app: { redis } } = this;const script = `ifredis.call('get', KEYS[1]) == ARGV[1]thenreturn redis.call('del', KEYS[1])elsereturn 0end`;const result = await redis.eval(script, 1, key, count);return Boolean(result);
}
http://www.lryc.cn/news/134245.html

相关文章:

  • VMware上搭建的虚拟机突然本地无法连接服务器
  • JDBC回顾
  • mq 消息队列 mqtt emqx ActiveMQ RabbitMQ RocketMQ
  • 沃尔玛卖家必看!解决订单被Kan、Feng号问题的终极方案!
  • 浅谈日常使用的 Docker 底层原理-三大底座
  • 前端面试:【DOM】编织网页的魔法
  • 基于MATLAB开发AUTOSAR软件应用层Code mapping专题-part 2 Inport和Outports 标签页介绍
  • 第9步---MySQL的索引和存储引擎
  • Numpy入门(3)—线性代数
  • php的openssl_encrypt是不是自动做了PKCS5Padding?
  • 在本地创建repository及上传至github
  • 情人节特别定制:多种语言编写动态爱心网页(附完整代码)
  • Docker mysql主从同步安装
  • docker update 命令
  • 阻塞和挂起的区别和联系
  • 水力发电厂测量装置配置选型及厂用电管理系统
  • 【RabbitMQ】RabbitMQ整合SpringBoot案例
  • 如何在window下cmd窗口执行linux指令?
  • c++基础系列:字符串、向量和数组
  • docker 05(dockerfile)
  • PostMan 测试项目是否支持跨域
  • jsp 协同过滤 图书管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目
  • 商城-学习整理-高级-商城业务-商品上架es(十)
  • 【水文学法总结】河道内生态流量计算方法(含MATLAB实现代码)
  • 特斯拉Model 3的七年狂飙
  • 物流签收异常,财务对账复杂,怎么解决?
  • docker之镜像与数据卷
  • 服务器为什么会被攻击?43.248.186.x
  • 文心一言 VS 讯飞星火 VS chatgpt (79)-- 算法导论7.4 4题
  • 整理mongodb文档:索引