一次难得的MySQL优化初实践
去年项目重构,系统切换为微服务架构,数据库各方面也做了分库分表的一些实践,感觉那段时间写代码酣畅淋漓,爽,然,老系统依然需要继续维护,看目前情况,两个系统可能会在未来几年时间内同时对用户提供。我们开发没意见,一切为运营服务(内心OS:产品和运营大大们能不能尽快推进切换啊,毕竟老一套还要写JSP,这其中酸爽,难以体会)。
这日,老大丢了一条慢查询SQL语句,说老系统服务中的这条SQL语句查询超过4s+了,看看业务中具体那儿在用并确认一下是否存在优化空间,具体查询语句示例如下(业务安全问题,不可对外展示):
select o.attr1,o.attr2,o.attr3, u.attr4, u.attr5 from order o outer join user u on
o.u_id=u.id and o.createAt>='2022-03-20 00:00:00' and o.status="success" and o.feeType='TV' and o.payType='ali_pay' ....
该业务的主要功能是定时每5s定时从三方更新用户会员订购权益。好的,领下任务,遂开始在测试环境上执行查看了一下SQL语句的执行计划,
id select_type table type possible_key key key_len ref rows Extra
1 SIMPLE this_ index PRIMARY 4 56 Using where
1 SIMPLE user2_ eq_ref PRIMARY PRIMARY 4 this_.user_id 1 Using where
测试环境就是只有50多条测试订购单数据,看起来,虽然是使用了主键索引,但是rows范围是整个表,这个主要是查询语句中的根据订单表的id进行了降序排序,去掉排序就可以看出来,实际上就是全表扫描了无疑:
id select_type table type possible_key key key_len ref rows Extra
1 SIMPLE this_ index ALL 4 56 Using where
1 SIMPLE user2_ eq_ref PRIMARY PRIMARY 4 this_.user_id 1 Using where
好吧,让运维确认了现网订购单表数据,达到340W+条,查询时间达到4.737s,这个量级,全表扫描是无法接受的,且目前每天以超过1w左右的量持续增加,这种情况,必须查询优化了,优化第一C位,必须是利用索引呀。 目前订单表除了默认id主键,在user_id字段上加了索引,用于业务关联查询,但对目前这个需求来说,无法有效减少扫描范围,需要考虑新建索引。 确定建立怎样的索引?
- 查询条件中有多个条件:status(订单状态),payType(支付类型) feeType(计费类型) createAt(订单创建时间),除了createAt,其他几个字段均是枚举类型值,根据索引创建原则,枚举值类型的字段自然不合适,所以createAt是作为创建索引的最佳字段
- 根据现有业务,考虑创建的索引能否服务更多的业务需求?
重新整体梳理整个服务应用,订单表还有如下应用:
- 查询渠道某个时间范围的支付成功情况(1处,应用频率较低,主要用于运营后端查询)
- 查询某个用户TV付费服务类型订购成功记录(1处,定时每30s执行一次)
- 根据时间范围查询TV端订购记录(1处,定时每30s执行一次) 所以结合具体的业务形态,决定使用复合索引进一步来提升索引复用和查询共用能力。 索引创建如下:
alter table order add index time_user_status_idx(createAt,user_id,status)
确定了处理思路,找老大讨论了一下,比较服务目前的业务情况,那就和晚上的服务上线一并处理了,凌晨12点,运维同学传来捷报,执行时间为0.001s,OK,收工。 虽然这次的优化不是非常深入,但是也确实明白了业务中当数据量上来后性能优化的重要性。未来的开发路漫长,需要多多思考。
转载自:https://juejin.cn/post/7077917522439176228