14条生产环境项目踩坑复盘

news2024/11/26 10:48:44
  1. 缓存需要等事务结束之后再删除,避免旧数据导致数据库和缓存不一致。

说明:

比如线程A在减少账户的余额(11->10),执行了数据库更新,但是事务还未提交,但把缓存删除了。

线程B从缓存里去获取账户的余额,缓存里没有,于是从数据库里去查询。

由于数据库的隔离级别是读已提交,所以拿不到线程A更新的值(10),读取的是未更新的值(11),并存入缓存。

接下来直到这个缓存(11)失效之前,应用拿到的值都是错的。

也就意味着用户看到的数据也是错误的,用户看到自己还剩11,实际上的剩余只剩10了。

正例:

transactionTemplate.execute(status -> {
    try {
        // 3、操作数据库
        this.handlerDb(account);
    } catch (Exception e) {
        status.setRollbackOnly();
        throw e;
    }
    return null;
});
// 5.事务结束之后再删除缓存,避免缓存拿到错误的数据
this.handlerCache(account);

反例:

transactionTemplate.execute(status -> {
    try {
        // 3、操作数据库
        this.handlerDb(account);
        // 4.缓存被删除之后,事务还未提交,其他线程从数据库里拿到旧数据,更新了缓存,从而导致数据库和缓存不一致
        this.handlerCache(account);
    } catch (Exception e) {
        status.setRollbackOnly();
        throw e;
    }
    return null;
});
  1. 事务的失效场景

2.1 被代理的类没有被Spring所管理

2.2 事务的起点是被this 调用,没有真正的去调用代理类

2.3 方法抛出的异常在方法内被捕获,没有被事务拦截器所拦截

  1. 事务的传播机制

说明:

使用Spring事务的时候需要注意事务的传播,如果使用的是Propagation.REQUIRES_NEW ,事务嵌套的时候,如果两个事务分别对同一个数据进行更新,会出现死锁。

比如事务1更新用户的性别,嵌套事务2更新用户的余额,双方都在等待对方释放锁后提交事务,从而导致死锁。

如果需要不同的事务传播机制,可以使用两个bean,设置不同的事务隔离级别。

正例:

@SpringBootConfiguration
public class TransactionConfig {

    @Bean
    public TransactionTemplate transactionTemplate(DataSourceTransactionManager dataSourceTransactionManager) {
        TransactionTemplate transactionTemplate = new TransactionTemplate(dataSourceTransactionManager);
        // 可以设置事务传播方式
   transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        return transactionTemplate;
    }

    @Bean
    public TransactionTemplate newTransactionTemplate(DataSourceTransactionManager dataSourceTransactionManager) {
        TransactionTemplate transactionTemplate = new TransactionTemplate(dataSourceTransactionManager);
        // 可以设置事务传播方式
     transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        return transactionTemplate;
    }
}

  1. 定时任务加分布式锁

说明:

使用定时任务执行任务的时候要加分布式锁,避免这个定时任务还未执行完成,又被下一个定时任务抢到同一个任务导致重复执行

  1. 异常信息捕获打印,打印的时候加上相关信息

说明:

捕获异常的时候需要打印异常堆栈、所处位置、对应的业务标识

正例:

return transactionTemplate.execute(status -> {
    try {
         // ...
    } catch (Exception e) {
        log.error("待处理订单超时或失败操作事务回滚 ,提现id是 {}, 异常信息是 ", withdrawApply.getId(), e); 
        // ...
        return false;
    }
    return true;
});
 

反例:

return transactionTemplate.execute(status -> {
    try {
         // ...
    } catch (Exception e) {
         // ...
        return false;
    }
    return true;
});

  1. 代码里不写死任何测试数据,只能通过配置文件来判断

说明:

测试环境为了方便测试,往往都有一些写死的测试代码,稍有不慎可能就会合并到正式环境,造成严重的损失。

比如在测试环境写死短信验证码为123456,合并到正式环境的时候,如果检查的时候遗漏了,用户可以通过123456登录任何一个账号,操作对方的数据。

为了避免这种情况的发生,不应当在代码里写死任何的测试数据,测试和正式环境的代码不应当有任何区别,区别只能在配置文件上,这样比对代码的时候,每次就只需要比对配置文件,而不需要关注代码。

同时测试数据不能是随意可以猜出来的值,比如123456

正例:

