共享池是oracle缓存程序数据的地方
库高速缓存:执行过的每一句SQL语句,都存有解析后的内容
解析包括语句的语法,检验提及的对象,以及确认对象的用户权限。
数据字典高速缓存区:oracle使用的系统参数
使用最近最少使用算法(Least Recently Used, LRU),用来管理共享池中的对象。
写SQL语句时,一定要考虑如果高效的使用共享池。
SQL> select sql_text,sql_id,child_number,hash_value,executions from v$sql where upper(sql_text) like '%EMPLOYEES%';
SQL_TEXT SQL_ID CHILD_NUMBER HASH_VALUE EXECUTIONS
-------------------------------------------------------------------------------- ------------- ------------ ---------- ----------
select * from employees where department_id=60 3advtjun8csb4 0 2827379044 3
select /* a comment */ * from employees where department_id=60 fs9k1uvtkk817 0 4079558695 1
SELECT * FROM EMPLOYEES WHERE DEPARTMENT_ID=60 86sbrvcmd3mv5 0 651284325 1
三条语句,返回相同的结果,但oracle认为他们是不同的,因为oracle首先将字符串转换为散列值。这个散列值就是放在库高速缓存中的关键字。
在解析中绑定变量
SQL> variable v_dept number
SQL> exec :v_dept := 10
SQL> select * from employees where department_id = :v_dept;
SQL> exec :v_dept := 60
SQL> select * from employees where department_id = :v_dept;
锁存器是oracle为了读取存放在库高速缓存或者其它内存结构中的信息时必须获得的一种锁。锁存器可以保护库高速速缓存或其它内存结构中的信息被两个同时进行的会话修改,或一个会话正要读取的信息被另一个会话修改而导致的损坏。
互拆锁:是一个序列化组件,用来阻止多个线程同时访问一个共享结构。优点是占用内存少,可以快速获取或释放。
语法解析仍然要使用锁存器。
oracle获取锁存器的频率越多,越可能触发争夺。越需要等待较长的时间。因此正确编写代码,较少使用锁存器,也就是硬解析,是非常重要的。
Paste_Image.png测试物理读和逻辑读
E:\plustrce.sql
set echo on
drop role plustrace;
create role plustrace;
grant select on v_$sesstat to plustrace;
grant select on v_$statname to plustrace;
grant select on v_$mystat to plustrace;
grant plustrace to dba with admin option;
set echo off
SQL> conn sys/0524 as sysdba;
SQL> @'E:\plustrce.sql'
SQL>grant plustrace to scott;
SQL> grant all on employees to scott;
SQL> conn scott/scott
SQL> alter system flush buffer_cache;
SQL> alter system flush shared_pool;
SQL> set autotrace traceonly statistics;
SQL> select * from employees where department_id=60;
SQL> set autotrace off;
SQL> alter system flush buffer_cache;
SQL> alter system flush shared_pool;
SQL> set autotrace traceonly statistics;
SQL> select * from employees where department_id=60;
SQL> set autotrace off;
应开发出更多重用共享池和缓冲区缓存中信息的代码。
查询转换
发生在查询执行完语法和权限检查之后,优化器为了决定最终的执行计划而为不同的计划计算成本预估之前。转换和优化是两个不同的任务。
- 视图合并
--对于大多数select-project-join(SPJ)类型查询会自动应用简单视图合并
select *
from orders o, (select sales_rep_id from orders) o_view
where o.sales_rep_id = o_view.sales_rep_id(+)
and o.order_total > 10000;
--不使用提示
select *
from orders o, (select /*+ NO_MERGE */ sales_rep_id from orders) o_view
where o.sales_rep_id = o_view.sales_rep_id(+)
and o.order_total > 10000;
--不使用提示
--当查询包含聚合计算如group by, distinct或外关联时,就会使用复杂的视图合并,从而消除包含聚合计算的视图,使用更少的资源生成结果集。
select e1.last_name,e1.salary,v.avg_salary
from employees e1,
(select department_id,avg(salary) avg_salary from employees e2 group by department_id) v
where e1.department_id=v.department_id and e1.salary>v.avg_salary;
--使用merge提示
select /*+ MERGE(v) */ e1.last_name,e1.salary,v.avg_salary
from employees e1,
(select department_id,avg(salary) avg_salary from employees e2 group by department_id) v
where e1.department_id=v.department_id and e1.salary>v.avg_salary;
--关闭_complex_view_merging
alter session set "_complex_view_merging"=FALSE;
explain plan for
select /*+ MERGE(v) */ e1.last_name,e1.salary,v.avg_salary
from employees e1,
(select department_id,avg(salary) avg_salary from employees e2 group by department_id) v
where e1.department_id=v.department_id and e1.salary>v.avg_salary;
select * from table(dbms_xplan.display);
--如果_complex_view_merging=False,即使用了MERGE,也不会进行视图合并
- 子查询解嵌套
--不相关子查询的解嵌套转换
select *
from employees
where employee_id in (select manager_id from departments);
explain plan for
select *
from employees
where employee_id in (select manager_id from departments);
select * from table(dbms_xplan.display);
--使用NO_UNNEST提示
--不进行查询转换将会选用filter运算而不是nested loops连接 filter效率更低
select *
from employees
where employee_id in (select /*+ NO_UNNEST */ manager_id from departments);
explain plan for
select *
from employees
where employee_id in (select /*+ NO_UNNEST */ manager_id from departments);
select * from table(dbms_xplan.display);
--相关子查询的解嵌套转换
select outer.employee_id,
outer.last_name,
outer.salary,
outer.department_id
from employees outer
where outer.salary >
(select avg(inner.salary)
from employees
inner where inner.department_id = outer.department_id);
explain plan for
select outer.employee_id,
outer.last_name,
outer.salary,
outer.department_id
from employees outer
where outer.salary >
(select avg(inner.salary)
from employees
inner where inner.department_id = outer.department_id);
select * from table(dbms_xplan.display);
--相关列变成了联结条件而子查询的剩余部分用来生成内嵌视图
- 联结消除
--主外键表消除
select e.*
from employees e, departments d
where e.department_id = d.department_id;
explain plan for
select e.*
from employees e, departments d
where e.department_id = d.department_id;
select * from table(dbms_xplan.display);
departments表的联结是如何完全消除的,之所以能够消除,是因为departments表中没有任何一列出现在查询列表中。并且由于主-外键约束,使得对于employees表中的每一行,在departments表中最多有一行匹配的记录。
/*
外联结表消除 外联表确保employees表中的每一行在结果集中至少出现一次,在jobs.job_id列上的唯一健约束,确保了对于employees表中的
每一行,在jobs表中最多有一行与之相匹配。这两个属性保证了employees表中的每一行在结果集中出现并且仅出现一次。
*/
select e.first_name, e.last_name, e.job_id
from employees e, jobs j
where e.job_id = j.job_id(+);
explain plan for
select e.first_name, e.last_name, e.job_id
from employees e, jobs j
where e.job_id = j.job_id(+);
select * from table(dbms_xplan.display);
Note:
如果在查询的任何地方使用了联结键,则不支持联结消除。
如果主外键约束包含多个列,则不支持联结消除。
- 排序消除
--order by消除
select count(*)
from (select d.department_name
from departments d
where d.manager_id = 7
order by d.department_name);
explain plan for
select count(*)
from (select d.department_name
from departments d
where d.manager_id = 7
order by d.department_name);
select * from table(dbms_xplan.display);
--使用no_query_transformation提示,让优华器不要对查询进行移除排序的转换
select /*+ no_query_transformation */
count(*)
from (select d.department_name
from departments d
where d.manager_id = 7
order by d.department_name);
explain plan for
select /*+ no_query_transformation */
count(*)
from (select d.department_name
from departments d
where d.manager_id = 7
order by d.department_name);
select * from table(dbms_xplan.display);
- 谓词推进
--谓词推进
select e1.last_name, e1.salary, v.avg_salary
from employees e1,
(select department_id, avg(salary) avg_salary
from employees e2
group by department_id) v
where e1.department_id = v.department_id
and e1.salary > v.avg_salary
and e1.department_id = 60;
explain plan for
select e1.last_name, e1.salary, v.avg_salary
from employees e1,
(select department_id, avg(salary) avg_salary
from employees e2
group by department_id) v
where e1.department_id = v.department_id
and e1.salary > v.avg_salary
and e1.department_id = 60;
select * from table(dbms_xplan.display);
--rownum不仅会禁止谓词推进,而且也会禁止视图合并
select e1.last_name, e1.salary, v.avg_salary
from employees e1,
(select department_id, avg(salary) avg_salary
from employees e2
where rownum > 1 --rownum prohibits predicate pushing!
group by department_id) v
where e1.department_id = v.department_id
and e1.salary > v.avg_salary
and e1.department_id = 60;
explain plan for
select e1.last_name, e1.salary, v.avg_salary
from employees e1,
(select department_id, avg(salary) avg_salary
from employees e2
where rownum > 1 --rownum prohibits predicate pushing!
group by department_id) v
where e1.department_id = v.department_id
and e1.salary > v.avg_salary
and e1.department_id = 60;
select * from table(dbms_xplan.display);
- 使用物化视图重写查询
--准备测试数据
drop table sales;
create table sales(
channel_id number,
promo_id number,
cust_id number,
amount_sold number,
time_id number,
prod_id number
);
drop table products;
create table products(
prod_id number,
prod_name varchar2(20)
);
drop table times;
create table times(
time_id number,
week_ending_day number
);
insert into products values(1,'衣服');
insert into products values(2,'鞋子');
insert into products values(3,'裤子');
insert into times values(1,1);
insert into times values(2,3);
insert into times values(3,5);
insert into sales values(1,1,1000,500,1,1);
insert into sales values(1,1,1000,500,2,2);
insert into sales values(1,1,1000,500,3,3);
insert into sales values(1,1,1000,500,1,3);
insert into sales values(1,1,1000,500,2,2);
--使用物化视图进行查询重写
select p.prod_id,
p.prod_name,
t.time_id,
t.week_ending_day,
s.channel_id,
s.promo_id,
s.cust_id,
s.amount_sold
from sales s, products p, times t
where s.time_id = t.time_id
and s.prod_id = p.prod_id;
explain plan for
select p.prod_id,
p.prod_name,
t.time_id,
t.week_ending_day,
s.channel_id,
s.promo_id,
s.cust_id,
s.amount_sold
from sales s, products p, times t
where s.time_id = t.time_id
and s.prod_id = p.prod_id;
select * from table(dbms_xplan.display);
create materialized view sales_time_product_mv
enable query rewrite as
select p.prod_id,
p.prod_name,
t.time_id,
t.week_ending_day,
s.channel_id,
s.promo_id,
s.cust_id,
s.amount_sold
from sales s, products p, times t
where s.time_id = t.time_id
and s.prod_id = p.prod_id;
select /*+ rewrite(sales_time_product_mv) */
p.prod_id,
p.prod_name,
t.time_id,
t.week_ending_day,
s.channel_id,
s.promo_id,
s.cust_id,
s.amount_sold
from sales s, products p, times t
where s.time_id = t.time_id
and s.prod_id = p.prod_id;
--rewrite提示来打开查询重写转换
--通过guery-rewrite_enabled参数默认启用的
explain plan for
select /*+ rewrite(sales_time_product_mv) */
p.prod_id,
p.prod_name,
t.time_id,
t.week_ending_day,
s.channel_id,
s.promo_id,
s.cust_id,
s.amount_sold
from sales s, products p, times t
where s.time_id = t.time_id
and s.prod_id = p.prod_id;
select * from table(dbms_xplan.display);
当发生硬解析时,oracle将会确定哪个计划对于该查询是最优的。就是oracle访问查询所使用的对象并返回相应结果数据将会采用的一系列步骤。
统计信息包括针对对象如表和索引收集统计信息,系统统计信息。
优化器是oracle内核中的代码路径,负责为查询确定最佳执行计划(使用统计信息)。
执行一个SQL查询,解析,绑定,执行,提取的步骤。
一次fetch调用将会访问缓冲区缓存中的一个或多个数据块。每次访问一个数据块时,oracle都会从该块中取出数据行然后在一次回路中返回给客户端。一次返回的行数是可配置的。
SQL*Plus 默认为15, 通过 set arraysize n来更改
jdbc 默认为10, 通过 ((OracleConnection)conn).setDefaultRowPrefetch(n)来更改。
列大小是如影响逻辑读取的
SQL> set arraysize 5
SQL> select * from employees;
已选择9行。
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
9 consistent gets
0 physical reads
0 redo size
1282 bytes sent via SQL*Net to client
427 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
9 rows processed
SQL> set arraysize 15
SQL> select * from employees;
已选择9行。
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
8 consistent gets
0 physical reads
0 redo size
1152 bytes sent via SQL*Net to client
416 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
9 rows processed
SQL> set arraysize 45
SQL> select * from employees;
已选择9行。
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
8 consistent gets
0 physical reads
0 redo size
1152 bytes sent via SQL*Net to client
416 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
9 rows processed
SQL语句执行时的步骤汇总
Reference:
Oracle Concepts Guide
网友评论