慢sql经验总结

在过去的工作中,如何在各个环境中(开发、测试、生产)快速准确的发现应用的慢sql,并进行高效的推动,是一个很大的挑战。以下是一些经验

慢sql的定义

我们将执行时间超过1秒的sql都定义为慢sql。同时会导致

  1. 系统的响应时间延迟,影响用户体验
  2. 资源占用增加,增高了系统的负载,其他请求响应时间也可能会收到影响。
  3. 慢SQL占用数据库连接的时间长,如果有大量慢SQL查询同时执行,可能会导致数据库连接池的连接被全部占用,导致数据连接池打满、缓冲区溢出等问题,使数据库无法响应其他请求。
  4. 还有可能造成锁竞争增加、数据不一致等问题

因此,需要及时发现和优先慢sql,对系统稳定性是非常重要的

慢sql常见原因

  1. 缺乏索引/索引未生效,导致数据库全表扫描,会产生大量的IO消耗,产生慢SQL。
  2. 单表数据量太大,会导致加索引的效果不够明显。
  3. SQL语句书写不当,例如join或者子查询过多、in元素过多、limit深分页问题、order by导致文件排序、group by使用临时表等。
  4. 数据库在刷“脏页”,redo log写满了,导致所有系统更新被堵住,无法写入了。
  5. 执行SQL的时候,遇到表锁或者行锁,只能等待锁被释放,导致了慢SQL。

如何发现慢sql

  1. 数据库开启慢sql日志:数据库会将执行时间超过一定阈值的sql记录到日志中
  2. 链路追踪:通过慢接口以及代码,定位慢sql
  3. 开发过程中,进行代码review。通过对sql语句分析,发现索引使用不当、造成全表扫描、sql扫描行数过多、出现文件排序等。强制其修改后再提交
  4. 对数据库进行监控,重点对cpu使用情况、会话数,发现异常及时告警同时安排值班同时定位问题,尽量在第一时间解决数据库风险。同事建立问题跟踪,若因业务影响则指定负责人推进。

制定sql规范

  1. **【强制】**不允许使用select *
  2. **【强制】**不要使用count(列名)或count(常量)来替代count(),count()就是SQL92定义的标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关。
  3. **【强制】**count(distinct col) 计算该列除NULL之外的不重复数量。注意 count(distinct col1, col2) 如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0。
  4. **【强制】**若列会执行计算,则强制其使用默认值不允许为NULL,当某一列的值全是NULL时,count(col)的返回结果为0,但sum(col)的返回结果为NULL,因此使用sum()时需注意NPE问题。
  5. **【强制】**使用ISNULL()来判断是否为NULL值。
  6. **【强制】**对于数据库中表记录的查询和变更,只要涉及多个表,都需要在列名前加表的别名(或表名)进行限定。
  7. **【强制】**在代码中写分页查询逻辑时,若count为0应直接返回,避免执行后面的分页语句。
  8. **【强制】**不得使用外键与级联,一切外键概念必须在应用层解决。

执行计划相关解释

通过explain语句可以提供关于SQL查询执行的详细信息和执行计划,并且可以了解sql的索引使用情况以及数据访问方式。通过使用Explain语句,可以了解SQL是如何执行的,并且可以看出其可能存在的性能问题。

字段 解析
id 查询的每个操作的唯一标识符,无特殊含义
select type 查询操作的类型,常见的包括SIMPLE(简单查询)、PRIMARY(主键查询)、SUBQUERY(子查询)、DERIVED(派生表查询)等
table 表示查询操作设计的表名
partitions 表示查询操作涉及的分区信息
type 表示查询操作的连接类型或访问类型,常见的包括ALL(全表扫描)、index(索引扫描)、range(范围扫描)、ref(基于索引的引用)、eq_ref(唯一索引使用)、const(常数表)、system(系统表)等
possible_keys 表示查询可能使用的索引
key 表示查询操作实际使用的索引
key_len 表示索引使用的字节数
ref 表示查询操作使用的索引之间的引用关系
rows 执行查询操作时,mysql认为必须要去检查和判断的记录条数
filtered 表示符合查询条件的数据百分比
Extra 表示额外的信息和备注,如“Using where”表示使用了WHERE子句过滤,“Usingindex“表示只使用了索引而没有访问表等。

需要重点关注几个特别的情况,出现以下几点都可能造成sql性能的降低。

  1. 使用全表扫描,性能最差,即type=“ALL”
  2. 扫描行数过多,即rows>阈值,阈值一般为10w
  3. 查询时使用了排序操作,也比较耗时,即Extra包含"Using filesort”
  4. 索引类型为index,代表全盘扫描了索引的数据,Extra信息为Using where,代表要搜索的列没有被索引覆盖,需要回表,性能较差。

规范

  1. 主键不允许使用uuid,小表可以使用自助主键,大表的主键必须使用递增的id组件
  2. 尽量保证核心sql查询的,where、order by、group by 都可以用上索引。建立联合索引,尽量让区分度大的在索引左边,经常做范围查询的字段放在最后一个
  3. 索引原则上不应该超过3个。尽量利用1到2个多字段联合索引,抗下80%以上的查询。然后在剩下20%场景,建立一个联合索引。保证99%以上的查询都能充分利用索引

慢sql治理流程

  1. 高危慢sql,建立任务持续跟踪,每个版本汇报直到问题解决。并指定处理优先级,不同的优先级可以容忍的时间不同,若超过容忍时间,则影响团队评价。
  2. 针对重点业务或慢sql高发团队,安排有经验的同事协助推进。
  3. 新功能开发过程中,根据实际情况,及时处理或排期处理。必须在排期时间内完成改造,超期则影响团队评价

总结

慢sql可能引起很严重的系统性能问题,影响系统的可用性和稳定性。需要及时发现和治理。因此,我们建立了一套发现-分析-治理的流程,极大的减少了因慢sql引起的系统性能问题

Talk is cheap, show me the bug/code.
使用 Hugo 构建
主题 StackJimmy 设计