关于 shared cursor,parent cursor,child cursor

http://space.itpub.net/50897/viewspace-584304

1. shared SQL,parent cursor,child cursor 概念:

  所有SQL都是Oracle暗式共享的(implicitly sharable)。当用户A发出一条SQL后,Oracle会根据SQL文本内容生成hash value(10g还有唯一的SQL_ID),以便能够快速找到 Shared pool已经存在的相同SQL。如果找不到,则Oracle会为这个SQL创建一个parent cursor和一个child cursor,这与SQL是否共享是没有关系的。

  parent cursor包含了SQL TEXT和相关的hash value,v$sqlarea中的每一行代表了一个parent cursor,根据address表示了其内存地址。

  child cursor包含了SQL的metadata,即使得这个SQL可以执行的所有相关信息,如OBJECT和权限,优化器设置,执行计划等。v$sql中中的每一行表示了一个child cursor,根据hash value和address与parent cursor 关联。child cursor有自己的address,即V$SQL.CHILD_ADDRESS。

  第一个child cursor总是使用0来表示其创建顺序,V$SQL.CHILD_NUMBER = 0。因此,当从V$SQL_PLAN中查找某个SQL的执行计划时,要注意你写对了CHILD_NUMBER 。

如果有多个child cursor,则表示parent cursor有多个版本,v$sqlarea中的version_count字段就会纪录下来。

2. 如何确定SQL 是否可以共享:

  假设用户A执行完一条SQL后,退出然后重新登陆,发出同一条SQL,则根据hash value找到Shared pool中已经存在的parent cursor,然后把 此parent cursor下的child cursor list搜寻一边,判断metadata是否完全相同,如果是,则这条sql可以共享,就完成了一次soft parse。
 
  假设用户B之后发出一条SQL文本完全一样的SQL,但访问的table不是A用户的,而是B用户自己的,则metadata出现AUTH_CHECK_MISMATCH 和
  TRANSLATION_MISMATCH ,无法共享child cursor。Oracle会因此在此parent cursor 下创建一个新的child cursor,也就是一个hard parse。
 
  因此,SQL 是否可以共享是与parent cursor无关的,而是由child cursor决定的。
 
  从v$sql_shared_cursor可以获得详细的无法共享的原因:
  select * from v$sql_shared_cursor where kglhdpar = <parent address>  --or sql_id = ''
  select * from v$sql_shared_cursor where address = <parent address>   --or sql_id = ''

一般常见的mismatch是:
 
  OPTIMIZER_MISMATCH  : 优化器环境设置不同,一般是optimizer相关参数
  BIND_MISMATCH      : 绑定变量的值的长度在第二次执行的时候发生显著的变化    AUTH_CHECK_MISMATCH : 授权关系不匹配
  TRANSLATION_MISMATCH: 事务环境不匹配

其实最常见的是 BIND_MISMATCH ,在10g中可以测试一下:

create table t1(col1 varchar2(4000));

declare
v_col1 varchar2(4000);
begin
v_col1 := 't';
for i in 1..30 loop
v_col1 := v_col1 ||'t';
insert into t1 values(v_col1);
end loop;
end;
/

--可以看出,变量长度在30以下的时候,还是只有一个child cursor:

SQL_TEXT                    EXECUTIONS CHILD_NUMBER  ADDRESS  HASH_VALUE
--------------------------- ---------- ------------  -------- ----------
INSERT INTO T1 VALUES(:B1 )         30            0  9E355F10 2351142747

declare
v_col1 varchar2(4000);
begin
v_col1 := 'tttttttttttttttttttttttttttttt';
for i in 31..4000 loop
v_col1 := v_col1 ||'t';
insert into t1 values(v_col1);
end loop;
end;
/

--可以看出,变量长度变化导致了四个child cursor 存在:

SQL_TEXT                     EXECUTIONS CHILD_NUMBER CHILD_ADDRESS  ADDRESS  HASH_VALUE
---------------------------- ---------- ------------ -------------  -------- ----------
INSERT INTO T1 VALUES(:B1 )          32            0 9E355DCC       9E355F10 2351142747
INSERT INTO T1 VALUES(:B1 )          96            1 9E34BA18       9E355F10 2351142747
INSERT INTO T1 VALUES(:B1 )        1872            2 9E34B8D4       9E355F10 2351142747
INSERT INTO T1 VALUES(:B1 )        2060            3 9E34B790       9E355F10 2351142747

在数据库操作中,游标(Cursor)用于遍历和操作查询结果集。当使用游标时,可能会遇到与游标锁相关的问题,尤其是在多线程或并发访问的情况下。以下是关于数据库游标锁问题的解析及其解决方法。 ### 游标锁问题 游标锁通常发生在游标打开期间,数据库为了保证数据的一致性和完整性,会对正在访问的数据行进行锁定。这种锁定机制在并发访问时可能导致以下问题: - **死锁**:当两个或多个事务相互等待对方释放锁时,就会发生死锁。 - **阻塞**:一个事务持有锁的时间过长,导致其他事务长时间等待,影响系统性能。 - **资源竞争**:多个事务同时尝试获取相同的锁,可能导致资源竞争,进而影响系统的吞吐量。 ### 游标锁的类型 1. **共享锁(Shared Locks)**:允许多个事务同时读取同一资源,但在共享锁存在时,任何事务都不能修改数据。 2. **排他锁(Exclusive Locks)**:确保事务可以读取数据,并且阻止其他事务获取该资源的任何类型的锁。 3. **更新锁(Update Locks)**:一种介于共享锁和排他锁之间的锁,用于防止多个事务同时更新同一资源。 ### 解决方法 1. **减少事务的持有时间**:尽量缩短事务的执行时间,以减少锁的持有时间,从而降低死锁和阻塞的可能性。 2. **使用合适的隔离级别**:根据业务需求选择适当的事务隔离级别,例如读已提交(Read Committed)、可重复读(Repeatable Read)等,以平衡一致性和性能[^1]。 3. **避免全表扫描**:通过优化查询语句和索引设计,减少不必要的全表扫描,从而减少锁的范围。 4. **使用乐观锁**:在更新数据时,检查数据是否被其他事务修改过。如果数据未被修改,则更新成功;否则,回滚并重新尝试操作。 5. **使用游标时及时关闭**:确保在使用完游标后立即调用 `close()` 方法释放资源。在 Android 中,`Cursor` 的 `close()` 方法会调用 `releaseReference()`,最终触发 `onAllReferencesReleased()` 释放所有引用[^1]。 6. **避免嵌套事务**:尽量避免在事务中开启新的事务,因为这会增加死锁的风险。 ### 示例代码 以下是一个简单的示例,展示了如何在 Android 中正确使用和关闭 `Cursor`: ```java SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor = null; try { cursor = db.query("my_table", null, null, null, null, null, null); if (cursor.moveToFirst()) { do { // 处理数据 } while (cursor.moveToNext()); } } finally { if (cursor != null && !cursor.isClosed()) { cursor.close(); // 及时关闭游标 } } ``` ### 游标管理 在 Android 中,`startManagingCursor()` 方法用于管理游标的生命周期,但在某些版本中可能存在兼容性问题。例如,在 Honeycomb(Android 3.0)及更高版本中,该方法已被弃用。为了避免兼容性问题,可以使用 `CursorLoader` 或手动管理游标的生命周期[^2]。 ### 总结 游标锁问题是数据库并发访问中的常见挑战。通过合理设计事务、优化查询、及时释放资源以及选择合适的锁机制,可以有效减少锁问题的发生,提高系统的稳定性和性能。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值