解决web端登录时等待过久并偶尔抛出事务相关异常

问题描述

在这里插入图片描述

分析

登录函数中有一个事务,如下:

在这里插入图片描述

事务里面有一个有可能操作比较耗时的过程:

在这里插入图片描述

在新增登录日志的时候,获取用户的ip。

在这里插入图片描述

具体干了啥不重要,重要的是发了一个http请求,并且是串行的,所以这个请求比较耗时的可能是很大的,并且具备不确定性因素。

在这里插入图片描述

反思

TransactionRollbackException的文档注释为:

This exception indicates that the transaction associated with processing of the request has been rolled back, or marked to roll back. Thus the requested operation either could not be performed or was not performed because further computation on behalf of the transaction would be fruitless

可能的过程为:线程1进入事务、然后进行了一次update操作,获得了一个排他锁,然后被卡在了获取ip的那个地方,即此事务持有着排他锁,然后还长时间不结束(50s+),然后线程2也进入了事务,此时在进行update的时候,需要等待线程1释放排它锁,在50秒过后,仍未获取到锁,此时获取锁时间超过了预设,抛出上述异常。

解决

规避潜在的耗时操作。但是由于此服务没人维护,因此通过本地编译,然后拉包替换相应class文件,再上传到服务器的方式进行修改。

复现

查看获取锁的超时阀值:

SHOW VARIABLES LIKE '%timeout%';

输出如下:

+-----------------------------+----------+
| Variable_name               | Value    |
+-----------------------------+----------+
| connect_timeout             | 10       |
| delayed_insert_timeout      | 300      |
| have_statement_timeout      | YES      |
| innodb_flush_log_at_timeout | 1        |
| innodb_lock_wait_timeout    | 50       |
| innodb_rollback_on_timeout  | OFF      |
| interactive_timeout         | 28800    |
| lock_wait_timeout           | 31536000 |
| net_read_timeout            | 30       |
| net_write_timeout           | 60       |
| rpl_stop_slave_timeout      | 31536000 |
| slave_net_timeout           | 60       |
| wait_timeout                | 28800    |
+-----------------------------+----------+
13 rows in set (0.01 sec)

代码如下:

// --------------------------Controller------------
  /**
   * 测试web端登录锁表
   */
  @GetMapping("/hotline/test")
  public void testSomeThing() throws InterruptedException {
    testService.doSomething();
  }
// ----------------------------Service----------------
@Service
public class TestService {

  @Autowired
  private BizConfMapper confMapper;

  @Transactional(rollbackFor = Exception.class)
  public void doSomething() throws InterruptedException {
    // 共享锁
    BizConf conf = confMapper.selectByPrimaryKey(0);
    // 排它锁
    confMapper.updateByPrimaryKey(conf);
    // 等待让下个线程超时,最起码要大于50
    Thread.sleep(60000);
  }
}

日志输出与项目中出现的错误信息基本一致,如下:

在这里插入图片描述

@Transactional注解中加入timeout后,报错不一样,但是阔以理解为spring框架为我们抛出了异常。如下:

在这里插入图片描述

后记

其中对我理解这种现象有很大帮助的资料为这一张图,它让我明白了锁与事务之间的关系。

在这里插入图片描述

参考: https://segmentfault.com/a/1190000014133576

Read more

Volcano 与 Kubernetes GPU 调度学习笔记

本笔记系统整理 Volcano 调度器、Kubernetes 调度框架、GPU Device Plugin、HAMi 等云原生 AI 调度领域的核心知识,适合用于学习、复习和工程实践参考。 目录 * 第一部分:Volcano 入门 * 1. Volcano 是什么 * 2. 安装与快速使用 * 3. 核心特性一览 * 第二部分:Volcano 整体架构 * 4. Volcano 解决的核心问题 * 5. 整体架构与数据流 * 6. 三层抽象模型 * 第三部分:Volcano 核心实现原理 * 7. Session 机制 * 8. Gang Scheduling 实现 * 9. Queue 与 DRF 公平调度

容器镜像(4):镜像的常用工具箱

容器镜像(4):镜像的常用工具箱

前几篇在讲多架构镜像时已经用过 skopeo 和 crane 做镜像复制,这篇系统整理这两个工具的完整能力,同时介绍几个日常操作镜像时同样好用的工具。 一、skopeo:不依赖 Daemon 的镜像瑞士军刀 skopeo 的核心价值是绕过 Docker daemon,直接与 Registry API 交互。上一篇用它做镜像复制和离线传输,但它的能力远不止于此。 1.1 安装 # Ubuntu / Debian sudo apt install -y skopeo skopeo --version # skopeo version 1.15.1 1.2 inspect:免拉取检查镜像元数据 docker inspect 需要先把镜像拉到本地,skopeo inspect 直接向 Registry

容器镜像(3):多架构镜像构建

容器镜像(3):多架构镜像构建

一、什么是多架构镜像 1.1 OCI Image Index 上一篇介绍了单平台镜像的结构:一个 Manifest 指向 Config 和若干 Layer blob。多架构镜像在此之上多了一层——OCI Image Index(也叫 Manifest List),是一个轻量的索引文件,把多个单平台 Manifest 组织在一起: $ docker manifest inspect golang:1.22-alpine { "schemaVersion": 2, "mediaType": "application/vnd.oci.image.index.v1+json", "manifests&

容器镜像(2):containerd 视角下的镜像

容器镜像(2):containerd 视角下的镜像

一、为什么需要了解 containerd 如果你只用 docker run 跑容器,从来不关心底层,那可以不了解 containerd。但如果你在用 Kubernetes,或者想真正理解"容器运行时"是什么,containerd 是绕不开的。 事实上,当你执行 docker run 的时候,containerd 早就在后台悄悄工作了——Docker 从 1.11 版本开始,就把核心运行时剥离出来交给 containerd 负责。 1.1 Docker 的架构演变 早期的 Docker(1.10 及之前)是一个"大一统"的单体程序:一个 dockerd