热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[oracle性能优化]sql调优基础

不会根据执行计划进行sql调优的dba是不合格的,sqltuning是基本技能0.优化器的基本概念    为sql语句找到最好的,执行成本最低的执行计划    制定执行计划是以sq

不会根据执行计划进行sql调优的dba是不合格的,sql tuning是基本技能


0. 优化器的基本概念

     为sql语句找到最好的,执行成本最低的执行计划
     制定执行计划是以sql语句中涉及到的对象的统计信息为基础的。


1. 统计信息的介绍

    --表的统计信息(user_tables, user_tab_statistics)
   
    select num_rows, --表中的记录数
    blocks, --表中数据所占的数据块数
    empty_blocks, --表中的空块数
    avg_space, --数据块中平均的使用空间
    chain_cnt, --表中行连接和行迁移的数量
    avg_row_len, --每条记录的平均长度
    last_analyzed -- 最近一次搜集统计信息的时间
    from user_tables where table_name='new_sales';
   
    --搜集表的统计信息
    exec dbms_stats.gather_table_stats('sh','new_sales');
   
    --再来执行一次
    select num_rows, --表中的记录数
    blocks, --表中数据所占的数据块数
    empty_blocks, --表中的空块数
    avg_space, --数据块中平均的使用空间
    chain_cnt, --表中行连接和行迁移的数量
    avg_row_len, --每条记录的平均长度
    last_analyzed -- 最近一次搜集统计信息的时间
    from user_tables where table_name='new_sales';
   
   
     --列的统计信息 (user_tab_columns, user_tab_col_statistics,user_tab_histograms)
     select column_name,
     num_distinct, --唯一值的个数
     low_value, --列上的最小值
     high_value, --列上的最大值
     density, --选择率因子(密度) = 1/(ndv),如果不存在柱状图的话
     num_nulls, --空值的个数
     num_buckets, --直方图的bucket个数
     histogram --直方图的类型
     from user_tab_columns
     where table_name='new_sales'
   
     --搜集柱状图
     exec dbms_stats.gather_table_stats('sh', 'new_sales', method_opt => 'for all columns size 1 for columns size 254 cust_id');
    
    
     --再来看看统计信息
     select column_name,
     num_distinct, --唯一值的个数
     low_value, --列上的最小值
     high_value, --列上的最大值
     density, --选择率因子(密度) = 1/(ndv),如果不存在柱状图的话
     num_nulls, --空值的个数
     num_buckets, --直方图的bucket个数
     histogram --直方图的类型
     from user_tab_columns
     where table_name='new_sales'
    
     select
     column_name,
     endpoint_number,
     endpoint_value,
     from user_tab_histograms
     where table_name='new_sales' and column_name='cust_id'
    
   
    扩展统计信息 (user_stat_extensions)
     select e.extension col_group, t.num_distinct, t.histogram
     from user_stat_extensions e, user_tab_col_statistics t
     where e.extension_name=t.column_name
     and t.table_name='new_sales';
    
     --搜集扩展统计信息
     declare
        cg_name varchar2(30);
     begin
        cg_name := dbms_stats.create_extended_stats('sh','new_sales','(prod_id,cust_id)');
     end;

    select sys.dbms_stats.show_extended_stats_name('sh','new_sales', '(prod_id,cust_id)') col_group_name
     from dual;

    exec dbms_stats.gather_table_stats('sh','new_sales', method_opt => -
     'for columns (prod_id,cust_id) size skewonly');
    


2.统计信息不准确容易导致的问题


表统计信息不准确
    导致了表的访问方式出现了问题(全表扫描和使用索引)
    导致了表和表的链接方式出现问题(应该使用hash join,却是用了nest loop)
   

列统计信息不准确
    导致了访问表的方式不同(错误的索引)
    导致了表的连接方式不同(应该使用hash join , 但是使用了nest loop)

索引的统计信息不准确
    导致了访问表的方式不同(应该使用索引,但是使用了全表扫描)

+++++++++++++++++++++++++++++++++++++++++++

--当天线上表

create table sales_online

(
  prod_id      number  not null  ,      
  cust_id       number  not null,      

time_id      date  not null,        

channel_id     number  not null,      

promo_id       number  not null,      

quantity_sold  number(10,2) not null,

amount_sold    number(10,2) not null)

--历史归档表

create table sales_part (
  prod_id      number  not null  ,      
  cust_id       number  not null,      

time_id      date  not null,        

channel_id     number  not null,      

promo_id       number  not null,      

quantity_sold  number(10,2) not null,

amount_sold    number(10,2) not null)

partition by range (time_id)

(

partition part_20171218 values less than (to_date('19-12-2017','dd-mm-yyyy')),

partition part_20171219 values less than (to_date('20-12-2017','dd-mm-yyyy'))

);

insert into sales_part

select prod_id,cust_id, sysdate-2,channel_id,promo_id,quantity_sold,amount_sold

from new_sales;

insert into sales_part

select prod_id,cust_id, sysdate-1,channel_id,promo_id,quantity_sold,amount_sold

