笔记(上):mysql-DuplicateUpdate和java的threadpool的“死锁“

news2024/11/24 22:37:58

今天给大家讲讲最近2个有意思的issue,分享一下我学到的

  • mysql DuplicateUpdate的用法要注意的点
  • java的threadpool使用不当会造成“死锁”问题

mysql DuplicateUpdate的用法要注意的点

有个issue说遇到了一个这样的问题,

这个朋友使用我开源的job调度框架 https://github.com/yuzd/Hangfire.HttpJob

存储用的是mysql,采用的实现是 https://github.com/arnoldasgudas/Hangfire.MySqlStorage

set表的id是自增主键,正常理解 都是慢慢自增上去的,但是发现是大幅度跳跃式的自增, 真相是什么?

首先针对这个问题,首先我们搞清楚在hangfire中和storage相关的部分如下

 

image

  • hangfire server调度依赖storage
  • storage抽象出来一层api(解耦)
  • 第三方扩展(不关心具体的storage实现)
  • 不同的storage具体实现(比如mysql,sqlserver等)

Hangfire.Httpjob其实只是依赖了storage api那一层,也没有能力去直接写sql去执行, 只能用api去操作hangfire的那几张表(比如set表)

那么问题肯定不是在扩展层,而是得去看看mysqlstorage的实现源码,针对set表的处理逻辑

https://github.com/arnoldasgudas/Hangfire.MySqlStorage/blob/0bd1016f715c8c6617ce22fb7b2ce5b6c328d2fb/Hangfire.MySql/MySqlWriteOnlyTransaction.cs#L155


  public override void AddToSet(string key, string value, double score)
    {
        Logger.TraceFormat("AddToSet key={0} value={1}", key, value);

        AcquireSetLock();
        QueueCommand(x => x.Execute(
            $"INSERT INTO `{_storageOptions.TablesPrefix}Set` (`Key`, `Value`, `Score`) " +
            "VALUES (@Key, @Value, @Score) " +
            "ON DUPLICATE KEY UPDATE `Score` = @Score",
            new { key, value, score }));
    }

这里是用了ON DUPLICATE KEY UPDATE 的语句

这个语法是在mysql 4.1(2005)引入的,意思是 insert的时候遇到主键已存在 就执行后面 的update

但是就是这个功能 会造成自增主键成跳跃式增长,增长跨度和SQL的执行次数成正比

根据朋友提供的截图

 

image

虽说是会跳跃,但是这个增长也太夸张了

打上断点调试发现

是hangfire server 不断的在调用,目的是把下一次执行时间(秒级别的时间戳)写到set表中

 

image 

image 

image

打上日志可以看到有非常多相同值的调用,这仅仅是一个job,这个自增速度得再乘以job的个数,难怪了

既然找到原因了,就提个PR 修改下


 public override void AddToSet(string key, string value, double score)
        {
            Logger.TraceFormat("AddToSet key={0} value={1}", key, value);
        
            AcquireSetLock();
            QueueCommand(x =>
            {
                var sql = "";
                if (key == "recurring-jobs") // 只发现这个key存在这个问题
                {
                     // key+value是uniq 改成先update 如果没有成功 再insert
                    sql = $"UPDATE `{_storageOptions.TablesPrefix}Set` set `Score` = @score where `Key` = @key and `Value` = @value";
                    var updateRt = x.Execute(sql, new { score = score, key = key, value = value });
                    if (updateRt < 1)
                    {
                        sql = $"INSERT INTO `{_storageOptions.TablesPrefix}Set` (`Key`, `Value`, `Score`) " +
                              "VALUES (@Key, @Value, @Score) ";
                        x.Execute(
                            sql,
                            new { key, value, score });
                    }
                }
                else
                {
                    sql = $"INSERT INTO `{_storageOptions.TablesPrefix}Set` (`Key`, `Value`, `Score`) " +
                          "VALUES (@Key, @Value, @Score) " +
                          "ON DUPLICATE KEY UPDATE `Score` = @Score";
                   x.Execute(
                       sql,
                       new { key, value, score });
                }
        
                //Console.WriteLine(sql + " ==> " + key + "@" + value + "@" + score);
            });
        }

改完之后测试,id自增一切正常:

 

image

注意上面演示的mysql存储是用的官方推荐的,

