SQL性能问题已经逐步发展成为数据库性能的首要问题,80%的数据库性能问题都是因SQL而导致

1.1 基数(CARDINALITY)

某个列唯一键(Distinct_Keys)的数量叫作基数。比如性别列,该列只有男女之分,所以这一列基数是2。主键列的基数等于表的总行数。基数的高低影响列的数据分布。

以测试表test为例,owner列和object_id列的基数分别如下所示。

 SQL> select count(distinct owner),count(distinct object_id),count(*) from test;
COUNT(DISTINCTOWNER) COUNT(DISTINCTOBJECT_ID) COUNT(*)
-------------------- ------------------------ ----------

TEST表的总行数为72 462,owner列的基数为29,说明owner列里面有大量重复值,object_id列的基数等于总行数,说明object_id列没有重复值,相当于主键。owner列的数据分布如下。

 SQL> select owner,count(*) from test group by owner order by  desc;
OWNER COUNT(*)
-------------------- ----------
SYS
PUBLIC
SYSMAN
ORDSYS
APEX_030200
MDSYS
XDB
OLAPSYS
SYSTEM
CTXSYS
WMSYS
EXFSYS
SH
ORDDATA
OE
DBSNMP
IX
HR
PM
FLOWS_FILES
OWBSYS_AUDIT
ORDPLUGINS
OUTLN
BI
SI_INFORMTN_SCHEMA
ORACLE_OCM
SCOTT
APPQOSSYS
OWBSYS

owner列的数据分布极不均衡,我们运行如下SQL。

select * from test where owner='SYS';

SYS有30 808条数据,从72 462条数据里面查询30 808条数据,也就是说要返回表中42.5%的数据。

