Django 管理并发操作之乐观锁

乐观锁其实并不是锁。通过SQL的where子句中的条件是否满足来决定是否满足更新条件来更新数据库,通过受影响行数判断是否更新成功,如果更新失败可以再次进行尝试,如果多次尝试失败就返回更新失败的结果。

为了验证乐观锁的使用情况:使用乐观锁时必须设置数据库的隔离级别是Read Committed(可以读到其他线程已提交的数据)。如果隔离级别是Repeatable Read(可重复读,读到的数据都是开启事务时刻的数据,即使其他线程提交更新数据, 该线程读取的数据也是之前读到的数据),乐观锁如果第一次尝试失败,那么不管尝试多少次都会失败。 (Mysql数据库的默认隔离级别是Repeatable Read,需要修改成Read Committed, 可以用 show variables like ‘transaction_isolation’; 来确认数据库事务隔离级别)。

def concurrence(request):
    s1 = transaction.savepoint()
    print 'begin: {0}'.format(time.time())
    retry_time =5
    for i in range(retry_time):
        db = UserInfo.objects.filter(id=4).first()
        old_balance = int(db.password)
        new_balance = old_balance - 1
        # 用 password 列来模拟余额, 在 sleep 期间把通过 mysql client 修改数据库的值
        time.sleep(10)
        res = UserInfo.objects.filter(id=4, password=str(old_balance)).update(password=str(new_balance))
        if res == 0:
            if i == retry_time - 1:
                print 'update failed: {0}'.format(time.time())
                transaction.savepoint_rollback(s1)
                return HttpResponse('update failed')
            continue
        else:
            print 'update successfully: {0}'.format(time.time())
            return HttpResponse('update successfully')
    return HttpResponse('concurrence test')

视图函数需要注意:
我用 model  UserInfo 中的 password 列来模拟用户的余额。
关键的地方是: UserInfo.objects.filter(id=4, password=str(old_balance)).update(password=str(new_balance))
比如本次是取款 1 元,在视图函数中 sleep(10) 的期间,
如果用户的余额发生了变更(比如通过其他的 MySQL Client 连接数据库执行更新操作)即不等于 old_balance 了,那么上述语句的执行就会失败。
就需要去重试来更新.

关于悲观锁的文章,请参考:Django 管理并发操作之悲观锁即select_for_update

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值