浅析Jeecgboot中mybatisplus不支持Postgres SKIP LOCKED语法问题

news2024/12/23 3:31:04

目录

1、场景及问题

2、数据库及各框架版本信息

3、错误回放

4、根因分析及确认

5、解决问题

6、总结


1、场景及问题

场景:

在调用腾讯位置服务时有用到key值,因为每个key值都有自己的额度,所以在表里存了多个key,简称key池;

并发场景下当从key池获取可用的key并更新额度时,行锁(for update)也就尤为重要;

高并发场景下 SKIP LOCKED对于资源竞争特别有用,减少事务等待(阻塞)从而提高并发性能;

问题:

Postgres中直接使用 for update SKIP LOCKED 是没问题的,然而在mybatisplus中却报错

2、数据库及各框架版本信息

        是在开源框架jeecgboot微服务版中遇到了该问题,其本质仍是该框架引用mybatisplus及其依赖版本太低导致的。

  • Postgres:PostgreSQL 12.17 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 7.3.1 20180712 (Red Hat 7.3.1-12), 64-bit
  • jeecgboot:3.6.3
  • mybatisplus: 3.5.3.1
  • jsqlparser:4.4 

3、错误回放

        报错的Sql:

    <--获取一个未被锁定的可用key-->
    <select id="getOptimalNumber" resultType="org.jeecg.modules.**.entity.MdmNumber">
        select id, mdm_number.user, password, token, inquire_day
        from primary_mdm.mdm_number
        where type = #{type}
          and (inquire_time &lt; #{inquireTime} or inquire_time is null)
          and inquire_day &lt; #{inquireDay}
          and status = '1'
        ORDER BY inquire_day ASC LIMIT 1 for
        update SKIP LOCKED
    </select>

        报错堆栈:

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Failed to process, Error SQL: select id, mdm_number.user, password, token, inquire_day
        from primary_mdm.mdm_number
        where type = ?
          and (inquire_time < ? or inquire_time is null)
          and inquire_day < ?
          and status = '1'
        ORDER BY inquire_day ASC LIMIT 1 for
        update SKIP LOCKED
### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Failed to process, Error SQL: select id, mdm_number.user, password, token, inquire_day
        from primary_mdm.mdm_number
        where type = ?
          and (inquire_time < ? or inquire_time is null)
          and inquire_day < ?
          and status = '1'
        ORDER BY inquire_day ASC LIMIT 1 for
        update SKIP LOCKED
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:96)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:441)
	at com.sun.proxy.$Proxy168.selectOne(Unknown Source)
	at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:160)
	at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:89)
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148)
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
	at com.sun.proxy.$Proxy169.getOptimalNumber(Unknown Source)
	at org.jeecg.modules.odm.number.service.MdmNumberService.getOptimalNumber(MdmNumberService.java:69)
	at org.jeecg.modules.odm.number.service.MdmNumberService$$FastClassBySpringCGLIB$$7fcd0ad.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy.invokeMethod(CglibAopProxy.java:386)
	at org.springframework.aop.framework.CglibAopProxy.access$000(CglibAopProxy.java:85)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:703)
	at org.jeecg.modules.odm.number.service.MdmNumberService$$EnhancerBySpringCGLIB$$7d736354.getOptimalNumber(<generated>)
	at org.jeecg.modules.odm.hco.service.MdmWjwHcoService.initLocation(MdmWjwHcoService.java:42)
	at org.jeecg.modules.odm.hco.service.MdmWjwHcoService$$FastClassBySpringCGLIB$$3de5fea9.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:792)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:707)
	at org.jeecg.modules.odm.hco.service.MdmWjwHcoService$$EnhancerBySpringCGLIB$$3ced8ac8.initLocation(<generated>)
	at org.jeecg.modules.xxljobtask.OdmJobHandler.retry(OdmJobHandler.java:49)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.xxl.job.core.handler.impl.MethodJobHandler.execute(MethodJobHandler.java:29)
	at com.xxl.job.core.thread.JobThread.run(JobThread.java:152)
Caused by: org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Failed to process, Error SQL: select id, mdm_number.user, password, token, inquire_day
        from primary_mdm.mdm_number
        where type = ?
          and (inquire_time < ? or inquire_time is null)
          and inquire_day < ?
          and status = '1'
        ORDER BY inquire_day ASC LIMIT 1 for
        update SKIP LOCKED
### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Failed to process, Error SQL: select id, mdm_number.user, password, token, inquire_day
        from primary_mdm.mdm_number
        where type = ?
          and (inquire_time < ? or inquire_time is null)
          and inquire_day < ?
          and status = '1'
        ORDER BY inquire_day ASC LIMIT 1 for
        update SKIP LOCKED
	at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:153)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:145)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:76)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:427)
	... 33 more
Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Failed to process, Error SQL: select id, mdm_number.user, password, token, inquire_day
        from primary_mdm.mdm_number
        where type = ?
          and (inquire_time < ? or inquire_time is null)
          and inquire_day < ?
          and status = '1'
        ORDER BY inquire_day ASC LIMIT 1 for
        update SKIP LOCKED
	at com.baomidou.mybatisplus.core.toolkit.ExceptionUtils.mpe(ExceptionUtils.java:39)
	at com.baomidou.mybatisplus.extension.parser.JsqlParserSupport.parserSingle(JsqlParserSupport.java:52)
	at com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor.beforeQuery(TenantLineInnerInterceptor.java:68)
	at com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor.intercept(MybatisPlusInterceptor.java:78)
	at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:62)
	at com.sun.proxy.$Proxy266.query(Unknown Source)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:151)
	... 41 more
Caused by: net.sf.jsqlparser.parser.ParseException: Encountered unexpected token: "SKIP" "SKIP"
    at line 8, column 16.

Was expecting one of:

    ";"
    "NOWAIT"
    "OF"
    <EOF>

	at net.sf.jsqlparser.parser.CCJSqlParser.generateParseException(CCJSqlParser.java:31468)
	at net.sf.jsqlparser.parser.CCJSqlParser.jj_consume_token(CCJSqlParser.java:31301)
	at net.sf.jsqlparser.parser.CCJSqlParser.Statement(CCJSqlParser.java:163)
	at net.sf.jsqlparser.parser.CCJSqlParserUtil.parseStatement(CCJSqlParserUtil.java:188)
	at net.sf.jsqlparser.parser.CCJSqlParserUtil.parse(CCJSqlParserUtil.java:63)
	at net.sf.jsqlparser.parser.CCJSqlParserUtil.parse(CCJSqlParserUtil.java:38)
	at com.baomidou.mybatisplus.extension.parser.JsqlParserSupport.parserSingle(JsqlParserSupport.java:49)
	... 46 more

4、根因分析及确认

 在只用 for update 时运行是正常的,加上SKIP LOCKED 就异常了,所以问题就出于此;

 由于SKIP LOCKED 在Postgres里执行是正常的,所以不存在其版本不支持问题;

 仔细看报错堆栈不难发现,Was expecting one of 后面有个NOWAIT ,猜测大概率是jsqlparser 不认SKIP LOCKED导致的。

        下面就该求证这个猜测了


        首先我们去jsqlparser 的github 去看其各Releases的更新,查找SKIP LOCKED后确认之前猜测是正确的;

        

        我们再去看看mybatisplus官网有没有更新过这个组件,还真有:

        最后看下项目中引用,jsqlparser 4.4 肯定是有问题的了。

5、解决问题

        如果你觉得只需要把mybatisplus升级成3.5.6或者把jsqlparser升级成4.6(更激进的去升级jeecgboot),将会发现有些代码编译报错(运行时更是暗礁满满)。

        解决办法总是多次实践,进而权衡风险和工作量等因素后给出,这也是当下任何AI 都无法做到的。

        最终解决如下:

        升级mybatisplus到3.5.3.2并排除其jsqlparser引用,最后引用jsqlparser 4.6。

		<!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.2</version>
            <exclusions>
                <exclusion>
                    <groupId>com.github.jsqlparser</groupId>
                    <artifactId>jsqlparser</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.github.jsqlparser</groupId>
            <artifactId>jsqlparser</artifactId>
            <version>4.6</version>
        </dependency>

        当然本次升级并非全无改动,有个ConstAnalyzer.java 有俩接口要实现,这并不影响业务运行,如图:

6、总结

        只要是组件或框架的调整,少不了到处多测试下,稳定优先;

        如果不升级组件有没有办法实现类似效果呢?其实可以在获取一个可用key时不加锁,获取到后在加行锁(for update NOWAIT),反正4.4版本看样子是支持NOWAIT的。当然最终还是要看实测效果的。

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

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

相关文章

基于Java的城市公交管理系统/SSM的城市公交查询系统/计算机专业/课设

摘 要 网络技术的不断发展&#xff0c;使网络成为人们的日常生活中不可缺少的一部分&#xff0c;而城市公交管理系统是网络的一种新型体现&#xff0c;它以其特有的便捷和快速的特点得到了广泛的认可。当前的城市公交管理系统不仅没有建立起整体的管理系统&#xff0c;为企业定…

Go语言中常见的多线程同步方法

什么是线程、进程、协程 Go 源文件经过编译器处理后&#xff0c;会产生可执行文件&#xff0c;不同系统有不同的格式。可执行文件在操作系统上执行一次&#xff0c;就对应一个进程 进程可以理解为执行中的程序&#xff0c;是一个动态的概念&#xff0c;同一份可执行文件执行多…

Django 表单error_messages , 表单校验提示

在Django中&#xff0c;error_messages是表单字段的一个参数&#xff0c;允许你为特定的验证错误自定义错误消息。默认情况下&#xff0c;Django的表单字段会为常见的验证错误提供默认的错误消息。但是&#xff0c;你可能想要为你的应用提供更加用户友好的或者本地化的错误消息…

成为git砖家(2): gitk 介绍

大家好&#xff0c;我是白鱼。这篇我们介绍 gitk。 gitk 和 fork 界面对比 当我们在 macOS 上执行 brew install git 后&#xff0c; 得到了 git 命令行工具。 然而这条命令并不会安装 gitk. gitk 是 git 自带的图形化界面工具&#xff0c;也可以称为“穷人版 fork”&#xf…

如何穿透模糊,还原图片真实面貌

目录 图像清晰化的魔法棒&#xff1a;AI如何穿透模糊&#xff0c;还原图片真实面貌 前言 论文背景 论文思路 模型介绍 复现过程 演示视频 使用方式 本文所涉及所有资源均在传知代码平台可获取。 图像清晰化的魔法棒&#xff1a;AI如何穿透模糊&#xff0c;还原图片真实面貌 在我…

使用Docker搭建MySql的主从同步+ShardingSphere搭建Mysql的读写分离

参考课程 尚硅谷ShardingSphere5实战教程&#xff08;快速入门掌握核心&#xff09;_哔哩哔哩_bilibili 主服务器 创建容器 docker run -d \ -p 3306:3306 \ -v /kira/mysql/master/conf:/etc/mysql/conf.d \ -v /kira/mysql/master/data:/var/lib/mysql \ -e MYSQL_ROOT…

java学习---异常

前言 由于被分母不能为0&#xff0c;所以代码到int yn/m;会抛出异常&#xff0c;停止运行下去&#xff0c;但是如果是个庞大的代码&#xff0c;因为这种小错误而整个程序崩溃&#xff0c;会大大影响代码整体的健壮性&#xff0c;所以此时就需要我们得异常处理了 选中异常代码部…

正则采集器——前端搭建

前端使用有名的饿了么管理后台&#xff0c;vue3版本vue3-element-admin&#xff0c;首先从gitee中克隆一个vue3-element-admin模板代码vue3-element-admin: Vue3 Element Admin开箱即用的中后台管理系统前端解决方案&#xff0c;然后在此基础上进行开发。 1、修改vite.config.…

【深入理解SpringCloud微服务】深入理解Ribbon原理并手写一个微服务负载均衡器

深入理解Ribbon原理并手写一个微服务负载均衡器 负载均衡器理解Ribbon原理手写一个微服务负载均衡器总体设计LoadBalanceClientHttpRequestFactorySimpleLoadBalanceClientSimpleLoadBalancerLoadBalanceRulespring.factories与LoadBalanceConfig 负载均衡器 在微服务架构里面…

应用层_计算机网络

文章目录 应用层HTTP用户与服务器的交互&#xff1a;cookieWeb缓存HTTP/2 SMTPDNS&#xff1a;因特网的目录服务P2P文件分发BitTorrentCDN内容分发网 应用层 应用层协议定义了运行在不同端系统上的应用程序进程如何相互传递报文。应用层协议定义了以下内容&#xff1a; 交换的…

结构性设计模式-外观模式

