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. 吴裕雄--天生自然JAVA数据库编程:执行数据库更新操作

    import java.sql.Connection ; import java.sql.DriverManager ; import java.sql.Statement ; public clas ...

  2. Windows进程通信-共享内存空间

    三个模块 1,game.exe,三个方法,控制台输入指令('A','B','R')分别控制三个方法的调用: 2,WGDll.dll,要注入到game进程中的dll文件: 3,myconsole.exe ...

  3. An attempt was made to call the method com.google.gson.GsonBuilder.setLenient()Lcom/google/gson/GsonBuilder; but it does not exist. Its class, com.google.gson.GsonBuilder, is available from the foll

    SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/G:/sharp/repo ...

  4. Tomcat删除时问题——eclipse部署tomcat时弹出Resource'/Servers' does not exist

    如果你删除一个项目的Servers文件,或者相应文件损坏等,会出现错误, Resource '/Servers' does not exist 那么就需要把它在控制台出的Servers下所部署的Tom ...

  5. C中的文件操作函数[笔记]

    头件 : #include<stdio.h> 两个必须函数: FILE * fopen(const char * path,const char * mode); //path:文件路径 ...

  6. pytesseract 识别率低提升方法

    pytesseract 识别率低提升方法 一.跟换识别语言包 下载地址https://github.com/tesseract-ocr/tessdata 二.修改图片的灰度 from PIL impo ...

  7. 008-PHP定义数组

    <?php /*定义数组$Cities[]*/ $Cities[0] = "北京"; $Cities[1] = "天津"; $Cities[2] = &q ...

  8. 《新标准C++程序设计》4.6(C++学习笔记16)

    重载流插入运算符和流提取运算符 流插入运算符:“<<” 流提取运算符:“>>” cout 是在 iostream 中定义的,ostream 类的对象. “<<” 能 ...

  9. javascript 原型链污染

    原理①javascript中构造函数就相当于类,并且可以将其实例化 ②javascript的每一个函数都有一个prototype属性,用来指向该构造函数的原型同样的javascript的每一个实例对象 ...

  10. HTML文本域(文本框)禁止修改写入数据方法

    html文本域有时需要禁止修改内容,方法如下: 加入readonly=""或readonly="readonly" 如下:<input name=&quo ...