通常有这种情况,某个表或者准确的说是表的某条记录被锁(TX锁),在业务层面排查之余,一般都会想知道是哪条记录被锁,每次被锁的是否是同一条记录?还是每次都不同?通过记录可以找到这条记录可以在哪个模块、哪个业务流程中被操作到,有助于定位问题。
但是思前想后,好像不大好找,oracle的锁机制不同于一些数据库,oracle没有一个集中式的锁管理器,oracle的记录锁(行级排他锁存在于数据块上),只有事务到达那一行的时候才能知道这行是否被锁。举个例子,现在科技大厦的物业需要统计C座每层公司的门是不是都上锁了,那么有两种办法:

1.物业有一张清单,每次某个公司上锁都要告诉物业一声:我的门锁了!于是物业记录一笔,当公司开门后,再通知物业:我开锁了!物业于是把之前记录清掉。
2.物业派业务员每层去查看,公司的门是不是上锁。

很显然,第一种方式可能最直观。方便,不足的是需要额外的资源去维护这个清单(锁管理器),随着行级锁数量的升级,这个锁管理器的开销会越来越大,对于这种数据库来说,锁就是一种稀缺资源。
oracle采用的方式是第二种,就是没有一个锁管理器,事务想锁定哪条记录,直接到达该记录,然后上锁,不过上锁之前都要检查该记录是不是被锁定了(数据块的事务槽记录着事务、锁信息),如果被锁,则事务被阻塞,直到锁被释放。所以,对于oracle来说,1个锁和1亿个锁的开销是一样的。所以锁对于oracle来说算不得什么稀缺资源,所以oracle的事务默认是不自动提交的。
所以,在oracle的内存结构、数据字典和V$视图中都无法得到哪条记录被锁。
如果想知道哪条记录被锁,怎么办呢?很遗憾,我没有找到很好的办法,我遍历了ask tom的网站,tom也说没什么好办法,除非dump数据文件来分析。
不过我这里却想到一个比较笨但是感觉也算巧妙的方法,既然只有事务到达那一行的时候才能知道是否被锁,那么我可以使用一个游标遍历所有的行,然后尝试将该行以no wait的方式锁定,如果能锁定则ok,否则no wait会报错,那么我就可以捕获这个异常,知道是哪条记录被锁:

--记录锁探测器
--author:zhangxsh
--2013.5.13
create or replace procedure PR_LOCK_DETECT as
can_not_lock exception;
pragma exception_init(can_not_lock, -54);
id number(14);
begin
--注意:以主键的形式遍历
for rec in (select n_hzbh from t_hztj_aj) loop
begin
id := rec.n_hzbh;
select n_hzbh
into id
from t_hztj_aj w
where w.n_hzbh = rec.n_hzbh
for update nowait;
rollback;
exception
when can_not_lock then
dbms_output.put_line('记录:' || id || '被锁');
end;
end loop;
end;

测试:锁定9条记录:

select * from t_hztj_aj where rownum<10 for update 

探测结果:

记录:10010000000088被锁
记录:10010000000217被锁
记录:10010000046875被锁
记录:10010000046883被锁
记录:10010000052098被锁
记录:10070000000295被锁
记录:10070000000570被锁
记录:10070000001005被锁
记录:10070000013248被锁

方法的不足:

1.由于for update会尝试在记录上加锁,如果锁定成功则会操作数据块使数据块变脏,生产环境下会产生额外的redo log,这个日志的量我没有测试,不过应该不大。
2.for update相当于一次轻量级的全表更新,表如果很大,则会产生相当量的块变脏情况,除了产生1的情况外,则oracle不会自动清除事务信息,下次select查询到来时会重启动事务清理操作,会瞬间产生大量的redo。

通过测试发现,执行这个存储过程,大致相当于将全表更新一遍,
执行存储过程探测时,生成的redo 量为33760000,而执行
update t_hztj_aj set n_bh=n_bh;生成的redo为44670000左右。
所以该探测的过程的代价约为全表更新代价的75%左右。由该例子也可以看出,不一定只要insert、update/delete语句才产生redo,select语句一样可以产生redo,而且数量还很大。所以每执行一遍这个存储过程基本等同于将全表更新一遍,对于生产来说,会产生大量的日志,除非迫不得已,否则最好不要使用。
同时,select * from tab for update也最好少用,测试发现这个操作会产生12000000左右的redo。
以上测试基于6W条数据左右的表进行。

这个帖子描述了TX锁和TM锁的一些区别和联系,tom说的很经典也很清楚
https://forums.oracle.com/forums/thread.jspa?messageID=2442603&tstart=0#2442603

You cannot actually in truth see ANY ROW LOCKS in Oracle - they are not manifested in a memory structure to be queried up.
--只存在于数据块上,因为没有一种内存结构或者机制去单独记录锁信息