但是但是建议使用mysql作为存储的使用

 https://github.com/MiloszKrajewski/Hangfire.Storage.MySql

官方推荐的版本有死锁的bug,有主键自增膨胀(归根到底还是没有控制好锁) 参考issue:

  • https://github.com/arnoldasgudas/Hangfire.MySqlStorage/issues/63
  • https://github.com/arnoldasgudas/Hangfire.MySqlStorage/pull/97

java的threadpool使用不当会造成“死锁”问题

 

 image

这个原因先说出来: threadpool的线程被占用完后,再来的task会往queue里面丢,如果这个时候在这个pool的线程里面 future.get()的话会导致task runner(执行器)被堵住,没人从队列里面取任务了~

(简单来说就是 线程在wait future返回,而这个future在queue里面苦苦等待新释放的线程去执行,就像死锁一样,我在等你的结果,而结果在等待着被执行)

好家伙,这个场景有点熟悉,因为我在项目中也用过Future.get()// 虽说有设置timeout

但是这个问题的重要一点是,这种花式“死锁” jvm是检测不出来的,下面有测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/58206.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件包管理器yum与编辑器vim的使用

目录yum的背景yum的生态yum的本土化&#xff08;换源&#xff09;yum的使用查看软件包安装软件卸载软件vim的使用Vim的基本基本介绍Vim的模式切换Vim的光标定位Vim的文本复制Vim的文本编辑Vim的底行模式的操作vim的配置管理员权限(sudo)的配置yum的背景 yum的生态 在日常的生…

Node.Js基础知识

Node.Js架构 Natives modules 当前层内容由JS实现提供 应用程序可直接调用库&#xff0c;例如&#xff1a;fs、path、 http等JS语言无法直接操作底层硬件设置&#xff08;–》Builtin modules 胶水层&#xff09; Builtin modules 胶水层 在V8引擎下&#xff0c;主要是帮助…

geomtextpath | 成功让你的ggplot注释拥有傲人曲线!~

1写在前面 最近的世界杯结果的确是让人大跌眼镜&#x1f576;️, 日本队&#x1f1ef;&#x1f1f5;先后击败世界杯冠军, 德国队&#x1f1e9;&#x1f1ea;和西班牙队&#x1f1ea;&#x1f1f8;, 韩国队&#x1f1f0;&#x1f1f7;逆转葡萄牙&#x1f1f5;&#x1f1f9;, 踩着…

定时轮询-长轮询-websocket

一般网页 点击网页后&#xff0c;前端向后端 发送请求&#xff08;使用http协议&#xff09; 那么如何实现后端主动先前端发送数据&#xff0c;比如&#xff1a;网页游戏 聊天室 文件共享 扫码登入 http定时轮询 &#xff08;轮询&#xff09; 在前端代码中每隔一段时间向后…

宝塔面板Linux

目录 1.宝塔面板简介 2.安装与连接 3.安装软件 4.端口管理 5.宝塔面板设置 这里主要讲的是宝塔面板的主要操作&#xff0c;本节应与xshell与xftp共同使用 1.宝塔面板简介 宝塔面板是一款服务器管理软件&#xff0c;支持windows和linux系统&#xff0c;可以通过Web端轻松…

第六章- Verilog HDL 高级程序设计举例【Verilog】

第六章 Verilog HDL 高级程序设计举例【Verilog】前言推荐第六章 Verilog HDL 高级程序设计举例状态机用状态机设计1101序列检测器需要定义几个状态&#xff1f;代码设计——端口信号声明状态寄存器次态生成逻辑设计(C1模块)输出逻辑设计(C2模块)完整代码测试结果用状态机设计1…

vSphere-ESXi

VMware-vShpere 第一步&#xff1a; 在VMware虚拟机上安装一台ESXI 开启虚拟机 至此&#xff0c;虚拟机ESXI安装完毕 第二步&#xff1a;使用ESXI虚拟机IP上浏览器 登录后&#xff0c;上传一个映像文件至ISO文件 将VM虚拟机关机 手动添加一块硬盘 然后再使用虚拟机IP进入浏览器…

python入门——基础语法

python入门——基础语法 注释 单行注释&#xff1a;使用# 多行注释&#xff1a;使用 “”" “”" type()&#xff1a;查看数据的类型 int()&#xff1a;转化为整数 float()&#xff1a;转化为浮点数 str()&#xff1a;转化为字符串 标识符不可以使用这些 …

