【深入解析--eygle】 学习笔记 1.2 shared pool原理 Shared Pool是Oracle SGA设置中最复杂也是最重要的一部分内容,Oracle通过Shared Pool来实现SQL共享、减少代码硬解析等,从而提高数据库的性能。在某些版本中,如果设置不当,Shared Pool可能会极大影响数
Shared Pool是Oracle SGA设置中最复杂也是最重要的一部分内容,Oracle通过Shared Pool来实现SQL共享、减少代码硬解析等,从而提高数据库的性能。在某些版本中,如果设置不当,Shared Pool可能会极大影响数据库的正常运行。
在Oracle 7之前,Shared Pool并不存在,每个Oracle连接都有一个独立的Server进程与之相关联,Server进程负责解析和优化所有SQL和PL/SQL代码。典型的,在OLTP环境中,很多代码具有相同或类似的结构,反复的独立解析浪费了大量的时间以及资源,Oracle最终认识到这个问题,并且从PL/SQL开始尝试把这部分可共享的内容进行独立存储和管理,于是Shared Pool作为一个独立的SGA组件开始被引入,并且其功能和作用被逐渐完善和发展起来。
在这里注意到,Shared Pool最初被引入的目的,也就是它的本质功能在于实现共享。如果用户的系统代码是完全异构的(假设代码从不绑定变量,从不反复执行),那么就会发现,这时候Shared Pool完全就成为了一个负担,它在徒劳无功地进行无谓的努力:保存代码、执行计划等期待重用,并且客户端要不停的获取Latch,试图寻找共享代码,却始终一无所获。如果真是如此,那这是我们最不愿看到的情况,Shared Pool变得有害无益。当然这是极端,可是在性能优化中我们发现,大多数性能低下的系统都存在这样的通病:代码极少共享,缺乏或从不实行变量绑定。优化这些系统的根本方法就是优化代码,使代码(在保证性能的前提下)可以充分共享,减少无谓的反复硬/软解析。
实际上,Oracle引入Shared Pool就是为了帮助我们实现代码的共享和重用。了解了这一点之后,我们在应用开发的过程中,也应该有意识地?高自己的代码水平,以期减少数据库的压力。这应该是对开发人员最基本的要求。
Shared Pool主要由两部分组成,一部分是库缓存(Library Cahce),另一部分是数据字典缓存(Data Dictionary Cache)。Library Cache主要用于存储SQL语句、SQL语句相关的解析树、执行计划、PL/SQL程序块(包括匿名程序块、存储过程、包、函数等)以及它们转换后能够被Oracle执行的代码等,这部分信息可以通过v$librarycache视图查询;至于Data Dictionary Cache主要用于存放数据字典信息,包括表、视图等对象的结构信息,用户以及对象权限信息,这部分信息相对稳定,在Shared Pool中通过字典缓存单独存放,字典缓存的内容是按行(Row)存储的(其他数据通常按Buffer存储),所以又被称为Row Cache,其信息可以通过V$ROWCACHE查询。
17:44:15 sys@felix SQL>desc v$librarycache;
Name Null? Type
---------------------------- ---------------------------------
NAMESPACE VARCHAR2(64)
GETS NUMBER
GETHITS NUMBER
GETHITRATIO NUMBER
PINS NUMBER
PINHITS NUMBER
PINHITRATIO NUMBER
RELOADS NUMBER
INVALIDATIONS NUMBER
DLM_LOCK_REQUESTS NUMBER
DLM_PIN_REQUESTS NUMBER
DLM_PIN_RELEASES NUMBER
DLM_INVALIDATION_REQUESTS NUMBER
DLM_INVALIDATIONS NUMBER
17:44:40 sys@felix SQL>
17:44:40 sys@felix SQL>desc v$rowcache;
Name Null? Type
------------------------------ -------------------------------
CACHE# NUMBER
TYPE VARCHAR2(11)
SUBORDINATE# NUMBER
PARAMETER VARCHAR2(32)
COUNT NUMBER
USAGE NUMBER
FIXED NUMBER
GETS NUMBER
GETMISSES NUMBER
SCANS NUMBER
SCANMISSES NUMBER
SCANCOMPLETES NUMBER
MODIFICATIONS NUMBER
FLUSHES NUMBER
DLM_REQUESTS NUMBER
DLM_CONFLICTS NUMBER
DLM_RELEASES NUMBER
17:50:55 sys@felix SQL>
下图说明了Shared Pool各个部分协同工作以及与Buffer Cache的配合。
从Oracle Database 11g开始,在Shared Pool中划出了另外一块内存用于存储SQL查询的结果集,称为ResultCache Memory。以 前Shared Pool的主要功能是共享SQL,减少硬解析,从而?高性能,但是SQL共享之后,执行查询同样可能消耗大量的时间和资源,现在Oracle尝试将查询的结果集缓存起来,如果同一SQL或PL/SQL函数多次执行(特别是包含复杂运算的SQL), 那 么 缓 存 的查 询 结 果 可 以 直 接 返 回给用户,不需要真正去执行运算,这样就又为性能带来了极大的提升。
结果集缓存(Result Cache)是Oracle Database 11g新引入的功能,除了可以在服务器端缓存结果集(ServerResult Cache)之外,还可以在客户端缓存结果集(Client Result Cache)。
服务器端的Result Cache Memory由两部分组成:
(1) SQL Query Result Cache:存储SQL查询的结果集。
(2) PL/SQL Function Result Cache:用于存储PL/SQL函数的结果集。
Oracle通过一个新引入初始化参数result_cache_max_size 来控制该Cache的大小。如果result_cache_max_size=0 则表示禁用该特性。参数result_cache_max_result 则控制单个缓存结果可以占总的ServerResult Cache大小的百分比。
09:47:20 sys@felix 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 1M
result_cache_mode string MANUAL
result_cache_remote_expiration integer 0
09:48:09 sys@felix SQL>
上面显示的参数中result_cache_mode用于控制Server result cache的模式,该参数有3个可选设置。
(1) 设置auto:则优化器会自动判断是否将查询结果缓存。
(2) 设置manual:则需要通过查询提示result_cache来告诉优化器是否缓存结果。
(3) 设置force :则尽可能地缓存查询结果(通过提示no_result_cache可以拒绝缓存)
09:52:31 scott@felix SQL>create table felix asselect * from dba_objects;
Table created.
09:53:28 scott@felix SQL>alter systemflush SHARED_POOL;
System altered.
09:53:42 scott@felix SQL>alter system flushBUFFER_CACHE;
System altered.
09:54:06 scott@felix SQL>set autot on;
09:54:25 scott@felix SQL>select count(*) fromfelix;
COUNT(*)
----------
75613
Execution Plan
----------------------------------------------------------
Plan hash value: 2587295606
--------------------------------------------------------------------
| Id |Operation | Name | Rows | Cost (%CPU)| Time |
--------------------------------------------------------------------
| 0 |SELECT STATEMENT | | 1 | 301 (1)| 00:00:04 |
| 1 | SORTAGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| FELIX | 63221 | 301 (1)| 00:00:04 |
--------------------------------------------------------------------
Note
-----
- dynamicsampling used for this statement (level=2)
Statistics
----------------------------------------------------------
70 recursive calls
0 db block gets
1167 consistent gets
1351 physical reads
0 redo size
528 bytes sent via SQL*Net to client
523 bytes received via SQL*Netfrom client
2 SQL*Net roundtrips to/fromclient
5 sorts (memory)
0 sorts (disk)
1 rows processed
09:54:44 scott@felix SQL>
现在再来看看在Server Result Cache下Oracle的行为,首先在result_cache_mode参数设置为MANUAL时:
09:56:02 scott@felix SQL>show parameterresult_cache_mode
NAME TYPE VALUE
------------------------------------ -----------------------------------
result_cache_mode string MANUAL
09:56:50 scott@felix SQL>
需要在SQL语句中手工指定Cache,这需要通过加入一个hints来实现,这个hints是result_cache:
09:56:50 scott@felix SQL>select /* result_cache */ count(*) from felix;
COUNT(*)
----------
75613
执行计划
------------------------------------------------------------ -----------
计划哈希值:2587295606
------------------------------------------------------------ -------------------------------------------------------
| ID |操作|名称 |行|成本(%CPU)|时间|
------------------------------------------------------------ -------------------------------------------------------
| 0 |选择语句| | 1 | 301 (1)| 00:00:04 |
| 1 |结果缓存 | 1hnnwscv2aj3631n497zczt04j | | | |
| 2 |排序聚合| | 1 | | |
| 3 |表访问已满|费利克斯 | 63221 | 301 (1)| 00:00:04 |
------------------------------------------------------------ -------------------------------------------
结果缓存信息(通过操作id标识):
------------------------------------------------------------ -------
1-列数=1;依赖项=(斯科特.费利克斯);属性=(单行);name=“select /* result_cache */ count(*) from felix”
注意
-----
- 用于此语句的动态采样(级别=2)
统计数据
------------------------------------------------------------ -----------
4 次递归调用
0 db 块获取
1137 一致获取
1077 次物理阅读
0 重做大小
通过 SQL*Net 发送到客户端
528 字节通过 SQL*Net 从客户端接收到 523 字节
与客户端之间的 2 次 SQL*Net 往返
0 种(记忆)
0 种(磁盘)
已处理 1 行
09:58:49 scott@felix SQL>
注意到这个执行计划已经和以往的不同,RESULTCACHE以1hnnwscv2aj3631n497zczt04j名称创建。那么在接下来的查询中,这个结果缓存就可以被利用:
09:58:49 scott@felix SQL>select /* result_cache */ count(*) from felix;
COUNT(*)
----------
75613
执行计划
------------------------------------------------------------ -----------
计划哈希值:2587295606
------------------------------------------------------------ -------------------------------------------------------
| ID |操作|名称 |行|成本(%CPU)|时间|
------------------------------------------------------------ -------------------------------------------------------
| 0 |选择语句| | 1| 301 (1)| 00:00:04 |
| 1 |结果缓存 | 1hnnwscv2aj3631n497zczt04j | | | |
| 2 |排序聚合 | | 1 | | |
| 3 |表访问已满|费利克斯 | 63221 | 301 (1)| 00:00:04 |
------------------------------------------------------------ -------------------------------------------
结果缓存信息(通过操作id标识):
------------------------------------------------------------ -------
1 - 列数=1;依赖项=(SCOTT.FELIX);属性=(单行); name="select /* result_cache */ count(*) from felix"
注意
-----
- 用于此语句的动态采样(级别=2)
统计数据
------------------------------------------------------------ -----------
0 次递归调用
0 db 块获取
0 一致获取
0 物理读取
0 重做大小
通过 SQL*Net 发送到客户端
528 字节通过 SQL*Net 从客户端接收到 523 字节
与客户端之间的 2 次 SQL*Net 往返
0 种(记忆)
0 种(磁盘)
已处理 1 行
10:01:08 scott@felix SQL>
在这个利用到Result Cache的查询中,一致性获取减少到0,直接访问结果集,不再需要执行SQL查询。这就是Result Cache的具体端点。
在以上测试中,当result_cache_mode设置为MANUAL时,只有使用提示的情况下,Oracle才会利用服务器结果集;而如果将result_cache_mode设置为AUTO,Oracle如果发现缓冲结果集已经存在,现在就自动使用。但是如果缓冲结果集不存在,Oracle并不会自动进行缓冲,只有使用HINT的情况下,Oracle才会将执行的结果集缓存。
可以通过查询v$result_cache_memory查看Cache的使用情况:
10:05:07 scott@felix SQL>select * fromV$RESULT_CACHE_MEMORY where free='NO';
ID 块偏移免费 OBJECT_ID 位置
---------- ---------- ---------- ------ -------------- ---------
0 0 0 否 0 0
1 0 1 否 1 0
10:05:12 scott@felix SQL>
V$RESULT_CACHE_MEMORY
V$RESULT_CACHE_MEMORY 显示所有内存块及其状态。
专栏 |
数据类型 |
描述 |
ID |
数字 |
唯一区块标识符(即区块编号) |
块 |
数字 |
块所属的Chunk(ID的高27位) |
偏移 |
数字 |
块在其块内的偏移量(ID 的低 5 位) |
免费 |
VARCHAR2(3) |
指示该块是否空闲(YES)或不空闲(NO) |
OBJECT_ID |
数字 |
内存块所属的缓存对象;如果内存块未分配给缓存对象,则为 NULL(FREE = YES) |
位置 |
数字 |
该块在缓存对象中的位置;如果内存块未分配给缓存对象,则为 NULL(FREE = YES) |
通过V$RESULT_CACHE_STATISTICS可以查询结果缓存的统计信息:
10:15:27 scott@felix SQL>select * fromV$RESULT_CACHE_STATISTICS;
IDNAME 值
------------------------------------------------------------ --- ------------------------------------
1 块大小(字节)1024
2块计数最大1024
3块计数当前 32
4结果大小最大(块)51
5创建计数成功1
6创建计数失败0
7找到计数 1
8失效计数 0
9删除计数无效0
10删除计数有效 0
11哈希链长度1
12查找副本计数 1
已选择 12 行。
10:15:34 scott@felix SQL>
V$RESULT_CACHE_OBJECTS 记录了缓存的对象:
10:20:54 scott@felix SQL>SELECT ID,TYPE,NAME,BLOCK_COUNT,ROW_COUNTFROM V$RESULT_CACHE_OBJECTS;
IDTYPE 名称 BLOCK_COUNT ROW_COUNT
---------- ------------------------------------- ----------------------- ----------- ----------
0依赖 SCOTT.FELIX 1 0
1Result select /* result_cache */ count(*) from 1 1
菲利克斯
10:21:19 scott@felix SQL>
V$RESULT_CACHE_OBJECTS 显示所有对象(缓存的结果和依赖项)及其属性。
专栏 |
数据类型 |
描述 |
ID |
数字 |
缓存对象的标识符(也是第一个块的ID) |
类型 |
VARCHAR2(10) |
缓存对象的类型:
|
状态 |
VARCHAR2(9) |
对象的状态:
|
BUCKET_NO |
数字 |
对象的内部哈希桶 |
哈希 |
数字 |
对象的哈希值 |
姓名 |
VARCHAR2(128) |
名称(例如 SQL 前缀或 PL/SQL 函数名称) |
命名空间 |
VARCHAR2(5) |
命名空间:
|
CREATION_TIMESTAMP |
日期 |
对象创建时间 |
CREATOR_UID |
数字 |
创建对象的 UID |
DEPEND_COUNT |
数字 |
依赖项(TYPE = Result)或依赖项(TYPE = Dependency)的数量 |
BLOCK_COUNT |
数字 |
缓存对象中的块总数 |
SCN |
数字 |
构建 SCN(TYPE = 结果)或失效 SCN(TYPE = 依赖项) |
COLUMN_COUNT |
数字 |
缓存结果中的列数脚 1 |
PIN_COUNT |
数字 |
此结果的活动扫描数量Footref 1 |
SCAN_COUNT |
数字 |
对缓存结果发起的扫描总数Footref 1 |
ROW_COUNT |
数字 |
缓存结果中的总行数Footref 1 |
ROW_SIZE_MAX |
数字 |
最大行的大小(以字节为单位)Footref 1 |
ROW_SIZE_MIN |
数字 |
最小行的大小(以字节为单位)Footref 1 |
ROW_SIZE_AVG |
数字 |
行的平均大小(以字节为单位)Footref 1 |
构建时间 |
数字 |
构建缓存结果所花费的时间(以百分之一秒为单位)Footref 1 |
LRU_NUMBER |
数字 |
LRU 列表位置(值越小,最近使用过)Footref 1 |
OBJECT_NO |
数字 |
依赖对象的字典对象编号Foot 2 |
无效 |
数字 |
对象使其依赖项无效的次数Footref 2 |
SPACE_OVERHEAD |
数字 |
结果的开销(以字节为单位)Footref 1 |
SPACE_UNUSED |
数字 |
结果的未使用空间(以字节为单位)Footref 1 |
CACHE_ID |
VARCHAR2(93) |
结果的 CacheId(如果是依赖项,则为对象名称) |
CACHE_KEY |
VARCHAR2(93) |
结果的CacheKey(如果是依赖项,则为对象名称) |
DB_LINK脚 3 |
VARCHAR2(3) |
可能的值:
|
校验和Footref 3 |
数字 |
结果对象的校验和。校验和是针对结果缓存对象中减去对象标头的所有块计算的。 |
脚注 1 这些列仅对 TYPE = Result 有效;否则,它们为 NULL。
脚注 2 这些列仅对 TYPE = Dependency 有效;否则,它们为 NULL。
脚注 3 此专栏从 Oracle Database11g 第 2 版 (11.2.0.4) 开始提供
表 7-6 与服务器和客户端结果缓存相关的视图和表
|
描述 | ||||||||||||||
V$RESULT_CACHE_STATISTICS | 列出各种服务器结果缓存设置和内存使用统计信息。 | ||||||||||||||
V$RESULT_CACHE_MEMORY | 列出服务器结果缓存中的所有内存块及其对应的统计信息。 | ||||||||||||||
V$RESULT_CACHE_OBJECTS | 列出结果位于服务器结果缓存中的所有对象及其属性。 | ||||||||||||||
V$RESULT_CACHE_DEPENDENCY | 列出服务器缓存中的结果之间的依赖关系详细信息以及这些结果之间的依赖关系。 | ||||||||||||||
CLIENT_RESULT_CACHE_STATS$ | 存储从 OCI 客户端进程获取的客户端结果缓存的缓存设置和内存使用统计信息。该统计表包含每个使用结果缓存的客户端进程的条目。客户端进程终止后,数据库会从此表中删除它们的条目。客户端表列出了类似V$RESULT_CACHE_STATISTICS的信息。 另请参阅: Oracle 数据库参考,了解有关 CLIENT_RESULT_CACHE_STATS$ 的详细信息 | ||||||||||||||
DBA_TABLES、USER_TABLES、ALL_TABLES | 包括一个 RESULT_CACHE 列,显示表的结果缓存模式注释。如果表尚未注释,则此列显示 DEFAULT。此列适用于服务器和客户端结果缓存。 |