千家信息网

Redis中怎么实现一个分布式文件夹锁

发表于:2024-11-24 作者:千家信息网编辑
千家信息网最后更新 2024年11月24日,Redis中怎么实现一个分布式文件夹锁,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。思考和调研分布式锁常见的三种实现方式
千家信息网最后更新 2024年11月24日Redis中怎么实现一个分布式文件夹锁

Redis中怎么实现一个分布式文件夹锁,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

思考和调研

分布式锁常见的三种实现方式:数据库、zookeeper/etcd(临时有序节点)、redis(setnx/lua脚本),各有千秋。

数据库实现分布式锁

实现

原理简单易实现,创建一张lock表,存储锁定的资源、上锁对象、获取锁的资源、获取锁时间等,获取锁时查询该资源是否存在记录,存在且未过失效时间则获取锁失败,不存在则插入一条数据并且获取锁成功;释放锁则更简单,删除锁数据即可。

缺点
  • 释放锁删除数据时,会出现死锁情况

优点
  • 实现简单

zookeeper/etcd实现分布式锁

详见zookeeper总结

redis实现分布式锁

详见Redis总结

分布式文件夹锁实现过程

基于开文处所列情况,要覆盖所有复杂情况很难,但是实现基本的文件夹锁是必须的,故选择了redis+lua脚本,具体代码如下

java获取锁工具类

/** * redis工具类 */public class RedisLockUtils {    static final Long SUCCESS = 1L;    static final String LOCKED_HASH = "cs:lockedHashKey";    static final String GET_LOCK_LUA_RESOURCE = "/lua/getFileLock.lua";    static final String RELEASE_LOCK_LUA_RESOURCE = "/lua/releaseFileLock.lua";    static final Logger LOG = LoggerFactory.getLogger(RedisLockUtils.class);        /**     * 获取文件夹锁     * @param redisTemplate     * @param lockProjectId     * @param lockKey     * @param requestValue     * @param expireTime 单位:秒     * @return     */    public static boolean getFileLock(RedisTemplate redisTemplate, Long lockProjectId, String lockKey, String requestValue, Integer expireTime) {        LOG.info("start run lua script,{{}} start request lock",lockKey);        long start = System.currentTimeMillis();        DefaultRedisScript luaScript =new DefaultRedisScript<>();        luaScript.setLocation(new ClassPathResource(GET_LOCK_LUA_RESOURCE));        luaScript.setResultType(String.class);        Object result = redisTemplate.execute(                luaScript,                Arrays.asList(lockKey, LOCKED_HASH + lockProjectId),                requestValue,                String.valueOf(expireTime),                String.valueOf(System.currentTimeMillis())        );        boolean getLockStatus = SUCCESS.equals(result);        LOG.info("{{}} cost time {} ms,request lock result:{}",lockKey,(System.currentTimeMillis()-start), getLockStatus);        return getLockStatus;    }    /**     * 释放文件夹锁     * @param redisTemplate     * @param lockProjectId     * @param lockKey     * @param requestValue     * @return     */    public static boolean releaseFileLock(RedisTemplate redisTemplate, Long lockProjectId, String lockKey, String requestValue) {        DefaultRedisScript luaScript =new DefaultRedisScript<>();        luaScript.setLocation(new ClassPathResource(RELEASE_LOCK_LUA_RESOURCE));        luaScript.setResultType(String.class);        Object result = redisTemplate.execute(                luaScript,                Arrays.asList(lockKey, LOCKED_HASH + lockProjectId),                requestValue        );        boolean releaseLockStatus = SUCCESS.equals(result);        LOG.info("{{}}release lock result:{}", lockKey, releaseLockStatus);        return releaseLockStatus;    }}

lua脚本

获取文件夹锁
入参说明

requestKey为请求锁的路径,requestValue为请求锁的value,应为请求锁时生成的UUID,确保解锁人只能为上锁人,lockedKeys为存放所有锁的哈希表的key,这里用常量加项目id的方式,确保一个项目的所有锁存在一个哈希表里面,expireTime为锁的过期时间,nowTime为当前时间,由于lua脚本里面获取当前时间消耗性能且获取的是redis服务器上的当前时间,可能不准确。