-application-dev.yml

  # 简易验证码配置
  simpleCodeConfig:
    # 是否开启
    enable: true
    # 验证码
    code: 255213

- LoginFacade.java

boolean simpleCodeEquals = mallConfig.getSimpleCodeConfig().getEnable() && Objects.equals(mallConfig.getSimpleCodeConfig().getCode(), loginVO.getCode());
if (!codeEquals && !simpleCodeEquals) {
     // ...
}

反例:

- LoginFacade.java


if (Objects.equals(loginVO.getCode(), "123456")) {
     // ...
}

  1. 不使用BeanUtils 进行拷贝,可以装GenerateAllSetter 和 Alibaba Cloud AI Coding Assistant。

说明:

因为代码里有很多vo、po、bo的转换,所以为了方便,很多时候会使用BeanUtils进行拷贝。

但最好不要用这个,BeanUtils的拷贝可能会带来意想不到的后果,比如本来只是想把新对象修改的值拷贝过来,但却把新对象的null值也拷过来了,导致数据丢失。

或者BeanUtils拷贝的时候,新对象和旧对象的值位置传反,导致数据未能拷贝过来。

推荐添加 GenerateAllSetter idea插件,可以生成这个对象对应的get和set方法,再加上 Alibaba Cloud AI Coding Assistant 的补全功能,可以轻松完成两个对象之间的转换。

正例:

  1. 预生成订单号,避免重复付款,订单号需要唯一索引。

说明:

在涉及到支付、付款、退款等逻辑的时候,订单号需要在创建该笔订单的时候生成,而不是等到真实操作的时候生成,避免真实操作的时候,保存订单号失败,导致一笔订单拥有两个订单号,从而重复退款或者重复付款。

正例:

比如用户中奖,获得了微信红包,那么在保存这条中奖记录之前,就需要生成对应的支付订单号,同步保存下来。真实付款的时候,拿这个订单号去付款。

反例:

保存中奖记录的时候没有保存订单号,付款的时候临时生成订单号这种情况,假如第一次付款由于代码的bug导致付款成功,但订单号等记录未保存下来,那么再次支付的时候会生成新的订单号,导致重复付款,造成资金损失。

  1. 分页order by 时,所排序的字段不能是相同值。

说明:

MySQL order by 语句里,如果有相同的值,MySQL会进行随机排序。如果同时进行了分页,那么上一页和下一页可能会出现重复的值。所以需要在排序的值里加上一个唯一的值,来避免重复数据的出现。

比如大多数时候都需要order by create_time ,但是在并发或者批量导入数据的情况下,很多数据的create_time可能相同,这时候分页就会出现重复值。为了避免这种情况,需要再加上 order by 主键/唯一索引的值 的逻辑。

正例:

select name from user order by create_time desc, id desc limit 10

反例:

select name from user order by create_time desc limit 10

  1. 更新数据库的时候,新建一个对象

说明:

很多时候为了更新方便,会使用全量更新的方式,但这样很容易覆盖其他更新的值。除非特殊情况,或者有十足的把握,否则更新对象的值时,需要使用新建一个对象的方式去更新

正例:

如果要更新status=2

UserInfo user = this.getById(1);
UserInfo updateUser = new UserInfo();
updateUser.setId(user.getId());
updateUser.setStatus(2);
this.updateById(updateUser);

反例:

UserInfo user = this.getById(1);
user.setStatus(2);
this.updateById(updateUser);

比如拿到的这个对象user数据是 id=1,status=1,score=1,此时更新语句是;

update user_info set status =2, score = 1 where id = 2.

如果同时有另外一个线程,把score已经更新为2,然后这条语句才执行。socre就会变回1,这个线程的更新就丢失了。

就可能会出现意料之外的问题。

  1. 线上项目增加字段时不要给字段设置非null限制

说明:

避免功能上线前,旧代码对表进行新增操作时因为非空限制导致接口不可用;如果在增加字段是添加了默认值的情况下可以添加非空限制,需要注意的是这个默认值是否适用旧数据;

对于数字类型比如:int、bigint、decimal、double增加非nul限制的情况下,会自动填充0;

对于字符串类型比如:varchar增加非null限制的情况下,会自动填充空字符串;

如果新增了一个用于表示状态的字段,其中0是其中一个状态,如果旧数据被自动填充的0不应该是初始值,会造成数据混乱;

  1. 修改线上项目已有接口要兼容未修改前的情况

