关于锁的问题

无聊客 2009-02-18 10:09:59
先说明下我的应用场景,有一张表,里面记录了一堆电话号码,我有个客户端程序,使用VC+ADO,有个录入界面,输入用户以后,需要从该表中选出一个号码供使用,因为我的客户端同时可能有多台终端使用,当一个号码被一个客户端选出以后,希望其他客户端在Select的时候,无法看到这个号码,以保证唯一性,不知道该如何写SQL?
试过Select * from table for update nowait, 但是不知为什么会失败,目标数据库应该是ORACLE 8I,请各位大虾指教,谢谢先!
...全文
247 31 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
31 条回复
切换为时间正序
请发表友善的回复…
发表回复
小墨鱼 2009-02-26
  • 打赏
  • 举报
回复
好帖。。学习了。。21楼的方法真管用。。以后碰到这样问题就不用四处找了。呵呵!
oraclelogan 2009-02-25
  • 打赏
  • 举报
回复
[Quote=引用 29 楼 yzx0023 的回复:]
21楼的大侠的方法在sqlplus上已经测试过,过几天放程序里试一下,通过的话就结帖,先感谢各位的热心帮忙了
[/Quote]

嗯,21的方法不错!
无聊客 2009-02-25
  • 打赏
  • 举报
回复
21楼的大侠的方法在sqlplus上已经测试过,过几天放程序里试一下,通过的话就结帖,先感谢各位的热心帮忙了
oracle13 2009-02-25
  • 打赏
  • 举报
回复
21 楼的大哥你太牛了
Finder_Way 2009-02-24
  • 打赏
  • 举报
回复
学习!
xuqunying0545 2009-02-24
  • 打赏
  • 举报
回复
学习一下下
又是违规昵称 2009-02-24
  • 打赏
  • 举报
回复
21楼的办法看起来非常的不错,学习了~~~~~~~~~
oraclelogan 2009-02-24
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 sleepzzzzz 的回复:]
oracle对select动作是不加锁的,但是使用skip locked可以解决你的问题。
测试代码如下:

SQL codecreate table test(a number,b number);

insert into test values(1,2);
insert into test values(3,4);
insert into test values(8,9);
commit;

---session 1 模拟选中一个号码

SQL> select * from test where a =1 for update skip locked;

A B
---------- ----------
1 …
[/Quote]

学习了,谢谢!
Andy__Huang 2009-02-24
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 sleepzzzzz 的回复:]
上面的测试数据库版本是10g.
[/Quote]
我在oracle9i的sqlplus里面测试也通过了;
Andy__Huang 2009-02-24
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 sleepzzzzz 的回复:]
oracle对select动作是不加锁的,但是使用skip locked可以解决你的问题。
测试代码如下:

SQL codecreate table test(a number,b number);

insert into test values(1,2);
insert into test values(3,4);
insert into test values(8,9);
commit;

---session 1 模拟选中一个号码

SQL> select * from test where a =1 for update skip locked;

A B
---------- ----------
1 …
[/Quote]

sleepzzzz大侠是正确答案,我难通过了

sleepzzzzz 2009-02-24
  • 打赏
  • 举报
回复
上面的测试数据库版本是10g.
sleepzzzzz 2009-02-24
  • 打赏
  • 举报
回复
oracle对select动作是不加锁的,但是使用skip locked可以解决你的问题。
测试代码如下:
create table test(a number,b number);

insert into test values(1,2);
insert into test values(3,4);
insert into test values(8,9);
commit;

---session 1 模拟选中一个号码

SQL> select * from test where a =1 for update skip locked;

A B
---------- ----------
1 2


---session 2 对a=1再进行select

SQL> select * from test where a = 1 for update skip locked;

未选定行

-- session 3 全表select
SQL> select * from test for update skip locked;

A B
---------- ----------
3 4
8 9

SQL>



PS:在程序里自已对事务进行控制,不要自动commit了,不然达不到你想要的效果。
oraclelogan 2009-02-24
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 lpc19598188 的回复:]
另一个办法是在所有号码的表中加一个字段,叫做终端号码
如果哪个终端选定了一个号,那么将此记录select for update锁定,不许别人更新,
然后将此字段终端号更新为本客户机的终端号

