SQL 语句执行感觉很慢,怎么回事?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL 语句执行感觉很慢,怎么回事?相关的知识,希望对你有一定的参考价值。

我的一个数据库,共 1107566 条记录,不管是更新,还是执行存储过程,都是很慢,有办法解决吗?
更新全部记录的相关字段值!

到这个数量级的全部更新,肯定会很慢。
第一。你的记录不一定在同一个partition,
第二。不明白为什么那么多人建议你建索引,你建的索引越多,你的更新速度越慢,因为你更新记录的同时,还有更新索引。
第三。你必须知道更新速度慢的瓶颈在哪里。是读写太多,还是内存不够,还是CUP不够快,然后对症下药。

下面介绍两个简单的办法,也许有效:
第一:
把这个100W行的表纵向劈成两个,用外键关系连接,一个装小的,经常改变的数据比如ID,外键,状态值,时间等,另一个装大的,不经常改变的数据,比如很长的字符串,xml,text 等。
这样更新时操作小的这个表,可以大大节约内存和CPU 开销,降低磁盘操作。
坏处就是查询时会慢些。
第二:
把这100W行横向切成很多个表,比如每个月的记录装在一个表里,这样每个表的记录数可能只有几万,查询,更新都会快很多。
坏处是查询,更新都不如原来好写。
参考技术A 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 查询的优化,添加更合适的索引可以避免性能问题:执行计划使用索引并不意味着就能执行快。
参考技术B 100万条数据啊,数据量不少了,你可要建立一个合理的索引,这样可以大大提高速度,而你说的执行慢,存储过程不好说,不知道你写的是否合理,不合理的代码和合理的代码查询速度是相差很远的,从几分钟甚至更久与十几妙钟的差别。

如果你确定你的语法没法再优化,数据库索引已经是最佳的话,你可以考虑将此数据表老数据,已经很少用到的数据备份到其他表去,如果要查询历史数据,要么在允许查询要么你就通过程序处理去查询这个备份的数据表。
参考技术C 需要建立索引。更新也是先查询再更新,所以要先提高查询效率!!!

全表扫描? 看来你得升级硬件了,或者优化调整oracle系统的参数设置了。

还有一个办法,就是把大表分解成多个小表。
参考技术D 2个办法
1)建立合适的索引可以适当提高效率,这个是最主要的。

2) 把你的sql写漂亮点,少用点in exists之类的,

一条sql执行很慢可能的原因,如何优化

文章目录

sql怎么会变慢呢?

1、大多数情况下很正常,偶尔很慢,则有如下原因

(0)、 表数据量太大了

(1)、数据库在刷新脏页,例如 redo log 写满了需要同步到磁盘。

(2)、执行的时候,遇到锁,如表锁、行锁。

以上是关于SQL 语句执行感觉很慢,怎么回事?的主要内容,如果未能解决你的问题,请参考以下文章

SQL 语句执行很慢是怎么回事?

插入的SQL语句中有中文值时中文变问号是怎么回事?

sql2005数据库只能执行查询语句不能执行删除语句是怎么回事啊,请问有没有遇到这个问题的朋友,执行delete

一条sql执行很慢可能的原因,如何优化

sql server 用INSERT语句添加数据时 语句中若有中文或英文字母 执行时就报错 是怎么回事?

SQL Server数据库更新语句突然变得很慢