0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一个update语句执行要10s,大厂的架构真无语!

jf_ro2CN3Fa 来源:芋道源码 2023-01-29 10:45 次阅读


一、问题描述

2022年7月2x日,窗外夕阳将落不落,余晖洒落在街道上,远处的热浪仿佛在说:嘿,欢迎来到烤箱中的瑞士卷—成都!

“嘿!”,我回过神来看到一只洁白纤细的手落在我的肩膀上,眼光从窗外收回顺着手臂快速扭跟过去,然后看到脸色暗淡夹杂着些许痘痘的测试妹纸一脸的严肃!“昨天晚上上线后,这个后台执行更新信息非常缓慢,这里肯定有问题!”

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

二、问题分析

“好的,我排查一下”。三步并作两步回到工位,掀开MacBook Pro的盖子、打开显示器的电源、输入链路日志跟踪系统地址、复制traceId、查看日志…… 这一套操作熟悉得令人心疼。

排查日志初步发现实际调用了两次,第一次执行时间接近10s,调用超时,第二次执行时间接近5s。你肯定也想到了,RPC调用retry设置了值。对RPC配置检查之后确实设置的是retry=1,实际项目中,增、删、改等操作不应设置retry。

通过调用链排查发现update执行非常耗时。聪明的你一定也第一时间怀疑update语句有性能问题。把update语句拿出来:

updatetableseta=#{1},b=#{2},...whereid=#{0}(id主键)

这下傻眼了,根据主键id更新怎么可能要执行10s?

masaga?!!

为了验证我的猜想,command + 空格、键入idea并回车、打开对应的工程、定位到对应的方法处、迅速浏览一遍并思索片刻之后,真相大白!

06f25d70-9f73-11ed-bfe3-dac502259ad0.jpg

服务B执行完update语句之后,事务commit之前,还有两个异步通知任务,使用的是spring的@Async注解,自定义的线程池,跟踪日志中的线程标志,排查过程中发现有的异步任务居然由原线程执行!进一步分析日志发现这种现象并不是一直发生,有时又是由异步线程执行。开始排查线程池,线程池果然设置了callRunner的失败策略。

所以,由原线程执行时,事务的范围如下:

06fe1174-9f73-11ed-bfe3-dac502259ad0.jpg

定位到原因之后,修改线程池参数为常见策略,初始和最大线程数相同,队列数9999,保证线程池的线程充足性。

修复

灰度

招呼测试妹纸测试

自信满满,悠闲喝水

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

三、梅开二度

“在高并发场景下,复现了这个问题,你快看一下”!测试妹纸对我投来鄙视的眼光犀利的说道:“修复好了,再喊我回归哈,拜拜~”。

奇怪,为什么这么平常的一个update语句,怎么会执行这么长时间呢?难道出发了框架的bug导致事务提交延迟?不对不对,这个方向想偏了~也没有其他地方在更新这个表了呀?不可能有表锁,更不可能有行锁呀……

masaga?!!

根据表锁以及行锁的思路,为了验证我的内心OS猜想,立即使用 show processlist 进行了连接查询,果然有重大发现,除了服务B有连接之外,还有服务A的连接。而服务A又是服务B的上游系统!系统架构如下:

070c7a52-9f73-11ed-bfe3-dac502259ad0.jpg

执行顺序如下:

顺序 服务 执行动作 关键点
1 服务A 执行update语句 数据库 行锁 lock
2 服务A 调用服务B RPC 调用
3 服务B 执行update语句 数据库 行锁 waiting
4 服务A 调用服务B超时 RPC timeout
5 服务A 再次调用服务B RPC retry
6 服务A 调用调用服务B再次超时 RPC timeout
7 服务A PRC调用超时异常 数据库 事务回滚 行锁 unlock
8 服务B 执行 数据库 行锁 竞争

不被人信任的滋味很难受!为了重新赢回测试妹纸对我的信任,这次的bug修复只需成功不许失败!

四、解决方案

知道病根之后,问题就很简单了。

最理想的方案

对微服务架构进行重构,但这样做带来的收益不高,现在手上还有优先级更高的事情要做。

07185eda-9f73-11ed-bfe3-dac502259ad0.jpg
最实际的方案

是将服务A对服务B的调用和服务A的事务分离出来。这样就不存在锁竞争的问题了。

072741ca-9f73-11ed-bfe3-dac502259ad0.jpg

五、总结

看到了这里,你心里是不是已经在想:我靠,大厂的系统架构真的很垃圾,我都是关着灯的~(走错片场~)

其实这个系统变成这样是有历史原因的,如果当初的开发者能够采用DDD的思想或者能够明白微服务的对象高内聚思想,或许今天就不会发生在我身上这场研发与测试之间的信任危机。

夕阳落下,夜晚笼罩着大地。路旁的小猫咪悠然站了起来,张大嘴巴打个哈欠的同时伸了个懒腰,然后走向3号门口,等待着心地善良的加班儿投食猫粮。“验证通过,早点下班”。不远处传来测试妹纸的声音,夹杂着中央空调吹出的风声。



