前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >分布式锁的实现以及在定时器中的应用

分布式锁的实现以及在定时器中的应用

作者头像
逝兮诚
发布2019-10-30 18:06:49
1.2K0
发布2019-10-30 18:06:49
举报
文章被收录于专栏:代码人生代码人生

分布式锁是为了保证分布式各系统对于资源的强占,独占。分布式锁的设计与多线程锁设计一样,都是通过一个信号量,对它进行CAS(compare and set)原子操作来实现乐观锁,或通过一个独占锁实现悲观锁,悲观锁不推荐。

乐观锁

乐观锁的核心是通过信号量代表资源,通过CAS的操作去标志改信号被占用。CAS成功,代表资源没有被占用,执行任务;CAS失败,代表资源被占用或处理过,不执行改资源。

失败后循环CAS的操作就叫做无锁自旋。JUC源码中,锁的实现,就是通过safe进行无锁自旋。

分布式锁的应用 - 定时任务

分布式锁在定时任务时会被使用到。分布式服务上,每个服务都有定时任务,如何保证定时任务执行的资源只执行一次,可以用分布式锁来锁住资源实现。也可以使用hash资源定位服务来实现。

定时任务分布式锁按锁的粒度,有两种思路实现。一种是定时任务加锁,即一个定时任务,只可在一个服务上执行;另一种是定时任务的每个资源加锁,即定时任务在所有服务上都执行,但是每个资源自会在一个服务节点执行。

定时任务,定时任务加锁

给定时任务加上一个信号量,定时任务执行时,CAS一下,如果信号加上去,就代表没有其他节点执行定时任务,就执行;如果CAS失败,就代表已经执行了,就不要再执行这次任务了。

下面一种通过数据库来实现 ,我们加上一个定时任务表,字段有执行时间,version字段,每个定时任务对应表中的一条记录,通过update ... where version = and update_date=做CAS操作。分布式系统CAS操作失败,代表该定时任务已被其他节点执行,它就不用执行了。

代码语言:javascript
复制
CREATE TABLE `job_mutex` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `job_type` tinyint(3) NOT NULL,
  `update_time` datetime DEFAULT NULL,
  `version` int(12) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
代码语言:javascript
复制
TaskJobSchedu taskJobSchedu = taskJobScheduMapper.queryTaskJobScheduWait(jobType);
//update job_mutex set version=#{new_veriosn}, update_time=#{now} where version=#{verison} and TO_DAYS(update_time) = TO_DAYS(#{now})
//定时任务一天执行一次
if(taskJobScheduMapper.updateVersonToday(taskJobSchedu, taskJobSchedu.getVersion()+1, new Date())) {
  execute();
}

当当的开源项目Elastic-job就有如此实现

定时任务,按每个任务加锁

如果想将分布式锁的粒度放在每个资源上,即定时任务在每个节点服务上都执行,但是它们执行的资源不会重复。这个可以通过双写来解决。执行前先通过CAS写资源到一个中间状态,执行成功结束后,将资源写到完成状态。加上一个过期设置,如中间状态15分钟后,就认为资源执行失败,回滚重新执行。

这里是一种通过redis和DB双写来实现资源的,这里通过redisTemplate的setIsAbsent来做原子操作,db中资源那张表,加个字段表示是否执行定时任务。

如果执行任务失败,它就不会写到DB,在redis中的key超时后的定时任务会再次执行这个任务。

代码语言:javascript
复制
List<Task> tasks = getNotExecuteTaskInDB();
for(Task task: tasks) {
	ValueOperations<String, String> tasksRedis = redisTemplate.opsForValue();
	if (tasksRedis.setIfAbsent(keyPrev+task.id, "1", aliveTime, TimeUnit.MINUTES) {
		execute();
		setTaskToExecuted(task);
	}
}

悲观锁

悲观锁亦可实现,但不推荐,悲观锁使用数据库可以用select for update来独占资源。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-06-27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 乐观锁
    • 分布式锁的应用 - 定时任务
      • 定时任务,定时任务加锁
        • 定时任务,按每个任务加锁
        • 悲观锁
        相关产品与服务
        云数据库 Redis
        腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档