zoukankan      html  css  js  c++  java
  • 事务,悲观锁和乐观锁

    事务

    事务(Transaction):一个最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)

    一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成

    事务只和DML语句 ( 数据库操作语句 ) 有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同

    特性

    • 原子性(A) 事务是最小单位,不可再分

    • 一致性(C) 事务要求所有的DML语句操作的时候,必须保证同时成功或者同时失败

    • 隔离性(I) 事务A和事务B之间具有隔离性

    • 持久性(D) 是事务的保证,事务终结的标志(内存的数据持久到硬盘文件中)

    行为

    开启事务 Start Transaction

    事务结束 End Transaction

    提交事务 Commit Transaction

    回滚事务 Rollback Transaction

    django开启事务

    from django.db import transaction
    
    # 方式一
    with transaction.atomic(): # 开启事务,当with语句执行完成以后,自动提交事务
        ... # 数据库操作
    
    
    
    # 方式二
    @transaction.atomic
    def foo():
        ....
        sid=transaction.savepoint()  #开启事务
        ...
        transaction.savepoint_rollback(sid)  # 回滚
        ...
        transaction.savepoint_commit(sid)  # 提交
    
    
    # 事务回滚
    from django.db import transaction
    from rest_framework.views import APIView
    class OrderAPIView(APIView):
        def post(self,request):
            ....
            with transation.atomic():
                # 设置事务回滚的标记点
                sid = transation.savepoint()
    
                ....
    
                try:
                    ....
                except:
                    transation.savepoint_rallback(sid)
    

    悲观锁

    总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等)
    当其他线程想要访问数据时,都需要阻塞挂起。可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁
    保证同一时刻只有一个线程能操作数据,其他线程则会被 block

    运用场景

    ▧ 无脏读  上锁数据保证一致, 因此无脏读, 对脏读不允许的环境悲观锁可以胜任 

    ▧ 无并行  悲观锁对事务成功性可以保证, 但是会对数据加锁导致无法实现数据的并行处理.

    ▧ 事务成功率高  上锁保证一次成功, 因此在对数据处理的成功率要求较高的时候更适合悲观锁.

    ▧ 开销大  悲观锁的上锁解锁是有开销的, 如果超大的并发量这个开销就不容小视, 因此不适合在高并发环境中使用悲观锁 

    ▧ 一次性完成  如果乐观锁多次尝试的代价比较大,也建议使用悲观锁, 悲观锁保证一次成功

    使用

    from django.shortcuts import render
    from django.http import HttpResponse
    from django.views.generic import View
    from django.db import transaction
    from 应用名.models import 模型类名
     
     
    # 类视图 (并发,悲观锁)
    class MyView(View):
        
        @transaction.atomic
        def post(self, request):
            # select * from 表名 where id=1 for update;  
            # for update 就表示锁,只有获取到锁才会执行查询,否则阻塞等待。
            obj = 模型类名.objects.select_for_update().get(id=1)
            
            # 等事务提交后,会自动释放锁。
            
            return HttpResponse('ok')
    

    乐观锁

    总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁
    但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。
    如果发现数据被改了. 就进行事务回滚取消之前的操作

    运用场景

    ▧ 脏读  乐观锁不涉及到上锁的处理, 因此在数据并行需求的时候是更适合乐观锁,当然会产生脏读, 不过用回滚取消掉了.

    ▧ 高并发  相比起悲观锁的开销, 乐观锁也是比悲观锁更适合于高并发场景

    ▧ 事务成功率低  乐观锁不能保证每次事务的成功, 是使用回滚方式来保证数据一致性, 因此会导致事务成功率很低.

    ▧ 读多写少  乐观锁适用于读多写少的应用场景,这样可以提高并发粒度

    ▧ 开销小  可能会导致很多次的回滚都不能拿到正确的处理回应, 因此如果对成功性要求低,而且每次开销小比较适合乐观锁

    使用

    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': '创建成功'})
    
  • 相关阅读:
    理解多线程引用自 http://eagletff.blog.163.com/blog/static/11635092820105158493975/
    Delphi 完全时尚手册之 Visual Style 篇 (界面不错) 转自http://blog.csdn.net/iseekcode/article/details/4733229
    .Delphi下的COM编程 详解Delphi下的COM编程
    TPerlRegEx, delphi 下的正则表达式
    delphi 下多线程 并发操作 数据库(数据库锁) 注意事项
    关于利用其它版本看QQ的是否隐身
    QQ空间的一些操作
    关于自动申请QQ
    千千静听播放时出现杂音,而用其他播放器却没有
    无锡之行
  • 原文地址:https://www.cnblogs.com/chenwenyin/p/13544913.html
Copyright © 2011-2022 走看看