XXL-JOB (2)---任务重复调度问题排查

背景

  1. 在将xxl-job-admin部署到正式环境后,发现存在重复调度的问题。系统部署在k8s中,共起了3个pods,后端存储为TIDB。
  2. 发现问题后,当即降pods的副本数降到1,可见重复调度问题消除。
  3. 打开xxl-job-admin的日志问题可以看到一直在刷Write conflict xxx等报错日志。

排查过程

 开源项目遇到问题第一步,先上GitHub上搜issue。果不其然被我搜索到一模一样的问题。(ISSUE)。根据issues的描述,这个错误可以与TIDB有关。结合自己使用的存储底层,着手向这方面进行了排查。

 接下来让我们再次回看下xxl-job-admin中竞争调度的代码,从下面的图片中可以看出流程主要分为这么几个部分

  • (1)先通过jdbc获取一个数据库连接。
  • (2)将事务自动commit关掉。
  • (3)通过select for update的方式来锁住一行(排它锁)
  • (4)执行事务逻辑。
  • (5)事务执行完毕手动提交事务。
xxl-job-admin竞争调度核心代码

 通过查询TIDB的相关文档,发现在TIDB版本<= v3.0.8之前使用的乐观事务模型,也就是说不会进行锁等待,只会在事务提交冲突的时候进行报错(Write conflict)。再结合文档中描述,发现有两处可以需要确认的地方

  • (1)使用的TIDB版本<= v3.0.8?不是,我使用的是4.0.6版本。当文档也提这么一句话:只有新创建的集群才会默认使用悲观事务模式,从旧版本升级上来的并不会修改事务类型
  • (2)是否在事务中使用了自动提交?从上面的代码上看,在获取JDBC的时候有显式的关闭的事务提交。


 于是问题就转行成确认我当前使用的TIDB是不是从低版本升级上来的,或者确认当前默认使用的是不是悲观事务模型。经过询问TIDB同事,告诉我当前就是默认悲观事务,但是我不信。

 既然不信,那Talk is cheap. Show me the code,手动写两个main方法来测试下吧!就认准一条准则,已经是悲观事务模式的话,那么tidb执行for update的结果应该跟mysql的一样(锁等待)。假设表kantlin中有(2,1)这行数据,事务执行时序大概如下:

 MySQL的结果是在Tx1和Tx2都可以成功提交, t7时刻, select执行结果为b=22,且但Tx2从t3时刻开始,会被阻塞,一直到t6时刻, Tx1完成提交后, Tx2才能提交,所有在mysql中执行select for update排它锁语句是会等待锁的,没问题。
 但是TiDB中, Tx1提交会失败(WriteConflict),到了t7时刻, select执行结果为b=21,所以证明当前的还是处于乐观事务模式。
 有了这些证据后,再次请TIDB同事确认,TIDB同事经过一番排除后发现确实是从旧版本升级上来的,应该没有改默认的事务类型,并帮我手动的指定事务级别为悲观事务模式。我接着再对系统增加pods副本,发现没有重复调度的问题,日志打印也正常了。

其它思考

使用乐观锁事务模型优缺点是什么?

 TiDB 使用 Percolator 事务模型, 冲突检测只在事务提交时才触发, 而MySQL则是通过锁等待机制(如SELECT … FOR UPDATE)解决冲突问题.TiDB这样设计有好有坏, 在冲突小的情况下,由于没有锁等待,系统的并发性能更好. 但在冲突严重的情况下, 会造成事务失败增多,影响并发性能。

如何使用TIDB?

 作为开发,当前正从mysql向tidb切换过度,总是很直接就认为当mysql来用就可以了,而没有过多的研究。从这次小小的掉坑,也学到不少tidb的知识。作为DBA的同事,他们确实是没有责任帮你升级事务模型,那么就全靠开发把握了,还好是很容易在测试期间就发现的问题,但是想象下如何是在生产发现,并且涉及到跟钱有关的批扣,那问题就大了。生产无小事!

其它的方式实现锁?

 如果你手头上真的只有一个<= v3.0.8版本的TIDB,但是又想多实例部署xxl-job-admin的话,那么其实基于DB实现分布式锁主要有三种,xxl-jobs使用的是排它锁,另外的唯一索引锁或CAS乐观锁也都可以实现的,保险点再弄个监控线程清理长期占用的锁就可以了。

参考文档

TiDB 悲观事务模式

乐观事务模型下写写冲突问题排查

你可能感兴趣的:(XXL-JOB (2)---任务重复调度问题排查)