说明:

前端通常会有缓存,需要刷新后才能显示前端最新的页面,如果接口入参增加并且增加了非空限制,会导致有缓存的用户调用这个接口总是报错;如果兼容之前的情况会导致业务出现问题可以不兼容;如果是查询展示的接口通常都需要兼容;

  1. 调用第三方接口打印调用时长

说明:

调用第三方接口在方法结束后打印耗时,在出问题后可以排查是否是因为第三方的问题导致的;

第三方接口执行时间过长导致大部分线程都在等待第三方返回,导致服务卡顿或不可用时可以通过日志排查问题;

  1. 使用JacksonUtil 或者 Gson 进行反序列化时,反序列化的对象结构层级不能太深,否则会导致OOM

说明:

出现一个接口调用后程序OOM,排查后发现因为方法入参有HttpServletRequest,编写的全局拦截器在异常时会进行方法入参的反序列化,打印入参。由于HttpServletRequest 这个对象里面的方法和属性较多,反序列化时占用了近3G内存,导致程序OOM。

这是因为Gson 解析对象的时候,需要解析对象的属性和属性名称,如果对象层级较深,解析时间会非常的长,而且解析后的结果对象因为尚未完成,不会被垃圾回收,所以会一直占用内存。

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

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

相关文章

Python Numpy基础教程

本文是一个关于Python numpy的基础学习教程,其中,Python版本为Python 3.x 什么是Numpy Numpy Numerical Python,它是Python中科学计算的核心库,可以高效的处理多维数组的计算。并且,因为它的许多底层函数是用C语言编…

TC358775XBG替代方案|完美替代 TC358775XBG替代方案|低BOM成本DSI转LVDS方案CS5518

TC358775XBG替代方案|完美替代 TC358775XBG替代方案|低BOM成本DSI转LVDS方案CS5518 TC358775XBG芯片的主要功能是DSI到LVDS桥,通过DSI链路实现视频流输出,以驱动LVDS兼容的显示面板。该芯片支持单链路LVDS高达1366768 24位像素分辨率,双链路L…

了解webpack

文章目录一、webpack是什么?二、为什么要使用webpack三、webpack的五个核心概念四、安装webpack提示:以下是本篇文章正文内容,下面案例可供参考 一、webpack是什么? 本质上,webpack 是一个用于现代 JavaScript 应用程…

PyTorch学习笔记:nn.MSELoss——MSE损失

PyTorch学习笔记:nn.MSELoss——MSE损失 torch.nn.MSELoss(size_average None,reduce None,reduction mean)功能:创建一个平方误差(MSE)损失函数,又称为L2损失: l(x,y)L{l1,…,lN}T,ln(xn−yn)2l(x,y)L…

设计模式:建造者模式教你创建复杂对象

一、问题场景 当我们需要创建资源池配置对象的时候&#xff0c;资源池配置类里面有以下成员变量: 如果我们使用new关键字调用构造函数&#xff0c;构造函数参数列表就会太长。 如果我们使用set方法设置字段值&#xff0c;那minIdle<maxIdle<maxTotal的约束逻辑就没地方…

【计组】内存和总线

课程链接&#xff1a;深入浅出计算机组成原理_组成原理_计算机基础-极客时间 一、虚拟内存和内存保护 日常使用的操作系统下&#xff0c;程序不能直接访问物理内存。内存需要被分成固定大小的页&#xff08;Page&#xff09;&#xff0c;再通过虚拟内存地址&#xff08;Virtu…

如何创建一个有效的FAQ/常见问题解答页面?

如果您的网站上没有常见问题解答页面&#xff0c;那么您将错过本可以节省的数小时的销售和支持时间&#xff0c;以及本可以推动购买的新客户。尽管与登录页面或销售页面不同&#xff0c;常见问题解答页面显然不会显示直接的投资回报&#xff0c;但它可以在其他因素上节省您的团…

内网渗透(十五)之内网信息收集-域环境中定位域管理员

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

多路查找树

1.二叉树与 B 树 1.1二叉树的问题分析 二叉树的操作效率较高&#xff0c;但是也存在问题, 请看下面的二叉树 二叉树需要加载到内存的&#xff0c;如果二叉树的节点少&#xff0c;没有什么问题&#xff0c;但是如果二叉树的节点很多(比如 1 亿)&#xff0c; 就 存在如下问题:问…

洞察:2022年医疗行业数据安全回顾及2023年展望

