likes
comments
collection
share

一次难得的MySQL优化初实践

作者站长头像
站长
· 阅读数 14

去年项目重构,系统切换为微服务架构,数据库各方面也做了分库分表的一些实践,感觉那段时间写代码酣畅淋漓,爽,然,老系统依然需要继续维护,看目前情况,两个系统可能会在未来几年时间内同时对用户提供。我们开发没意见,一切为运营服务(内心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字段上加了索引,用于业务关联查询,但对目前这个需求来说,无法有效减少扫描范围,需要考虑新建索引。 确定建立怎样的索引?

  1. 查询条件中有多个条件:status(订单状态),payType(支付类型) feeType(计费类型) createAt(订单创建时间),除了createAt,其他几个字段均是枚举类型值,根据索引创建原则,枚举值类型的字段自然不合适,所以createAt是作为创建索引的最佳字段
  2. 根据现有业务,考虑创建的索引能否服务更多的业务需求? 重新整体梳理整个服务应用,订单表还有如下应用:
    • 查询渠道某个时间范围的支付成功情况(1处,应用频率较低,主要用于运营后端查询)
    • 查询某个用户TV付费服务类型订购成功记录(1处,定时每30s执行一次)
    • 根据时间范围查询TV端订购记录(1处,定时每30s执行一次) 所以结合具体的业务形态,决定使用复合索引进一步来提升索引复用和查询共用能力。 索引创建如下:
alter table order add index time_user_status_idx(createAt,user_id,status)

确定了处理思路,找老大讨论了一下,比较服务目前的业务情况,那就和晚上的服务上线一并处理了,凌晨12点,运维同学传来捷报,执行时间为0.001s,OK,收工。 虽然这次的优化不是非常深入,但是也确实明白了业务中当数据量上来后性能优化的重要性。未来的开发路漫长,需要多多思考。