「数据库」干活!携程酒店慢查询治理之路

一、背景

慢查询指的是数据库中查询时间超过了指定的阈值的SQL,这类SQL通常伴随着执行时间长、服务器资源占用高、业务响应慢等负面影响。随着携程酒店业务的不断扩张,再加上大量的SQLServer转MySQL项目的推进,慢查询的数量正在飞速增长,每日的报警量也居高不下,因此慢查询的治理优化已经是刻不容缓,此文主要针对MySQL。

二、慢查询治理实践

2.1 SQL上线流程优化

之前的流程发布比较快捷,但是随着质量差的SQL发布迁移得越来越多,告警和回退数量也随之变多,综合下来,数据库风险方面不容乐观,该流程需要优化。

和旧流程相比,新增了一个SQLReview的环节,将潜在的慢查询提前筛选出来优化,确保上线的SQL质量,在此流程保障下,所有上线到生产的SQL性能都能在DBA评估后的可控范围内,在研发提交审核后,会收到审批的事件单。

携程目前是存在自动化review审核的平台,但是由于酒店业务场景比较复杂,研发对于SQL的理解水平参差不齐,平台给出的建议并不能做到面面俱到,因此还没有被广泛使用于流程中,仅作为一个参考。

2.2 理解查询语句

要优化慢查询,首先要知道慢查询是如何产生的,执行计划是怎么样的,最后考虑如何去优化查询。

一条sql的执行主要分成如图几个步骤:

1)SQL语法的缓存查询(QC)

2)语法解析(SQL的编写、关键字的语法之类)

3) 生成执行计划

4) 执行查询

5) 输出结果

通常慢查询都发生在“执行查询”这步,读懂查询计划,可以有效地帮助我们分析SQL性能差的原因。

在SQL前面加上EXPLAIN,就可以查看执行计划,计划以“表”的形式展示:

具体字段含义可以参考MySQL官方的解释,这里不多赘述。

2.3 优化慢查询

通过执行计划就可以定位到问题点,通常可以分为这几种常见的原因。

(1) 索引层面

这个查询由于缺少name字段索引,产生了全表扫描:

select * from hotel where name=’xc’;

补上索引之后,提示使用到了索引。

如图所示,索引失效的大致原因可以分为八类,这些场景通过查看执行计划都会发现产生type=ALL或者type=index的全表扫描。

explain select * from hotel where name like '%酒店%';

explain select * from hotel where name like '%酒店%'or Bookable='T';

explain select * from hotel where name <>'酒店';

explain select * from hotel where substring(name,1,2)='酒店';

create table t1 (

col1 varchar(3) primary key

)engine=innodb default charset=utf8mb4;

t1表的col1为varchar类型,但是参数传入的是数值类型,结果产生了隐形转换,索引失效导致type=index的全表扫描。

Where条件不符合“最左匹配原则”,则索引会失效。

alter table hotel add index idx_hotelid_name_isdel(hotelid,name,status);

以下条件均可以命中联合索引:

explain select * from hotel where hotelid=10000 and name='ctrip' and status='T';

explain select * from hotel where hotelid=10000 and name='ctrip';

explain select * from hotel where hotelid=10000;

但是以下条件无法使用到联合索引:

explain select * from hotel where name='ctrip' and status='T';

explain select * from hotel where name='ctrip';

explain select * from hotel where status='T';

索引字段的数据分布不均匀,表数据量过小的情况下,MYSQL查询优化器可能认为返回的数据量本身就很多,通过索引扫描并不能减少多少开销,此时选择全表扫描的权重会提高很多。

不带where条件直接查询修改全表是很危险的操作,表数据量够大的话,尽量拆分成多批次操作。

优化中遇到的案例:

某天发现有一台DB服务器IO异常,服务器链接开始堆积,引发了大量应用报错

监控显示此时repl延迟已经有25分钟,集群几乎处于无高可用状态,非常的危险。

登陆服务器排查后发现有一条全表删除的SQL在通过JOB系统跑,该表的数据量很大:

-tarpresqls "delete from XXXXXX"

最后紧急Kill这条SQL后恢复正常,直接在生产删除全表是很危险的操作。

MySQL中存在force index()、ignore index()方式来强制使用/忽略特定的索引。

这种方式可能会导致执行计划选择不到最优的索引,从而导致计划走偏。

