继上一篇《Mysql事务探索及其在Django中的实践(一)》交代完问题的背景和Mysql事务基础后,这一篇主要想介绍一下事务在Django中的使用以及实际应用给我们带来的效率提升。

  首先贴上Django官方文档中关于Database Transaction一章的介绍:https://docs.djangoproject.com/en/1.9/topics/db/transactions/

  在Django中实现事务主要有两种方式:第一种是基于django ORM框架的事务处理,第二种是基于原生地执行SQL语句的transaction处理。

基于django ORM框架的事务处理

  默认情况下,django的事务处理是自动提交(auto-commit),即在执行model.save(),model.delete()时,所有的改动会被立即提交,相当于数据库设置了auto commit,没有任何隐藏的rollback。

  在网上查了一些资料,了解到django手动配置事务的方式主要有三种:第一种是将一个http request的所有数据库操作包裹在一个transaction中,第二种是通过transaction中间件对http请求的事务拦截,第三种是自己在view中通过装饰器灵活控制事务(我们的平台最后用的就是这一种)。

  1.将一个http request的所有数据库操作包裹在一个transaction中

  这种方式配置非常简单,只需要在settings.py中的database配置中加入‘ATOMIC_REQUESTS’: True即可。如图1所示:

  

            图1 Database中加入'ATOMIC_REQUESTS':True

  通过这种配置,django在调每个view方法之前会开始一个事务,当且仅当该响应没有任何问题,django才会提交这个事务;如果view中出现了异常,则django会回滚该事务。这样做的好处显而易见,就是安全简便,但是随着网站的流量变大,如果每个view被调用时都打开一个事务就会变得有点繁重,从而会降低网站的效率。它对性能的影响取决于你的应用的查询效率以及你的数据库处理锁的能力。此外,使用这种方式,回退的只是数据库的状态,而不包括其他非数据库项的操作,例如发送email等。

  2.通过transaction中间件对http请求的事务拦截

  配置方法是在settings.py中配置MIDDLEWARE_CLASSES,如图2所示:

  

                      图2 transaction中间件配置

  需要注意的是,这样配置之后,与你中间件的配置顺序是有很大关系的。在 TransactionMiddleware 之后的所有中间件都会受到事务的控制。但CacheMiddleware,UpdateCacheMiddleware,FetchFromCacheMiddleware 这些中间件不会受到影响,因为cache机制有自己的处理方式,用了内部的connection来处理。另外TransactionMiddleware 只对默认的数据库配置有效,如果要对另外的数据连接用这种方式,必须自己实现中间件。(此处必须声明,对于这种方法,本人没有仔细研究过,只是借鉴了一下网上的资料)

  3.自己在view中通过装饰器灵活控制事务

  最后种方式,通过装饰器灵活配置,也是我们平台最后采用的方式。

    1)@transaction.autocommit

      

    django默认的事务处理,采用此装饰模式这种方式可以忽视全局的transaction配置。

    2)@transaction.commit_on_success

    

    采用此装饰模式,当此方法的所有工作完成后,才会提交事务。

    3)@transaction.commit_manually

    

    采用这种方式,你可以自由地随意提交或回滚事务,完全自己处理。如果没有调用commit()或rollback(),则程序会抛出TransactionManagementError异常。

基于原生地执行SQL语句的transaction处理

  再来讲讲Django中第二种事务处理方式,即用原生地执行SQL语句的方式。

  

                        图3 原生地执行SQL语句中的事务处理

  这种处理方式比较简单,以图3中的方法为例,首先定义了一个游标cursor,通过cursor任意地执行sql语句,最后通过transaction.commit_unless_managed()来提交事务。

