最近在看《企业应用架构模式》,并且思考数据持久层的实现,从而也发现了目前自己项目中持久层的一个漏洞。
一、漏洞
项目中的数据访问层的实现是这样的,一个数据表对应一个实体(贫血实体,里面只有对应数据库字段的属性),另外加一个数据访问类,这里面封装了类似GetEntity(long id)、Update(Entity entity)、Add(Entity entity)这样的方法。用起来倒是也蛮好用的,但是其实这个里面有个很大的问题:如果在程序中,先用GetEntity方法取得实体,然后修改实体字段,再调用Update(Entity entity)修改数据库,那么这就会有数据同步的问题了。打个比方,一个用户把数据取出来修改了一个属性的值,此时还没有调用Update方法,又有一个用户把这个实体取出来修改了另一个属性,这个时候他们俩都提交了数据,那么会发生什么情况?一定有一个人的操作被另一个人覆盖了。
说的简单明了一点,就相当于一个多线程的程序对同一份数据的修改完全是在一个没有锁的环境下发生的。当然,在用户量和访问量很小的情况下,发生的概率是很低的,但是这在企业级开发中是绝对不能容忍的,用一个简单的乐观锁也许就能解决这个问题。
二、数据访问机制
在《企业引用架构模式》中,提到了工作单元的概念,当对数据的进行一系列操作的时候,会把这些修改记录在工作单元中,最后由工作单元负责一次性提交到数据。这种做法的理论根据,当然就是 数据库连接是宝贵的资源,因此在一次与数据库的交互中,完成一系列操作,而不是每次操作都去直接访问数据,这样做还有一个好处就是,比较方便和容易实现事务,一系列操作在一并提交数据库的时候,如果中间有哪一步失败,那么在数据库级别实现回滚。
但是这样做,在高并发访问的环境下就牺牲了可访问性了,试想当一个用户的一系列操作的工作单元提交数据库时,会锁住数据库相关的表(SQL SERVER应该是锁表的),那么后面的用户线程将会被阻塞,排队的用户多了,那么后面用户的线程将会被牺牲掉。因此对于一个高并发访问的应用,还是应该使用短连接(即对于数据库连接快取快放)来操作,但是短连接又不能保证数据的事务性,这的确一个矛盾的问题,也许这就是开发企业级应用和大型网站的区别吧,各自关注点不同,所以采用不同的数据库访问策略。