那么其它终端来选此号的时候,想要更新为它的终端号就不行了,因为此号被select for update了
如果前一终端断电,那么事务回退,锁定失效,这个号也就可以再次被其它人发现了

这个方案与上一方案的区别在于,这一方案会导致所有号码…
[/Quote]

这个I/O频繁是没有办法的事情,比较可行的是为每一个字段建立select排他锁。
又是违规昵称 2009-02-24
  • 打赏
  • 举报
回复
另一个办法是在所有号码的表中加一个字段,叫做终端号码
如果哪个终端选定了一个号,那么将此记录select for update锁定,不许别人更新,
然后将此字段终端号更新为本客户机的终端号

那么其它终端来选此号的时候,想要更新为它的终端号就不行了,因为此号被select for update了
如果前一终端断电,那么事务回退,锁定失效,这个号也就可以再次被其它人发现了

这个方案与上一方案的区别在于,这一方案会导致所有号码的表(肯定是一张大表)I/O频繁
sheeplittl 2009-02-24
  • 打赏
  • 举报
回复
这样的问题,我们以前也曾经碰到过,基本上通过for update ,或者放入到另外一个表的方式基本上不可能实现,
可实现的方法是通过一个中间应用程序例如java小的应用的同步取得号码的过程。
又是违规昵称 2009-02-24
  • 打赏
  • 举报
回复
表A中可以将电话号码这一字段设为主键,
假设用户1选定了号8888,用户2同时也来选8888,
那么用户2在插入8888到表A中的过程中,会抛一个主键冲突的异常
程序捕获此异常后,在前台弹出提示:对不起,此号已被选定!同时在界面上刷新号码

实际上,如果采用这种方案,在电话号码列表的界面刷新比较快的前提下,
出现冲突的几率会非常的小,而且绝大多数情况下只会冲突一次
为什么呢?什么情况下会出现冲突呢?这个我不啰嗦了

如果前台号码列表的界面刷新不快,那么任何方案都有较大概率出现一次的冲突
Andy__Huang 2009-02-24
  • 打赏
  • 举报
回复
按楼主这样要求做,这个真的有难度!
不过我觉得应该用程序或芯片组去控制好一些;而且你的程序是用VC写的,根据通信端口的状态,判断如果有用户已经select这个号码,其他户只能等;

又是违规昵称 2009-02-24
  • 打赏
  • 举报
回复
[Quote=引用楼主 yzx0023 的帖子:]
试过Select * from table for update nowait, 但是不知为什么会失败,目标数据库应该是ORACLE 8I,请各位…
[/Quote]

当然会失败,因为oracle中select for update只能阻塞写,不能阻塞读,
基本上也没有什么办法可以阻塞读取的(也可能是我还不知道)
唯一的办法就是不select它

我觉得,可以另建一张表A,如果有客户select号码了,把它存到这张表里面来
其它客户端查询的时候查所有的电话号码,并排除表A中的号码,
如果最终没有选定这个号,再从表A中删除它
无聊客 2009-02-24
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 yf520gn 的回复:]
引用 4 楼 yzx0023 的回复:
顶下,楼上的暂时没看明白,回去试下,有没有简单点方案啊,我觉得类似的应用应该蛮多的。。。

锁如果处理不好的话会造成你系统的不稳定,比如死锁;
LZ的问题可以考虑从数据处理方法上解决,比如再你的电话号码表中增加一个FLAG字段,当该号码空闲的时候置为0,被选中的时候置为1;
所有客户查看电话号码的SQL语句写成这样:SELECT * FROM YOURTABLE WHERE FLAG=0;


PS:LZ可用分都掌…
[/Quote]
采纳建议,加100分
无聊客 2009-02-24
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 mosaic 的回复:]
所有的客户端一样的逻辑:
先update 该号码的记录(比如update一个状态字段,将状态未使用改为已使用),然后select, 再提交。

另外的终端也来update和select的时候,就取到原来那条记录了。

这样好像可以吧?
[/Quote]
这样也有问题啊,如果在取出号码后发生异常,比如电脑断电,没有改回这个字段,那这个号码就永远没人用了啊
加载更多回复(11)

17,382

社区成员

发帖
与我相关
我的任务
社区描述
Oracle 基础和管理
社区管理员
  • 基础和管理社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