COUNT(*)计算行数有哪些优化手段
--无索引
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
SET AUTOTRACE TRACEONLY
SET LINESIZE 1000
SET TIMING ON
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
--普通索引
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
CREATE INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(OBJECT_NAME);
SET AUTOTRACE TRACEONLY
SET TIMING ON
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
--唯一索引
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
UPDATE T_20170704_COUNT_LHR_01 T SET T.OBJECT_NAME=T.OBJECT_NAME||ROWNUM;
CREATE unique INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(OBJECT_NAME);
SET AUTOTRACE TRACEONLY
SET TIMING ON
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
--常数索引
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
CREATE INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(0);
SET AUTOTRACE TRACEONLY
SET TIMING ON
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
--常数压缩索引
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
CREATE INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(0) COMPRESS;
SET AUTOTRACE TRACEONLY
SET TIMING ON
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
--位图索引
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
UPDATE T_20170704_COUNT_LHR_01 SET OBJECT_NAME='lhr';
UPDATE T_20170704_COUNT_LHR_01 SET OBJECT_NAME='ldd' WHERE ROWNUM<=20000;
CREATE BITMAP INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(OBJECT_NAME);
SET AUTOTRACE TRACEONLY
SET TIMING ON
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
ALTER INDEX IDX_OBJECT_NAME PARALLEL 8;
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
-- 物化视图
DROP MATERIALIZED VIEW MV_COUNT_T_LHR;
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
UPDATE T_20170704_COUNT_LHR_01 SET OBJECT_NAME='lhr';
UPDATE T_20170704_COUNT_LHR_01 SET OBJECT_NAME='ldd' WHERE ROWNUM<=20000;
CREATE MATERIALIZED VIEW MV_COUNT_T_LHR
BUILD IMMEDIATE
REFRESH ON COMMIT
ENABLE QUERY REWRITE
AS
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
SET AUTOTRACE TRACEONLY
SET LINESIZE 1000
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
--缓存结果集
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
SET LINESIZE 1000
SET AUTOTRACE TRACEONLY
SELECT /*+ RESULT_CACHE */ COUNT(*) FROM T_20170704_COUNT_LHR_01;
--业务分析
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01 WHERE ROWNUM=1;
一、普通表(无索引)
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
COUNT(*)
----------
79300
LHR@orclasm > SET AUTOTRACE TRACEONLY
LHR@orclasm > SET LINESIZE 1000
LHR@orclasm > SET TIMING ON
LHR@orclasm > LHR@orclasm > LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
Elapsed: 00:00:00.01
Execution Plan
----------------------------------------------------------
Plan hash value: 1395805058
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 317 (1)| 00:00:04 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| T_20170704_COUNT_LHR_01 | 91429 | 317 (1)| 00:00:04 |
--------------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1139 consistent gets
0 physical reads
0 redo size
527 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
二、普通索引
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
CREATE INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(OBJECT_NAME);
SET AUTOTRACE TRACEONLY
SET TIMING ON
LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
Elapsed: 00:00:00.20
Execution Plan
----------------------------------------------------------
Plan hash value: 1178070731
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 114 (1)| 00:00:02 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| IDX_OBJECT_NAME | 91429 | 114 (1)| 00:00:02 |
---------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
400 consistent gets
0 physical reads
0 redo size
527 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
1、主键索引(唯一索引)
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
CREATE UNIQUE INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(OBJECT_NAME);
SET AUTOTRACE TRACEONLY
SET TIMING ON
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
2、常数索引
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
CREATE INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(0);
SET AUTOTRACE TRACEONLY
SET TIMING ON
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
Elapsed: 00:00:00.01
Execution Plan
----------------------------------------------------------
Plan hash value: 1178070731
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 45 (3)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| IDX_OBJECT_NAME | 86597 | 45 (3)| 00:00:01 |
---------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
151 consistent gets
0 physical reads
0 redo size
528 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
3、常数压缩索引
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
ALTER TABLE T_20170704_COUNT_LHR_01 MODIFY OBJECT_NAME NOT NULL;
CREATE INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(0) COMPRESS;
SET AUTOTRACE TRACEONLY
SET TIMING ON
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
LHR@orclasm > LHR@orclasm > LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
Elapsed: 00:00:00.00
Execution Plan
----------------------------------------------------------
Plan hash value: 1178070731
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 38 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| IDX_OBJECT_NAME | 86597 | 38 (0)| 00:00:01 |
---------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
129 consistent gets
0 physical reads
0 redo size
528 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
三、位图索引
试验如下:
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
UPDATE T_20170704_COUNT_LHR_01 SET OBJECT_NAME='lhr';
UPDATE T_20170704_COUNT_LHR_01 SET OBJECT_NAME='ldd' WHERE ROWNUM<=20000;
CREATE BITMAP INDEX IDX_OBJECT_NAME ON T_20170704_COUNT_LHR_01(OBJECT_NAME);
SET AUTOTRACE TRACEONLY
SET TIMING ON
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
Elapsed: 00:00:00.00
Execution Plan
----------------------------------------------------------
Plan hash value: 1696023018
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 5 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | BITMAP CONVERSION COUNT | | 91429 | 5 (0)| 00:00:01 |
| 3 | BITMAP INDEX FAST FULL SCAN| IDX_OBJECT_NAME | | | |
-----------------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
5 consistent gets
0 physical reads
0 redo size
527 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
1、位图索引+并行
ALTER INDEX IDX_OBJECT_NAME PARALLEL 8;
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
并行技术可以加快执行速度,但一致性读有所增加,但并行还是能加快整体运行速度。
四、物化视图
这主要是应用在数据库更新不是非常频繁场景,用的是空间换时间。
DROP MATERIALIZED VIEW MV_COUNT_T_LHR;
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
UPDATE T_20170704_COUNT_LHR_01 SET OBJECT_NAME='lhr';
UPDATE T_20170704_COUNT_LHR_01 SET OBJECT_NAME='ldd' WHERE ROWNUM<=20000;
CREATE MATERIALIZED VIEW MV_COUNT_T_LHR
BUILD IMMEDIATE
REFRESH ON COMMIT
ENABLE QUERY REWRITE
AS
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
SET AUTOTRACE TRACEONLY
SET LINESIZE 1000
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
LHR@orclasm > SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
Elapsed: 00:00:00.08
Execution Plan
----------------------------------------------------------
Plan hash value: 571421573
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 3 (0)| 00:00:01 |
| 1 | MAT_VIEW REWRITE ACCESS FULL| MV_COUNT_T_LHR | 1 | 13 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
531 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
五、缓存结果
在Oracle 11g中提供了结果集缓存特性。该缓存是在共享内存中存储全部的结果集。如果一个查询SQL被执行,且它对应的结果集在缓存中,那么,该SQL的几乎全部开销都可以避免。
DROP TABLE T_20170704_COUNT_LHR_01 PURGE;
CREATE TABLE T_20170704_COUNT_LHR_01 AS SELECT * FROM DBA_OBJECTS;
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01;
SET LINESIZE 1000
SET AUTOTRACE TRACEONLY
SELECT /*+ RESULT_CACHE */ COUNT(*) FROM T_20170704_COUNT_LHR_01;
LHR@orclasm > LHR@orclasm > SELECT /*+ RESULT_CACHE */ COUNT(*) FROM T_20170704_COUNT_LHR_01;
Elapsed: 00:00:00.00
Execution Plan
----------------------------------------------------------
Plan hash value: 1395805058
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 317 (1)| 00:00:04 |
| 1 | RESULT CACHE | 6pp2f468gdjnj9v3s3mfwffd7t | | | |
| 2 | SORT AGGREGATE | | 1 | | |
| 3 | TABLE ACCESS FULL| T_20170704_COUNT_LHR_01 | 86597 | 317 (1)| 00:00:04 |
------------------------------------------------------------------------------------------
Result Cache Information (identified by operation id):
------------------------------------------------------
1 - column-count=1; dependencies=(LHR.T_20170704_COUNT_LHR_01); attributes=(single-row); name="SELECT /*+ RESULT_CACHE */ COUNT(*) FROM T_20170704_COUNT_LHR_01"
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
0 consistent gets
0 physical reads
0 redo size
528 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
六、根据业务规则判断
若统计行数只是为了判断表中是否有记录,则可以使用ROWNUM=1,所以改写后的SQL变为:
SELECT COUNT(*) FROM T_20170704_COUNT_LHR_01 WHERE ROWNUM=1;
该SQL无论表中数据多大,性能都不会太差。
七、分析需求
仔细分析需求后,可能会发现,统计行数这条SQL根本就是多余的,那么这条SQL语句就可以直接砍掉了。
八、总结
手段 |
命令 |
执行计划 |
主要原理 |
详细说明 |
性能情况 |
全表扫描 |
|
TABLE ACCESS FULL |
全表扫描 |
OLTP中,通常是最慢的方式。 |
逻辑读为1139 |
增加普通索引 |
CREATE INDEX IDX_OBJECT_NAME ON T(OBJECT_NAME); |
INDEX FAST FULL SCAN |
从全表扫描转成全索引扫描。 |
因为索引一般比表小的多,所以全表扫描转成全索引扫描,性能能大幅度提升。 |
逻辑读为400 |
常数索引 |
CREATE INDEX IDX_OBJECT_NAME ON T(0); |
INDEX FAST FULL SCAN |
从全表扫描转成全索引扫描。 |
常数索引比普通索引更小。 |
逻辑读为151 |
常数压缩索引 |
CREATE INDEX IDX_OBJECT_NAME ON T(0) COMPRESS; |
INDEX FAST FULL SCAN |
从全表扫描转成全索引扫描。 |
常数压缩索引比常数索引更小。 |
逻辑读为129 |
位图索引 |
CREATE BITMAP INDEX IDX_OBJECT_NAME ON T(OBJECT_NAME); |
BITMAP INDEX FAST FULL SCAN |
从BTREE索引扫描转成位图索引扫描。 |
位图索引的大小比BTREE索引要小的多,所以位图索引扫描快。 |
逻辑读为5 |
物化视图 |
CREATE MATERIALIZED VIEW MV_COUNT_T BUILD IMMEDIATE REFRESH ON COMMIT ENABLE QUERY REWRITE AS SELECT COUNT(*) FROM T; |
MAT_VIEW REWRITE ACCESS FULL |
空间换时间。 |
要注意,如果数据要求比较实时,就不适用。 |
逻辑读为3 |
缓存结果 |
SELECT /*+ RESULT_CACHE */ COUNT(*) FROM T; |
RESULT CACHE |
直接把查询结果拿来用。 |
要注意,如果数据频繁更新,就不适用。 |
逻辑读为0 |
业务理解 |
SELECT COUNT(*) FROM T WHERE ROWNUM=1; |
|
如果COUNT(*)只是为了判断条数,就加上ROWNUM=1来判断是否为1。 |
业务需求转换,获取条数有的时候,只是为了看看表是否为空,这时候是否是1条和是否大于0其实是一样的。 |
不言而喻 |
分析需求 |
据说,这个COUNT(*)统计条数语句,是多余的!直接砍了这条语句,这里没有SQL! |
无敌! |
位图索引可以按很高密度存储数据,因此往往比B树索引小很多,前提是在基数比较小(列重复度比较高)的情况下。位图索引是保存空值的,因此可以在COUNT中利用。位图索引不太适合OLTP类型数据库。物化视图是应用在数据要求不怎么及时的场景下。若表频繁更新,则不适合缓存结果集。
优化没有止境,对数据库了解越多,能想到的方法就越多。
你真的会用索引吗?来看看COUNT(*)到底能有多快
编辑手记:韩老师在数据库性能优化方面有很丰富的经验,出版书籍《SQL 优化最佳实践》,感谢韩老师和机械工业出版社的授权,我们会在接下来的每周二分享书中的经典篇章,与大家共同成长。
案例说明
一个大表的COUNT,究竟能有多快?除类似物化视图的做法,我们所能做到的极限能有多快?这不是一个真实的案例,而是根据笔者在网上发的一篇帖子整理而来。通过对一条SQL,采用多种方式持续优化过程,表明SQL优化的手段随着优化者掌握的技能增多,其可能存在的手段也在不断增多。
1、数据准备
2、全表扫描
全表扫描的代码如下(共用124秒,好慢呀):
由上可知,全表扫描耗时较长。
3、主键索引
主键索引的代码如下:
通过引入索引,执行计划变成索引快速全扫描,因扫描块数较少,因此耗时也大大减少,共用33秒,快多了。
4、常数索引
常数索引的代码如下:
常数索引在存储密度上要高于普通字段索引,因此扫描块数更少,耗时也更少,共耗时29秒。
5、常数压缩索引
常数压缩索引的代码如下
索引压缩进一步减少了扫描规模,耗时缩减到27秒。
6、位图索引
位图索引不同于B树索引,其存储密度更高。这里是采用status字段,如果使用常数索引,其规模将更小。这种手段用时0.9秒,这是质的飞跃。
7、位图索引+并行
并行技术可以较快执行速度。一致性读有所增加,但并行还是能加快整体运行速度,这种手段耗时0.03秒,竟然又快了不少。
结论分析
位图索引可以按很高密度存储数据,因此往往比B树索引小很多。前提是在基数比较小的情况下。
位图索引是保存空值的,因此可以在COUNT中利用。
众所周知,位图索引不太适合OLTP类型数据库。该实例仅为了测试展示
案例启示
优化没有止境,对数据库了解越多,你能想到的方法就越多。
--本文节选自《SQL 优化最佳实践》第一章。
从简单Sql探索优化之道
本文需要优化的语句是select count(*) from t,这简单的统计语句一出,估计不少人纳闷了,能有啥优化空间,还优化之道,什么gui。
哦,其实简单的背后不简单,来,跟作者一起看看如何“不择手段”,让count(*) 飞起来。不过我们用意的关键是让读者去思考,为什么能飞。
为什么能飞?嗯,因为我们掌握了Sql优化之道。那优化之道是什么?不着急,来,随我们来看看Sql飞的过程吧。
一 、优化过程
1. 普通思路
用了啥手段:啥没用!
性能啥情况:逻辑读为1048。
2. 增加索引
用了啥手段:建了一个btree索引。
性能啥情况:逻辑读从1048减少到372。
3. 位图索引
用了啥手段:建了一个位图索引。
性能啥情况:逻辑读从372瞬间缩减为6。
简单地说说位图索引的结构,比如T表有4个字段,分别是ID、NAME、SEX和STATUS,其中SEX取值仅为男或女,有时由于不知道性别,暂时为空,具体如下:
4. 物化视图
用了啥手段:应用了物化视图。
性能啥情况:逻辑读从6缩减为3。
说明:这主要是应用在数据库更新不是非常频繁场景,用的是空间换时间。
5. 缓存结果集
用了啥手段: 利用缓存结果集技术。
性能啥情况:逻辑读从3缩减为0。
在11g中,Oracle提供了结果集缓存特性。该缓存是在共享内存中存储全部的结果集,如果一个查询SQL被执行,且它对应的结果集在缓存中,那么,该SQL的几乎全部开销都可以避免。
6. 业务理解迎来速度之王
用了啥手段: 做了一件很奇怪的事,似乎把Sql改写的看不懂了。
select count(*) from t where rownum=1; |
和下面的逻辑是等价的。
性能啥情况:表不管多大,永远只访问第1条,速度问题还需要纠结吗?
二 、优化总结
三、总结
这本是一个简单的语句,却可以神奇的完成一次又一次优化,性能不断提升,优化过程涵盖了Sql执行计划和索引的理解、根据不同场景选择不同技术、根据业务进行等价改写这三大技巧,可谓非常的经典。简单的背后不简单,充满了人生的智慧,还请多多体会。
哦,还没说,Sql优化之道是什么?嗯,请看下面:
- 优化知识本身+根据场景选择技术+把握业务需求!
作者介绍:梁敬彬
- 【DBA+社群】数据库专家。
- 福富研究院副理事长;福富软件特级专家。
- 公司四星级内训师、ITPUB版主及ITPUB社区专家,十余年数据库设计调优及培训相关经验,多次应邀参加中国数据库技术大会任演讲嘉宾。
- 此外还著有多本畅销数据库技术书籍,其代表作《收获,不止Oracle》极为畅销,上市3个月内就完成第3次印刷。新书《从菜鸟到Sql优化大师》即将上市......
About Me
...............................................................................................................................● 本文作者:小麦苗,只专注于数据库的技术,更注重技术的运用
● 本文在itpub(http://blog.itpub.net/26736162)、博客园(http://www.cnblogs.com/lhrbest)和个人微信公众号(xiaomaimiaolhr)上有同步更新
● 本文itpub地址:http://blog.itpub.net/26736162/abstract/1/
● 本文博客园地址:http://www.cnblogs.com/lhrbest
● 本文pdf版及小麦苗云盘地址:http://blog.itpub.net/26736162/viewspace-1624453/
● 数据库笔试面试题库及解答:http://blog.itpub.net/26736162/viewspace-2134706/
● QQ群:230161599 微信群:私聊
● 联系我请加QQ好友(646634621),注明添加缘由
● 于 2017-07-01 09:00 ~ 2017-07-31 22:00 在魔都完成
● 文章内容来源于小麦苗的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解
● 版权所有,欢迎分享本文,转载请保留出处
...............................................................................................................................
拿起手机使用微信客户端扫描下边的左边图片来关注小麦苗的微信公众号:xiaomaimiaolhr,扫描右边的二维码加入小麦苗的QQ群,学习最实用的数据库技术。