1.速度慢原因:
- 数据库扫描数据行数过多(和表行数多少有一定关系)
- SQL写法不科学导致的索引失效,出现1.1描述;
2.慢的问题点:
- 使用“*”,数据库高频访问时,数据库需要额外解析“*”,浪费时间;
- 数据库查询字段未建立索引,导致全表数据扫描;
- 字段已建立索引,但SQL写法导致索引无法使用,如下示例:
- 模糊查询:name like ‘%张’ or name like ‘_张’;
- 模糊查询:score != 80;
- 范围查询:name in (‘张三’,‘李四’);
- 索引字段运算:where score+10 = 100;
- 子查询中使用:group by子句
- 联合索引:index(name,score),查询:where score=80;
- 隐式转换:where name=12315;
- Null直接参与比较:where name=null;
3.优化方案:
- 使用明确的列名查询:select u.id,u.name from user u;
- 建立合理的索引,主键约束、外键约束自带索引,优先在主键上查询;
- 替换的SQL写法:
- 模糊查询(like):视情况解决,一般是搜索引擎;
- 模糊查询(!=):视情况解决,可采用排除法;
- 范围查询(in):改用exists;
- 索引字段运算:where score = 100–10;
- 子查询(group by):视情况解决,可考虑临时表;
- 联合索引(name_score):拆分索引或查询name;
- 隐式转换:改为where name=‘12315‘;保持类型一致;
- Null直接参与比较:改为where name is null;
4.特殊说明:
- Group by子句是我们常见的导致效率低下的原因,解决办法视情况不同而不同,下边举一个实际的例子对比下:
- 用户表user:uid主键,name姓名
- 分数表user_score:usid主键,uid用户表外键,item科目,score分数
- 两张表分别是用户表user和用户分数表user_score,现在统计算每个用户的总分。
方法1:
SELECT
u.uid,
u.name,
(SELECT
SUM(score)
FROM
user_score
WHERE uid = u.uid) sumScore
FROM
USER u ;
方法2:
SELECT
u.uid,
u.name,
SUM(us.score) sumScore
FROM
USER u
LEFT JOIN user_score us
ON us.uid = u.uid
GROUP BY u.uid,
u.name ;
方法3:
SELECT
u.uid,
u.name,
s.score sumScore
FROM
USER u
LEFT JOIN
(SELECT
uid,
SUM(score) score
FROM
user_score
GROUP BY uid) s
ON u.uid = s.uid ;
此三种方法,方法1查询速度会明显优于方法2以及方法3。方法2使用了group by语句。方法3使用了group by子句,导致子句查询出来的结果会存入缓存,在缓存中进行查询时left join无法引用索引。
方法4:
SELECT
u.uid,
u.name,
s.sumScore
FROM
(SELECT
us.uid,
SUM(us.score) sumScore
FROM
user_score us
GROUP BY us.uid) s
LEFT JOIN USER u
ON u.uid = s.uid ;
方法4(效率略优于方法3,差异基本可忽略,也是常被遗忘的方法)。
解释说明:
如果有100个学生,200条学分(每人2条):
方法1 数据行扫描数:
最低是:100user+100*1 SUM=200次,
最高是:100user+100*1 SUM=200次;
方法2 数据行扫描数:
最低是:100user+100groupby+100*1 SUM=300次,
最高是:100user+100groupby+100*1 SUM=300次;
方法3 数据行扫描数:
最低是:100user+200groupby+100*1 leftjoin=400次,
最高是:100user+200groupby +100*100 leftjoin=13000次;
有最高和最低是因为可能第一次查询就匹配到,也可能扫描到最后才匹配到数据。
5.索引的建立原则:
- 索引会影响物理表插入和删除效率,并非越多越好,每张表索引尽量不超过3个;
- 尽量不要在长文本字段建立索引,索引字段长度尽量不超过32位;
- 如果已经建立了a_b的联合索引,则无需再在a字段上建立索引;
6.视图创建原则:
- 视图尽量不要嵌套,逻辑视图本身不支持索引,嵌套后查询时间基本上是级数级增长,数据库本身也无法对此种查询做优化;
- 如确实需要嵌套,尽量建立物理视图,但建立物理视图会导致服务器本身压力增大,磁盘消耗增多,物理视图依赖的物理表的插入和删除等效率受到影响;
转载请注明出处:BestEternity亲笔。