延伸

  上面介绍了Django中的两种事务处理的方式,到这里我突然想到一个问题:

  如果一个方法中既包含了装饰器@transaction.commit_on_success,又执行了原生SQL语句的事务提交,当方法出现异常导致事务回滚时,原生的SQL语句所提交的事务会不会被回滚?

  为了验证这个问题,我用Flask写了两个接口来进行验证:

  

  接口delete_and_insert和接口delete_and_insert2都是先通过cursor事务提交执行清除表,然后往表里循环插入数据,当满足条件i=480的时候,抛出一个ValueError。唯一的区别就是接口1中采用了装饰器@transaction.commit_on_success,而接口2中没有。实际执行发现:

  1.在调接口1时,原数据库表中的数据不会变化,说明通过cursor执行清除表的操作也会回滚。

  2.在调接口2时,原数据库表中的数据被删除,数据库留下的是新的数据:所有的name都为user,而age从1到480。

  从而证明,当view方法加了装饰器@transaction.commit_on_success后,即使view中使用了cursor执行原生sql语句,并执行了transaction.commit_unless_managed(),但是如果view中有异常抛出,整个view方法的内容都会回滚。 

 

实际应用

  最后,回归最初的问题本身,当我把事务应用到我们平台的后台接口中后,发现了一个意外的惊喜:接口A的执行时间从原来的5-10分钟一下子缩短到了几秒钟就完成了。欣喜之余仔细思考了一下才觉得性能显著提升的原因应该是:在应用事务之前,所有SQL语句都是自动提交的,每插入一条数据,数据库表的索引可能就需要重建一次,当大量的sql语句逐一插入时,数据库表的索引就需要不断地重建,其中就需要耗费大量的时间。而在应用事务之后,所有的插入是一次性提交,数据库表的索引只需要重建一次,大大减少了开销。

  这也验证了数据库的索引不是万能的,合理的建立索引确实能大大地优化查询速度,因为索引的存储结构就像一本字典一样,我们在查找某个特定的字时会根据拼音的首字母的方式先找到该字的第一个字母所在页数,然后直接跳到那一页往后去翻。然而这也决定了字典在初始存储这些字时就需要根据这些字的特点将每个字放在特定的存储位置。当有新的字加入时,为了插入到特定的位置,就必须重新建立映射关系。

补充:合理建立索引

  下面是我工作中搜集的一些关于索引建立的规则,也欢迎大家参考,指正:

  1.搜索的索引列 
  最适合索引的列是出现在where子句种的列,或连接子句中指定的列。 而不是select关键字后的选择列表的列。
  
  2.使用唯一索引
  索引的列的基数越大,索引效果越好。比如id每行都不同,出生日期也不太相同,适合作索引。 而性别男或女只有两者情况,即使加索引,不管搜索哪个值都会得出大约一半的行。
  
  3.使用短索引 
  例如对字符串列进行索引,应该制定一个前缀长度。比如一个CHAR(200)的列,如果前10或20个字符内就大不相同,则可以只对前10个或20个字符进行索引。节省大量索引空间,也使查询更快,涉及的磁盘IO较少。较短的键值,索引高速缓存中的块能容纳更多的键值。
 
  4.不要过度索引
  每个额外的索引都要占用额外的磁盘空间,降低写操作的性能。因为在写修改表的内容时,索引必须进行更新,有时可能需要重构。 就比如一本字典,加入新的字必须要根据目录插入指定位置,后面的内容都要更新。
  
  5. 选择最常作为访问条件的列作为主键
  InnoDB有一个默认的保存顺序,按照主键或内部列进行访问的速度是最快的(比唯一索引还要快)。

         

  

