365bet官网是多少

诡异,明明更新成功了状态,查不出来了

发布时间 2025-12-31 13:24:47 作者 admin 阅读 220

一、前言程序员小明遇到一个非常诡异的问题,明明在前面已经将数据状态更新成功了,可是有些数据(并非所有)后续按照更新后的状态查询数据没查到,导致防御代码判断为空直接返回,没有执行后续的同步操作。

查了很久,百撕不得其姐。

于是程序员小明求助师兄,师兄说:“说来话长,你直接看明明如月学长的文章吧…”

二、场景复现下面是一个复现问题的代码:

代码语言:javascript复制import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

@Service

public class MyService {

@Autowired

private MyRepository myRepository;

private final ExecutorService threadPool = Executors.newFixedThreadPool(5);

public void updateAndSyncData(List ids, String newState) {

log.info("更新状态和同步数据, ids:{}, newState:{}" ,ids, newState)

// Step 1: 修改数据状态

List entities = myRepository.findByIds(ids);

entities.foreach(entity->{

entity.setState(newState);

myRepository.save(entity);

});

// Step 2: 在新的线程中查询数据状态并调用新接口来同步数据

threadPool.submit(() -> {

ids.foreach(id->{

try{

// 根据新的状态查询数据

MyEntity entity = myRepository.findByIdAndState(id,newState);

if(entity == null){

log.info("未查询到数据, id:{}, newState:{}" ,id, newState)

return;

}

//调用下游接口同步

log.info("执行下游同步, id:{}, newState:{}" ,id, newState)

callNewInterfaceToSyncData(entity, newState);

}catch(Exception e){

log.error("执行下游同步失败, id:{}, newState:{}" ,id, newState)

}

});

});

}

private void callNewInterfaceToSyncData(MyEntity entity,String state) {

// 在这里调用新接口来同步数据到其他系统

}

} 注:这里只是为了演示问题,请不要较真细节问题,比如性能问题。

给你 2 分钟的时间思考一下可能得原因有哪些?

[2 分钟]

经验丰富的程序员会有种预感,可能和多线程有关系。

但源码非常让人困惑,虽然是新启动线程池执行任务,根据新状态查询数据,但是线程池任务提交前状态状态已经更新完毕了啊?!

除非…

三、问题分析查问题,我们需要:大胆猜想,小心求证。

3.1 猜想1:代码逻辑有误?有可能代码逻辑有问题,比如更新状态的语句有问题,根据 ID 和状态的查询 SQL 有问题等。

经过重新代码审查,发现逻辑, 底层 SQL 语句也没问题没问题。

3.1 猜想2:有报错,导致状态修改失败或者查询成功同步失败?通过日志发现没有任何报错,经过核实可能出错的地方都会有异常日志,所以排除。

3.2 猜想3:查询前被其他线程修改了?有一种可能是在异步查询之前,状态被其他线程改掉了。

通过日志和数据库中的数据更新时间都证明,并没有被其他线程修改过。

3.3 猜想4:外层有事务?从上述代码看确实没有看到有开启事务。

继续往上翻,翻了四五层发现的确开启了事务!!

因此,真相大白。

外部开启了事务修改了状态,在线程池中根据新的状态查询部分数据时由于事务还没提交,用新的状态查不到,从而导致后续的同步任务没有更新。

可能有些人会说,这不难吧??

的确,当你看到这里似乎觉得很简单,但当你写代码层数过深时,很容易忘记外部开启了事务。

另外,很多时候有些犯类似错误的同学你问他他都会,写的时候可能没有注意到。

四、相关知识点4.1 事务四大特性原子性(Atomicity):事务中的所有操作要么全部成功,要么全部失败回滚,不会出现部分执行的情况。一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说事务执行前后,数据库中的数据满足预定义的规则和约束。隔离性(Isolation):事务之间是相互隔离的,一个事务的执行不会受到另一个事务的影响。不同的隔离级别可以防止脏读、不可重复读和幻读等并发问题。持久性(Durability):事务一旦提交,它对数据库中数据的改变就是永久性的,即使发生系统故障或者数据库崩溃,也不会丢失已提交的数据。

在一个事务中修改了数据状态,但是该事务在你创建新线程去查询这些更改时还没有提交。因此,新线程中的查询不能看到这些未提交的更改,这是因为它处于一个不同的事务或非事务状态中。