审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • macbook
    +关注

    关注

    0

    文章

    479

    浏览量

    41442
  • 线程
    +关注

    关注

    0

    文章

    489

    浏览量

    19495
  • 系统架构
    +关注

    关注

    1

    文章

    65

    浏览量

    23432

原文标题:一个update语句执行要10s,大厂的架构真垃圾!

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    鸿蒙TypeScript入门学习第6天:【条件语句

    条件语句用于基于不同的条件来执行不同的动作。 TypeScript 条件语句是通过一条或多条语句执行结果(True 或 False)
    的头像 发表于 04-01 13:51 429次阅读
    鸿蒙TypeScript入门学习第6天:【条件<b class='flag-5'>语句</b>】

    伺服电机低速运转时间长,或低速停10s,开10秒 , 会使电机过热或烧毁电机吗?

    伺服电机低速运转时间长,或低速停10s,开10秒 , 会使电机过热或烧毁电机吗? 还是低速运转需配减速机? 负载很小, 应该不会有扭矩不够的情况,会不会有低频振荡
    发表于 01-10 07:58

    单片机if是什么语句

    单片机中的if语句是一种条件语句,用于根据不同的条件执行不同的代码块。在程序执行过程中,条件语句用来决定是否
    的头像 发表于 01-05 14:04 490次阅读

    单片机中for语句的运用

    单片机中的for语句是一种常见的循环控制结构,用于重复执行一段代码块,可以简化程序的编写和减少代码量。本文将详细介绍单片机中for语句的运用。 一、for语句的基本结构和功能 for
    的头像 发表于 01-05 14:02 604次阅读

    oracle执行sql查询语句的步骤是什么

    Oracle数据库是一种常用的关系型数据库管理系统,具有强大的SQL查询功能。Oracle执行SQL查询语句的步骤包括编写SQL语句、解析SQL语句、生成
    的头像 发表于 12-06 10:49 409次阅读

    oracle的update语法

    Oracle是一种强大的关系型数据库管理系统,具有广泛的应用,UPDATE语句是用于修改数据库中现有记录的重要操作之一。在本文中,我们将详细介绍Oracle的UPDATE语法及其用法。 首先,我们
    的头像 发表于 12-05 16:22 635次阅读

    java中的switch语句 case的取值

    Java中的switch语句是一种用于多重条件判断的语句,用于根据不同的条件执行不同的代码块。在switch语句中,case关键字用来指定不同的取值。 在Java中,switch
    的头像 发表于 11-30 16:05 333次阅读

    python if语句多个条件怎么用

    所有条件都必须为True才会执行if语句中的代码块,or运算符表示只要有一个条件为True就会执行代码块,而not运算符则用于反转条件的结果。 以下是一个示例代码,演示了如何在Python中使用多个条件
    的头像 发表于 11-21 16:45 1055次阅读

    Rust的 match 语句用法

    执行不同的代码,这在处理复杂的逻辑时非常有用。在本教程中,我们将深入了解 Rust 的 match 语句,包括基础用法、进阶用法和实践经验等方面。 基础用法 match 语句是 Rust 中的一种控制流
    的头像 发表于 09-19 17:08 657次阅读

    Python条件和条件语句

    if语句 对于if语句,若条件判定为真,那么后面的语句块就会被执行。若条件判定为假,语句块就会被跳过,不会
    的头像 发表于 09-12 16:45 358次阅读

    Python中什么是语句

    条件判断 语句块 什么是语句块呢? 语句块是在条件为真(条件语句)时执行或者执行多次(循环
    的头像 发表于 09-12 16:41 628次阅读

    WHILE语句如何执行

    令“FOR”、“WHILE”和“REPEAT-UNTIL”的运行:由于系统不显示这些指令中的变量值,因此可避免对循环时间造成影响。 WHILE允许由执行条件控制的循环地执行语句序列。执行
    的头像 发表于 09-10 09:48 501次阅读
    WHILE<b class='flag-5'>语句</b>如何<b class='flag-5'>执行</b>

    N76E003进入低功耗后,每隔10S会有大概10ma的电流冒出下又消失是为什么?

    请问各位大大,小弟初次使用N76E003,在该芯片进入低功耗后,为什么会大概每隔10S芯片会有大概10ma的电流冒出
    发表于 06-20 06:02

    Python的循环语句介绍

    哈喽大家好,我是知道。今天带大家了解下Python的循环语句 定义循环语句允许我们执行一个语句语句组多次 类型Python提供了两种不同类
    的头像 发表于 05-11 17:39 644次阅读

    Verilog中的If语句和case语句介绍

    我们在上一篇文章中已经看到了如何使用程序块(例如 always 块来编写按顺序执行的 verilog 代码。 我们还可以在程序块中使用许多语句来控制在我们的verilog设计中信号赋值的方式
    的头像 发表于 05-11 15:37 3131次阅读
    Verilog中的If<b class='flag-5'>语句</b>和case<b class='flag-5'>语句</b>介绍