1、将此购买逻辑通过存储过程完成,因为 DB 是唯一的,所以可以做到逻辑正确性;
2、可用 Count 字段(通常会使用额外的RowVersion 字段,timestamp 类型来做并发更新)来控制更新语句,通过检查更新是否成功来判断是否购买成功,类似如下代码:
var version= shop.RowVersion;
shop.count--;
update t set Count = shop.count from t where RowVersion = version;
3,将订单处理逻辑部署为单独的服务,此服务串行读取订单并处理。
逻辑很多,所以感觉3比较靠谱
如果这逻辑是靠数据库处理的话,可以使用事务。如果在程序里处理的话,保证所有请求的调用对象是同一个实例就行了。
在程序中,代码类似与这样
new shop=bllshop.getmodel(i);
if(shop.count>0)
shop.count=shop.count-1;
bllshop.update(shop);
在这段代码内是同一个实例shop,但是如果多个用户访问的时候就没法保证也保证不了同一个实例啊。
@王者永乐: 用lock
使用事务或单实例可能锁的粒度会比较大。
我有个想法:你的订单功能肯定有id的,你在保存的时候应该判断修改了什么字段,然后内存中维护id+字段的集合,修改完毕后再从集合中丢弃这个键。此期间并发修改的特定字段点保存的时候与此集合比对,若存在旧不能保存下去。。。另外还存在覆盖的问题,因为刚说的设计并没有逻辑让后面保存的订单在修改到同一字段的时候进行刷新一下再从新修改,所以楼主可以考虑增加一些避免覆盖的逻辑
如果只是图简单的话,小程序也不需要高效率,那么就在需要串行运行但代码处加锁吧
eg:
private static object syncobj=new object()
lock(syncobj)
{
//需要被串行的代码
}
@滴答的雨: 这样的话同一时刻只允许处理一个syncobj?这样肯定不行啊。如果是不同的商品,就不需要这样锁着了。
@王者永乐: 那就只能复杂的设计,维护一个键值集合了。因为使用数据库事务也会大范围的锁定表。
在库里面的表加个类型为timespan的字段
update table set count = count-1 where id = 1 and count <> 0
如果只剩下一件商品,又有两个人同时购买,那么执行以上sql语句,只有一个人会得到 "受影响行数 1",另一个人因为count被-1,变成0了,所以 count <> 0 这个where条件不成立