from new_sales;

commit;

create index sales_cust_idx on sales_online(cust_id);

create index sales_part_cust_idx on sales_part(cust_id);

--每天晚上把当天数据归档之后,再删除

declare
    v_sql varchar2(3000);

begin

   v_sql := 'alter table sales_part drop partition part_20171219';
    execute immediate v_sql;
   
    v_sql := 'alter table sales_part add partition part_'||to_char(sysdate+1,'yyyymmdd')||
    ' values less than (to_date('||''''||to_char(sysdate+1,'dd-mm-yyyy')||''''||','||''''||'dd-mm-yyyy'||
    ''''||'))';
    dbms_output.put_line(v_sql);
    execute immediate v_sql;
   
    v_sql := 'alter table sales_part exchange partition part_'||to_char(sysdate+1,'yyyymmdd')||
    ' with table sales_online';
    execute immediate v_sql;
    dbms_output.put_line(v_sql);
   
    v_sql := 'truncate table sales_online';
    execute immediate v_sql;
    dbms_output.put_line(v_sql);
   
    v_sql := 'alter index sales_part_cust_idx rebuild online';
    execute immediate v_sql;
    dbms_output.put_line(v_sql);
   
     v_sql := 'alter index sales_cust_idx rebuild online';
    execute immediate v_sql;
    dbms_output.put_line(v_sql);
  
    dbms_stats.gather_table_stats('sh', 'sales_part');
    dbms_stats.gather_table_stats('sh', 'sales_online');
   

end;

-- 检查统计信息

    select column_name,
     num_distinct, --唯一值的个数
     low_value, --列上的最小值
     high_value, --列上的最大值
     density, --选择率因子(密度) = 1/(ndv),如果不存在柱状图的话
     num_nulls, --空值的个数
     num_buckets, --直方图的bucket个数
     histogram --直方图的类型
     from user_tab_columns
     where table_name='sales_online'

   select num_rows, --表中的记录数
    blocks, --表中数据所占的数据块数
    empty_blocks, --表中的空块数
    avg_space, --数据块中平均的使用空间
    chain_cnt, --表中行连接和行迁移的数量
    avg_row_len, --每条记录的平均长度
    last_analyzed -- 最近一次搜集统计信息的时间
    from user_tables where table_name='sales_online';

   


====实例1


--进行查询

select c.cust_city, sum(amount_sold) from sales_part s, new_customers c

where s.cust_id = c.cust_id

and s.cust_id > 100

and time_id between to_date('2017-12-18 00:00:00' ,'yyyy-mm-dd hh24:mi:ss') and 

to_date('2017-12-18 01:00:00', 'yyyy-mm-dd hh24:mi:ss')

group by c.cust_city

----------------------------------------------------------------------------------------------------------

|   0 | select statement         |               |     1 |    48 |  1779   (1)| 00:00:22 | |        |

|   1 |  hash group by           |               |     1 |    48 |  1779   (1)| 00:00:22 | |        |

|*  2 |   hash join              |               |     1 |    48 |  1778   (1)| 00:00:22 | |        |

|   3 |    partition range single|               |     1 |    18 |  1373   (1)| 00:00:17 |     2 |     2 |

|*  4 |     table access full    | sales_part    |     1 |    18 |  1373   (1)| 00:00:17 |     2 |     2 |

|*  5 |    table access full     | new_customers | 54144 |  1586k|   405   (1)| 00:00:05 | |        |

-------------------------------------------------------------------------------------------

select c.cust_city, sum(amount_sold) from sales_online s, new_customers c

where s.cust_id = c.cust_id

and s.cust_id > 100

and time_id between to_date('2017-12-20 00:00:00' ,'yyyy-mm-dd hh24:mi:ss') and 

to_date('2017-12-20 01:00:00', 'yyyy-mm-dd hh24:mi:ss')

group by c.cust_city

------------------------------------------------------------------------------------------------

| id  | operation                     | name           | rows  | bytes | cost (%cpu)| time     |

------------------------------------------------------------------------------------------------

|   0 | select statement              |                |     1 |    65 |   406   (1)| 00:00:05 |

|   1 |  hash group by                |                |     1 |    65 |   406   (1)| 00:00:05 |

|*  2 |   hash join                   |                |     1 |    65 |   405   (1)| 00:00:05 |

|*  3 |    table access by index rowid| sales_online   |     1 |    35 |     0   (0)| 00:00:01 |

|*  4 |     index range scan          | sales_cust_idx |     1 |       |     0   (0)| 00:00:01 |

|*  5 |    table access full          | new_customers  | 54144 |  1586k|   405   (1)| 00:00:05 |

------------------------------------------------------------------------------------------------

--向表sales_online 中插入一些数据

insert into sales_online

select prod_id,cust_id, sysdate,channel_id,promo_id,quantity_sold,amount_sold

from new_sales;

commit;

--再次查询数据

select c.cust_city, sum(amount_sold) from sales_online s, new_customers c

where s.cust_id = c.cust_id

and s.cust_id > 100