Spring WebFlux简单使用

官网&#xff1a;https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html 1.WebFlux介绍 Spring WebFlux 是 Spring Framework 5.0中引入的新的响应式web框架。与Spring MVC不同&#xff0c;它不需要Servlet API&#xff0c;是完全异步且非阻…

蓝桥杯嵌入式LCD屏幕

文章目录前言一、拷贝官方例程二、cubeMX配置三、LCD代码及函数分析使用四、sprintf函数总结前言 本篇文章将带大家学习LCD屏幕的操作&#xff0c;LCD的配置是非常复杂的&#xff0c;在比赛上去实现这些驱动程序基本上是不可能的&#xff0c;当然了比赛官方也是知道这一点的&a…

SpringMVC基础篇:MVC基础知识

第一章&#xff1a;SpringMVC引言 一&#xff1a;什么是SpringMVC 概念&#xff1a;SpringMVC是在Spring框架基础上衍生而来的一个MVC框架&#xff0c;主要解决了原有的MVC框架过程中控制器&#xff08;Controller&#xff09;的问题。 SpringMVC是Java开发当中最主流的web技…

了解软件测试

软件测试课程 1.1课程内容&#xff1a; 软件基础课程 ------设计测试用例方法 自动化课程&#xff08;web自动化&#xff09;------ 抢票功能 性能测试课程 ----项目性能测试 1.2 什么是测试 1.21 生活中测试的案例 坐地铁&#xff0c;做核酸&#xff0c;扫核酸码 比如当我…

[附源码]计算机毕业设计学生社团信息管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

零基础学习软件测试,掌握四点就够了

近年来越来越多的人转行到软件测试这一领域&#xff0c;对于很多外行的人来说&#xff0c;肯定对这一行业有很多不了解&#xff0c;对于这一职业的职责以及要求都会不清楚&#xff0c;那么我们今天就来梳理一下关于软件测试行业的信息。 一、软件测试的主要职责你知道吗&#x…

裸辞闭关60天,啃下这些软件测试笔记,有幸通过阿里测开岗P6面试

时代在发展&#xff0c;互联网之下&#xff0c;稍有一些落后可能就会被淘汰掉&#xff0c;因此我们需要不断去审视自己&#xff0c;通过学习来让自己得到相应的提升。 近段时间&#xff0c;我也了解到很多小伙伴不清楚作为测试工程师应该掌握什么样的核心知识&#xff1f;实际…

暴力算法 --- 莫队

文章目录莫队基础莫队带修改莫队树上莫队回滚莫队莫队 什么是莫队&#xff1f; 答&#xff1a;优雅的暴力&#xff01;&#xff01;&#xff01; 基础莫队 重复的数 题目描述&#xff1a;给出一个长度为NNN的序列&#xff0c;有若干查询&#xff0c;每次查询区间[li,ri][l_i,…

网页信息抓取-网页信息采集器

抓取整个网页&#xff0c;我们怎么抓取整个网页自己想要的文章内容&#xff0c;很多人一想到内容抓取。就想到要学习一门编程语言&#xff0c;最让大家熟知的就是python爬虫。如果完全靠自己自学&#xff0c;又是从零基础开始学习Python的情况下&#xff0c;我认为至少需要半年…

Spring Boot 2 (六):使用 Docker 部署 Spring Boot 开源软件云收藏

云收藏项目已经开源2年多了,作为当初刚开始学习 Spring Boot 的练手项目,使用了很多当时很新的技术,现在看来其实很多新技术是没有必要使用的,但做为学习案例来讲确实是一个绝佳的 Spring Boot 实践。 从开源到现在,写了一些教程给大家介绍如何部署云收藏,如何在IDE中运…

[附源码]计算机毕业设计ssm校园二手交易平台

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

解决JSP中Bean在页面显示不正确问题(scope关键字)

问题出现 有一天我在编写JSP的程序时&#xff0c;在Java后端写了跳转并且传输数据语句&#xff0c;但前端界面渲染出来的数据却是我在DAO中初始化的数据。 第一句语句将book对象注入request的Session中&#xff0c;第二句实现跳转到JSP页面&#xff0c;第三句将此时的request和…