如何解决SQL查询速度太慢?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何解决SQL查询速度太慢?相关的知识,希望对你有一定的参考价值。

我的主机配置是P4 2.8G CPU/512M 内存/160G 硬盘/915P主板,windows 2000专业版操作系统,数据库是MS SQL2000企业版的.
现在有一个数据库,目前总容量在300M左右,日志只有1M,不算很大,其中有一个表hr_worktime,只有简单的9个列,均为varchar 10.该表已经建立了聚集索引,有383000条记录.然而在查询分析器中,运行select * from hr_worktime时,却要耗时54秒,实在是太慢!
各位帮分析会是什么原因!谢谢!
1,谢谢liuzixinr的解答,但我想这种小型的数据库应该用不着如此兴师动众,相对那些G级的,我这根本就不算什么,以前我们公司ERP数据服务器配置比这个还差,数据量要大N多倍,执行类似的语句也不会慢到哪里去,只需要几秒钟就够,而现在我的这个实在是有些离谱.
2,谢谢goodhappyer的意见,如果主要是输出慢的话,我执行update hr_worktime set bh='F'+bh where left(bh,1)='3'时,居然也要17秒,其中列bh已经包含在聚集索引中.
3,感谢caoyangx的意见,的确是有些道理,也有些效果.不过我想问下各位,速度快慢与打没有打SP4补丁有没有关系?

1. 执行计划中明明有使用到索引,为什么执行还是这么慢?

2. 执行计划中显示扫描行数为 644,为什么 slow log 中显示 100 多万行?
a. 我们先看执行计划,选择的索引 “INDX_BIOM_ELOCK_TASK3(TASK_ID)”。结合 sql 来看,因为有 "ORDER BY TASK_ID DESC" 子句,排序通常很慢,如果使用了文件排序性能会更差,优化器选择这个索引避免了排序。
那为什么不选 possible_keys:INDX_BIOM_ELOCK_TASK 呢?原因也很简单,TASK_DATE 字段区分度太低了,走这个索引需要扫描的行数很大,而且还要进行额外的排序,优化器综合判断代价更大,所以就不选这个索引了。不过如果我们强制选择这个索引(用 force index 语法),会看到 SQL 执行速度更快少于 10s,那是因为优化器基于代价的原则并不等价于执行速度的快慢;
b. 再看执行计划中的 type:index,"index" 代表 “全索引扫描”,其实和全表扫描差不多,只是扫描的时候是按照索引次序进行而不是行,主要优点就是避免了排序,但是开销仍然非常大。
Extra:Using where 也意味着扫描完索引后还需要回表进行筛选。一般来说,得保证 type 至少达到 range 级别,最好能达到 ref。
在第 2 点中提到的“慢日志记录Rows_examined: 1161559,看起来是全表扫描”,这里更正为“全索引扫描”,扫描行数确实等于表的行数;
c. 关于执行计划中:“rows:644”,其实这个只是估算值,并不准确,我们分析慢 SQL 时判断准确的扫描行数应该以 slow log 中的 Rows_examined 为准。
4. 优化建议:添加组合索引 IDX_REL_DEVID_TASK_ID(REL_DEVID,TASK_ID)

优化过程:
TASK_DATE 字段存在索引,但是选择度很低,优化器不会走这个索引,建议后续可以删除这个索引:
select count(*),count(distinct TASK_DATE) from T_BIOMA_ELOCK_TASK;+------------+---------------------------+| count(*) | count(distinct TASK_DATE) |+------------+---------------------------+| 1161559 | 223 |+------------+---------------------------+

在这个 sql 中 REL_DEVID 字段从命名上看选择度较高,通过下面 sql 来检验确实如此:
select count(*),count(distinct REL_DEVID) from T_BIOMA_ELOCK_TASK;+----------+---------------------------+| count(*) | count(distinct REL_DEVID) |+----------+---------------------------+| 1161559 | 62235 |+----------+---------------------------+

由于有排序,所以得把 task_id 也加入到新建的索引中,REL_DEVID,task_id 组合选择度 100%:
select count(*),count(distinct REL_DEVID,task_id) from T_BIOMA_ELOCK_TASK;+----------+-----------------------------------+| count(*) | count(distinct REL_DEVID,task_id) |+----------+-----------------------------------+| 1161559 | 1161559 |+----------+-----------------------------------+

在测试环境添加 REL_DEVID,TASK_ID 组合索引,测试 sql 性能:alter table T_BIOMA_ELOCK_TASK add index idx_REL_DEVID_TASK_ID(REL_DEVID,TASK_ID);
添加索引后执行计划:
这里还要注意一点“隐式转换”:REL_DEVID 字段数据类型为 varchar,需要在 sql 中加引号:AND T.REL_DEVID = 000000025xxx >> AND T.REL_DEVID = '000000025xxx'

