mysql大表联合查询优化,大事务优化,规避事务超时,锁等待超时与锁表

本文分享了针对大表联合查询的优化策略,包括业务代码层面的重复查询避免、SQL执行计划分析、索引优化、小表驱动大表等,以及如何通过减小事务粒度、避免远程调用等方法优化大事务处理,降低事务超时风险。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景:最近在做项目的同时做了一些优化,主要针对mysql大表(2亿+数据量未分库分表情况下)的联合查询以及生产上出现的一些事务超时和锁等待超时现象的优化,分享一些我个人的优化思路,只讲思路不贴代码哈。
一、大表查询优化

优化思路:

1.业务代码层面

1)梳理业务代码,是否存在重复的或者循环的查询数据库或远程api调用。如果存在此类代码,是否可以避免重复多余和循环的耗时操作(减少耗时与数据库连接次数),这类代码如果出现在事务里,可能造成事务超时和锁等待超时。

2.sql层面

1)explain查看执行计划,重点查看以下几列:

id:值越大,优先级越高,越先执行。

table:语句查询的表

type:查询类型,重点字段,用于查看sql是否走了索引,走的什么索引。

性能从优到低:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

possible_keys :查询可能用到的索引

key :实际用到的索引,为null表示没有用到索引

rows:找到记录需要扫描的行数,数值越小越好

2)如果扫全表,考虑添加索引。如果有索引未命中,考虑改sql写法,如查询条件等使其命中。对于范围查询,可能存在数据量少的时候mysql优化器会选择走索引,数据量大的时候全表扫描,这是mysql优化器在起作用,这个时候如果查询条件固定不变的情况下,考虑使用force (index)强制走索引,条件变化的情况下不建议强制走某一个索引,因为可能其他查询条件下又不走索引了。

3)小表驱动大表,如果联表查询,注意要小表驱动大表。我优化的原sql就是大表驱动小表,2亿连2千万。。。。优化之后小表驱动大表,性能有所提升

4)避免索引失效的情况,如in,or,!=等条件导致的索引失效

5)联合索引情况下,如(a,b,c),避免出现只查询b,bc和c的情况导致联合索引失效

6)尽量避免在sql中做统计,尽量在代码实现统计

二、大事务优化

1.减小事务颗粒度

spring中的@Transactional注解作用于方法上,如果方法内部除了事务操作外存在大量的其他耗时操作,如大量耗时查询等,极其容易导致事务超时甚至锁等待超时。场景:方法内部存在大量除事务外的耗时操作,导致事务超时了,或者前一个事务还未提交,后一个事务等待前一个事务释放锁的情况会报Lock wait timeout exceeded; try restarting transaction异常。解决办法:增加锁等待时间,或者使用编程式事务(TransactionTemplate)减小事务颗粒度,重点把耗时的非事务操作放在事务外,少用@Transactional,少用@Transactional,少用@Transactional。

2.事务中尽量避免远程调用

可能存在在A服务的事务中需要调用B服务的api获取数据,但可能由于网络不稳定等原因导致响应时间长,这个时候这个事务可能就被迫成为了大事务,哪怕它只有一个更新操作。

不仅仅远程调用,还有消息队列,缓存等,切记放在事务外。

这种代码不放在事务中需要保证数据一致性,这需要建立补偿机制和日志记录:比如消息队列的重发补偿。

3.避免在一个事务中一次性处理大量数据(如批量或循环更新1000条数据)

禁止在事务中循环update或insert,或者一次性批量操作大量数据。这样极大可能造成锁等待超时和锁表,导致生产事故。将不必要的数据库操作放置事务外,如日志插入等

4.部分业务抽离做异步

考虑事务内的部分业务是否可以异步处理,如日志记录,优惠券发送等非同步操作,可考虑异步处理。观察者模式实现或者消息队列实现,注意做好补偿机制,假如某个用户优惠券发放失败可以补偿发送

### SQL 数据库查询导致问题及其解决方案 #### 现象分析 当多个事务尝试访问同一张中的相同数据行时,可能会发生冲突。如果其中一个事务已经获得了排他(写操作),其他试图读取或写入该行的事务就必须等待第一个事务完成并释放。这种情况下就产生了所谓的“”。长时间未解除的定不仅会影响当前的操作效率,还可能引发连反应,使整个系统的性能下降。 #### MySQL 中的解策略 在MySQL环境中,可以通过以下几种方式来处理由查询引起的状况: - **检测死**:启用`innodb_print_all_deadlocks`参数以便自动记录所有的死事件[^1]。 - **手动终止会话**:通过执行 `SHOW PROCESSLIST;` 查看正在运行的任务列,并识别出那些持有长期住资源不放的进程号PID之后利用命令 `KILL PID;` 来强行结束它们的工作。 ```sql -- 显示所有活动线程的信息 SHOW FULL PROCESSLIST; -- 结束指定ID对应的连接 KILL CONNECTION_ID; ``` - **设置超时机制**:适当配置InnoDB存储引擎的相关变量如`innodb_lock_wait_timeout`以控制等待时间长度,超过设定时限则主动放弃获取的权利从而避免无限期挂起的情况出现。 #### Oracle 数据库里的应对措施 针对Oracle数据库而言,则有另外一套流程用于管理和缓解由于查询所造成的难题: - **查找定对象**:借助视图V$LOCKED_OBJECT以及DBA_LOCKS等工具定位具体被占用的对象名称所属实例编号。 - **强制回滚交易**:依据上述获得的结果集确定目标SID (Session ID),进而发出指令`ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;`立即停止有问题的服务请求并且回退正在进行中的更改动作。 ```sql -- 获取定信息 SELECT * FROM v$locked_object WHERE object_id = OBJ#; -- 强制断开特定会话 ALTER SYSTEM KILL SESSION 'session_id, serial_number' IMMEDIATE; ``` #### 预防性维护建议 为了从根本上减少甚至杜绝此类情况的发生频率,应当重视以下几个方面的改进工作: - 定义合理的索引来加速检索过程,特别是对于频繁参条件判断的关键列建立合适的索引结构能够显著降低全扫描带来的负面影响[^3]; - 编写高效的SQL脚本,遵循最佳实践编写简洁明了且易于解析的语句有助于减轻服务器负担的同时提高并发能力; - 调整应用程序逻辑设计模式,尽量缩短单个事物持续期间内的活跃周期,比如采用批量提交而非逐条更新的方式可以有效规避不必要的竞争局面形成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值