这样一优化系统整体性能立马提升
创始人
2025-07-02 18:50:37
0

环境:Spring5.3.23

1. 案例代码

先看下示例代码:

static class PersonService {
  @Resource
  private JdbcTemplate jdbcTemplate;
  /**
   * 这里的代码,先执行了更新操作,然后执行查询操作。
   * 业务代码非常的简单
   */
  @Transactional
  public void operator() {
    // 1
    int res = this.jdbcTemplate.update("update p_user t set t.address='akf' where t.id = 9000000") ;
    System.out.printf("更新: %d 条数据%n", res) ;
    // 2
    List> users = this.jdbcTemplate.queryForObject("select * from p_user x where x.username='h4F7i4B4'", (rs, rowNum) -> {
      List> datas = new ArrayList<>() ;
      while (rs.next()) {
        Map obj = new HashMap<>() ;
        obj.put("id", rs.getObject(1)) ;
        obj.put("username", rs.getObject(2)) ;
        obj.put("email", rs.getObject(3)) ;
        obj.put("password", rs.getObject(4)) ;
        obj.put("address", rs.getObject(5)) ;
        obj.put("age", rs.getObject(6)) ;
        datas.add(obj) ;
      }
      return datas ;
    }) ;
    System.out.println(users) ;
  }
}

先思考上面代码在什么样的场景下可以做相应的优化?

2. 环境准备

本地环境我准备了p_user表数据量2000W。

图片图片

数据库索引情况:

图片图片

这里除了主键,没有任何其它的索引。

这里不建立任何二级索引是为了模拟查询慢的场景。

执行上面的查询SQL:

图片图片

执行时间5s;感觉还好

3. 代码分析

在上面的代码中2个关键的数据库操作分别是更新与查询,这两个查询是没有任何关联的,相关独立;而在整个方法上是添加了@Transaction注解,整个方法的执行都是在一个事务中执行。那么这里的查询如果非常的慢是不是会对当前的修改记录或者是整个p_user表造成影响呢?

结合上面的数据表情况,当前表是没有任何索引的(除主键)。如果这里的查询比较慢,会发生什么情况呢?

首先你要知道MySQL中update语句会上什么锁?

回顾MySQL锁:

MySQL的UPDATE语句默认会带上一个写锁(Write Lock)。在MySQL中,写锁会阻塞其他事务对同一行的读取和写入操作,以确保数据的一致性和完整性。

当执行UPDATE语句时,MySQL会在相关的行上获取写锁。这样,其他事务无法修改或删除这些行,直到写锁被释放。这有助于防止在并发操作中发生数据冲突或不一致的情况。

还有点要注意mysql的锁机制是基于事务的,所以通常我们需要在一个事务中进行操作。随着事务的提交或回滚进行锁的释放

知道了update语句默认会带上写锁,那么这里的update锁的是id等于9000000的数据。前面提到了,只要事务提交或者回滚后锁才会被释放。

那如果这里我们接下来的查询比较慢那是不是我们这个锁的释放时间就会变长,其它事务将会被阻塞。一旦发生了阻塞我们系统的整体性能可能会受到影响。这里的update语句只会对id为9000000的数据上锁,如果咱们的更新语句是范围的或者条件是没有索引的那很可能就成了表锁,那这时候系统的性能会变的非常糟糕。也就是我们对这条id为9000000的数据要锁至少5s时间。

该如何优化上面的代码呢?

4. 代码优化

通过上面的分析,由于先执行了update语句,然后执行查询语句,如果查询比较慢那么我们的update语句形成的写锁(行锁,间隙锁或者表锁)时间会变长,对系统的整体性能会造成影响。所以这里我们只需要将查询和修改操作顺序进行下调整即可。

@Transactional
public void operator() {
  // 1
  List> users = this.jdbcTemplate.queryForObject("select * from p_user x where x.username='h4F7i4B4'", (rs, rowNum) -> {
    List> datas = new ArrayList<>() ;
    while (rs.next()) {
      Map obj = new HashMap<>() ;
      obj.put("id", rs.getObject(1)) ;
      // ...
      datas.add(obj) ;
    }
    return datas ;
  }) ;
  System.out.println(users) ;
  // 2
  int res = this.jdbcTemplate.update("update p_user t set t.address='akf' where t.id = 9000000") ;
  System.out.printf("更新: %d 条数据%n", res) ;
}

通过上面的优化,虽然该接口自身的性能并没有提升,但是在该接口中update语句形成的锁时间将大大的减少(在这里查询语句是没有锁的),如果同一时刻存在其它事务修改当前的数据不至于被阻塞太长时间,那其它接口的性能整体不就提高了么。

在上面的代码中首先是2个操作互不相干,其实完全可以把不需要事务的操作放到其它方法中(注意事务失效问题)。

static class PersonService {
  @Resource
  private JdbcTemplate jdbcTemplate;
  @Resource
  private PersonService ps ;


  public void query() {
    ps.update() ;
    List> users = this.jdbcTemplate.queryForObject("select * from p_user x where x.username='h4F7i4B4'", (rs, rowNum) -> {
      List> datas = new ArrayList<>() ;
      while (rs.next()) {
        Map obj = new HashMap<>() ;
        obj.put("id", rs.getObject(1)) ;
        // ...
        datas.add(obj) ;
      }
      return datas ;
    }) ;
    System.out.println(users) ;
  }
  
  @Transactional
  public void update() {
    int res = this.jdbcTemplate.update("update p_user t set t.address='akf' where t.id = 9000000") ;
    System.out.printf("更新: %d 条数据%n", res) ;
  }
}

上面代码己注入自己,解决在非事务方法中调用事务方法二导致事务失效。当然也可以通过AopContext来解决(不推荐),也可以把事务方法放到其它类中都可以解决。

完毕!!!

相关内容

热门资讯

PHP新手之PHP入门 PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的...
网络中立的未来 网络中立性是什... 《牛津词典》中对“网络中立”的解释是“电信运营商应秉持的一种原则,即不考虑来源地提供所有内容和应用的...
各种千兆交换机的数据接口类型详... 千兆交换机有很多值得学习的地方,这里我们主要介绍各种千兆交换机的数据接口类型,作为局域网的主要连接设...
粉嫩如何诠释霸道 东芝M805... “霸道粉”是个什么玩意东芝M805拿过来的时候,笔者扑哧笑了,不是笑这款笔记本,而是笑这款产品的颜色...
什么是大数据安全 什么是大数据... 在《为什么需要大数据安全分析》一文中,我们已经阐述了一个重要观点,即:安全要素信息呈现出大数据的特征...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
全面诠释网络负载均衡 负载均衡的出现大大缓解了服务器的压力,更是有效的利用了资源,提高了效率。那么我们现在来说一下网络负载...
如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
30分钟搞定iOS自定义相机 最近公司的项目中用到了相机,由于不用系统的相机,UI给的相机切图,必须自定义才可以。就花时间简单研究...
Intel将Moblin社区控... 本周二,非营利机构Linux基金会宣布,他们将担负起Moblin社区的管理工作,而这之前,Mobli...