Oracle 12c 以SH用户登录系统,执行一个简单的查询:
注意在执行计划中出现了:RESULT CACHE
SQL> SELECT COUNT(*)
2 FROM sales
3 WHERE AMOUNT_SOLD =
4 ( SELECT MIN(AMOUNT_SOLD) FROM sales
5 );
COUNT(*)
----------
38
已用时间: 00: 00: 02.34
执行计划
----------------------------------------------------------
Plan hash value: 353223879
-----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 5 | 1043 (3)| 00:00:01 | | |
| 1 | RESULT CACHE | 638dzksdy77tj25m4rq4mvdmdx | | | | | | |
| 2 | SORT AGGREGATE | | 1 | 5 | | | | |
| 3 | PARTITION RANGE ALL | | 256 | 1280 | 523 (3)| 00:00:01 | 1 | 28 |
|* 4 | TABLE ACCESS FULL | SALES | 256 | 1280 | 523 (3)| 00:00:01 | 1 | 28 |
| 5 | SORT AGGREGATE | | 1 | 5 | | | | |
| 6 | PARTITION RANGE ALL| | 918K| 4486K| 521 (3)| 00:00:01 | 1 | 28 |
| 7 | TABLE ACCESS FULL | SALES | 918K| 4486K| 521 (3)| 00:00:01 | 1 | 28 |
-----------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("AMOUNT_SOLD"= (SELECT MIN("AMOUNT_SOLD") FROM "SALES" "SALES"))
Result Cache Information (identified by operation id):
------------------------------------------------------
1 - column-count=1; type=AUTO; dependencies=(SH.SALES); attributes=(single-row); name="SELECT COUNT(*)
FROM sales
WHERE AMOUNT_SOLD =
( SELECT MIN(AMOUNT_SOLD) FROM sales
)"
Note
-----
- SQL plan baseline "SQL_PLAN_cstmbjapmzq3wb8285edf" used for this statement
统计信息
----------------------------------------------------------
3201 recursive calls
132 db block gets
8857 consistent gets
1851 physical reads
13044 redo size
550 bytes sent via SQL*Net to client
607 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
370 sorts (memory)
0 sorts (disk)
1 rows processed
以SYS用户连接到PDB后,查看关于Result Cache的初始化参数设置:
SQL> show parameter result;
NAME TYPE VALUE
------------------------------------ ---------------------- ------------------------------
client_result_cache_lag big integer 3000
client_result_cache_size big integer 0
result_cache_max_result integer 5
result_cache_max_size big integer 12384K
result_cache_mode string AUTO
result_cache_remote_expiration integer 0
可以看到 result_cache_mode默认值为:AUTO
结果集缓存(Result Cache)是共享全局区域(SGA)或客户端应用程序内存中的一个内存区域,用于存储数据库查询或查询块的结果以便重用。缓存的行在SQL语句和会话之间共享,除非它们变得过时。
可以分为:
客户端结果缓存与驻留在SGA中的服务器结果缓存不同。当启用客户端结果缓存时,可以将查询结果集缓存在客户端、服务器或两者上。即使服务器结果缓存被禁用,也可以启用客户端缓存。
当查询执行时,数据库搜索缓存内存,以确定结果是否存在于结果缓存中。如果结果存在,则数据库从内存中检索结果,而不是执行查询。如果没有缓存结果,则数据库执行查询,将结果作为输出返回,并将结果存储在结果缓存中。
当用户重复执行查询和函数时,数据库从缓存中检索行,从而减少响应时间。当依赖数据库对象中的数据被修改时,缓存结果变得无效。
在HR用户下,执行下面的查询语句:
注意,使用了 /*+ RESULT_CACHE */ Hints提示,通过查看执行计划,可以看到,执行计划中产生了Result Cache。
Result Cache的ID为:36cy5bwyvd4bs1srpnt3yg1420。
SQL> SELECT /*+ RESULT_CACHE */ department_id, AVG(salary)
2 FROM hr.employees
3 GROUP BY department_id;
DEPARTMENT_ID AVG(SALARY)
------------- -----------
100 8601.33333
30 4150
7000
20 9500
70 10000
90 19333.3333
110 10154
50 3475.55556
40 6500
80 8955.88235
10 4400
60 5760
已选择 12 行。
已用时间: 00: 00: 00.54
执行计划
----------------------------------------------------------
Plan hash value: 1192169904
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 11 | 77 | 4 (25)| 00:00:01 |
| 1 | RESULT CACHE | 36cy5bwyvd4bs1srpnt3yg1420 | | | | |
| 2 | HASH GROUP BY | | 11 | 77 | 4 (25)| 00:00:01 |
| 3 | TABLE ACCESS FULL| EMPLOYEES | 107 | 749 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Result Cache Information (identified by operation id):
------------------------------------------------------
1 - column-count=2; dependencies=(HR.EMPLOYEES); name="SELECT /*+ RESULT_CACHE */ department_id, AVG(salary)
FROM hr.employees
GROUP BY department_id"
统计信息
----------------------------------------------------------
138 recursive calls
12 db block gets
300 consistent gets
3 physical reads
1152 redo size
877 bytes sent via SQL*Net to client
608 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
12 sorts (memory)
0 sorts (disk)
12 rows processed
进而执行查询:
SQL> SELECT id, type, creation_timestamp, block_count,
2 column_count, pin_count, row_count
3 FROM V$RESULT_CACHE_OBJECTS
4 WHERE cache_id ='36cy5bwyvd4bs1srpnt3yg1420';
ID TYPE CREATION_TIMES BLOCK_COUNT COLUMN_COUNT PIN_COUNT ROW_COUNT
---------- -------------------- -------------- ----------- ------------ ---------- ----------
194 Result 01-10月-18 1 2 0 12
已用时间: 00: 00: 00.27
执行另外一个查询:RESULT_CACHE Hint Specified in a WITH View
SQL> WITH summary AS
2 ( SELECT /*+ RESULT_CACHE */ department_id, avg(salary) avg_sal
3 FROM hr.employees
4 GROUP BY department_id )
5 SELECT d.*, avg_sal
6 FROM hr.departments d, summary s
7 WHERE d.department_id = s.department_id;
DEPARTMENT_ID DEPARTMENT_NAME MANAGER_ID LOCATION_ID AVG_SAL
------------- ------------------------------------------------------------ ---------- ----------- ----------
10 Administration 200 1700 4400
20 Marketing 201 1800 9500
30 Purchasing 114 1700 4150
40 Human Resources 203 2400 6500
50 Shipping 121 1500 3475.55556
60 IT 103 1400 5760
70 Public Relations 204 2700 10000
80 Sales 145 2500 8955.88235
90 Executive 100 1700 19333.3333
100 Finance 108 1700 8601.33333
110 Accounting 205 1700 10154
已选择 11 行。
已用时间: 00: 00: 00.29
执行计划
----------------------------------------------------------
Plan hash value: 523547400
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 11 | 517 | 7 (29)| 00:00:01 |
| 1 | MERGE JOIN | | 11 | 517 | 7 (29)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| DEPARTMENTS | 27 | 567 | 2 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | DEPT_ID_PK | 27 | | 1 (0)| 00:00:01 |
|* 4 | SORT JOIN | | 11 | 286 | 5 (40)| 00:00:01 |
| 5 | VIEW | | 11 | 286 | 4 (25)| 00:00:01 |
| 6 | RESULT CACHE | 86qrwagbnhbkpbk0d2nfkjygkp | | | | |
| 7 | HASH GROUP BY | | 11 | 77 | 4 (25)| 00:00:01 |
| 8 | TABLE ACCESS FULL | EMPLOYEES | 107 | 749 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("D"."DEPARTMENT_ID"="S"."DEPARTMENT_ID")
filter("D"."DEPARTMENT_ID"="S"."DEPARTMENT_ID")
Result Cache Information (identified by operation id):
------------------------------------------------------
6 - column-count=2; dependencies=(HR.EMPLOYEES); name="SELECT /*+ RESULT_CACHE */ department_id, avg(salary) avg_sal
FROM hr.employees
GROUP BY department_id "
统计信息
----------------------------------------------------------
120 recursive calls
4 db block gets
210 consistent gets
2 physical reads
588 redo size
1277 bytes sent via SQL*Net to client
608 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
8 sorts (memory)
0 sorts (disk)
11 rows processed
在这个示例中,如执行计划的步骤6所示,直接从缓存中检索摘要视图结果。name列中的值是结果的缓存ID。
如果不需要Result Cache时,也可以使用Hints提示,请对比下面的示例:
(SH模式下,查询Sales表)
SQL> SELECT /*+ NO_RESULT_CACHE */ prod_id, SUM(amount_sold)
2 FROM sales
3 GROUP BY prod_id
4 ORDER BY prod_id;
已选择 72 行。
已用时间: 00: 00: 00.30
执行计划
----------------------------------------------------------
Plan hash value: 4109827725
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 72 | 648 | 556 (9)| 00:00:01 | | |
| 1 | SORT GROUP BY | | 72 | 648 | 556 (9)| 00:00:01 | | |
| 2 | PARTITION RANGE ALL| | 918K| 8075K| 521 (3)| 00:00:01 | 1 | 28 |
| 3 | TABLE ACCESS FULL | SALES | 918K| 8075K| 521 (3)| 00:00:01 | 1 | 28 |
----------------------------------------------------------------------------------------------
Note
-----
- SQL plan baseline "SQL_PLAN_28c8zs51tg4h5ebe69af4" used for this statement
统计信息
----------------------------------------------------------
66 recursive calls
81 db block gets
1768 consistent gets
1 physical reads
8936 redo size
2280 bytes sent via SQL*Net to client
651 bytes received via SQL*Net from client
6 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
72 rows processed
SQL> SELECT /*+ RESULT_CACHE */ prod_id, SUM(amount_sold)
2 FROM sales
3 GROUP BY prod_id
4 ORDER BY prod_id;
已选择 72 行。
已用时间: 00: 00: 00.22
执行计划
----------------------------------------------------------
Plan hash value: 4109827725
--------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 72 | 648 | 556 (9)| 00:00:01 | | |
| 1 | RESULT CACHE | 1vf2p8s2503gdgx1wcrs8pvr49 | | | | | | |
| 2 | SORT GROUP BY | | 72 | 648 | 556 (9)| 00:00:01 | | |
| 3 | PARTITION RANGE ALL| | 918K| 8075K| 521 (3)| 00:00:01 | 1 | 28 |
| 4 | TABLE ACCESS FULL | SALES | 918K| 8075K| 521 (3)| 00:00:01 | 1 | 28 |
--------------------------------------------------------------------------------------------------------------------
Result Cache Information (identified by operation id):
------------------------------------------------------
1 - column-count=2; dependencies=(SH.SALES); name="SELECT /*+ RESULT_CACHE */ prod_id, SUM(amount_sold)
FROM sales
GROUP BY prod_id
ORDER BY prod_id"
统计信息
----------------------------------------------------------
6 recursive calls
36 db block gets
1686 consistent gets
0 physical reads
588 redo size
2280 bytes sent via SQL*Net to client
651 bytes received via SQL*Net from client
6 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
72 rows processed
SQL>
对比上述两个查询的执行计划,可以发现,第一个查询没有 Result Cache,而第二个查询存在 Result Cache。
客户端缓存的结果存储在最外层的查询结果,这是“列定义的应用程序。子查询和查询块是cached槽。
下面的流程图展示客户端与数据库的登录会话。这个过程有一个客户端,客户端缓存的结果表示在多应用共享会话运行时在客户端的过程。如果一个应用程序的会话信息的查询,然后检索的行从数据库和在客户端缓存的结果。如果其他应用程序会话运行相同的查询,那么它们的检索结果也是从客户端缓存获得的。
The client result cache transparently keeps the result set consistent with session state or database changes that affect it. When a transaction changes the data or metadata of database objects used to build the cached result, the database sends an invalidation to the OCI client on its next round trip to the server.
Server Result Cache Initialization Parameters
Parameter | Description |
---|---|
|
Specifies the memory allocated to the server result cache. To disable the server result cache, set this parameter to 0. |
|
Specifies the maximum amount of server result cache memory (in percent) that can be used for a single result. Valid values are between 1 and 100. The default value is 5%. You can set this parameter at the system or session level. |
|
Specifies the expiration time (in minutes) for a result in the server result cache that depends on remote database objects. The default value is 0, which specifies that results using remote objects will not be cached. If a non-zero value is set for this parameter, DML on the remote database does not invalidate the server result cache. |
另外,默认情况下,当Oracle启动时,Oracle会在Shared Pool中为Server Result Cache分配一定的内存空间。这个Server Result Cache内存的大小取决于Shared Pool的大小和选择的内存管理模式(自动共享SGA管理还是手动SGA管理)。
Automatic shared memory management
If you are managing the size of the shared pool using the SGA_TARGET
initialization parameter, Oracle Database allocates 0.50% of the value of the SGA_TARGET
parameter to the result cache.
Manual shared memory management
If you are managing the size of the shared pool using the SHARED_POOL_SIZE
initialization parameter, then Oracle Database allocates 1% of the shared pool size to the result cache.
注意:Oracle Database will not allocate more than 75% of the shared pool to the server result cache.
通过执行:EXECUTE DBMS_RESULT_CACHE.MEMORY_REPORT,可以查看Result Cache报告,
SQL> SET SERVEROUTPUT ON;
SQL> EXECUTE DBMS_RESULT_CACHE.MEMORY_REPORT;
R e s u l t C a c h e M e m o r y R e p o r t
[Parameters]
Block Size = 1K bytes
Maximum Cache Size = 12384K bytes (12384 blocks)
Maximum Result Size = 619K bytes (619 blocks)
[Memory]
Total Memory = 12152 bytes [0.001% of the Shared Pool]
... Fixed Memory = 12152 bytes [0.001% of the Shared Pool]
... Dynamic Memory = 0 bytes [0.000% of the Shared Pool]
PL/SQL 过程已成功完成。
已用时间: 00: 00: 00.38
SQL>
通过执行过程:EXEC DBMS_RESULT_CACHE.FLUSH 可以清楚Server Result Cache。
SQL> SELECT id, type, creation_timestamp, block_count,
2 column_count, pin_count, row_count
3 FROM V$RESULT_CACHE_OBJECTS
4 WHERE cache_id ='36cy5bwyvd4bs1srpnt3yg1420';
ID TYPE CREATION_TIMES BLOCK_COUNT COLUMN_COUNT PIN_COUNT ROW_COUNT
---------- -------------------- -------------- ----------- ------------ ---------- ----------
194 Result 01-10月-18 1 2 0 12
已用时间: 00: 00: 00.40
SQL> EXEC DBMS_RESULT_CACHE.FLUSH;
PL/SQL 过程已成功完成。
已用时间: 00: 00: 00.04
SQL> SELECT id, type, creation_timestamp, block_count,
2 column_count, pin_count, row_count
3 FROM V$RESULT_CACHE_OBJECTS
4 WHERE cache_id ='36cy5bwyvd4bs1srpnt3yg1420';
未选定行
已用时间: 00: 00: 00.01
Client Result Cache Initialization Parameters
Parameter | Description |
---|---|
|
Specifies the maximum size of the client result cache for each client process. To enable the client result cache, set the size to 32768 bytes or greater. A lesser value, including the default of 0, disables the client result cache. Note: If the |
|
Specifies the amount of lag time (in milliseconds) for the client result cache. The default value is 3000 (3 seconds). If the OCI application does not perform any database calls for a period of time, then this setting forces the next statement execution call to check for validations. If the OCI application accesses the database infrequently, then setting this parameter to a low value results in more round trips from the OCI client to the database to keep the client result cache synchronized with the database. |
|
Specifies the release with which Oracle Database must maintain compatibility. For the client result cache to be enabled, this parameter must be set to 11.0.0.0 or higher. For client caching on views, this parameter must be set to 11.2.0.0.0 or higher. |
An optional client configuration file overrides client result cache initialization parameters set in the server parameter file.
Note: The client result cache lag can only be set with the CLIENT_RESULT_CACHE_LAG
initialization parameter.
结果缓存模式是一个数据库设置,用于确定哪些查询有资格在服务器和客户端结果缓存中存储结果集。如果查询符合缓存条件,则应用程序检查结果缓存以确定查询结果集是否存在于缓存中。如果存在,则直接从结果缓存中检索结果。否则,数据库执行查询并将结果作为输出返回,并将其存储在结果缓存中。Oracle建议对只读或读取主要数据库对象的查询进行结果缓存。
当启用结果缓存时,数据库还缓存调用非确定性PL/SQL函数的查询。当缓存调用这些函数的SELECT语句时,结果缓存跟踪PL/SQL函数和数据库对象的数据依赖性。但是,如果函数使用未被跟踪的数据(例如序列、SYSDATE、SYS_CONTEXT和包变量),则对调用该函数的查询使用结果缓存会产生过时的结果。在这方面,结果缓存的行为与缓存PL/SQL函数相同。因此,在选择启用结果缓存时,始终要考虑数据的准确性和性能。
设置 result cache 模式:
通过设置 RESULT_CACHE_MODE 初始化参数来设置
可以在数据库实例级别 instance (ALTER SYSTEM
),会话级别 session (ALTER SESSION
), 或者在数据库参数文件中设置。
Values for the RESULT_CACHE_MODE Parameter
Value | Description |
---|---|
|
Query results can only be stored in the result cache by using a query hint or table annotation. This is the default and recommended value. |
|
All results are stored in the result cache. If a query result is not in the cache, then the database executes the query and stores the result in the cache. Subsequent executions of the same SQL statement, including the result cache hint, retrieve data from the cache. Sessions uses these results if possible. To exclude query results from the cache, the Note: |
假设在同一个Session会话中执行下面的两条查询,观察下面两条SQL查询的执行计划,看看能发现什么?
SQL查询一:
SELECT *
FROM ( SELECT /*+ RESULT_CACHE */ department_id, manager_id, count(*) count
FROM hr.employees
GROUP BY department_id, manager_id ) view1
WHERE department_id = 30;
执行结果:
SQL> SELECT *
2 FROM ( SELECT /*+ RESULT_CACHE */ department_id, manager_id, count(*) count
3 FROM hr.employees
4 GROUP BY department_id, manager_id ) view1
5 WHERE department_id = 30;
DEPARTMENT_ID MANAGER_ID COUNT
------------- ---------- ----------
30 100 1
30 114 5
已用时间: 00: 00: 00.09
执行计划
----------------------------------------------------------
Plan hash value: 2700420355
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 107 | 4173 | 4 (25)| 00:00:01 |
|* 1 | VIEW | | 107 | 4173 | 4 (25)| 00:00:01 |
| 2 | RESULT CACHE | 5a7n6czum41gcas673839j2b5y | | | | |
| 3 | HASH GROUP BY | | 107 | 749 | 4 (25)| 00:00:01 |
| 4 | TABLE ACCESS FULL| EMPLOYEES | 107 | 749 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("DEPARTMENT_ID"=30)
Result Cache Information (identified by operation id):
------------------------------------------------------
2 - column-count=3; dependencies=(HR.EMPLOYEES); name="SELECT /*+ RESULT_CACHE */ department_id, manager_id, count(*) count
FROM hr.employees
GROUP BY department_"
统计信息
----------------------------------------------------------
19 recursive calls
4 db block gets
20 consistent gets
0 physical reads
588 redo size
761 bytes sent via SQL*Net to client
608 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
2 rows processed
SQL查询二:
WITH view2 AS
( SELECT /*+ RESULT_CACHE */ department_id, manager_id, count(*) count
FROM hr.employees
GROUP BY department_id, manager_id )
SELECT *
FROM view2
WHERE count BETWEEN 1 and 5;
执行结果:
SQL> WITH view2 AS
2 ( SELECT /*+ RESULT_CACHE */ department_id, manager_id, count(*) count
3 FROM hr.employees
4 GROUP BY department_id, manager_id )
5 SELECT *
6 FROM view2
7 WHERE count BETWEEN 1 and 5;
DEPARTMENT_ID MANAGER_ID COUNT
------------- ---------- ----------
40 101 1
60 103 4
70 101 1
80 149 5
149 1
110 205 1
50 100 5
90 100 2
10 101 1
90 1
30 100 1
60 102 1
100 101 1
20 100 1
20 201 1
110 101 1
100 108 5
30 114 5
80 100 5
已选择 19 行。
已用时间: 00: 00: 00.09
执行计划
----------------------------------------------------------
Plan hash value: 2700420355
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 107 | 4173 | 4 (25)| 00:00:01 |
|* 1 | VIEW | | 107 | 4173 | 4 (25)| 00:00:01 |
| 2 | RESULT CACHE | 5a7n6czum41gcas673839j2b5y | | | | |
| 3 | HASH GROUP BY | | 107 | 749 | 4 (25)| 00:00:01 |
| 4 | TABLE ACCESS FULL| EMPLOYEES | 107 | 749 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("COUNT">=1 AND "COUNT"<=5)
Result Cache Information (identified by operation id):
------------------------------------------------------
2 - column-count=3; dependencies=(HR.EMPLOYEES); name="SELECT /*+ RESULT_CACHE */ department_id, manager_id, count(*) count
FROM hr.employees
GROUP BY department_id, manager_id"
统计信息
----------------------------------------------------------
10 recursive calls
4 db block gets
4 consistent gets
0 physical reads
588 redo size
1160 bytes sent via SQL*Net to client
619 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
19 rows processed
因为在SQL查询二中使用了 Result Cache的 hints提示,在SQL查询二中使用了SQL查询一缓存的结果。