and time_id between to_date('2017-12-20 00:00:00' ,'yyyy-mm-dd hh24:mi:ss') and 

to_date('2017-12-20 01:00:00', 'yyyy-mm-dd hh24:mi:ss')

group by c.cust_city

------------------------------------------------------------------------------------------------

|   0 | select statement              |                |     1 |    65 |   406   (1)| 00:00:05 |

|   1 |  hash group by                |                |     1 |    65 |   406   (1)| 00:00:05 |

|*  2 |   hash join                   |                |     1 |    65 |   405   (1)| 00:00:05 |

|*  3 |    table access by index rowid| sales_online   |     1 |    35 |     0   (0)| 00:00:01 |

|*  4 |     index range scan          | sales_cust_idx |     1 |       |     0   (0)| 00:00:01 |

|*  5 |    table access full          | new_customers  | 54144 |  1586k|   405   (1)| 00:00:05 |

------------------------------------------------------------------------------------------------

       

--手动搜集统计信息或许是一个办法

exec dbms_stats.gather_table_stats('sh', 'sales_online', cascade => true);

-------------------------------------------------------------------------------------

| id  | operation           | name          | rows  | bytes | cost (%cpu)| time     |

-------------------------------------------------------------------------------------

|   0 | select statement    |               |     1 |    48 |  1641   (1)| 00:00:20 |

|   1 |  hash group by      |               |     1 |    48 |  1641   (1)| 00:00:20 |

|*  2 |   hash join         |               |     1 |    48 |  1640   (1)| 00:00:20 |

|*  3 |    table access full| sales_online  |     1 |    18 |  1235   (1)| 00:00:15 |

|*  4 |    table access full| new_customers | 54144 |  1586k|   405   (1)| 00:00:05 |

-------------------------------------------------------------------------------------

执行计划变了过来。但是这不是一个好的办法,因为在生产时间搜集统计信息比较危险。

—-可以这样做

declare
    v_sql varchar2(3000);

begin

   v_sql := 'alter table sales_part drop partition part_20171219';
    execute immediate v_sql;
   
    --导出统计信息
    dbms_stats.export_table_stats(ownname =>'sh',tabname=>'sales_online',stattab=>'sales_online_st',statid => 'a2');
   
    v_sql := 'alter table sales_part add partition part_'||to_char(sysdate+1,'yyyymmdd')||
    ' values less than (to_date('||''''||to_char(sysdate+1,'dd-mm-yyyy')||''''||','||''''||'dd-mm-yyyy'||
    ''''||'))';
    dbms_output.put_line(v_sql);
    execute immediate v_sql;
   
    v_sql := 'alter table sales_part exchange partition part_'||to_char(sysdate+1,'yyyymmdd')||
    ' with table sales_online';
    execute immediate v_sql;
    dbms_output.put_line(v_sql);
   
    v_sql := 'truncate table sales_online';
    execute immediate v_sql;
    dbms_output.put_line(v_sql);
   
    v_sql := 'alter index sales_part_cust_idx rebuild online';
    execute immediate v_sql;
    dbms_output.put_line(v_sql);
   
     v_sql := 'alter index sales_cust_idx rebuild online';
    execute immediate v_sql;
    dbms_output.put_line(v_sql);
  
    dbms_stats.gather_table_stats('sh', 'sales_part');
    --dbms_stats.gather_table_stats('sh', 'sales_online');

   --导入统计信息
    dbms_stats.import_table_stats(ownname => 'sh', tabname => 'sales_online', stattab => 'sales_online_st', statid => 'a2', no_invalidate => true);

end;



推荐阅读
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • 本文介绍了游标的使用方法,并以一个水果供应商数据库为例进行了说明。首先创建了一个名为fruits的表,包含了水果的id、供应商id、名称和价格等字段。然后使用游标查询了水果的名称和价格,并将结果输出。最后对游标进行了关闭操作。通过本文可以了解到游标在数据库操作中的应用。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
  • MySQL多表数据库操作方法及子查询详解
    本文详细介绍了MySQL数据库的多表操作方法,包括增删改和单表查询,同时还解释了子查询的概念和用法。文章通过示例和步骤说明了如何进行数据的插入、删除和更新操作,以及如何执行单表查询和使用聚合函数进行统计。对于需要对MySQL数据库进行操作的读者来说,本文是一个非常实用的参考资料。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 本文介绍了在MySQL8.0中如何查看性能并解析SQL执行顺序。首先介绍了查询性能工具的开启方法,然后详细解析了SQL执行顺序中的每个步骤,包括from、on、join、where、group by、having、select distinct、union、order by和limit。同时还介绍了虚拟表的概念和生成过程。通过本文的解析,读者可以更好地理解MySQL8.0中的性能查看和SQL执行顺序。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 本文主要复习了数据库的一些知识点,包括环境变量设置、表之间的引用关系等。同时介绍了一些常用的数据库命令及其使用方法,如创建数据库、查看已存在的数据库、切换数据库、创建表等操作。通过本文的学习,可以加深对数据库的理解和应用能力。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
author-avatar
我的饭桶女神
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有