【每日一摩斯】-Shared Pool优化和Library Cache Latch冲突优化 (1523934.1)-系列1
【每日一摩斯】-Shared Pool优化和Library Cache Latch冲突优化 (1523934.1)-系列1
什么是Shared Pool?
Oracle的实例主要包括共享内存(主要是SGA,还有PGA)和Background Processes,其中SGA中又包括了Shared Pool、Buffer Cache、Redo Log Buffer以及其它一些内存区。
Oracle在SGA的一个特定区域中保留SQL语句、Package是、对象信息以及其它一些内容,这就是Shared Pool。这个共享内存区域是由一个复杂的cache和heap manager 构成的。它需要解决三个基本问题:
1. 每次分配的内存大小是不一致的,从几个字节到上千个字节;
2. 因为shared pool的目的是为了最大化共享信息,所以不是每次一个用户用完之后就可以释放这段内存(在传统的heap manager方式会遇到这个问题)。内存中的信息可能对于其他session来说是有用的——Oracle并不能事先知道这些内容是否会被再次用到;
3. Shared pool中的内容不能被写入到硬盘区域中,这一点和传统cache是不一样的。只有“可重建”的信息可以被覆盖,因为他们可以在下次需要时重建。
基于这些背景,我们就可以理解shared pool的管理是一件非常复杂的事情。
MOS介绍这部分知识时提到了若干术语:
(1)、Literal SQL(翻译过来又叫字面SQL)
一个Literal SQL语句是指在predicate中使用具体值,而不是使用绑定变量,即不同的执行语句使用的具体值可能是不一样的。
例1:应用程序使用了:
SELECT * FROM emp WHERE ename='CLARK';
而不是:
SELECT * FROM emp WHERE ename=:bind1;
例2: 以下语句不用绑定变量但是也不会被认为是literal SQL,因为这个语句可以被多次执行共享。
SELECT sysdate FROM dual;
例 3: 如果整个应用都是用相同的值'2.0'来检查'version'的话,那么这个语句可以被认为是可以共享的。
SELECT version FROM app_version WHERE version>2.0;
(2)、Hard Parse(硬解析)
简单讲,一个新的SQL被执行,但又不在Shared Pool中,那么它将被完整地解析一次(这里的过程其实很多,主要包括语法语义检查、执行计划的解读与选择),这其中牵扯到访问dictionary cache、Shared pool latch的获取、执行计划的计算等等,其中执行计划的计算是最耗CPU资源的。
(3)、Soft Parse(软解析)
与硬解析相对的,如果一个session请求的一个已经在Shared pool的SQL语句,无需进行上述执行计划计算等这些步骤,就被称为‘Soft Parse',对于应用来说,它只需请求解析这个语句。(实际上还有所谓的软软解析)。
(4)、完全相同的语句?
如果两个SQL语句的含义相同但是没有使用相同的字符,那么Oracle认为它们是不同的语句。比如SCOTT在一个Session中提交的这两个语句:
SELECT ENAME from EMP;
SELECT ename from emp;
尽管它们实际上是相同的,但是因为大写字母‘E’和小写字母'e'的区别,他们不会被认为是完全相同的语句。
为什么有如上说法呢?因为Oracle会将一个SQL转换为ASCII值,然后利用HASH函数计算其对应的hash值,到Library Cache中找是否有对应于此唯一HASH值的bucket,如果有比较bucket中是否存在该SQL,若没有则执行硬解析。那么大小写的ASCII是不同的,因此要求是case-insensitive。
(5)、Sharable SQL
如果是两个不同的session发起了完全相同的SQL语句,这也不意味着这个语句是可以共享的。比如说:用户SCOTT下有一个表EMP,发起了下面的语句:
SELECT ENAME from EMP;
用户FRED 有一个自己的表也叫EMP并且发起相同的语句:
SELECT ENAME from EMP;
尽管语句完全一样但是由于需要访问的EMP表是不同的对象,所以需要对这条语句产生不同的版本。
这里MOS没有展开说,实际上这里说到的两个用户,其对应于两个schema,我们知道不同的schema中有不同的对象,不同schema内的对象若被其它用户访问,需要DBA或该用户赋予相关权限。另外,SQL硬解析时需要将对象进行名转换,比如讲同义词转换为实际的对象,我想这里SCOTT执行的SELECT ENAME from EMP应该转换成SELECT ENAME from SCOTT.EMP。因此这两个SQL是不同的。
有很多条件来判断两个完全一致的SQL文本是不是真的是完全相同(以至于他们可以被共享),包括:
语句中引用的所有的对象名必须都被解析成实际相同的对象。
发起语句的session中的optimizer相关的参数应该一致。
绑定变量的类型和长度应该是"相似的"。(这里不做详细讨论,但是类型和长度的不同确实会导致语句被分为不同的版本)
发起语句的NLS (National Language Support)设置必须相同。
(6)、语句的版本
正如之前在'Sharable SQL'中描述的,如果两个语句字面上完全相同但是又不能被共享,则会对相同的语句产生不同的'version',即版本。如果Oracle要匹配一个包含多个版本的语句,它将不得不检查每一个版本来看它们是不是和当前被解析的语句完全相同。所以最好用以下方法来避免高版本数(high version count)。
客户端使用的绑定变量最大长度需标准化。
如果有大量的schema会包含相同名字的对象,那么避免使用一个相同的SQL语句。比如:SELECT xx FROM MYTABLE; 并且每个用户都有一个自己的MYTABLE 的情况。
在Oracle 8.1可以将_SQLEXEC_PROGRESSION_COST设置成'0'。
(7)、Library Cache和Shared Pool latches
shared pool latch是用来保护从shared pool中分配和释放内存的关键性操作。
Library cache latche(以及Oracle 7.1中的library cache pin latch)是用来保护library cache中的操作。
所有的这些Latch都是潜在的资源争用的对象,latch gets发生的次数直接受到shared pool中活动(activity)个数的影响,特别是parse操作。任何减少latch gets或者shared pool中活动(activity)个数的尝试都有助于提高性能和可扩展性。