过去的2022年&#xff0c;统筹安全与发展&#xff0c;在医疗信息化发展道路中&#xff0c;数据安全不可或缺。这一年&#xff0c;实施五年多的《网络安全法》迎来首次修改&#xff0c;《数据安全法》、《个人信息保护法》实施一周年&#xff0c;配套的《数据出境安全评估办法》…

用户标签体系建设

一、什么是标签体系 通过给每个用户打标&#xff0c;方便后续通过用户标签来快速筛选用户&#xff0c;给不同的用户满足不一样的需求。 标签体系也能给不同的用户标签群体&#xff0c;推送不同的内容&#xff0c;从而达到精准营销的目的。 二、标签体系实施架构 标签体系架…

盾王电力防护、森林防火预警应急广播方案

适用场景&#xff1a;针对普通路人与进入有涉电危险的区域人员作区别预警&#xff1b;三重音频、广告牌子宣传、LED显示、视频监控、预警干预。主要功能 第一重预警&#xff1a;对岸边上的行人&#xff0c;当你进入有涉电危险的警戒12米范围内。人体感应器探测到会马上发出语音…

光纤、光模块、光纤交换机、光模块组网设计与案例

光纤组网已是当今智能化弱电行业里一种常见的组网方式&#xff0c;组建远距离无线、监控网络时&#xff0c;往往需要使用光纤进行连接通信&#xff0c;使用光纤收发器是经济适用型做法&#xff0c;尤其是在室外的使用。其实光纤收发器不仅可以成对使用&#xff0c;还可以配合光…

ESP-IDF:在客户端网页上实现拍照按钮功能,并跳转新页面显示图片

ESP-IDF:在客户网页上实现拍照按钮功能&#xff0c;并跳转新页面显示图片 核心代码&#xff1a; /* Send response with custom headers and body set as the* string passed in user context*/ //const char* resp_str (const char*) req->user_ctx; const char *resp_s…

MASA Stack 1.0 发布会讲稿——实践篇

MASA Stack 1.0 实践篇 产品智能化 产品智能化的改造怎么做&#xff1f; 我们以采用运营商网络场景的物联网架构举例&#xff0c;如图从左到右&#xff0c;在设备端我们研发了一款净水行业通用的物联网盒子&#xff0c;它带有各种传感器&#xff0c;如TDS、温度、流量、漏水检…

80%的代码AI帮你写?还没这么夸张,不过也快了

兔年春节一过&#xff0c;APIcat进入到云服务版本的开发阶段&#xff0c;过年前发生了一件大事&#xff0c;Chatgpt横空出世&#xff0c;不少人预测Chatgpt会替代的10大行业&#xff0c;其中就有程序员。 这时&#xff0c;一位特斯拉的老哥出来说&#xff0c;GitHub Copilot帮…

Redis简介

Redis是一款开源的、高性能的键-值存储&#xff08;key-value store&#xff09;。它常被称作是一款数据结构服务器&#xff08;data structure server&#xff09;。 Redis的键值可以包括字符串&#xff08;strings&#xff09;类型&#xff0c;同时它还包括哈希&#xff08;h…

Netty 中的 Channel执行完close之后, 还能进行write吗?

问题来源&#xff1a;项目中出现顶号操作的时候&#xff0c;正常情况下被顶掉的连接应该收到一个 “同一账号登录&#xff0c;请退出重登” 的错误消息&#xff0c; 但是偶现客户端接收不到消息的情况&#xff08;连接实际上已经被服务器干掉了&#xff0c;客户端就呆呆的&…

word和wps添加mathtype选项卡

word或wps添加mathtype选项卡 前提 安装好word或wps安装好mathtype 步骤 确认word或wps具体安装位置确认word或wps位数为32位还是64位复制mathtype中的MathPage.wll文件和MathType Commands 2016.dotm文件到STARTUP位置添加受信任位置添加加载项 安装位置 通过开始页面&a…

三套大厂网络安全工程师面试题(附答案解析)冲刺金三银四

2023年已经开始了&#xff0c;先来灵魂三连问&#xff0c;年初定的目标是多少&#xff1f;薪资能涨吗&#xff1f;女朋友能找到吗&#xff1f; 好了&#xff0c;不扎大家的心了&#xff0c;接下来进入正文。 由于我之前写了不少网络安全技术相关的文章和回答&#xff0c;不少…