wx191163 2024-07-10 22:50 采纳率: 0%
浏览 13

jdbc postgresql 并行查询查不到数据?

jdbc postgresql 遇到一个问题,已有方法:saveAll findById 两个方法执行都是在一个单独的事务里的,测试代码如下:
先saveAll 返回一组ID,然后用这一组ID并行执行findById,偶尔会出现返回null。即,saveAll事务已经提交的数据,结果偶尔查不到。

saveAll 返回一组ID

这一组ID.parallelStream.forEach(id -> {

 // 这行findById 偶尔返回null
  findById(id);
  
});

saveAll 事务提交了,然后根据已插入的来并行查询,结果偶尔查不到。

问了chatgpt说,并行情况下,有可能虽然代码逻辑中findById是在saveAll执行结束后才执行的,但可能在到了数据库里,由于findById是并行执行的,所以可能findById比saveAll的提交事件更早执行,所以和代码表述的业务逻辑的预期不一致查不到数据。
如果是串行的,则findById一定是在saveAll提交后才会执行,并且我修改上面的测试代码为list.stream 改为非并行测试,结果是每次都可以查到数据。

我在照着spring data jpa功能写一个类似功能的框架,过程中遇到了7 8 个问题了,都是同样的代码mysql和sqlite测试没问题,pgsql就报错,比如前几天刚遇到的 在AOP类里根据自定义事务注解的隔离级别来更改java.sql.Connection的隔离级别,pgsql就报错[不能在事务交易过程中更改隔离级别]。

那么,这个并行查询查不到的问题,是否也因为pgsql的严格要求而出现的?同样的上面的测代码,mysql就没问题每次都测试通过。
我知道spring的事务注解就是在当前线程下,即目标方法代码都是在当前线程下单线程执行才有效的。而并行查询,比如16个线程,配置连接池10个连接,可能saveAll执行后,16个线程中有一个使用和saveAll相同的连接来执行findById,那么这个线程就可以查到,因为它相对于saveAll虽然不是用的同一个线程来执行,但是用的同一个java.sql.Connection连接对象,所以相当于和saveAll是串行执行的。而对于其他15个线程去抢另外9个连接,这9个连接执行的findById可能在执行saveAll的连接的事务还没commit时就先执行了,所以导致查不出数据。对吗?

这似乎和pgsql的事务隔离级别没关系,尽管这段测试代码是一个saveAll事务和N个findById事务,它默认为读已提交,我改为其他3种级别,这段测试代码仍然偶尔返回null。那就只能按上面描述那么理解:saveAll的commit事件还没执行,findById就已经执行了,导致查不到数据,对吗?但对于 READ UNCOMMITTED 又该怎么理解呢,它根本就没有[commit]这个概念对吗?

目前想到的解决方法有三个:
1、不用并行查询来测试。但现实业务逻辑中都是并行的,不可能让所有查询排队执行。但是否可以忍受很短时间的[查不到]?即数据库存在数据,但是查不到数据的这段时间非常非常短。
2、连接池个数配置为1,看似解决了问题,实际上是让所有的CRUD操作都排队用这唯一的连接来串行执行。这肯定不行
3、同样在内部代码处理为同一个TABLE的查询排队执行,比2好不了多少

  • 写回答