4.2 事务和线程的关系事务是指数据库中一组逻辑上相关的操作,它们要么都执行,要么都不执行。事务的四大特性是原子性、一致性、隔离性和持久性。线程是指程序中一条执行路径,它可以并发地执行多个任务。线程之间可以共享内存和资源,但也需要同步和协调。事务和线程的关系主要取决于数据库连接和事务管理的方式。数据库连接是指程序和数据库之间的通信通道,事务管理是指控制事务的开始、提交和回滚的过程。一种常见的方式是基于线程绑定的数据库连接和声明式的事务管理。这种方式下,每个线程在执行事务时会获取一个独立的数据库连接,并通过注解或配置来声明事务的边界和属性。这样可以保证每个线程有自己的事务上下文,不会相互干扰。另一种方式是基于共享的数据库连接和编程式的事务管理。这种方式下,多个线程可以使用同一个数据库连接,并通过代码来手动控制事务的开始、提交和回滚。这样可以节省数据库连接资源,但也需要注意线程安全和事务隔离问题。五、解决办法解决办法有很多,常见如下:

在异步执行前先在事务查询出来(事务如果在后续回滚,异步的逻辑可能也会被正常执行)。在执行异步逻辑之前提交事务。可以使用 TransactionSynchronizationManager来注册一个回调,该回调将在当前事务成功提交后执行。这允许你在事务提交后执行特定的逻辑(更合理)。去掉异步逻辑,都改成同步逻辑。由于具体实现并不困难,这里就不用代码演示了。具体采用什么策略需要根据实际的情况来决定。

六、启示6.1 注重代码审查这个问题如果代码审查仔细的话还是能够看出来的。

比如被审查者,从 Facade 一层一层往 Dao 层讲解代码逻辑,审查的同学看到事务和异步,有很大可能看出这个问题。

当然,这不能仅依靠代码审查,大家使用线程池时应该主动思考可能造成的问题。

6.2 大胆猜想,小心求证我认为差问题应该:“大胆猜想,小心求证”。

不要乱猜,乱猜容易浪费大量的时间。

需要根据问题的表现,根据自己的专业能力反向推测可能的原因,并且根据代码、日志、数据库数据等论证自己的猜测。

当然,很多“诡异的问题” 由于“不识庐山真面目,只缘身在此山中”,有时候找周围的同学帮看一眼更容易更早定位原因。

6.3 知行合一、学以致用(“八股文”的误解)在面试的时候,问求职者:“事务的四大特征”,绝大多数人都可以“倒背如流”。很多人甚至认为这是“八股文”,毫无意义。

然而,实际编码过程中,很容易忘记这些知识,导致知识和运用脱离。

学习的目的是:学以致用,正如孤尽老师所说:“记忆、理解、表达、融合”。

其实记忆并不意味着掌握,能够做到知行合一,能够表达融会贯通才代表真正掌握了知识。

七、总结本文讲解事务未提交时异步查询不到数据导致代码效果不符合预期的情况,并给出了解决办法。

大家在事务中使用异步线程执行任务时要特别注意你这个问题。

大家要加强代码审查,有很大概率可以避免一些问题。同时,大家查问题时,一定要以“证据为依据”,“大胆猜想,小心求证”。

相关推荐

ios9.3.4更新了什么 ios9.3.4更新内容汇总

苹果发布了ios9.3.4,还说ios9.3的用户必升到ios9.3.4,很多朋友好奇ios9.3.4更新了什么?今天小编就为大家一一介绍! ios9.3.4更新内容: 苹果在iOS 9.3.

07-08 分类 365bet官网是多少

打麻将,麻将

iOS打麻将游戏排行榜 打麻将,一般又称打麻将,麻将。打麻将古已有之,以前叫打马吊,“三缺一”这三个字更是老少皆知,可见麻将的风靡程

08-09 分类 365bet官网是多少

英雄皮肤

Champion skin (or just skin) refers to the color scheme or appearance of a champion. In League of Legends, most skins can be bought from the Riot Store with Riot Points, while others are or

07-02 分类 365bet官网是多少

如何轻松下载 Minecraft PC 版

发现之间的差异 我的世界 爪哇和基岩。 了解如何安装游戏并利用 Minecraft Launcher。 探索 Game Pass 的免费选项和好处。 了解类似于 Minecraft 的替代

09-07 分类 best365官网登录入口

详解iPhone下载PP助手的两种简便方法

导读本文将为大家详细介绍在iPhone上如何下载PP助手,以及两种简便的安装方法。PP助手提供了一种便捷的方式下载各种应用,而下文将详细说明

11-07 分类 365bet官网是多少

贪吃蛇大作战

4399游戏盒 9.1.0.38 隐私|权限|功能 开发者:四三九九网络股份有限公司 2025-08-11更新 本产品适用年龄10岁以上 公司地址:厦门市思明区软件园2期

09-07 分类 best365官网登录入口