http://blog.itpub.NET/11627468/viewspace-1764753/

一、quartz数据库锁

其中,QRTZ_LOCKS就是Quartz集群实现同步机制的行锁表,其表结构如下:

点击(此处)折叠或打开

  1. --QRTZ_LOCKS表结构
  2. CREATE TABLE `QRTZ_LOCKS` (
  3. `LOCK_NAME` varchar(40) NOT NULL,
  4. PRIMARY KEY (`LOCK_NAME`)
  5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  6. --QRTZ_LOCKS记录
  7. +-----------------+
  8. | LOCK_NAME |
  9. +-----------------+
  10. | CALENDAR_ACCESS |
  11. | JOB_ACCESS |
  12. | MISFIRE_ACCESS |
  13. | STATE_ACCESS |
  14. | TRIGGER_ACCESS |
  15. +-----------------+

注:此表结构在2.2版本有新增字段,这里暂时不考虑。
可以看出QRTZ_LOCKS中有5条记录,代表5把锁,分别用于实现多个Quartz Node对Job、Trigger、Calendar访问的同步控制。 
关于行锁的机制:
1、mysql >  set autocommit=0;    //先把mysql设置为不自动提交。
2、 select * from es_locks where lock_name = 'TRIGGER_ACCESS' for update ;     //线程一通过for update 可以把这行锁住
3、 select * from es_locks where lock_name = 'TRIGGER_ACCESS' for update ;     //线程二通过for update 无法获得锁,线程等待。
4、commit;        //线程一通过commit 释放锁
5、 //线程二可以访问到数据,线程不再等待。

所以,通过这个机制,一次只能有一个线程来操作 加锁 -  操作 - 释放锁。  如果 操作 的时间过长的话,会带来集群间的主线程等待。
数据库行锁是一种悲观锁,锁表时其它线程无法查询。

源码中关于数据库集群加锁的方法有如下几种:
1、executeInNonManagedTXLock方法的含义是自己管理事务,不让容器管理事务的加锁方法。

点击(此处)折叠或打开

  1. executeInNonManagedTXLock(
  2. String lockName,
  3. TransactionCallback<T> txCallback , final TransactionValidator<T> txValidator )

三个参数lockName的值是上面所说的TRIGGER_ACCESS,表示要加锁的类型。
txCallback是加锁后再回调的方法。
txValidator是验证方法,一般为null
函数先执行加锁,再回调要操作的方法,然后再解锁。
看一下源码:

点击(此处)折叠或打开

  1. if (lockName != null) {
  2. // If we aren't using db locks, then delay getting DB connection
  3. // until after acquiring the lock since it isn't needed.
  4. if (getLockHandler().requiresConnection()) {
  5. conn = getNonManagedTXConnection();
  6. }
  7. transOwner = getLockHandler().obtainLock(conn, lockName);
  8. }
  9. if (conn == null) {
  10. conn = getNonManagedTXConnection();
  11. }
  12. final T result = txCallback.execute(conn);
  13. try {
  14. commitConnection(conn);
  15. } catch (JobPersistenceException e) {
  16. rollbackConnection(conn);
  17. if (txValidator == null || !retryExecuteInNonManagedTXLock(lockName, new TransactionCallback<Boolean>() {
  18. @Override
  19. public Boolean execute(Connection conn) throws JobPersistenceException {
  20. return txValidator.validate(conn, result);
  21. }
  22. })) {
  23. throw e;
  24. }
  25. }
  26. Long sigTime = clearAndGetSignalSchedulingChangeOnTxCompletion();
  27. if(sigTime != null && sigTime >= 0) {
  28. signalSchedulingChangeImmediately(sigTime);
  29. }
  30. return result;
  31. } catch (JobPersistenceException e) {
  32. rollbackConnection(conn);
  33. throw e;
  34. } catch (RuntimeException e) {
                rollbackConnection(conn);
                throw new JobPersistenceException("Unexpected runtime exception: "
                        + e.getMessage(), e);
            } finally {
                try {
                    releaseLock(lockName, transOwner);
                } finally {
                    cleanupConnection(conn);
                }
            }

2、如果不是通过这种回调方法的加锁,一般是:
getLockHandler().obtainLock
执行
commitConnection(conn)
releaseLock
cleanupConnection

二、源码分析锁

目前代码中行锁只用到了STATE_ACCESS 和TRIGGER_ACCESS 这两种。