思路说明

首先,通过GET key判断是否有人正在操作这个文件夹,若有人在操作则直接返回0(获取锁失败),否则获取存放该项目锁的哈希表里面的所有key,遍历所有key,通过lua脚本的string.find函数对比该key和请求的key是否存在包含或被包含关系,若存在包含关系且未失效,则返回0(获取锁失败),否则则可获取锁,设置key和过期时间及存入哈希表(哈希表内存放请求锁的key和请求时间),最后返回1(获取锁成功)。

例如请求上图中项目下的C文件夹的锁,请求路径为:项目/A/C,当另一个人想操作D文件夹,请求路径为:项目/A/C/D,此时查询到存储这个项目所有锁定key的哈希表,里面包含项目/A/C这个key,这两个key通过lua函数string.find发现项目/A/C/D包含项目/A/C,且未到过期时间,则获取锁失败,否则获取锁成功。

local requestKey=KEYS[1]local lockedKeys=KEYS[2]local requestValue=ARGV[1]local expireTime=ARGV[2]local nowTime=ARGV[3]if redis.call('get',requestKey)then    return 0endlocal lockedHash = redis.call('hkeys',lockedKeys)for i=1, #lockedHash do    if string.find(requestKey,lockedHash[i]) or string.find(lockedHash[i],requestKey)    then        local lockTime = redis.call('hget',lockedKeys,lockedHash[i])        if (nowTime-lockTime) >= expireTime * 1000        then            redis.call('hdel',lockedKeys,lockedHash[i])        else            return 0        end    endendredis.call('set',requestKey,requestValue)redis.call('expire',requestKey,expireTime)redis.call('hset',lockedKeys,requestKey,nowTime)return 1
释放文件夹锁
入参说明

requestKey为请求锁的路径,requestValue为请求锁的value,应为请求锁时生成的UUID,确保解锁人只能为上锁人,lockedKeys为存放所有锁的哈希表的key,这里用常量加项目id的方式,确保一个项目的所有锁存在一个哈希表里面。

local requestKey=KEYS[1]local lockedKeys=KEYS[2]local requestValue=ARGV[1]if redis.call('get', requestKey) == requestValuethen    redis.call('hdel', lockedKeys,requestKey)    return redis.call('del',requestKey)else    return 0end

优点

  1. 灵活,锁定的范围可以随requestKey变化而变化

  2. 性能不错,经测试除了第一次lua脚本未缓存耗时较长,第二次之后则在10ms左右可得到请求结果

缺点

  1. 可靠性依赖redis

  2. 不是可重入锁

  3. 维护成本较高,需熟知redis的5种数据结构及lua脚本

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注行业资讯频道,感谢您对的支持。

项目 文件 文件夹 时间 哈希 脚本 分布式 数据 路径 成功 情况 方式 资源 优点 函数 工具 常量 性能 数据库 缺点 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 app软件开发公司河南网联 进入游戏失败该游戏已经在服务器 hp服务器磁盘陈列管理 成都做网约车软件开发选天合人宜 网络安全教育知识新闻稿 绿园区现代化网络安全欢迎咨询 网赚服务器 南京直销软件开发如何收费 服务器硬盘什么品牌好 服务器硬盘灯闪烁什么问题 深圳德勤网络技术有限公司 hmi软件开发怎么看 广州维猎网络技术有限公司 阿坝软件开发销售公司 亚信网络安全产业研究院 天津服务器虚拟化定制云空间 成都软件开发公司项目 网络技术与小企业组建步骤论文 浪潮8路服务器 聚光灯 ftp服务器修改文档 连云港推广网络技术推荐咨询 黑龙江守时模块服务器 做跨境电商是从代理商租服务器吗 软件开发该怎么学 中国公路桥梁基础数据库 大专自学数据库好找人吗 网络安全手抄报的简笔画 凌度网络技术有限公司 调度自数据库系统由什么构成 c 数据库连接 只写一次
0