您的位置:首页 > 职场人生

作为一个程序员,有些知识你应该知道----关于并发和数据库封锁

2005-07-24 15:41 1051 查看
[原创]作为一个程序员,有些知识你应该知道----关于并发和数据库封锁

近来碰上件很郁闷的事,国内某CMM3级的开发公司答应给我们的业务系统进行改造,但从4月拖到现在,效率真是低,新的系统又需要旧系统的支持,花了百来万就做出这么个垃圾的JAVA程序,实在忍无可忍,自己操刀上阵,一个人花了一周把他们用j2ee做了半年的程序改造成了 asp .net 程序。

第一步,解决一个他们郁闷了很久,但是连开发人员自己都弄不明白的问题。

现存问题:系统老是会生成2个以上同样的案号。过去一年中发生了5次左右,但是开发人员认为自己写的代码是不可能发生这种事的。

业务需求如下:
取案号:格式为4位年份+2位月份+2位日期+4位顺序号(超过4位不补0)
其中顺序号要求每天从1开始,一般一天不会发生1万件以上的案件,所以要求<999的案号前面补0,如果超过1万件,就直接返回顺序号。

表结构
表名 TradeSerial
字段1 字符型日期
字段2 当前顺序号

例如:
TradeSerial
20050701 23

以下是他们写的JAVA代码

复查完这段JAVA代码,我查点吐血。居然连事务都没有用。项目组成员JAVA水平差到这个地步无语了。

问题分析:
出错原因是没有认识到b/s程序某些情况下是一个多线程程序,当2个用户几乎在同时取案号时,发生了并发问题。

问题的解决办法:
临界区封锁,注意,仅用事务并不一定能解决这个问题。

针对这个问题给出以下几个解决的方案:

解决方案1.在程序中用锁技术,使用lock 对临界区代码加锁,保证同一时间只有一个线程执行取案号的代码。

//生成案件编号
public string GetAJBH()
{
...
lock(this)
{
try
{
//取案号
}
.......

但是这里有一个问题。今后的系统可能不仅仅是一个B/S结构的系统,有些功能可能要在PDA上完成。这时这种方法就不能保证案号不冲突了。

方案2:在数据库里实现。
显然,这个是最好的解决办法。数据库一但选定,不太会改变。

但是这里又出现2个问题。
1.MS Sqlserver数据库:对于Sqlserver,只要用事务就可以完成以上功能,如果没有记错的话,SQLSERVER一但开始事务就是使用表封锁了。

2.Oracle数据库。很遗憾,Oracle中仅用事务仍然可能会出现脏读现象。需要人工加锁。
其DML锁有如下三种封锁方式:
(1)、共享封锁方式(SHARE)
(2)、独占封锁方式(EXCLUSIVE) (注:lock table 表名 in exclusive mode;)
(3)、共享更新封锁(SHARE UPDATE)(注:只要select ... from 表 for update即可进行封锁)
这里又有一个问题。那就是你选用那个锁,答案应该是独占封锁方式。

因为共享更新封锁是一种行封锁。针对取案号假设写了如下存储过程:

create or replace procedure getbh(ajbh out varchar) is
i int;
bh number default -99;--假设没有值
CURSOR C1 IS select SERIALNO into bh from TRADESERIAL where TRADEDATE=to_char(sysdate,'yyyyMMdd') for update;
begin
FOR I IN C1 LOOP--当天有值,那么值加1
update TradeSerial set SerialNo=SerialNo+1;
bh:=I.SERIALNO+1;
END LOOP;
if(bh <> -99) then--当天有值,返回
fillzero(bh,4,ajbh);
ajbh:=to_char(sysdate,'yyyyMMdd')||ajbh;
return;
else--新的一天了
bh:=1;
update TradeSerial set SerialNo=1,TRADEDATE=to_char(sysdate,'yyyyMMdd');
end if;
fillzero(bh,4,ajbh);
ajbh:=to_char(sysdate,'yyyyMMdd')||ajbh;
end getbh;

经单步调试试验证明依然无法避免重号,因为如果新的一天开始了,那么表中的日期与当前日期不同,所做的select语句值为空,行封锁就不存在。如果有2个用户同时取案号。那么会取到2个当天的1号案件。所以要用 独占封锁方式。

create or replace procedure getbh(ajbh out varchar) is
i int;
bh number default -99;--假设没有值
CURSOR C1 IS select SERIALNO into bh from TRADESERIAL where TRADEDATE=to_char(sysdate,'yyyyMMdd');
begin
lock table TRADESERIAL in exclusive mode;
FOR I IN C1 LOOP--当天有值,那么值加1
update TradeSerial set SerialNo=SerialNo+1;
bh:=I.SERIALNO+1;
END LOOP;
if(bh <> -99) then--当天有值,返回
fillzero(bh,4,ajbh);
ajbh:=to_char(sysdate,'yyyyMMdd')||ajbh;
return;
else--新的一天了
bh:=1;
update TradeSerial set SerialNo=1,TRADEDATE=to_char(sysdate,'yyyyMMdd');
end if;
fillzero(bh,4,ajbh);
ajbh:=to_char(sysdate,'yyyyMMdd')||ajbh;
end getbh;

感想:上面这个问题只是举的一个小例子,要求看似简单,以为会点数据库操作就OK了,但是深究下去,问题是很复杂的。
可是目前IT人员大多有一种浮燥的心理。技术层出不穷,什么都想学,但常常浮于表面。而且大学生多了,心态大多都有点问题。学了一周的JSP,asp就号称精通,连学纺织,化工,机械类的人都招来当程序员。
以前说重学历,但是现在大学教育质量普遍下降,刚出来的毕业生啥也不懂。学历考不住了。
以前说重经验,但是很多情况是,很多非科班出身的程序员基础差,而且一天到晚忙着挣钱做项目,技术上的东西一点没心思学。结果经验是丰富了。可是所谓的经验却是错误的经验,而且还在不断的积累错误的经验,项目搞糟了,跳槽走人。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: