目录

  • 建表注意事项
  • 把常用字段提取出来建表
  • 把Text等超长字段单独存放到冗余表中
  • 与其他表关联的外键建立索引
  • 适当的冗余字段
  • 选择合适的字段类型
  • 建立合理的索引
  • 值差异大的字段适合建索引
  • 查询频繁的字段建立索引
  • 使用覆盖索引避免回表
  • 不要建立过多的索引
  • 使用Explain查看下Sql的执行计划
  • sql语句的注意事项
  • 避免使用子查询
  • 大型表的分页查询limit 需要优化
  • 避免类型转换 无论是隐式的还是显式的
  • 避免字符串的编码转换
  • 使用预编译查询
  • 避免使用having
  • 使用union all 代替 union
  • 必要的情况才使用事务
  • 使用合理框架和设置缓存等其他手段
  • 使用连接池
  • 使用缓存
  • mysql设置合理的参数


我只是一个开发,平时也零零散散的了解过sql优化点, 但是面试的时候,无法系统的说明白。 所以在这里简单的总结梳理下。 下图中的知识点我也只是简单的了解的,有错误遗漏之处欢迎指点。

sql server查询语句优化 sql优化查询的方法_mysql

建表注意事项

如果我们建表的时候就把一些问题考虑到, 那么在查询的时候也会大大提高性能的。 常见的注意点如下。

把常用字段提取出来建表

比如 有学生表(学号, 姓名,年龄,爱好, 才艺, 格言, 评价, 自评,其他A, 其他B,其他C,其他C,其他C );

可以看到这个表包含很多的字段,但是往往常用的字段就是前面三个字段, 后面的一堆字段平时都不会有查询的情况。

可以把前面的四个字段提取出来建立一个表专门用来查询, 后面的字段在另外一张表上用学号来关联。

把Text等超长字段单独存放到冗余表中

这个其实和上面的思想一致, 因为Text超长字段查询会大大的拖慢速度, 把其单独的提取出去,只在需要的时候查询。

与其他表关联的外键建立索引

建立索引了后, 联表查询速度很快

适当的冗余字段

比如成绩表如果只是关联学生表的学号, 那么在查看成绩单的时候,还需要联表查询下学生的姓名, 我们可以把学生姓名冗余到成绩表中, 这样避免了一次联表的操作。

但是注意冗余了字段,就意味着你需要维护多处地方, 如果学生的姓名修改了, 你除了需要修改学生表姓名, 还需要修改成绩表中的姓名。 所以冗余的字段最好是不会改变的字段。

选择合适的字段类型

  • 时间使用timestamp类型,
  • ip地址不要使用字符串, 可以使用int类型
  • 年龄字段不要使用bigint

建立合理的索引

建立索引是一种性价比最高的一种优化手段。 但是索引建立也需要注意一些事项

值差异大的字段适合建索引

像性别字段,只有两个值的情况,建立索引没有意义。 但是像身份证号每个人都不一样的字段很适合建立索引, 如果确定字段的值不会重复的情况, 最好建立唯一索引。

查询频繁的字段建立索引

比如经常作为查询条件的字段, 建立索引的性价比高。 如果把索引建在一个不会常用作查询条件的字段上, 好久都不会查一次, 反而维护索引还消耗数据库性能。

使用覆盖索引避免回表

如果一个查询在索引中就能够找到其所有的需要的字段值, 就没有必要进入表中查询了。 我们可以使用这个特性, 建立联合索引,把经常查询的字段都放入索引中。

但是需要注意联合索引中字段的顺序。

不要建立过多的索引

索引可以极大提高查询的效率, 但是对于写入是不友好的, 每次写入都要重新维护下索引。 所以建立了过多的索引不进占用大量空间还会导致写入变慢

使用Explain查看下Sql的执行计划

我们可以使用explain 来查看下sql的执行计划,然后合理的调整sql语句, 避免写出影响性能的sql。

sql server查询语句优化 sql优化查询的方法_mysql_02

  • select_type
    查询的类型, 是指简单查询还是子查询等
  • table
    查询使用到的表
  • type 这次查询的类型。 注意和上面的select_type不一样

all :表示全表扫描, 查询没有用到索引, 是最坏的情况
ref :索引命中记录了, 希望碰到的情况
index: 扫描全部索引, 比全表扫描好一点
ref_eq : 索引命中,并且结果只有一个, 常见于唯一索引的等于查询
const : 主键等于查询, 直接转换成常量, 最快。

  • possible_keys: 可能会用到的索引。
    列在这个字段上的索引可能会用到, 给你参考的
  • key 真正实际使用到的索引
  • key_len 真正用到的索引的长度,太长的索引并不好
  • rows: 本次查询扫描的行数, 数字并不是精确的, 扫描的函数越少越快
  • filtered: 符合条件的记录数占检索记录数的百分比

我们在每次写sql语句的时候还是提倡使用这个来看下sql的执行计划。

sql语句的注意事项

在写sql的时候避免一些坑也能提高查询效率。

避免使用子查询

我们可以使用联表查询来代替

大型表的分页查询limit 需要优化

语句 select * from table limti 100w, 10 在大表中执行是很慢的。 如果我们改成下面的形式

select id from table limit 100w, 1
select * from table  where  id > ${id}  limit 0 ,10

这样就把一个全表扫描的操作 拆成了根据主键id来查询的两步操作, 我在自己的项目中试过, 在900w的表中, 前者耗时29s左右, 后者优化好耗时在2.3s 。

避免类型转换 无论是隐式的还是显式的

select * from table where phone = 18788888888

在上面的语句中,phone 手机号是一个字符串,但是我的查询条件传入的却是一个数字, 这条语句,在查询的时候mysql会把phone转换成数字后在比价。

由于在phone中加上了隐式的转换类型的函数, 这个语句就不会使用phone上的索引了。

当然显式的使用sub函数等等的mysql函数, 都会使得索引失效, 所以我们可以在调用sql之前通过代码把类型转换正确。

避免字符串的编码转换

比如我们联表查询的时候,连接的两张表的编码不一致, 作为on条件的连接字段就不会走索引了

使用预编译查询

一个sql语句的执行流程大致如下。

sql server查询语句优化 sql优化查询的方法_mysql_03

图来自:

一条sql语句需要经历词法分析预发分析,调用计划等等编译操作,如果我们使用预编译sql, 那么在后面的查询中可以省略掉这些编译过程, 极大提高sql的执行效率。

避免使用having

having是需要把查询结果查出来后在进行计算条件的, 效率低下。 完全可以在where中就把条件过滤掉

使用union all 代替 union

对于不会重复的记录或者重复也没有关系的记录直接使用union all 。
union会去重复记录, 去重耗时较长

必要的情况才使用事务

事务是一个很重要的功能,但是我们的日常使用中,有很多的场景完全可以不用事务。

使用合理框架和设置缓存等其他手段

使用连接池

应用中建立数据库连接是一个很耗时间的操作, 所以使用连接池可以提高连接使用率。

  • 连接池设置合理的最大和空闲连接数
  • 设置合理的连接存活时间
  • 监控慢sql语句,推荐使用druid数据连接池

使用缓存

mybatis 可以使用一级二级缓存

mysql设置合理的参数

  • innodb_buffer_pool_size 的设置
    对于写多读少的场景, 可以设置的大点。
    读多写少的场景,要设置小点。
  • 其他的buffer的设置, mysql有很多的缓存等参数的设置, 设置合理也会提高性能

这部分的内容我了解的不太清楚的。 所以简单说下