zoukankan      html  css  js  c++  java
  • django-mysql事务

    django文档:https://yiyibooks.cn/xx/django_182/topics/db/transactions.html

    mysql事务

    1) 事务概念

    一组mysql语句,要么执行,要么全不不执行。

    2) 事务的特点

    1原子性:一组事务,要么成功;要么撤回。

    2稳定性 有非法数据(外键约束之类),事务撤回。

    3隔离性事务独立运行。一个事务处理后的结果,影响了其他事务,那么其他事务会撤回。事务的100%隔离,需要牺牲速度。

    4可靠性:软、硬件崩溃后,InnoDB数据表驱动会利用日志文件重构修改。可靠性和高速度不可兼得, innodb_flush_log_at_trx_commit 选项 决定什么时候吧事务保存到日志里。

    3) 事务控制语句

    BEGINSTART TRANSACTION;显式地开启一个事务;

    COMMIT;也可以使用COMMIT WORK,不过二者是等价的。COMMIT会提交事务,并使已对数据库进行的所有修改称为永久性的;

    ROLLBACK;有可以使用ROLLBACK WORK,不过二者是等价的。回滚会结束用户的务,并撤销正在进行的所有未提交的修改;

    SAVEPOINT identifierSAVEPOINT允许在事务中创建一个保存点,一个事务中可以有多个SAVEPOINT

    RELEASE SAVEPOINT identifier;删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;

    ROLLBACK TO identifier;把事务回滚到标记点;

    4) mysql事务隔离级别

    SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
    Read Uncommitted(读取未提交内容)

    在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。
    Read Committed(读取提交内容)

    这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。
    Repeatable Read(可重读)

           这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的幻影行。InnoDBFalcon存储引擎通过多版本并发控制(MVCCMultiversion Concurrency Control)机制解决了该问题。

    Serializable(可串行化) 
           这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

             这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:

    脏读(Drity Read)某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

    不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

    幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几行(Row)数据,而另一个事务却在此时插入了新的几数据,先前的事务在接下来的查询中,就会发现有几数据是它先前所没有的。

     

    5) 设置mysql事务的隔离级别

    打开mysql配置文件: sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf, 添加如下行。

     

    保存配置文件,重启mysql服务。

    sudo service mysql restart

    视图函数view.py中

    from django.db import transaction  # 事务处理
    class OrderCommitView(View):
        '''订单创建'''
        @transaction.atomic()   # 事务处理装饰器
        def post(self, request):
            '''订单创建'''
            # 判断用户是否登录
            user = request.user
            if not user.is_authenticated():
                # 用户未登录
                return JsonResponse({'res':0, 'errmsg':'用户未登录'})
    
            # 接收参数
            addr_id = request.POST.get('addr_id')
            pay_method = request.POST.get('pay_method')
            sku_ids = request.POST.get('sku_ids') # 1,3
    
            # 校验参数
            if not all([addr_id, pay_method, sku_ids]):
                return JsonResponse({'res':1, 'errmsg':'参数不完整'})
    
            # 校验支付方式
            if pay_method not in OrderInfo.PAY_METHODS.keys():  # 需要在orders/model.py中添加PAY_METHODS字典
                return JsonResponse({'res':2, 'errmsg':'非法的支付方式'})
    
            # 校验地址
            try:
                addr = Address.objects.get(id=addr_id)
            except Address.DoesNotExist:
                # 地址不存在
                return JsonResponse({'res':3, 'errmsg':'地址非法'})
    
            # todo: 创建订单核心业务
    
            # 组织参数
            # 订单id: 20171122181630+用户id
            order_id = datetime.now().strftime('%Y%m%d%H%M%S')+str(user.id)
    
            # 运费
            transit_price = 10
    
            # 总数目和总金额
            total_count = 0
            total_price = 0
    
            # 设置保存点
            save_id = transaction.savepoint()
            try:
                # todo: 向df_order_info表中添加一条记录
                order = OrderInfo.objects.create(order_id=order_id,
                                                 user=user,
                                                 addr=addr,
                                                 pay_method=pay_method,
                                                 total_count=total_count,
                                                 total_price=total_price,
                                                 transit_price=transit_price)
    
                # todo: 用户的订单中有几个商品,需要向df_order_goods表中加入几条记录
                conn = get_redis_connection('default')
                cart_key = 'cart_%d'%user.id
    
                sku_ids = sku_ids.split(',')
                for sku_id in sku_ids:
                    # 获取商品的信息
                    try:
                        sku = GoodsSKU.objects.get(id=sku_id)
                    except:
                        # 商品不存在
                        transaction.savepoint_rollback(save_id)  # 回滚到保存点
                        return JsonResponse({'res':4, 'errmsg':'商品不存在'})
    
                    # 从redis中获取用户所要购买的商品的数量
                    count = conn.hget(cart_key, sku_id)
    
                    # todo: 判断商品的库存
                    if int(count) > sku.stock:
                        transaction.savepoint_rollback(save_id)  # 回滚到保存点
                        return JsonResponse({'res': 6, 'errmsg': '商品库存不足'})
    
                    # todo: 向df_order_goods表中添加一条记录
                    OrderGoods.objects.create(order=order,
                                              sku=sku,
                                              count=count,
                                              price=sku.price)
    
                    # todo: 更新商品的库存和销量
                    sku.stock -= int(count)
                    sku.sales += int(count)
                    sku.save()
    
                    # todo: 累加计算订单商品的总数量和总价格
                    amount = sku.price*int(count)
                    total_count += int(count)
                    total_price += amount
    
                # todo: 更新订单信息表中的商品的总数量和总价格
                order.total_count = total_count
                order.total_price = total_price
                order.save()
            except Exception as e:
                transaction.savepoint_rollback(save_id)  # 回滚到保存点
                return JsonResponse({'res': 7, 'errmsg': '下单失败'})
    
            # 如果没失败则提交事务
            transaction.savepoint_commit(save_id)
            # todo: 清除用户购物车中对应的记录
            conn.hdel(cart_key, *sku_ids)
    
            # 返回应答
            return JsonResponse({'res':5, 'message':'创建成功'})
  • 相关阅读:
    贪吃蛇模块划分
    你今天听见《风声》了吗“ 学习无处不在继潜伏后《风声》观后
    贪吃蛇结束条件分析
    CSS fieldset制作表单,抛弃Table
    两个路由器串联
    .net 几种获取路径方法
    C# 对文件与文件夹的操作 删除、移动与复制
    mvc3在IIS中的部署(转)
    HTTP 错误 500.21 Internal Server Error
    遗留问题
  • 原文地址:https://www.cnblogs.com/yifengs/p/11667812.html
Copyright © 2011-2022 走看看