Mysql事务探索及其在Django中的实践(二)的更多相关文章

  1. Mysql事务探索及其在Django中的实践(一)

    前言 很早就有想开始写博客的想法,一方面是对自己近期所学知识的一些总结.沉淀,方便以后对过去的知识进行梳理.追溯,一方面也希望能通过博客来认识更多相同技术圈的朋友.所幸近期通过了博客园的申请,那么今天 ...

  2. 05-雷海林-mysql备份原理与在TDSQL中的实践

    05-雷海林-mysql备份原理与在TDSQL中的实践 下载地址: http://files.cnblogs.com/files/MYSQLZOUQI/05-%E9%9B%B7%E6%B5%B7%E6 ...

  3. Django中模型(二)

    Django中模型(二) 三.定义模型 1.模型.属性.表.字段间的关系: 一个模型类在数据库中对应一张表:在模型类中定义的属性,对应该模型对照表中的字段. 2.定义属性 A.概述 ·django根据 ...

  4. Django 中事务的使用

    目录 Django 中事务的使用 Django默认的事务行为 在HTTP请求上加事务 在View中实现事务控制 使用装饰器 使用context manager autocommit() commit_ ...

  5. python连接redis、redis字符串操作、hash操作、列表操作、其他通用操作、管道、django中使用redis

    今日内容概要 python连接redis redis字符串操作 redis之hash操作 redis之列表操作 redis其他 通用操作,管道 django中使用redis 内容详细 1.python ...

  6. mysql事务隔离分析

    首先说明下,这里主要内容为整理总结网络搜索的零散信息. 写在最前面,mysql事务是在Innodb引擎中得以实现的,如果这点不了解的话,请自行了解. 事务直接数据的可见性通过MVCC(多版本并发控制) ...

  7. django中使用mysql数据库的事务

    django中怎么使用mysql数据库的事务   Mysql数据库事务: 在进行后端业务开始操作修改数据库时,可能会涉及到多张表的数据修改,对这些数据的修改应该是一个整体事务,即要么一起成功,要么一起 ...

  8. django中怎么使用mysql数据库的事务

    Mysql数据库事务: 在进行后端业务开始操作修改数据库时,可能会涉及到多张表的数据修改,对这些数据的修改应该是一个整体事务,即要么一起成功,要么一起失败. Django中对于数据库的事务,默认每执行 ...

  9. 事务的隔离级别,mysql中开启事务、django中开启事务

    目录 一.事务的特性 二.数据库中开启事务 三.Django中开启事务的两种方式 第一种 第二种 四.事务的隔离级别 隔离级别 如何查看mysql隔离级别? 修改事务的隔离级别 隔离级别解释 read ...

随机推荐

  1. 关于这段时间学习 EntityFramework的 一点感悟

    Ado.Net,用了N多年,Entity Framework也关注了很多年. 每当项目转型的时候,就花费大巴的时间,学习一番,潮流的东西. 这个Orm很多,这个EF很火,这么多年了,我还是不敢用,虽然 ...

  2. 渗透测试工具BurpSuite做网站的安全测试(基础版)

    渗透测试工具BurpSuite做网站的安全测试(基础版) 版权声明:本文为博主原创文章,未经博主允许不得转载. 学习网址: https://t0data.gitbooks.io/burpsuite/c ...

  3. 探索ASP.NET MVC5系列之~~~6.Session篇(进程外Session)

    其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/dunitian/p/4822808.ht ...

  4. Could not create SSL connection through proxy serve-svn

    RA layer request failedsvn: Unable to connect to a repository at URL xxxxxx 最后:Could not create SSL ...

  5. C#基础篇 - 理解委托和事件

    1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托是C#中类型安全的,可以订阅一个或多个具有相同签名方法的函数指针.简单理解,委托是一种可以把函数当做参数传递的类型.很多情况下,某 ...

  6. 谈谈一些有趣的CSS题目(九)-- 巧妙的实现 CSS 斜线

    开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...

  7. javascript排序

    利用array中的sort()排序 w3cfunction sortNumber(a,b) { return a - b } var arr = new Array(6) arr[0] = " ...

  8. 原生JS实现-星级评分系统

    今天我又写了个很酷的实例:星级评分系统(可自定义星星个数.显示信息) sufuStar.star();使用默认值5个星星,默认信息 var msg = [........]; sufuStar.sta ...

  9. 海鑫智圣:物联网漫谈之MQTT协议

    什么是MQTT协议 MQTT(消息队列遥测传输协议)是IBM在1999年专门针对物联网等应用场景来制订的轻量级双向消息传输协议,它主要是为了解决物联网上使用到的设备的互相通信的问题,以及这些设备与后端 ...

  10. centos6和centos7防火墙的关闭

    CentOS6.5查看防火墙的状态: [zh@localhost ~]$service iptable status 显示结果: [zh@localhost ~]$service iptable st ...