Index merge方法可以对同一个表使用多个索引分别进行条件扫描,检索多个范围扫描并将结果合并为一个。

但是,当遇到如图2个索引字段分布都很差的情况时(status与bookable的区分度都很低),2个索引的结果集存在大量数据需要merge,性能就会变得很糟糕。

(2) SQL频率

(3) 写法不规范

最常见的分页写法就是使用limit,在分页查询时,我们会在 LIMIT 后面传两个参数,一个是偏移量(offset),一个是获取的条数(limit)。当偏移量很小时,查询速度很快,但是随着 offset 变大时,查询速度会越来越慢。

MySQL Limit 语法格式:

SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset

例如下列分页查询:

当limit只有0,10时,执行还是很快,但是随着offset增加,可以看到深度分页的情况下,分页越深,扫描的行数就越多,性能也就越来越差了。

explain select * from testlimittable order by id limit 1000, 10;

explain select * from testlimittable order by id limit 10000, 10;

explain select * from testlimittable order by id limit 20000, 10;

explain select * from testlimittable order by id limit 30000, 10;

explain select * from testlimittable order by id limit 40000, 10;

explain select * from testlimittable order by id limit 50000, 10;

explain select * from testlimittable order by id limit 60000, 10;

*:警惕通过分页写法来实现循环分批的逻辑,limit深分页实现不了将大量数据拆分成若干小份的效果

分批可以采用分段拉取减少扫描的行数,如果分段拉取不连续的话可以传入上一次拉取最大的值作为下一次的起始值:

由于where条件的字段数据分布问题,会导致max和min的查询非常慢:

explain select max(id) from hotel where hotelid=10000 and status='T';

由于hotelid=10000的数据分布比较多,可以看到扫描数很高:

a. 添加联合索引

alter table hotel add index idx_hotelid_status(hotelid,status);

在索引覆盖下,extra提示Select tables optimized away,这意味着在查询执行期间不需要读取表,可以通过索引直接返回结果。

b. 改写为order by的方式

explain select id from hotel where hotelid=10000 and status='T' order by id desc limit 1;

扫描数很少,虽然是type=index的索引扫描,但是由于MYSQL对limit的优化,实际上并不会全表扫描。

通常SQL在使用Group by及Order by后,会产生临时表和文件排序操作。若查询条件的数据量非常大,temporary和filesort都会产生额外的巨大开销。

a. 使用索引来满足排序聚合

alter table hotel add index idx_name_hotelid(name,hotelid);


此时MYSQL可以通过访问索引来避免执行filesort 及temporary操作

b. 取消隐形排序

在某些情况下,Group by会默认实现隐形排序,通过添加ORDER BY NULL可以取消这种隐形排序。

*注意从MySQL 8.0开始,不会再有这种情况了,因此不需要ORDER BY NULL写法了

(4)资源

在读写很热的表上,通常会发生锁资源争夺,从而导致慢查询的情况。

a. 谨慎使用for update查询

b. 增删改尽量保证使用到索引

c. 降低并发,避免对同一条数据进行反复的修改

往客户端发送数据时发生网络波动导致的慢查询

CPU利用率高,磁盘IO经常满载,导致慢查询

总结

慢查询治理是一个长期且漫长的过程,不应等SQL超时报错后才开始考虑优化,从一开始就要建立完善的日常化流程体系,才能有效的控制慢查询的增长。

但是经过长期优化后发现,仅仅从数据库层面优化,并不能实现慢查询完全“清零”,还有很多的痛点来自于业务逻辑和应用层面本身。这也需要研发工程师着重优化业务逻辑、应用策略,并加强数据库培训,在编写SQL时切勿过于随意,贪图省事,否则事后再优化会变得相当困难。

文章来源:携程技术_https://mp.weixin.qq.com/s?__biz=MjM5MDI3MjA5MQ==&mid=2697273692&idx=1&sn=845c6338e79114be5e6eef82f38924ff&chksm=8376de68b401577e792ef45b84738032c32c4cefe0e5040776d0c67d989e06233602791f848a&scene=178&cur_album_id=2170120724240302083#rd

展开阅读全文

页面更新:2024-05-13

标签:酒店   写法   字段   索引   流程   条件   操作   数据库   业务   计划   数据

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top