当进程想要获取锁存器而此时该锁存器正被其他进程持有时产生Latch Free(锁存器空闲)等待事件,类似于排队,Oracle使用锁存器来保护数据结构。一次只能在一个进程在获得锁存器后修改或检查数据结构。其他需要访问该数据结构的进程必须等到它们获得锁存器后。不同于排队
的是,请求锁存器的进程不需要在队列中等待。如果获取锁存器失败,则进程仅仅等待一小段时间就可以再次请求锁存器。这一小段等待时间成为“自旋”(spin)。如果在一次或多次自旋重复(spin iterations)之后还没获取锁存器,进程就休眠一小段时间,然后再次尝试获取锁存器,
接下来休眠更长一段时间,直到获得锁存器。
最常见的锁存器有:cache buffer chains(高速缓存缓冲区链)、library cache(高速缓存)
和shared pool(共享池)。
1、等待参数
latch free的等待参数描述如下:
◎ P1 进程等待的锁存器地址。
◎ P2 锁存器号,同v$latchname.latch#
要查找等待锁存器的名称,可以是哟功能如下SQL语句:
SELECT *
FROM v$latchname
WHERE latch# = &P2_Value;
◎ P3 尝试的次数;显示进程试图获取锁存器的次数计数器。
2、等待时间
该时间的等待时间呈指数级增长。他不包含进程为锁存器自旋(spin) 花费的时间。
以不等待模式获取的锁存器在IMMEDIATE_GETS和IMMEDIATE_MISSES列中统计。
通过愿意等待模式获取锁存器在GETS和MISSES列中有统计。特殊锁存器的GETS列在每次进程
通过愿意等待模式请求该锁存器时递增。
如果锁存器不能用,那么进程会在CPU上自旋(spin)一小段时间,重新尝试该锁存器。
v$system_Event视图中的latch free等待事件的total_waits统计追踪进程以愿意等待模式
无法获得一个锁存器的次数。v$latch视图中特殊锁存器的sleeps统计追踪进程在锁存器上睡眠
的次数。因为进程在_SPIN_COUNT次数后无法获得锁存器时没有别的事可做只好睡眠,total_times
应该等于sleeps的综合,然而,很多时候total_times大于sleeps的总合。这是因为sleeps统计
只有在锁存器GET操作成功才更新,而不是每次尝试是更新。
因为latch free等待事件总是很短,所以可以看到total_waits的一个较大的数字,这是计
数器在一段短时间内发生的等待事件。
3、常见原因、诊断和动作
锁存器征用表名另一个进程持有该锁存器过长的时间。当结合高需求量,这种争用带
来的影响将会扩大,从而明显地降低性能。锁存器争用在高度并发的环境中很普遍。可以
根据如下SQL查看进程竞争的热锁存器:
SELECT NAME,
Gets,
Misses,
Immediate_Gets,
Immediate_Misses,
Sleeps
FROM V$latch
ORDER BY Sleeps;
◎ shared pool锁存器和library cache锁存器的争用——解析
shared pool锁存器和library cache锁存器的争用主要是由于紧密的硬解析。过多
的硬解析通常出现在主要使用带有字面值的SQL语句的应用程序中。应解析是代价
昂贵的才做,并且在解析期间必须持有子library cache锁存器。
①.使用下面语句查数据库中硬解析的数量:
SELECT a.*,
SYSDATE - b.Startup_Time Days_Old
FROM V$sysstat a,
V$instance b
WHERE a.NAME LIKE 'parse%';
②.发现执行许多硬解析的当前会话:
SELECT a.Sid,
c.Username,
b.NAME,
a.VALUE,
Round((SYSDATE - c.Logon_Time) * 24) Hours_Connected
FROM V$sesstat a,
V$statname b,
V$session c
WHERE c.Sid = a.Sid
AND a.Statistic# = b.Statistic#
AND a.VALUE > 0
AND b.NAME = 'parse count (hard)'
ORDER BY a.VALUE;
③.标识作为绑定变量的合适候选的literal SQL语句:
SELECT Hash_Value,
Substr(Sql_Text, 1, 80)
FROM V$sqlarea
WHERE Substr(Sql_Text, 1, 40) IN
(SELECT Substr(Sql_Text, 1, 40)
FROM V$sqlarea
HAVING COUNT(*) > 4
GROUP BY Substr(Sql_Text, 1, 40))
ORDER BY Sql_Text;
④.正确的设置SESSION_CACHED_CURSOES参数:
每当SQL语句到达时,Oracle检查该语句是否已经在库高速缓存中。如果是的话,
只需要很少的开销就可以执行该语句;这种进程被称为软解析。在硬解吸不行时,软
解析也不好。library cache锁存器在软解析中获得。Oracle仍然必须检查语句的语法
和语义,除非该语句高速缓存在会话的游标高速缓存中。通过正确的设置
SESSION_CACHED_CURSOES参数,可以减少library cache锁存器持有时间。然而最好的
方法是减少软解析的数量,这只可通过应用程序完成。
通过在v$sqlarea视图中查找带有大量parse_calls的语句,你可以找到令人不
愉快的语句。
如果发现数据库具有长共享池空闲列表,并且应用程序使用literal SQL,那么就
应该考虑减少SHARED_POOL_SIZE。这将减少share pool锁存器的争用。
也应该使用dbms_shared_pool.keep过程钉住共享池中可重用的对象。
v$db_object_cache视图具有保存相关对象的信息。
◎ library cache锁存器的争用——带有高版本数的语句
Oracle使用多个子游标来区分一些SQL语句,这些SQL语句具有相同的字符,但是不能
被共享,因为他们引用不同的底层对象。例如:如果数据库有三个Customer表,并且
每个表属于不同的模式,那么每个拥有者发布的SELECT * FROM Customer语句将具有
相同的散列值,但是具有不同的子段数量。当被解析语句散列值匹配带有高的子段数
或版本数的语句散列值时,Oracle必须比较语句和所有的现有版本。必须在检查期间
持续持有library cache锁存器,着可能造成其他进程无法实现他们的library cache
锁存器请求。通过在数据库中使用唯一的对象名,可以最小化这个问题。
查询列出v$sqlarea中版本数大于20的所有SQL语句:
SELECT Version_Count,
Sql_Text
FROM V$sqlarea
WHERE Version_Count > 20
ORDER BY Version_Count,
Hash_Value;
◎ cache buffer chains锁存器
在将数据读入SGA时,他们的缓冲区头被防止在悬挂散列存储桶的链表中(散列链)。
这种内存结构由大量子cache buffers chains锁存器(也称散列锁存器或CBC锁存器)
保护。希望在添加、删除、搜索、检查、读取、或修改块的进程必须首先获得cache
buffers chains锁存器,从而保护链上的缓冲区。这样就可以保证独占访问,并防
止其它进程接下来读取或改变同一个链。为了完整性牺牲了并发性。通过设置
_DB_BLOCK_HASH_LATCHS参数可以调整散列锁存器数量。
如下SQL取实例中散列锁存器的数量:
SELECT COUNT(DISTINCT(Hladdr)) FROM X$bh;
①.cache buffers chains锁存器的争用——底效率的SQL语句
低效率的SQL语句是cache buffers chains锁存器争用的主要原因。当和高并发
率混合在一起时,花费在latch free等待事件上的时间可能更多。在某些环境中,
应用程序打开执行相同的低效率SQL语句的多个并发会话,并且这些SQL语句都没法得
到相同的数据集,这种情况下相当普遍。
如果集注下面3个方面,你可以做的很好:
◆ 每个逻辑读取需要一个latch gey操作和一个CPU
◆ 从latch get例程中获得的唯一方法是获得锁存器
◆ 在任意时刻,只有一个进程可以拥有cache buffer链,并且这个锁存器覆盖
许多数据块,另一个进程可能需要其中一些数据块。
②.cache buffers chains锁存器的争用——热块
热块(hot block)是cache buffer chains锁存器争用的另一个常见原因。当多个
会话重复访问一个或多个由同一个子chche buffer chains锁存器保护的块时,热块
就产生。这主要是一种应用程序问题。在大多数情况下,增加cache buffer chains
锁存器的数量对性能的改变没多大帮助。这是因为块被散列为散列存储桶和基于块地
址和散列存储桶数量的链,而不是基于cache buffer chains锁存器的数量。如果块
地址和散列存储桶的数量保持不变,则可能有少数热块仍然被一个cache buffer chains
锁存器覆盖,除非锁存器数量极大的增加。
捕获参与争用的SQL语句:
-- Using the P1RAW from the above example (00000400837D7800).
SELECT a.Hladdr,
a.File#,
a.Dbablk,
a.Tch,
a.Obj,
b.Object_Name
FROM X$bh a,
Dba_Objects b
WHERE (a.Obj = b.Object_Id OR a.Obj = b.Data_Object_Id)
AND a.Hladdr = '&P1RAW_Value'
UNION
SELECT Hladdr,
File#,
Dbablk,
Tch,
Obj,
NULL
FROM X$bh
WHERE Obj IN
(SELECT Obj
FROM X$bh
WHERE Hladdr = '&P1RAW_Value'
MINUS
SELECT Object_Id
FROM Dba_Objects
MINUS
SELECT Data_Object_Id FROM Dba_Objects)
AND Hladdr = '&P1RAW_Value'
ORDER BY 4;
展开块的方法:
◆ 通过ROWID删除并且重新插入一些行
◆ 输出表,较大的增加PCTFREE,并且输入数据。这可以最小化每个块的行数量,
将他们展开到多个块上。当然这是以存储空间作为代价,并且全表扫描会更慢。
◆ 最小化表中每个块的记录数量。这涉及转储少量数据块,用于获得每个块的当
前理想行数。手工插入你确定合适的行数,然后发布命令:
ALTER TABLE table_name MINIMIZE RECORED_PER_BLOCK。截取表和输入数据。
◆ 对于索引,对于较高的PCTFREE值可以重建他们,需要注意的是这种方法可能
增加索引的高度。
◆ 考虑见效块大小。这将对全表扫描带来极大的负面影响。
③.cache buffers chains锁存器的争用——长散列链
多个数据块可能被散列到同一个散列存储桶,他们和指针一起被连接到属于这个
散列存储桶的散列链上。对于大型数据库,散列链上块的数量可以达到数百个。进程
不得不顺序的扫描散列以获得所需的块,同时持有cache buffers chains锁存器不变。
我们称之为“追赶链”。当扫描长链时,锁存器必须被持有更厂的时间,并且可能造
成另一个进程无法实现她的cache buffers chains锁存器请求。
一直到Oracle 8.0,我们可以很容易地确定特定的散列链长度,因为散列锁存器、
散列存储桶和散列链之间的关系是1:1:1,散列链的长度等于有锁存器保护块的数量。
下面查寻报告每个散列上块的数量,带有10个或更多块的链为长链。
SELECT Hladdr,
COUNT(*)
FROM X$bh
GROUP BY Hladdr
ORDER BY 2;
通过使用_DB_BLOCK_HASH_BUSH_BUCKETS参数增加散列存储桶的数量,可以减少散
列的长度:
_DB_BLOCK_HASH_BUCKETS = 128021
_DB_BLOCK_HASH_LATCHES = 1024
Ratio = 128021 / 1024 = 125
的是,请求锁存器的进程不需要在队列中等待。如果获取锁存器失败,则进程仅仅等待一小段时间就可以再次请求锁存器。这一小段等待时间成为“自旋”(spin)。如果在一次或多次自旋重复(spin iterations)之后还没获取锁存器,进程就休眠一小段时间,然后再次尝试获取锁存器,
接下来休眠更长一段时间,直到获得锁存器。
最常见的锁存器有:cache buffer chains(高速缓存缓冲区链)、library cache(高速缓存)
和shared pool(共享池)。
1、等待参数
latch free的等待参数描述如下:
◎ P1 进程等待的锁存器地址。
◎ P2 锁存器号,同v$latchname.latch#
要查找等待锁存器的名称,可以是哟功能如下SQL语句:
SELECT *
FROM v$latchname
WHERE latch# = &P2_Value;
◎ P3 尝试的次数;显示进程试图获取锁存器的次数计数器。
2、等待时间
该时间的等待时间呈指数级增长。他不包含进程为锁存器自旋(spin) 花费的时间。
以不等待模式获取的锁存器在IMMEDIATE_GETS和IMMEDIATE_MISSES列中统计。
通过愿意等待模式获取锁存器在GETS和MISSES列中有统计。特殊锁存器的GETS列在每次进程
通过愿意等待模式请求该锁存器时递增。
如果锁存器不能用,那么进程会在CPU上自旋(spin)一小段时间,重新尝试该锁存器。
v$system_Event视图中的latch free等待事件的total_waits统计追踪进程以愿意等待模式
无法获得一个锁存器的次数。v$latch视图中特殊锁存器的sleeps统计追踪进程在锁存器上睡眠
的次数。因为进程在_SPIN_COUNT次数后无法获得锁存器时没有别的事可做只好睡眠,total_times
应该等于sleeps的综合,然而,很多时候total_times大于sleeps的总合。这是因为sleeps统计
只有在锁存器GET操作成功才更新,而不是每次尝试是更新。
因为latch free等待事件总是很短,所以可以看到total_waits的一个较大的数字,这是计
数器在一段短时间内发生的等待事件。
3、常见原因、诊断和动作
锁存器征用表名另一个进程持有该锁存器过长的时间。当结合高需求量,这种争用带
来的影响将会扩大,从而明显地降低性能。锁存器争用在高度并发的环境中很普遍。可以
根据如下SQL查看进程竞争的热锁存器:
SELECT NAME,
Gets,
Misses,
Immediate_Gets,
Immediate_Misses,
Sleeps
FROM V$latch
ORDER BY Sleeps;
◎ shared pool锁存器和library cache锁存器的争用——解析
shared pool锁存器和library cache锁存器的争用主要是由于紧密的硬解析。过多
的硬解析通常出现在主要使用带有字面值的SQL语句的应用程序中。应解析是代价
昂贵的才做,并且在解析期间必须持有子library cache锁存器。
①.使用下面语句查数据库中硬解析的数量:
SELECT a.*,
SYSDATE - b.Startup_Time Days_Old
FROM V$sysstat a,
V$instance b
WHERE a.NAME LIKE 'parse%';
②.发现执行许多硬解析的当前会话:
SELECT a.Sid,
c.Username,
b.NAME,
a.VALUE,
Round((SYSDATE - c.Logon_Time) * 24) Hours_Connected
FROM V$sesstat a,
V$statname b,
V$session c
WHERE c.Sid = a.Sid
AND a.Statistic# = b.Statistic#
AND a.VALUE > 0
AND b.NAME = 'parse count (hard)'
ORDER BY a.VALUE;
③.标识作为绑定变量的合适候选的literal SQL语句:
SELECT Hash_Value,
Substr(Sql_Text, 1, 80)
FROM V$sqlarea
WHERE Substr(Sql_Text, 1, 40) IN
(SELECT Substr(Sql_Text, 1, 40)
FROM V$sqlarea
HAVING COUNT(*) > 4
GROUP BY Substr(Sql_Text, 1, 40))
ORDER BY Sql_Text;
④.正确的设置SESSION_CACHED_CURSOES参数:
每当SQL语句到达时,Oracle检查该语句是否已经在库高速缓存中。如果是的话,
只需要很少的开销就可以执行该语句;这种进程被称为软解析。在硬解吸不行时,软
解析也不好。library cache锁存器在软解析中获得。Oracle仍然必须检查语句的语法
和语义,除非该语句高速缓存在会话的游标高速缓存中。通过正确的设置
SESSION_CACHED_CURSOES参数,可以减少library cache锁存器持有时间。然而最好的
方法是减少软解析的数量,这只可通过应用程序完成。
通过在v$sqlarea视图中查找带有大量parse_calls的语句,你可以找到令人不
愉快的语句。
如果发现数据库具有长共享池空闲列表,并且应用程序使用literal SQL,那么就
应该考虑减少SHARED_POOL_SIZE。这将减少share pool锁存器的争用。
也应该使用dbms_shared_pool.keep过程钉住共享池中可重用的对象。
v$db_object_cache视图具有保存相关对象的信息。
◎ library cache锁存器的争用——带有高版本数的语句
Oracle使用多个子游标来区分一些SQL语句,这些SQL语句具有相同的字符,但是不能
被共享,因为他们引用不同的底层对象。例如:如果数据库有三个Customer表,并且
每个表属于不同的模式,那么每个拥有者发布的SELECT * FROM Customer语句将具有
相同的散列值,但是具有不同的子段数量。当被解析语句散列值匹配带有高的子段数
或版本数的语句散列值时,Oracle必须比较语句和所有的现有版本。必须在检查期间
持续持有library cache锁存器,着可能造成其他进程无法实现他们的library cache
锁存器请求。通过在数据库中使用唯一的对象名,可以最小化这个问题。
查询列出v$sqlarea中版本数大于20的所有SQL语句:
SELECT Version_Count,
Sql_Text
FROM V$sqlarea
WHERE Version_Count > 20
ORDER BY Version_Count,
Hash_Value;
◎ cache buffer chains锁存器
在将数据读入SGA时,他们的缓冲区头被防止在悬挂散列存储桶的链表中(散列链)。
这种内存结构由大量子cache buffers chains锁存器(也称散列锁存器或CBC锁存器)
保护。希望在添加、删除、搜索、检查、读取、或修改块的进程必须首先获得cache
buffers chains锁存器,从而保护链上的缓冲区。这样就可以保证独占访问,并防
止其它进程接下来读取或改变同一个链。为了完整性牺牲了并发性。通过设置
_DB_BLOCK_HASH_LATCHS参数可以调整散列锁存器数量。
如下SQL取实例中散列锁存器的数量:
SELECT COUNT(DISTINCT(Hladdr)) FROM X$bh;
①.cache buffers chains锁存器的争用——底效率的SQL语句
低效率的SQL语句是cache buffers chains锁存器争用的主要原因。当和高并发
率混合在一起时,花费在latch free等待事件上的时间可能更多。在某些环境中,
应用程序打开执行相同的低效率SQL语句的多个并发会话,并且这些SQL语句都没法得
到相同的数据集,这种情况下相当普遍。
如果集注下面3个方面,你可以做的很好:
◆ 每个逻辑读取需要一个latch gey操作和一个CPU
◆ 从latch get例程中获得的唯一方法是获得锁存器
◆ 在任意时刻,只有一个进程可以拥有cache buffer链,并且这个锁存器覆盖
许多数据块,另一个进程可能需要其中一些数据块。
②.cache buffers chains锁存器的争用——热块
热块(hot block)是cache buffer chains锁存器争用的另一个常见原因。当多个
会话重复访问一个或多个由同一个子chche buffer chains锁存器保护的块时,热块
就产生。这主要是一种应用程序问题。在大多数情况下,增加cache buffer chains
锁存器的数量对性能的改变没多大帮助。这是因为块被散列为散列存储桶和基于块地
址和散列存储桶数量的链,而不是基于cache buffer chains锁存器的数量。如果块
地址和散列存储桶的数量保持不变,则可能有少数热块仍然被一个cache buffer chains
锁存器覆盖,除非锁存器数量极大的增加。
捕获参与争用的SQL语句:
-- Using the P1RAW from the above example (00000400837D7800).
SELECT a.Hladdr,
a.File#,
a.Dbablk,
a.Tch,
a.Obj,
b.Object_Name
FROM X$bh a,
Dba_Objects b
WHERE (a.Obj = b.Object_Id OR a.Obj = b.Data_Object_Id)
AND a.Hladdr = '&P1RAW_Value'
UNION
SELECT Hladdr,
File#,
Dbablk,
Tch,
Obj,
NULL
FROM X$bh
WHERE Obj IN
(SELECT Obj
FROM X$bh
WHERE Hladdr = '&P1RAW_Value'
MINUS
SELECT Object_Id
FROM Dba_Objects
MINUS
SELECT Data_Object_Id FROM Dba_Objects)
AND Hladdr = '&P1RAW_Value'
ORDER BY 4;
展开块的方法:
◆ 通过ROWID删除并且重新插入一些行
◆ 输出表,较大的增加PCTFREE,并且输入数据。这可以最小化每个块的行数量,
将他们展开到多个块上。当然这是以存储空间作为代价,并且全表扫描会更慢。
◆ 最小化表中每个块的记录数量。这涉及转储少量数据块,用于获得每个块的当
前理想行数。手工插入你确定合适的行数,然后发布命令:
ALTER TABLE table_name MINIMIZE RECORED_PER_BLOCK。截取表和输入数据。
◆ 对于索引,对于较高的PCTFREE值可以重建他们,需要注意的是这种方法可能
增加索引的高度。
◆ 考虑见效块大小。这将对全表扫描带来极大的负面影响。
③.cache buffers chains锁存器的争用——长散列链
多个数据块可能被散列到同一个散列存储桶,他们和指针一起被连接到属于这个
散列存储桶的散列链上。对于大型数据库,散列链上块的数量可以达到数百个。进程
不得不顺序的扫描散列以获得所需的块,同时持有cache buffers chains锁存器不变。
我们称之为“追赶链”。当扫描长链时,锁存器必须被持有更厂的时间,并且可能造
成另一个进程无法实现她的cache buffers chains锁存器请求。
一直到Oracle 8.0,我们可以很容易地确定特定的散列链长度,因为散列锁存器、
散列存储桶和散列链之间的关系是1:1:1,散列链的长度等于有锁存器保护块的数量。
下面查寻报告每个散列上块的数量,带有10个或更多块的链为长链。
SELECT Hladdr,
COUNT(*)
FROM X$bh
GROUP BY Hladdr
ORDER BY 2;
通过使用_DB_BLOCK_HASH_BUSH_BUCKETS参数增加散列存储桶的数量,可以减少散
列的长度:
_DB_BLOCK_HASH_BUCKETS = 128021
_DB_BLOCK_HASH_LATCHES = 1024
Ratio = 128021 / 1024 = 125
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/35489/viewspace-84741/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/35489/viewspace-84741/