1、TRIGGER_ACCESS
先了解一篇文章,通过源码来分析quartz是如何通过加锁来实现集群环境,触发器状态的一致性。 
http://www.360doc.com/content/14/0926/08/15077656_412418636.shtml可以看到触发器的操作主要用主线程StdScheduleThread来完成,不管是获取需要触发的30S内的触发器,还是触发过程。select和update触发器表时
都会先加锁,后解锁。如果数据库资源竞争比较大的话,锁会影响整个性能。可以考虑将任务信息放在分布式内存,如redis上进行处理。数据库只是定时从redis上load数据下来做统计。
参考:quartz详解2:quartz由浅入深   查看第四章第1,2节
实现都在JobStoreSupport类

加锁类型 加锁方法 底层数据库操作 备注
executeInNonManagedTXLock acquireNextTrigger selectTriggerToAcquire
selectTrigger
selectJobDetail
insertFiredTrigger
查询需要点火的trigger
选择需要执行的trigger加入到fired_trigger表
for执行 triggerFired selectJobDetail
selectCalendar
updateFiredTrigger
triggerExists updateTrigger
点火trigger
修改trigger状态为可执行状态。
recoverJobs updateTriggerStatesFromOtherStates
hasMisfiredTriggersInState doUpdateOfMisfiredTrigger
selectTriggersForRecoveringJobs
selectTriggersInState
deleteFiredTriggers
非集群环境下重新执行
failed与misfired的trigger
retryExecuteInNonManagedTXLock releaseAcquiredTrigger updateTriggerStateFromOtherState
deleteFiredTrigger
异常情况下重新释放trigger到初使状态。
triggeredJobComplete selectTriggerStatus
removeTrigger   updateTriggerState
deleteFiredTrigger
触发JOB任务完成后的处理。
obtainLock recoverMisfiredJobs hasMisfiredTriggersInState doUpdateOfMisfiredTrigger 重新执行misfired的trigger
可以在启动时执行,也可以由misfired线程定期执行。
clusterRecover selectInstancesFiredTriggerRecords
updateTriggerStatesForJobFromOtherState
storeTrigger
deleteFiredTriggers
selectFiredTriggerRecords
removeTrigger
deleteSchedulerState
集群有结点faied,让JOB能重新执行。
executeInLock
数据库集群里等同于
executeInNonManagedTXLock
storeJobAndTrigger updateJobDetail insertJobDetail
triggerExists
selectJobDetail
updateTrigger insertTrigger
保存JOB和TRIGGER配置
storeJob   保存JOB
removeJob   删除JOB
removeJobs   批量删除JOB
removeTriggers   批量删除triggers
storeJobsAndTriggers   保存JOB和多个trigger配置
removeTrigger   删除trigger
replaceTrigger   替换trigger
storeCalendar   保存定时日期
removeCalendar   删除定时日期
clearAllSchedulingData   清除所有定时数据
pauseTrigger   停止触发器
pauseJob   停止任务
pauseJobs   批量停止任务
resumeTrigger   恢复触发器
resumeJob   恢复任务
resumeJobs   批量恢复任务
pauseTriggers   批量停止触发器
resumeTriggers   批量恢复触发器
pauseAll   停止所有
resumeAll   恢复所有

---

2、STATE_TRIGGER
实现都在JobStoreSupport类

加锁类型 加锁方法 底层数据库操作 备注
obtainLock doCheckin clusterCheckIn 判断集群状态
先用LOCK_STATE_ACCESS锁集群状态
再用LOCK_TRIGGER_ACCESS恢复集群运行
     

---