【原创】如何找到Oracle中哪条记录被锁的更多相关文章

  1. ORACLE中常见的几种锁

    ORACLE中常见的几种锁: 0:none 1:null 空 2:Row-S 行共享(RS):共享表锁,sub share 3:Row-X 行独占(RX):用于行的修改,sub exclusive 4 ...

  2. session marked for kill处理oracle中杀不掉的锁

    ora-00031:session marked for kill处理oracle中杀不掉的锁   一些ORACLE中的进程被杀掉后,状态被置为"killed",但是锁定的资源很长 ...

  3. oracle中根据当前记录查询前一条和后一条记录

    select * from aa01_2014 where aaa001=(select c.p from (select aaa001,lag(aaa001,1,0)  over (order by ...

  4. Oracle前10条记录

    在Oracle怎样查询表中的top10条记录呢? select * from test where rownum <=10 下面是关于rownum的介绍 ==================== ...

  5. oracle 复制一条记录只改变主键不写全部列名

    场景:表TEST中有C1,C2,C3...字段,其中C1为主键,先需要复制表TEST中一条(C1='1'的)记录,修改主键列C1和需要变更的列后,再插入到表TEST中. procedure P_TES ...

  6. oracle中去重复记录 不用distinct

    用distinct关键字只能过滤查询字段中所有记录相同的(记录集相同),而如果要指定一个字段却没有效果,另外distinct关键字会排序,效率很低 . select distinct name fro ...

  7. SQL 父子表,显示表中每条记录所在层级

    1.sqlserer 中有一张父子关系表,表结构如下: CREATE TABLE [dbo].[testparent]( [ID] [int] IDENTITY(1,1) NOT NULL, [nam ...

  8. ora-00031:session marked for kill处理oracle中杀不掉的锁

    http://www.cnblogs.com/songdavid/articles/2223869.html 一些ORACLE中的进程被杀掉后,状态被置为"killed",但是锁定 ...

  9. 【转】ora-00031:session marked for kill处理oracle中杀不掉的锁

    一些ORACLE中的进程被杀掉后,状态被置为"killed",但是锁定的资源很长时间不释放,有时实在没办法,只好重启数据库.现在提供一种方法解决这种问题,那就是在ORACLE中杀不 ...

随机推荐

  1. .net MVC 碰到的问题

    1:问:回车会默认会触发页面从左边至右,从上到下索引位置第一的按钮事件.如何取消? 答:在不需要触发按钮事件的按钮中加一个属性:UseSubmitBehavior="false" ...

  2. handler looper 和 线程

     Handler的概念: 顾名思义,handler在英语中是“操作着,处理者的意思”,而官方的文档给出的概念是,handler允许你发送或者处理Message对象或者Runable对象,这两个对象都是 ...

  3. 关于jsb中js与c++的相互调用

    1.js调用c++函数 在c++中声明函数,名为functionCpp,通过spidermonkey中jsapi的JS_DefineFunction绑定一个js函数,名为functionJS,此函数名 ...

  4. Centos系统使用vpnc连接cisco的vpn服务

    安装vpnc 因为Centos官方源中,所含的软件安装包较少,这里需增加Fedora的源,依次执行: sudo rpm -ivh http://dl.fedoraproject.org/pub/epe ...

  5. 小白日记48:kali渗透测试之Web渗透-XSS(二)-漏洞利用-键盘记录器,xsser

    XSS 原则上:只要XSS漏洞存在,可以编写任何功能的js脚本 [反射型漏洞利用] 键盘记录器:被记录下的数据会发送到攻击者指定的URL地址上 服务器:kali 客户端 启动apache2服务:ser ...

  6. ScrollView嵌套ListView的滑动冲突问题,是看大神的方法的,作为学习以后用的到

    在工作中,曾多次碰到ScrollView嵌套ListView的问题,网上的解决方法有很多种,但是杂而不全.我试过很多种方法,它们各有利弊. 在这里我将会从使用ScrollView嵌套ListView结 ...

  7. 数据结构【一】:简单队列simple queue

    简单的FIFO队列实现,非线程安全! 1.queue.h : abstract data type queue #ifndef CUR_QUEUE_H #define CUR_QUEUE_H #inc ...

  8. jQery放大镜效果

    简单2:1的放大 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> < ...

  9. JavaScript中交换两个变量的值得三种做法(代码实现)

    javascript在编程时经常会涉及到如何交换两个变量的值,例如常见的冒泡排序,快速排序等:下面我讲根据自己近期所学总结几种常见的交换两个变量值的方法: 方法一:借助第三方变量交换两个变量的值 va ...

  10. Parallel.ForEach() 并行循环

    现在的电脑几乎都是多核的,但在软件中并还没有跟上这个节奏,大多数软件还是采用传统的方式,并没有很好的发挥多核的优势. 微软的并行运算平台(Microsoft’s Parallel Computing ...