数据库更新慢之谜:从10秒到瞬间的优化探索

需积分: 0 0 下载量 62 浏览量 更新于2024-08-03 收藏 832KB PDF 举报
"文章主要讲述了在某个项目中,一个UPDATE语句执行耗时长达10秒,引发性能问题。经过分析,发现问题出在RPC调用的重试机制以及服务内部的异步任务处理上。更新语句本身没有问题,但服务在更新后还有两个异步任务,在某些情况下这些任务在原事务线程中执行,导致事务持续时间延长。通过调整线程池配置,解决了这一问题。" 文章详细内容: 在本文中,作者首先描述了一个实际遇到的问题,即一个UPDATE语句执行时间异常长,达到10秒,这在高效运行的系统中是不可接受的。问题发生在2022年7月的一个晚上,测试人员报告了一个后台更新信息的性能问题。 接着,作者进行了问题分析,发现该UPDATE语句被调用了两次,第一次执行超时,第二次执行时间缩短,这是由于RPC调用的重试机制(retry=1)所致。通常,对于数据库的增、删、改操作,不应设置重试。进一步调查发现,UPDATE语句本身没有明显性能问题,因为它只是基于主键ID进行更新,按理说应该非常快。 作者怀疑问题可能在于服务的其他部分,经过IDEA的代码审查,发现服务B在执行UPDATE语句后,还在事务提交前触发了两个异步通知任务,这些任务使用了Spring的@Async注解和自定义线程池。关键的问题在于,有时这些异步任务会在原事务线程中执行,而不是预期的独立线程,这可能导致事务的生命周期被延长。这种情况并不总是出现,有时异步任务会正常在线程池的线程中执行。 通过对线程池配置的深入调查,作者发现了问题的根源——线程池的callRunner失败策略。当异步任务在原线程执行时,事务会保持开放状态,直到所有任务完成,这解释了为何UPDATE语句执行如此之慢。 最后,通过调整线程池配置,确保异步任务始终在新线程中运行,从而解决了这个问题。作者总结,对于这类性能问题,需要全面考虑服务的整个流程,包括事务管理、异步任务执行以及线程池配置,以确保系统的高效运行。