听棠.NET

用积极乐观的心态,面对压力
posts - 307, comments - 10812, trackbacks - 112, articles - 5
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

SmartPersistenceLayer3.1讲解(8)--- 并发处理篇

Posted on 2005-04-04 13:49 听棠.NET 阅读(...) 评论(...) 编辑 收藏

SmartPersistenceLayer 3.1.0.0 并发处理篇

提出问题

       数据并发问题不是新问题了。当两个用户都读取了同一条记录后,A用户进行了更新,B用户在更新时,有可能会覆盖A用户的修改,这就是典型的并发问题。

   这里有一篇MS的关于并发问题的文章:介绍 ADO.NET 中的数据并发[]

   我个人感觉最好的解决并发问题的方式是采用“时间戳TimeStamp”,这是由Sql Server数据库自行维护的,可以减少程序的管控,而且效率也相当高。

   采用时间戳的简单步骤:

在表中字义一个字段类型为TimeStamp即可,如字段TS

然后在更新与删除时进行TS的比较:
       Update tablename set …. Where key=@key and ts=@ts

   Delete from tablename Where key=@key and ts=@ts

然后根据Command执行返回的影响条数判断是否成功。

采用TimeStamp的好处就是此TimeStamp的值不需要程序维护,在Insert时会自动插入新值,在Update时,也会自动更新,因此从这一点上,TimeStamp是非常可取的。

 

但这个时间戳字段不是所有的数据库都有的。比如Access或是其他的,就算有的数据库也有时间戳,可能使用规则与Sql Server也不同,因此,如果系统是建在ORM的开发模式上,系统的异构数据库移植就会存在问题,因此,对于系统来说,要有应付所有数据库并发问题的机制。

 

分析问题

   为了寻找一个通用数据库都能采纳的并发处理机制,我们可以为表专门建一个字段,用来控制数据的并发:

   Insert时,插入新值

   Update时,进行旧值的比较,如果成功,也要更新此字段值

   Delete时,进行旧值比较,如果不同,则不成功

   这样的控制方式跟SQL Server的差不多,但需要手动维护此字段值,而这个操作,我们就可以交给ORM的持久层来完成。

   关于此值采用什么样的值?

   此字段值要求具有唯一性,在任何情况下都不能产生相同的值,或者说几乎不可能产生相同的值,自然会想到GUID,但我觉得GUID数值太大,不利于数值比较,所以决定采用System.DateTime.Now.Ticks这是一个12位的数字, 此属性的值为自 0001 1 1 日午夜 12:00 以来所经过时间以 100 毫微秒为间隔表示时的数字。因此发生相同值的可能性也相当小,比GUID更合适。

 

解决问题

   SPL(SmartPersistenceLayer)为了.NET持久层,在3.1.0.0版本中添加如上所述的并发处理机制。

   SmartPersistenceLayer的原理

     给表中定义一个字段为“时间戳”。

     Insert时会自动根据System.DateTime.Now.Ticks产生新值,插入到新记录中

     Update时,比较旧值是否相等,如果相等,则生成新的Ticks更新“时间戳”字段值,因此可以根据Update影响的条数来判断是否有并发错误。

     Delete时,比较旧值是否等,也可以通过影响条数来判断。

     在事务处理时,有个属性叫“是否强行执行”

     If(强行执行)

      {

                     则在处理过程中,就算遇到“影响条数为零”也会提交事务

}else

{

    如果遇到“影响条数为零”,则回滚事务

}

 

SmartPersistenceLayer的使用:

1.               如果要对某个表进行并发控制,则给此表定义一个字符型的字段,由于采用Ticks的值,所以字符长度建议20个字符以上,比如叫TS

2.               ClassMap.xml中的字段属性上添加“timestamp="true"”,以此标明是“时间戳”字段,如:
<attribute name="ts" column="ts" type="String" timestamp="true"/>

3.               在实体Save()时,用“影响条数”来判断是否存在并发操作:

StudentEntity Student=new StudentEntity(); 

Student.Id
=1

Student.Retrieve(); 

If(Student.IsPersistent) 



   Student.Name
=… 

  ….             
//属性赋值,不需要给TS赋值 

Try 



If(Student.Save()
>0


     
//操作成功 

}
else 



     
//存在并发错误,提示用户重新操作。 

}
 

}
catch 



     
//保存数据发生异常 

}
 

}
 

 

 

 

 

 

从上面的代码可以看出,遇到保存异常与并发操作是两回事,在我们进行“更新”都是建议进行一次Retrieve()操作的。这样可以减少发生并发错误的可能性。

其他的Delete()同上面的类似,也采用If(Student.Delete()>0)来进行判断,这也可以方便显示“影响条数”:
int affect=Student.Delete(); //affect
为删除的记录数

 

这是对于单个实体进行的并发控制,由于目前无法进行“批量更新”的并发控制,所以没有对UpdateCriteria进行并发控制,讨论文章:再谈数据的并发处理

 

事务中对并发的控制

SPL中,对事务也进行了并发控制,事务中定义了一个属性IsForceCommit:是否强行执行。

 如果IsForceCommit=false(此为默认值),则在事务中,如果遇到并发错误,整个事务都将Roolback

 如果IsForceCommit=true,则在事务中,就算遇到并发错误,也会Commit此事务

这里所说的并发错误,其实就是指Update()Delete()是否影响条数。

Transaction t=New Transaction(); 

t.IsForceCommit
=true;            //可选,默认为false 

t.AddSaveObject(Student); 

t.AddSaveObject(Student2); 

…. 

t.Process();             
//根据IsForceCommit的值进行事务提交 

 

到此为止,在SPL可以轻松实现分布式异构数据库的事务提交,这可以对SPL支持数据库的任意组合提供并发处理。

 

PS:SPL3.1版将不久发布。

                                                   听棠

                                                 2005-4-4