一、外观模式 有些人可能炒过股票&#xff0c;但其实大部分人都不太懂&#xff0c;这种没有足够了解证券知识的情况下做股票是很容易亏钱的&#xff0c;刚开始炒股肯定都会想&#xff0c;如果有个懂行的帮帮手就好&#xff0c;其实基金就是个好帮手&#xff0c;支付宝里就有许…

算力共享:如何理解、标识与调控多层次算力资源的异构性和复杂性,实现智能算力网生态诸要素有效互操作?

目录 鹏程云主机和NPU计算服务器关系 NPU计算服务器 两者关系 结论 两种不同类型的处理器或计算单元 FPGA MLU NS3(Network Simulator version 3) 一、基本属性 二、主要功能与特点 三、应用与前景 对象存储和HDD存储 一、定义与特点 二、应用场景 三、总结 对…

培养前端工程化思维,不要让一行代码毁了整个程序

看《阿丽亚娜 5 号&#xff08;Ariane 5&#xff09;火箭爆炸》有感。 1、动手写项目之前&#xff0c;先进行全局性代码逻辑思考&#xff0c;将该做的事情&#xff0c;一些细节&#xff0c;统一建立标准&#xff0c;避免为以后埋雷。 2、避免使用不必要或无意义的代码、注释。…

把 网页代码 嵌入到 单片机程序中 2 日志2024/7/26

之前不是说把 网页代码 嵌入到 单片机程序中 嘛! 目录 之前不是说把 网页代码 嵌入到 单片机程序中 嘛! 修改vs的tasks.json配置 然后 测试 结果是正常的,可以编译了 但是:当我把我都html代码都写上去之后 还是会报错!!! 内部被检测到了,没辙,只有手动更新了小工具代码 …

低功耗单声道音频编解码器ES8311中文规格书介绍

特征 具有ADC和DAC的低功耗单声道音频编解码器ES8311。 ES8311 QFN20封装的外形和丝印 系统 • 高性能、低功耗多位 delta-sigma 音频 ADC 和 DAC • I2S/PCM 主站或从站串行数据端口 • 256/384Fs、USB 12/24 MHz 和其他非标准音频系统时钟 • I2C 接口 模数转换器 • 24…

28 列表创建与删除

使用 “” 直接将一个列表赋值给变量即可创建列表对象。 my_list [a, #, 128, [12], [], {2, }, {a: 1, b: 2}] print(my_list) print(type(my_list)) print(id(my_list[0]), id(my_list[-1]))可以使用 list() 函数把元组、range对象、字符串、字典、集合或其他可迭代对象转换…

PDF管理器和查看器PdfDing

什么是 PdfDing &#xff1f; PdfDing 是一款自托管 PDF 管理器和查看器&#xff0c;可在多种设备上提供无缝用户体验。它设计精简、速度快&#xff0c;并且易于通过 Docker 设置。 功能特点 在多种设备上无缝基于浏览器的 PDF 查看使用标签整理 PDF干净且响应迅速的用户界面暗…

photoshop学习笔记——选区3

从窗口面板可以打开历史记录面板&#xff0c;历史记录面板保存了所有的操作 可以点击历史记录中某一条&#xff0c;回到当时的操作状态&#xff0c;也可以通过编辑中的 还原、重做、切换到最终状态逐步调整或直接跳到最终状态 回退之后&#xff0c;如果有新的操作&#xff0c;历…

GEE:设置ui.Map.Layer上交互矢量边界填充颜色为空,只显示边界

一、目标 最近在GEE的交互功能鼓捣一些事情&#xff0c;在利用buffer功能实现了通过选点建立一个矩形后&#xff0c;需要将该矩形填充颜色设为空&#xff0c;只留边界。 然而通过正常设置layer的可视化参数并不能实现这一目的。因此只能另辟蹊径&#xff0c;改为定义矢量边界…

项目开发实战案例 —— Spring Boot + MyBatis + Hibernate + Spring Cloud

作者简介 我是本书的作者&#xff0c;拥有多年Java Web开发经验&#xff0c;致力于帮助更多开发者快速掌握并运用Java Web技术栈中的关键框架和技术。本书旨在通过实战案例的方式&#xff0c;带领读者深入理解并实践Spring Boot、MyBatis、Hibernate以及Spring Cloud等热门技术…