Server Cache 介绍及工作原理
Oracle 11g 版本提供了一个新的独立的共享内存区域来缓存被应用程序频繁使用的结果集。我们称之为Server Result Cache. OLAP应用通过使用ServerResult Cache可以获得很大的性能提升。简单来说,Server Result Cache 允许一个查询结果集/函数返回值在内存中进行缓存,如果之后的查询/函数试图获得同样的结果集,那Oracle就会直接从缓存中读取,而避免了不必要的I/O.
Server Result Cache 是一个内存 区域,这个内存区域存在于SGA内。更进一步说,ServerResult Cache是存在于SharedPool 中的一块内存区域,这意味着被缓存的内容可以被不同的Session共享访问。如 下图所示Server Result Cache可以细分为两类,一类是SQL Query Result Cache,是用来缓存SQL的查询结果。另一类是PL/SQL function result cache,是用来缓存PL/SQL 函数返回的结果。

Server Result Cache 的工作原理就是每当一个执行一个查询时,数据库会首先在缓存区查看是否有已经缓存的结果,如果有,数据库将直接从缓存中读取结果,而不是执行查询。如果所需要的结果没有被缓存,那数据库就会执行这个查询,返回结果,并将结果缓存已被其他后续查询使用。当用户重复执行一个查询或者函数时,数据库会从缓存中获取结果,这样就大大降低了反应时间。缓存的结果在所依赖的数据对象发生变化时将变为无效状态。
SQL Query Result Cache
常规实现方法就是在SQL 查询语句中 加入RESULT_CACHE hint。除此之外,我们还可以通过设定数据库参数RESULT_CACHE_MODE(MANUAL,FORCE,DEFAULT)或者Table annotation来实现。这几种方式中,hint的方式是优先级最高的,其次是TableAnnotation,最后是RESULT_CACHE_MODE参数。让我们看一看SQL Query Result Cache运行的情况吧。执行如下 SQL( 我这里SQL和函数所用到的表均来自Oracle自带的HR Schema。),这个SQL试图统计每个部门的平均工资。
explain plan for select /*+ result_cache +*/ department_id, avg(salary) from hr.employees group by department_id; SELECT plan_table_output FROM table(DBMS_XPLAN.DISPLAY());
从上面的执行计划中,我们很清楚的看到Result Cache被使用了。 Oracle 是使用字典表 V$RESULT_CACHE_OBJECTS 来存储这些缓存结果集的信息的。
select TYPE,STATUS,NAME,CACHE_ID from V$RESULT_CACHE_OBJECTS where UPPER(status) = 'PUBLISHED';
通过上面的输出结果,我们可以看出Oracle维护了这个SQL查询所依赖的底层对象。那一旦底层对象发生了变化,Oracle会自动将缓存的查询结果集设置为无效状态。
但Oracle检测底层对象的变化也只能到表对象的级别,它无法精确定位到影响缓存的SQL 查询结果的那部分数据的变化。例如另起一个Session,执行如下语句
update employees set first_name = 'Matt',last_name = 'Zhang' where employee_id = 100; commit;
我们只更新了某个employee的名字,这本身与统计平均工资的查询没有太大的关系。那么,再回头查V$RESULT_CACHE_OBJECTS字典表,我们发现原来缓存的那个查询结果已经变成了Invalid。
应用场景
应用Result Cache的SQL 查询是需要重复大量的执行的。SQL查询需要多检索大量数据,并返回满足要求的小部分数据。尤其是当SQL查询中存在例如Group by ,Count,Average 等分组/计算函数/自定义函数的时候。SQL 查询所面向的数据不会经常变化。使用限制
如果SQL查询中包含如下情况,查询结果将不被缓存
临时表字典表Non-deterministic PL/SQL 函数 (指对于同一个/组参数值,函数的返回结果是变化的。查看Oracle Deterministic 关键字)Curval 和Nextval 伪函数SYSDATE,SYS_TIMESTAMP,CURRENT_DATE,CURRENT_TIMESTAMP,LOCAL_TIMESTAMP,USERENV,SYS_CONTEXT,和SYS_QUID 函数。我这里举一个简单的例子,就那SYSDATE来作为Where条件来查询结果,看是否能被Oracle 缓存。
select /*+ result_cache +*/
department_id, avg(salary)
from hr.employees
where hire_date > (sysdate - 365*8)
group by department_id;
首先说明这条SQL的查询肯定是有返回结果的。
那我们看看这条SQL的执行计划,看其是否被缓存了。
explain plan for select /*+ result_cache +*/ department_id, avg(salary) from hr.employees where hire_date > (sysdate - 365*8) group by department_id; SELECT plan_table_output FROM table(DBMS_XPLAN.DISPLAY());
很明显,上面这条加了sysdate的SQL查询并没有被Oracle缓存。
PL/SQL Function Result Cache
Function Result Cache 和Query Result Cache 共享一个ResultCache 内存区域。基于Function的结果集缓存必须要通过显示的声明,加上RESULT_CACHE关键字。方式如下
create or replace
function is_manager (p_emp_id IN employees.employee_id%TYPE)
RETURN BOOLEAN
RESULT_CACHE
IS
manager_count NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE('Manager status confirmation in progress.');
SELECT COUNT(1)
INTO manager_count
FROM departments
WHERE MANAGER_ID = p_emp_id;
IF manager_count > 0 THEN
RETURN (TRUE);
ELSE
RETURN (FALSE);
END IF;
END is_manager;
我们通过如下