悲观锁和乐观锁机制

Do better / 2023-08-25 / 原文

1 乐观锁

通俗易懂的来讲,乐观锁每次读取数据时都会先检查一遍数据,如果数据被其他线程修改,就会更新数据

如何使用

eg:

这是一个乐观锁的实现案例

 
from django.shortcuts import render
from django.http import JsonResponse
from django.views.generic import View
from django.db import transaction
from 应用名.models import GoodsSKU
 
 
# 类视图 (并发,乐观锁)
class MyView(View):
    
    @transaction.atomic
    def post(self, request):
        '''订单创建'''
        count = 3   # 订购3件商品
        
        # 设置事务保存点
        s1 = transaction.savepoint()
        
        # 乐观锁,最多尝试5次
        for i in range(5):
            # 查询商品的信息(库存)
            try:
                sku = GoodsSKU.objects.get(id=1)
            except:
                # 商品不存在
                transaction.savepoint_rollback(s1)
                return JsonResponse({'res': 1, 'errmsg': '商品不存在'})
 
            # 判断商品的库存
            if count > sku.stock:
                transaction.savepoint_rollback(s1)
                return JsonResponse({'res': 2, 'errmsg': '商品库存不足'})
 
            # 更新商品的库存和销量
            orgin_stock = sku.stock   # 原库存 (数据库隔离级别必须是Read Committed;如果是Repeatable Read,那么多次尝试读取的原库存都是一样的,读不到其他线程提交更新后的数据。)
            new_stock = orgin_stock - count   # 更新后的库存
            new_sales = sku.sales + count   # 更新后的销量
 
            # update 商品表 set stock=new_stock, sales=new_sales where id=1 and stock = orgin_stock
            # 通过where子句中的条件判断库存是否进行了修改。(并发,乐观锁)
            # 返回受影响的行数
            res = GoodsSKU.objects.filter(id=1, stock=orgin_stock).update(stock=new_stock, sales=new_sales)
            if res == 0:  # 如果修改失败
                if i == 4:
                    # 如果尝试5次都失败
                    transaction.savepoint_rollback(s1)
                    return JsonResponse({'res': 3, 'errmsg': '下单失败'})
                continue  # 再次尝试
 
            # 否则更新成功
            # 跳出尝试循环
            break
 
 
        # 提交事务
        transaction.savepoint_commit(s1)
 
        # 返回应答
        return JsonResponse({'res': 4, 'message': '创建成功'}

 

2 悲观锁

悲观锁则是在执行数据前就会假设数据会被其他线程修改,所以在执行过程中会对数据进行上锁,让数据处于锁定状态(这种方法往往依靠数据库提供的锁方法)

如何使用

使用select_for_update()方法

eg:

obj = 模型类名.objects.select_for_update().get(id=1)

 

3 适用场景

适用场景
悲观锁 写入频繁
乐观锁 读取频繁