4条回答 默认 最新

  • 专家-赤兔[在线] 全栈领域优质创作者 2024-07-10 22:50
    关注

    引自免费微信小程序:皆我百晓生

    你遇到的问题与PostgreSQL事务管理、并发和隔离级别有关。以下是对问题的分析和解决方案的建议:

    问题分析

    并发与事务提交顺序

    你提到在并行执行时偶尔会出现找不到数据的情况。这可能是由于并发执行时事务提交的顺序问题导致的。当多个线程并行执行时,它们可能在数据库层面上是交错执行的,导致事务提交的顺序并不是按照代码执行的顺序。因此,即使你在代码中先执行了saveAll再执行findById,也可能由于并发的原因导致某些时候数据库尚未完成事务提交。这样当其他线程执行查询时可能无法看到未提交的数据。

    PostgreSQL与MySQL的差异

    你提到在MySQL中测试没有问题,但在PostgreSQL中出现问题。这是因为不同的数据库管理系统(DBMS)在处理并发事务和隔离级别方面可能存在差异。PostgreSQL在某些情况下对事务的处理可能更加严格或有所不同。这可能导致在某些并发场景下出现不一致的结果。

    事务隔离级别与READ UNCOMMITTED

    关于事务隔离级别,你提到的理解是正确的。READ UNCOMMITTED确实没有提交的概念,允许读取未提交的数据。但在并发环境下,即使设置为READ UNCOMMITTED,也可能由于并发事务提交的顺序问题导致偶尔查不到数据的情况。这是因为虽然隔离级别允许读取未提交的数据,但数据库仍然需要处理并发事务的提交和回滚。如果查询发生在事务提交之前,可能仍然无法看到最新的数据。

    解决方案建议

    方案评估与优化

    对于你提到的三个解决方案:

    1. 不使用并行查询:这种方法虽然可以避免问题,但在实际业务逻辑中可能不可行,因为并行处理是提高性能的一种常见方式。但是可以考虑接受短暂的查不到数据的情况,只要这种情况不是频繁发生且不影响业务逻辑。确保用户明白可能的短暂数据不可见情况,并进行适当的设计以处理这种情况。
    2. 连接池配置为单一连接:这不是一个可持续的解决方案,因为它限制了并发处理的能力并可能导致性能问题。但可以作为一个临时解决方案来验证是否确实是并发和事务提交顺序的问题。如果是这个问题导致的,可以考虑其他解决方案来解决并发和事务管理的问题。确保数据库事务的正确管理可能需要更复杂的解决方案或代码调整来确保事务的正确顺序和并发控制。这可能涉及到使用锁或其他同步机制来确保事务的串行执行或至少确保某些操作的顺序执行。此外,可以考虑使用数据库特定的特性或工具来监控和分析事务的执行情况以更好地理解问题所在并找到解决方案。可以考虑寻找开源解决方案或与数据库提供商的技术支持团队联系以获得更具体的建议和支持关于你的问题还可能有其他的解决方式可以参考或者实现你的需求同时使用JDBC来进行连接多个数据库的尝试?理解你在构建一个类似的框架试图寻找JDBC库在多数据库环境中有一种可能性是将代码扩展到能够动态地在不同数据库之间切换,而不必为每个特定的数据库实现单独的代码在你的情况下可以尝试实现一个数据库适配器系统根据当前的数据库环境动态选择适当的查询方法和技术这是更为抽象化的事务管理系统包括适当的隔离级别选择和分布式锁的使用来处理并行查询的问题这种系统的实现可能会更复杂但它可以提供更大的灵活性和可扩展性最后为了快速找到解决方案还可以尝试进行一些压力测试和数据监控观察何时会出现查询不到数据的情况这可以帮助更好地理解和解决你的问题
    评论 编辑记录

报告相同问题?

问题事件

  • 创建了问题 7月10日

悬赏问题

  • ¥15 如何在vue.config.js中读取到public文件夹下window.APP_CONFIG.API_BASE_URL的值
  • ¥50 浦育平台scratch图形化编程
  • ¥20 求这个的原理图 只要原理图
  • ¥15 vue2项目中,如何配置环境,可以在打完包之后修改请求的服务器地址
  • ¥20 微信的店铺小程序如何修改背景图
  • ¥15 UE5.1局部变量对蓝图不可见
  • ¥15 一共有五道问题关于整数幂的运算还有房间号码 还有网络密码的解答?(语言-python)
  • ¥20 sentry如何捕获上传Android ndk 崩溃
  • ¥15 在做logistic回归模型限制性立方条图时候,不能出完整图的困难
  • ¥15 G0系列单片机HAL库中景园gc9307液晶驱动芯片无法使用硬件SPI+DMA驱动,如何解决?