quartz详解3:quartz数据库集群-锁机制的更多相关文章

  1. Redis详解(七)——集群

    Redis详解(七)--集群 ​Redis3.0版本之前,可以通过Redis Sentinel(哨兵)来实现高可用 ( HA ),从3.0版本之后,官方推出了Redis Cluster,它的主要用途是 ...

  2. 详解k8s原生的集群监控方案(Heapster+InfluxDB+Grafana) - kubernetes

    1.浅析监控方案 heapster是一个监控计算.存储.网络等集群资源的工具,以k8s内置的cAdvisor作为数据源收集集群信息,并汇总出有价值的性能数据(Metrics):cpu.内存.netwo ...

  3. 转载:quartz详解:quartz由浅入深

    转载网址:http://blog.itpub.net/11627468/viewspace-1763498/ 一.quartz核心概念 先来看一张图:         scheduler 任务调度器 ...

  4. Solr系列二:solr-部署详解(solr两种部署模式介绍、独立服务器模式详解、SolrCloud分布式集群模式详解)

    一.solr两种部署模式介绍 Standalone Server 独立服务器模式:适用于数据规模不大的场景 SolrCloud  分布式集群模式:适用于数据规模大,高可靠.高可用.高并发的场景 二.独 ...

  5. Kafka 详解(二)------集群搭建

    这里通过 VMware ,我们安装了三台虚拟机,用来搭建 kafka集群,虚拟机网络地址如下: hostname                      ipaddress             ...

  6. Zookeeper详解-伪分布式和集群搭建(八)

    说到分布式开发Zookeeper是必须了解和掌握的,分布式消息服务kafka .hbase 到hadoop等分布式大数据处理都会用到Zookeeper,所以在此将Zookeeper作为基础来讲解. Z ...

  7. Redis面试题详解:哨兵+复制+事务+集群+持久化等

    Redis主要有哪些功能? 1.哨兵(Sentinel)和复制(Replication) Redis服务器毫无征兆的罢工是个麻烦事,如何保证备份的机器是原始服务器的完整备份呢?这时候就需要哨兵和复制. ...

  8. 大数据入门第十六天——流式计算之storm详解(三)集群相关进阶

    一.集群提交任务流程分析 1.集群提交操作 参考:https://www.jianshu.com/p/6783f1ec2da0 2.任务分配与启动流程 参考:https://www.cnblogs.c ...

  9. 详解Mysql事务隔离级别与锁机制

    一.概述 我们的数据库一般都会并发执行多个事务,多个事务可能会并发的对相同的一批数据进行增删改查操作,可能 就会导致我们说的脏写. 胀读和不可重复读.幻读这些问题. 这些问题的本质都是数据库的多事务并 ...

随机推荐

  1. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 按钮:自适应大小的按钮组

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  2. Why Helm?【转】

    每个成功的软件平台都有一个优秀的打包系统,比如 Debian.Ubuntu 的 apt,Redhat.Centos 的 yum.而 Helm 则是 Kubernetes 上的包管理器. 本章我们将讨论 ...

  3. HiBench成长笔记——(1) HiBench概述

    测试分类 HiBench共计19个测试方向,可大致分为6个测试类别:分别是micro,ml(机器学习),sql,graph,websearch和streaming. 2.1 micro Benchma ...

  4. docker 运行ubuntu镜像 apt-get update 问题

    docker运行ubuntu镜像后,apt-getupdate出现问题如下: 根据上面的报错大概是因为....文件上没有生效(生效还需要10d 13h 33min 45s),看来是时间不够啊,需要等待 ...

  5. Solr查询和过滤器执行顺序剖析

    一.简介 Solr的搜索主要由两个操作组成:找到与请求参数相匹配的文档:对这些文档进行排序,返回最相关的匹配文档.默认情况下,文档根据相关度进行排序.这意味着,找到匹配的文档集之后,需要另一个操作来计 ...

  6. IDEA开发spring boot应用时 application.yml 或 application.properties 自定义属性提示

    在使用spring boot开发过程中,经常会定义一些应用自己的属性,直接写到application配置文件中使用@Value注解进行使用,这样使用也没有什么问题.不过我认为更优雅的方式是定义自己的属 ...

  7. xaml与CSS中的Margin顺序不同

    XAML中  Margin:左 上 右 下 CSS中      Margin:上 右 下 左

  8. NIO 聊天室代码实现

    服务器端 package com.ronnie.nio.groupChat; import java.io.IOException; import java.net.InetSocketAddress ...

  9. kali下的截图工具scrot、flameshot和deepin-scrot

    对于这几个截图工具,精简好用的应该是deepin-scrot了,这是个和QQ截图有类似功能的Linux截图工具.flameshot的功能是最多的,也很好用,虽然有的功能用不上. 1.scrot安装和使 ...

  10. C++获取文件夹中所有文件

    获取文件夹中的文件,用到过很多次,每次用的时候都要去查下,很烦,所以想自己写下,当然,借鉴了很多其他大佬的博客 主要实现的函数,如下: void getFiles( string path, vect ...