SQL> select /* "Percent" from dual;
Percent
----------
42.5160774

那么请思考,你认为以上查询应该使用索引吗?现在我们换一种查询语句。

select * from test where owner='SCOTT';

SCOTT有7条数据,从72 462条数据里面查询7条数据,也就是说要返回表中0.009%的数据。

select /* "Percent" from dual;
Percent
----------
.

请思考,返回表中0.009%的数据应不应该走索引?

如果你还不懂索引,没关系,后面的章节我们会详细介绍。如果你回答不了上面的问题,我们先提醒一下。当查询结果是返回表中5%以内的数据时,应该走索引;当查询结果返回的是超过表中5%的数据时,应该走全表扫描。

当然了,返回表中5%以内的数据走索引,返回超过5%的数据就使用全表扫描,这个结论太绝对了,因为你还没掌握后面章节的知识,这里暂且记住5%这个界限就行。我们之所以在这里讲5%,是怕一些初学者不知道上面问题的答案而纠结。

现在有如下查询语句。

select * from test where owner=:B1;

语句中,“:B1”是绑定变量,可以传入任意值,该查询可能走索引也可能走全表扫描。

现在得到一个结论:如果某个列基数很低,该列数据分布就会非常不均衡,由于该列数据分布不均衡,会导致SQL查询可能走索引,也可能走全表扫描。在做SQL优化的时候,如果怀疑列数据分布不均衡,我们可以使用select列,count(*) from表group by列order by 2 desc来查看列的数据分布。

如果SQL语句是单表访问,那么可能走索引,可能走全表扫描,也可能走物化视图扫描。在不考虑有物化视图的情况下,单表访问要么走索引,要么走全表扫描。现在,回忆一下走索引的条件:返回表中5%以内的数据走索引,超过5%的时候走全表扫描。相信大家读到这里,已经搞懂了单表访问的优化方法。

我们来看如下查询。

select * from test where object_id=:B1;

不管object_id传入任何值,都应该走索引。

我们再思考如下查询语句。

select * from test where object_name=:B1;

不管给object_name传入任何值,请问该查询应该走索引吗?

请你去查看object_name的数据分布。写到这里,其实有点想把本节名称改为“数据分布”。大家在以后的工作中一定要注意列的数据分布!

1.2 选择性(SELECTIVITY)

基数与总行数的比值再乘以100%就是某个列的选择性。

在进行SQL优化的时候,单独看列的基数是没有意义的,基数必须对比总行数才有实际意义,正是因为这个原因,我们才引出了选择性这个概念。

下面我们查看test表各个列的基数与选择性,为了查看选择性,必须先收集统计信息。关于统计信息,我们在第2章会详细介绍。下面的脚本用于收集test表的统计信息。

SQL> BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'SCOTT',
tabname => 'TEST',
estimate_percent => ,
method_opt => 'for all columns size 1',
no_invalidate => FALSE,
degree => ,
cascade => TRUE);
END;
/
PL/SQL procedure successfully completed.

下面的脚本用于查看test表中每个列的基数与选择性。

SQL> select a.column_name,
b.num_rows,
a.num_distinct Cardinality,
round(a.num_distinct / b.num_rows * , ) selectivity,
a.histogram,
a.num_buckets
from dba_tab_col_statistics a, dba_tables b
where a.owner = b.owner
and a.table_name = b.table_name
and a.owner = 'SCOTT'
and a.table_name = 'TEST';
12COLUMN_NAME NUM_ROWS CARDINALITY SELECTIVITY HISTOGRAM NUM_BUCKETS
--------------- ---------- ----------- ----------- --------- -----------
14OWNER . NONE
15OBJECT_NAME 61.05 NONE
16SUBOBJECT_NAME . NONE
17OBJECT_ID NONE
18DATA_OBJECT_ID 10.5 NONE
19OBJECT_TYPE . NONE
20CREATED 1.89 NONE
21LAST_DDL_TIME 1.95 NONE
22TIMESTAMP 2.04 NONE
23STATUS NONE
24TEMPORARY NONE
25GENERATED NONE
26SECONDARY NONE
27NAMESPACE . NONE
28EDITION_NAME NONE
rows selected.

请思考:什么样的列必须建立索引呢?

有人说基数高的列,有人说在where条件中的列。这些答案并不完美。基数高究竟是多高?没有和总行数对比,始终不知道有多高。比如某个列的基数有几万行,但是总行数有几十亿行,那么这个列的基数还高吗?这就是要引出选择性的根本原因。

当一个列选择性大于20%,说明该列的数据分布就比较均衡了。测试表test中object_name、object_id的选择性均大于20%,其中object_name列的选择性为61.05%。现在我们查看该列数据分布(为了方便展示,只输出前10行数据的分布情况)。

SQL> select *
from (select object_name, count(*)
from test
group by object_name
order by desc)
where rownum <= ;
7OBJECT_NAME COUNT(*)
------------------ ----------
9COSTS
10SALES
11SALES_CHANNEL_BIX
12COSTS_TIME_BIX
13COSTS_PROD_BIX
14SALES_TIME_BIX
15SALES_PROMO_BIX
16SALES_PROD_BIX
17SALES_CUST_BIX
18DBMS_REPCAT_AUTH
rows selected.

由上面的查询结果我们可知,object_name列的数据分布非常均衡。我们查询以下SQL。

select * from test where object_name=:B1;

不管object_name传入任何值,最多返回30行数据。

什么样的列必须要创建索引呢?当一个列出现在where条件中,该列没有创建索引并且选择性大于20%,那么该列就必须创建索引,从而提升SQL查询性能。当然了,如果表只有几百条数据,那我们就不用创建索引了。

下面抛出SQL优化核心思想第一个观点:只有大表才会产生性能问题。

也许有人会说:“我有个表很小,只有几百条,但是该表经常进行DML,会产生热点块,也会出性能问题。”对此我们并不想过多地讨论此问题,这属于应用程序设计问题,不属于SQL优化的范畴。

下面我们将通过实验为大家分享本文第一个全自动优化脚本。

抓出必须创建索引的列(请读者对该脚本适当修改,以便用于生产环境)。

首先,该列必须出现在where条件中,怎么抓出表的哪个列出现在where条件中呢?有两种方法,一种是可以通过V$SQL_PLAN抓取,另一种是通过下面的脚本抓取。

先执行下面的存储过程,刷新数据库监控信息。

begin
dbms_stats.flush_database_monitoring_info;
end;

运行完上面的命令之后,再运行下面的查询语句就可以查询出哪个表的哪个列出现在where条件中。

1select r.name owner,
o.name table_name,
c.name column_name,
equality_preds, ---等值过滤
equijoin_preds, ---等值JOIN 比如where a.id=b.id
nonequijoin_preds, ----不等JOIN
range_preds, ----范围过滤次数 > >= < <= between and
like_preds, ----LIKE过滤
null_preds, ----NULL 过滤
timestamp
from sys.col_usage$ u, sys.obj$ o, sys.col$ c, sys.user$ r
where o.obj# = u.obj#
and c.obj# = u.obj#
and c.col# = u.intcol#
and r.name = 'SCOTT'
and o.name = 'TEST';

下面是实验步骤。

我们首先运行一个查询语句,让owner与object_id列出现在where条件中。

1SQL> select object_id, owner, object_type
from test
where owner = 'SYS'
and object_id <
and rownum <= ;
OBJECT_ID OWNER OBJECT_TYPE
---------- -------------------- -----------
SYS TABLE
SYS INDEX
SYS TABLE
SYS TABLE
SYS CLUSTER
SYS INDEX
SYS TABLE
SYS INDEX
SYS INDEX
SYS INDEX
rows selected.

其次刷新数据库监控信息。

1SQL> begin
dbms_stats.flush_database_monitoring_info;
end;
/
5PL/SQL procedure successfully completed.

然后我们查看test表有哪些列出现在where条件中。

 1SQL> select r.name owner, o.name table_name, c.name column_name
from sys.col_usage$ u, sys.obj$ o, sys.col$ c, sys.user$ r
where o.obj# = u.obj#
and c.obj# = u.obj#
and c.col# = u.intcol#
and r.name = 'SCOTT'
and o.name = 'TEST';
8OWNER TABLE_NAME COLUMN_NAME
---------- ---------- ------------------------------
10SCOTT TEST OWNER
11SCOTT TEST OBJECT_ID

接下来我们查询出选择性大于等于20%的列。

 1SQL> select a.owner,
a.table_name,
a.column_name,
round(a.num_distinct / b.num_rows * , ) selectivity
from dba_tab_col_statistics a, dba_tables b
where a.owner = b.owner
and a.table_name = b.table_name
and a.owner = 'SCOTT'
and a.table_name = 'TEST'
and a.num_distinct / b.num_rows >= 0.2;
11OWNER TABLE_NAME COLUMN_NAME SELECTIVITY
---------- ---------- ------------- -----------
13SCOTT TEST OBJECT_NAME 61.05
14SCOTT TEST OBJECT_ID

最后,确保这些列没有创建索引。

1SQL> select table_owner, table_name, column_name, index_name
from dba_ind_columns
where table_owner = 'SCOTT'
and table_name = 'TEST';
5未选定行

把上面的脚本组合起来,我们就可以得到全自动的优化脚本了。

1SQL> select owner,
column_name,
num_rows,
Cardinality,
selectivity,
'Need index' as notice
from (select b.owner,
a.column_name,
b.num_rows,
a.num_distinct Cardinality,
round(a.num_distinct / b.num_rows * , ) selectivity
from dba_tab_col_statistics a, dba_tables b
where a.owner = b.owner
and a.table_name = b.table_name
and a.owner = 'SCOTT'
and a.table_name = 'TEST')
where selectivity >=
and column_name not in (select column_name
from dba_ind_columns
where table_owner = 'SCOTT'
and table_name = 'TEST')
and column_name in
(select c.name
from sys.col_usage$ u, sys.obj$ o, sys.col$ c, sys.user$ r
where o.obj# = u.obj#
and c.obj# = u.obj#
and c.col# = u.intcol#
and r.name = 'SCOTT'
and o.name = 'TEST');
30OWNER COLUMN_NAME NUM_ROWS CARDINALITY SELECTIVITY NOTICE
---------- ------------- ---------- ----------- ----------- ----------
32SCOTT OBJECT_ID Need index

读《SQL优化核心思想》:你不知道的优化技巧的更多相关文章

  1. 聊聊sql优化的15个小技巧

    前言 sql优化是一个大家都比较关注的热门话题,无论你在面试,还是工作中,都很有可能会遇到. 如果某天你负责的某个线上接口,出现了性能问题,需要做优化.那么你首先想到的很有可能是优化sql语句,因为它 ...

  2. Hive性能优化【核心思想、运行模式、并行计算】

    一.核心思想 把HQL当做MapReduce程序去优化. 注意,以下SQL不会转为MapReduce执行: 1.select仅查询本表字段. 2.where仅对本表字段做条件过滤. 二.启动Hive ...

  3. SQL Server 聚合函数算法优化技巧

    Sql server聚合函数在实际工作中应对各种需求使用的还是很广泛的,对于聚合函数的优化自然也就成为了一个重点,一个程序优化的好不好直接决定了这个程序的声明周期.Sql server聚合函数对一组值 ...

  4. SQL CASE WHEN语句性能优化

    背景:性能应该是功能的一个重要参考,特别是在大数据的背景之下!写SQL语句时如果仅考虑业务逻辑,而不去考虑语句效率问题,有可能导致严重的效率问题,导致功能不可用或者资源消耗过大.其中的一种情况是,处理 ...

  5. 「MySQL高级篇」explain分析SQL,索引失效&&常见优化场景

    大家好,我是melo,一名大三后台练习生 专栏回顾 索引的原理&&设计原则 欢迎关注本专栏:MySQL高级篇 本篇速览 在我们上一篇文章中,讲到了索引的原理&&设计原则 ...

  6. dede内链怎么优化,Dedecms内部链接优化技巧

    dede内链怎么优化,dedecms内部链接优化技巧 使用dedecms的过程中发现,可以通过dedecms的文档关键词维护功能.发表文章时候的关键词添加功能(也可以自动获取)以及核心设置里面的是否使 ...

  7. 对sql server查询速度的优化

    处理百万级以上的数据提高查询速度的方法: 1.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描. 2.对查询进行优化,应尽量避免全表扫描,首先应考 ...

  8. kylin优化的思想

    kylin的核心优势在于使用额外的空间存储预计算的结果,以换取查询时间的缩减. 所以我们要对额外的空间进行优化,并且在空间优化之后,cuboid无法完全命中时,对查询时间进行优化. 空间优化的思路就是 ...

  9. Sql Server性能排查和优化懒人攻略

    转载自作者zhang502219048的微信公众号[SQL数据库编程]:Sql Server性能排查和优化懒人攻略 很多年前,笔者那时刚从广东技术师范学院(现为广东技术师范大学,以前为广东民族学院)的 ...

随机推荐

  1. python基础(三)——类的研究

    class Employee: //定义类 以冒号结束 '所有员工的基类' //帮助信息 empCount = 0 def __init__(self, name, salary): //调用时初始化 ...

  2. Properties 类的使用

    定义: 表示一个持久的集合,可以存在流中,或者从流中加载.是Hashtable子类,map集合方法都可以用. 方法的使用: /* * 集合对象 properties继承Hashtable实现了Map接 ...

  3. 深入详解美团点评CAT跨语言服务监控(三)CAT客户端原理

    cat客户端部分核心类 message目录下面有消息相关的部分接口 internal目录包含主要的CAT客户端内部实现类: io目录包含建立服务端连接.重连.消息队列监听.上报等io实现类: spi目 ...

  4. [转]Serializable接口与Externalizable接口区别

    被Serializable接口声明的类的对象的内容都将被序列化,如果现在用户希望自己指定序列化的内容,则可以让一个类实现Externalizable接口,此接口定义如下: public interfa ...

  5. hadoop 常见 命令

    一  hadoop namenode 命令 1 格式化namanode 磁盘  hadoop namenode -format 二  hadoop fs 命令     和 linux  命令 非常类似 ...

  6. Laravel $request添加数据或数据修改

    laravel开发项目的时候,很多时候我们从$request里获取请求参数,今天在做项目的时候,遇到这样一个需求,就是请求参数到后台,后台需要根据判断重新给$request赋值,那如何去处理的 使用  ...

  7. Day 02 编程语言介绍及运行python

    一.编程语言介绍 1.1.机器语言:直接用计算机能理解的二进制指令编写程序,直接控制硬件. 1.2.汇编语言:用英文标签取代二进制指令编写程序,本质也是在直接控制硬件. 1.3.高级语言:用人能理解的 ...

  8. Zookeeper 四字命令 Four Letter Words

    1.zk可以通过它自身提供的简写命令来服务器进行交互 需要使用到nc命令,安装yum install nc echo  [commond] | nc [ip] [port] 2.[stat]  查看z ...

  9. ASP.NET AJAX入门系列(11):在多个UpdatePanle中使用Timer控件

    本文将使用Timer控件更新两个UpdatePanel控件,Timer控件将放在UpdatePanel控件的外面,并将它配置为UpdatePanel的触发器,翻译自官方文档. 主要内容 在多个Upda ...

  10. Spring Cloud Eureka 服务发现与消费

    服务发现与消费,其服务发现的任务是由Eureka的客户端完成,而服务的消费任务由Ribbon.JerseyClient等完成,Ribbon是一个基于HTTP和TCP的客户端负载均衡器:使用Jersey ...