避免锁表:为Update语句中的Where条件添加索引字段

最近在灰度环境中遇到一个问题:某项业务在创建数据时耗时异常长,但同样的代码在预发环境中并未出现此问题。起初我们以为是调用第三方接口导致的性能问题,但通过日志分析发现第三方接口的响应时间正常。最终,我们发现工单表的数据入库SQL一直处于等待状态。深入分析后,问题的核心暴露出来:另一业务流程中对工单表执行更新(UPDATE)操作的SQL,其where子句中涉及的字段缺少必要的索引,导致其他业务在操作表中的数据时需要等待该更新完成。今天就和大家分享一下这个经验。

_20240525233236.jpg

问题描述

mysql 修改数据时,如果where条件后的字段未加索引或者未命中索引会导致锁表。这种锁表行为会阻塞其他事务对该表的访问,显著降低并发性能和系统响应速度。

问题复现

我们在本地准备环境复现下,本地环境mysql使用的版本时8,首先准备一张表bus_pages,除了主键不创建其它索引,准备两个接口,一个修改,一个新增

less 复制代码
@Service
@Slf4j
public class BusTestServiceImpl implements BusTestService {

    @Resource
    private BusPagesService busPagesService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateInfo() {
        StopWatch sw = new StopWatch();
        sw.start();
        log.info("修改方法执行开始");
        LambdaUpdateWrapper<BusPagesEntity> updateWrapper = new LambdaUpdateWrapper();
        updateWrapper.eq(BusPagesEntity::getMarkId,18);
        updateWrapper.set(BusPagesEntity::getPage,LocalDateTime.now().toString());
        busPagesService.update(updateWrapper);

        try {
            Thread.sleep(40*1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        sw.stop();
        log.info("修改方法执行结束,耗时{}s",sw.getTime(TimeUnit.SECONDS));

    }

    @Override
    public void saveInfo() {
        StopWatch sw = new StopWatch();
        sw.start();
        log.info("新增方法执行开始");
        BusPagesEntity busPagesEntity = new BusPagesEntity();
        busPagesEntity.setPage(LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME));
        busPagesService.save(busPagesEntity);
        sw.stop();
        log.info("新增方法执行结束,耗时{}s",sw.getTime(TimeUnit.SECONDS));

    }


}

我们首先调用修改方法,然后在调用新增方法,可以看到新增的接口会一直等待修改的接口完成之后才会执行完成。

_20240525223958.jpg

然后我们给表bus_pagesmark__id字段创建索引

_20240525224441.jpg

然后在执行修改及新增接口,可以看到新增接口不会在等待修改接口执行完在去执行了

_20240525224736.jpg

注意: 并不是创建了索引就不会锁表,当我们的索引失效时,也会锁表

命令行查看(mysql版本8.0)

  • 查看被锁定的表
sql 复制代码
show OPEN TABLES where In_use > 0; 

此命令用于列出当前正在使用中的表,也就是说那些被锁定或正在进行某些操作(如读写操作)的表。

  • 查看正在等待锁资源的查询
csharp 复制代码
select * from performance_schema.data_lock_waits;
csharp 复制代码
select * from sys.innodb_lock_waits; 
  • 查看锁定数据
csharp 复制代码
select * from performance_schema.data_locks;
  • 查看正在运行中的事务或命令的详情
csharp 复制代码
select * from information_schema.innodb_trx;

总结

在编写Update语句时,务必注意Where条件中涉及的字段是否有索引支持。避免全表锁的关键在于优化查询,利用索引提高查询效率,减少系统性能的影响。通过合理地设计索引,并确保Update语句中的Where条件包含索引字段,可以有效地提升数据库的性能和并发能力。

相关推荐
一 乐8 小时前
婚纱摄影网站|基于ssm + vue婚纱摄影网站系统(源码+数据库+文档)
前端·javascript·数据库·vue.js·spring boot·后端
码事漫谈9 小时前
Protocol Buffers 编码原理深度解析
后端
码事漫谈9 小时前
gRPC源码剖析:高性能RPC的实现原理与工程实践
后端
踏浪无痕10 小时前
AI 时代架构师如何有效成长?
人工智能·后端·架构
程序员小假11 小时前
我们来说一下无锁队列 Disruptor 的原理
java·后端
武子康12 小时前
大数据-209 深度理解逻辑回归(Logistic Regression)与梯度下降优化算法
大数据·后端·机器学习
maozexijr12 小时前
Rabbit MQ中@Exchange(durable = “true“) 和 @Queue(durable = “true“) 有什么区别
开发语言·后端·ruby
源码获取_wx:Fegn089512 小时前
基于 vue智慧养老院系统
开发语言·前端·javascript·vue.js·spring boot·后端·课程设计
独断万古他化12 小时前
【Spring 核心: IoC&DI】从原理到注解使用、注入方式全攻略
java·后端·spring·java-ee
毕设源码_郑学姐12 小时前
计算机毕业设计springboot基于HTML5的酒店预订管理系统 基于Spring Boot框架的HTML5酒店预订管理平台设计与实现 HTML5与Spring Boot技术驱动的酒店预订管理系统开
spring boot·后端·课程设计