执行时间从 10s+ 降到 毫秒级别:
1 row in set (0.00 sec)
结论
一个典型的 order by 查询的优化,添加更合适的索引可以避免性能问题:执行计划使用索引并不意味着就能执行快。
参考技术A SQL Server查询速度慢的原因有很多,常见的有以下几种:

1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)

2、I/O吞吐量小,形成了瓶颈效应。

3、没有创建计算列导致查询不优化。

4、内存不足

5、网络速度慢

6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)

7、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)

8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。

9、返回了不必要的行和列

10、查询语句不好,没有优化
参考技术B 对于数据可以参照下面几点
1、优化SQL语句,SQL语句对查询速度影响最大
2、对于经常查询的字段作索引。但是这样会增加修改时的压力
4、优化SQLServer,比如给其分配固定的内存,预先分配查询内存,调整CPU使用率等。
5、优化硬件资源,比如使用更高的服务器或者硬盘,独立安排数据库的数据文件和索引文件,将数据文件分布于不同的物理硬盘上等等
6、考虑使用分布数据库或者对大表进行拆分

另外,2G的数据库应该不算很大了,我处理过18G的数据库,8000万条记录,查询速度可以被接受
参考技术C 把一个表分成几个表,可以按,ID分开,比如1-10000,10000-20000这样分成多个表,当然你可以用其它的方法分开,这样的SELECT的速度会快点,其实你看到的耗时54秒,主要是输出速度太慢了,不是查询慢 参考技术D 建议不要使用select * 这样数据量太大,可以加上select top 1000 * from hr_worktime

最好把一起不用的数据转移到备份库,这里保留需要的最新数据即可。

sql语句多表联查,查询速度太慢,超过10s,由于是菜鸟,不知道怎样优化

SELECT COUNT(DISTINCT sv.id) as count, m.mediatypename, s.name FROM t_project p, shannxi.dbo.bus_line sv, shannxi.dbo.bus_station bs, shannxi.dbo.bus_station_new bsn, shannxi.dbo.t_surround_value_report ss, shannxi.dbo.t_surround_district s, t_mediatype_infor m WHERE p.isdelete=0 AND p.companyid = 2248 AND p.regionid = 354 AND p.dataid = sv.id AND p.mediatypeinfoid=m.id AND p.mediatypeinfoid in ( select id from fn_get_mediatype_infor(5) ) AND sv.busid=bs.bus_id AND bsn.station_code = bs.station_code AND bs.station_name=bsn.station_name AND bsn.kind=bs.kind AND bsn.id = ss.projectid AND ss.datatype=8 AND ss.dataid = s.id GROUP BY m.mediatypename, s.name

确定是菜鸟,sql 写成这样 证明你逻辑很清楚啊,建议如果是初学者,代码规范一定要保持
不知道代码规范,可以去窗口format一下。。。
言归正传 你这个优化的话 可以把
AND p.mediatypeinfoid in (
select
id
from
fn_get_mediatype_infor(5)
)
上面这部分 换成 inner join追问

跟这个关系不大,当初就是为了更好查询才写的这个函数select id from fn_get_mediatype_infor(5)

追答

多表关联禁用 in () 。。。
这个会降低效率,你可以查查执行计划
不是说你的函数的问题 是 in 的问题

参考技术A 问题很可能出在这一句:
AND P.MEDIATYPEINFOID IN (SELECT ID FROM FN_GET_MEDIATYPE_INFOR(5)).
这个FN_GET_MEDIATYPE_INFOR里面的逻辑复杂吗?追问

跟这个没多大关系,主要是这三个二货表bus_line ,bus_station ,bus_station_new ,,公交线路经过的商圈,得通过它的站点来确定,然后又要跟媒体表,商圈表关联,所以很扯淡

追答

就这几张表关联,索引都加好了的话,一般的数据量不可能会这么慢.
这样,你把我说的那个条件注释掉,试一下用多久可以查出来.

追问

去掉一样,应该是那几张表的条件要不停做对比,耗时太久,每个表里面本里就上万条数据,几个表这样对比下来效率自然太慢

以上是关于如何解决SQL查询速度太慢?的主要内容,如果未能解决你的问题,请参考以下文章

如何解决mysql 查询和更新速度慢

如何解决BI系统性能差响应慢的问题

如何解决SQL Server查询速度缓慢的问题

如何解决SQL Server查询速度缓慢的问题

如何解决SQL Server查询速度缓慢的问题

sql语句多表联查,查询速度太慢,超过10s,由于是菜鸟,不知道怎样优化