前言 

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

问题背景

  本人最近在用python的Web开发框架Django开发一个平台,平台的数据库用的是远程的Mysql (RDS),出于对数据库信息的安全考虑,对访问数据库的ip作了限制。因此,我们是通过在服务器上用轻量级的python Web框架Flask搭建了一些数据存取接口,所有外部机子对数据库的访问是通过调用接口来实现的。而我们起这个web接口的方式非常简单,没有通过任何的容器,仅仅是使用 “python qvServer.py”的方式(qvServer.py是我们的python接口脚本)。这种起接口的方式会存在一个严重的问题:

   问题1 单线程:当Flask在处理某一个请求时,如果该请求还没有处理完成,而其他的应用程序又发送了新的请求,此时新请求需要等待,直到前一个请求处理完成时服务器才可以开始处理下一个请求。

当然,这样起也有好处:所有请求排队进行等候一一处理,不会有多个接口同时被调用,而同时对数据库表进行并行操作的风险。然后,在实际执行过程中,我们发现有一些接口的执行时间非常长,比如接口A在执行时,需要执行几千条的SQL插入命令,可能需要耗费将近5-10分钟的时间。此时将会导致其他请求严重阻塞!

  为了解决这个问题,参考了朋友的意见和书《Python Web测试驱动方法》中的内容,决定采用Python的容器gunicorn(Green Unicorn,俗称“绿色独角兽”)通过多个进程来起Flask接口服务。使用gunicorn起web服务的命令很简单:nohup gunicorn -w 4 -b 0.0.0.0:9001 qvServer:app &  之后我们查看linux进程的状态,如下图1所示:

图1 查看gunicorn进程

可以看到gunicorn是通过5个进程,一个父进程(PID:17135)和四个子进程来起qvServer,这样处理后,通过实际的执行验证,确实解决了请求不能并发处理的问题,服务器可以同时支持多个请求并行处理,处理效率大大提升,但是却出现了另一个新的问题:

  问题2 并发处理导致数据存储失败:还是那个接口A,几千条SQL插入命令在处理时,总是执行失败,且最终数据库总是会残留一部分的脏数据(部分插入成功,发生失败后后续插入操作被中断)

为了解决脏数据的问题,我们自然而然(其实是在老大的提醒下)地会想到引入数据库事务来管理请求,当发生异常时,将这一次的所有数据进行回滚,杜绝脏数据残留在数据库中。由于本人之前对这一块毫无所知,所以趁着清明假期特地学习了一下,主要是借鉴了《深入浅出MySQL——数据库开发、优化与管理维护》一书,下面就是对数据库事务的一些总结,以及在Django中的使用。

事务基础

  个人的理解:“事务”就是对一系列与数据库相关操作的封装。主要是当你在对数据库进行“写”操作时,你可以将一系列的操作进行“捆绑”,当且仅当所有这一系列操作都执行成功时,你再进行“一次提交”通知Mysql进行一次性地处理。如果中间的某一项操作出现了异常,你可以对整个事务包含的所有操作(包括已经成功的操作)进行“回滚”,回到执行前的状态。此外,你可以在你的这一系列操作中间设置“保存点”(savepoint),当出现异常时,你可以选择回滚到指定保存点,使得该保存点之前的所有操作执行有效,该保存点后的所有操作都被回退。

  默认情况下,Mysql的事务是自动提交的,即你每执行一条sql语句,都会立即提交到数据库。
  下面是事务的一些基本操作:

  start transaction —— 开始事务

  commit —— 提交事务

  commit and chain —— 提交事务,并启动一个新的事务

  rollback —— 回滚事务(默认回滚整个事务)

  savepoint test —— 定义savepoint,名为test  (注意:mysql支持指定回滚事务的一个部分,但是不能指定提交事务的一个部分;复杂的应用可以通过定义多个不同的savepoint,满足不同的条件回滚到不同的savepoint;但是,如果定义了相同名字的savepoint,则后面定义的会覆盖前面定义的!)

  rollback to savepoint test —— 回滚到savepoint test处

  release savepoint test —— 删除名为test的savepoint

  下面是一些实际的操作:

  1. 事务开始和提交。

  首先通过cmd命令行打开两个数据库连接(session1、session2),初始时表lists_userinfo为空。

  步骤1:session1中执行操作:开始事务--插入1行数据--查询

图2 事务开始和提交(1)

  步骤2:session2中执行操作:查询

   

             图3 事务开始和提交(2)

  可以看到,此时session1中开始了事务,且并未提交,session2中并未查询到任何结果,而session1可以查询到自己已经提交的数据。

  步骤3:session1中执行操作:提交事务--查询

  

              图4 事务开始和提交(3)

  步骤4:session2中执行操作:查询

  

          图5 事务开始和提交(4)

  可以看到,此时session2中才可以查询到session1插入的数据。即:只有当事务完成提交后,事务中的操作才会真正应用到数据库中。

  2. 事务回滚。

  步骤1:session1执行操作:开始事务--插入1行数据--查询--回滚--查询

  

                  图6 事务回滚(1)

  可以看到,在rollback后,事务的操作被回滚,实际并没有应用到Mysql中生效。

  3. 回滚到指定savepoint及删除指定savepoint。

  步骤1:session1执行操作:开始事务--插入1行数据--设置savepoint--再插入1行数据--查询--回滚到savepoint

  

            图7 回滚到指定savepoint及删除指定savepoint(1)

  可以看到,当回滚到指定保存点cy后,在保存点cy之后插入的数据没有提交到数据库。那么此时,在session2中进行查询会是什么结果呢?我们来看看步骤2。

  步骤2: session2执行操作:查询

  

    图8 回滚到指定savepoint及删除指定savepoint(2)

  可以看到此时session2中并没有查询到session1中插入的数据。

  步骤3: session1执行操作:提交

  

    图9 回滚到指定savepoint及删除指定savepoint(3)

  步骤4: session2执行操作:查询

  

    图10 回滚到指定savepoint及删除指定savepoint(4)

  可以看到,即使rollback后,也需要执行commit操作,数据库的事务才能应用生效。

  对于删除savepoint,大家可以自己尝试验证一下。步骤如下:

  

      图11 回滚到指定savepoint及删除指定savepoint(5)

  可以看到,当删除了savepoint后,系统就找不到指定的savepoint了。

延伸

  另外,作为一个Mysql菜鸟,在实践过程中还发现以下两点跟锁相关的事务特性:

  1.rollback不能回滚锁。

  2.start transaction开启新事务,会自动执行一个隐含的unlock tables操作。

  步骤1:session1执行操作: 对表加write锁--插入1行数据

  

             图12 事务与锁(1)

  步骤2:session2执行操作:查询

  

         图13 事务与锁(2)

  此时session2的查询处于阻塞状态。

  步骤3:session1执行操作:start transaction

  

              图14 事务与锁(3)

  步骤4:察看session2中之前的”查询“语句执行结果

  

              图15 事务与锁(4)

  可以看到,此时session2中的查询执行成功,说明session1中执行start transaction会隐含地将write锁释放。

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

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

    继上一篇<Mysql事务探索及其在Django中的实践(一)>交代完问题的背景和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 中事务的使用 Django默认的事务行为 在HTTP请求上加事务 在View中实现事务控制 使用装饰器 使用context manager autocommit() commit_ ...

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

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

  5. mysql事务隔离分析

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

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

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

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

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

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

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

  9. Django中MySQL事务的使用

    Django中事物的使用 from django.db import transaction @transaction.atomic通过transaction的@transaction.atomic装 ...

随机推荐

  1. JavaScript常见的五种数组去重的方式

    ▓▓▓▓▓▓ 大致介绍 JavaScript的数组去重问题在许多面试中都会遇到,现在做个总结 先来建立一个数组 var arr = [1,2,3,3,2,'我','我',34,'我的',NaN,NaN ...

  2. Angular企业级开发(3)-Angular MVC实现

    1.MVC介绍 Model-View-Controller 在20世纪80年代为程序语言Smalltalk发明的一种软件架构.MVC模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并 ...

  3. ubuntu系统下如何修改host

    Ubuntu系统的Hosts只需修改/etc/hosts文件,在目录中还有一个hosts.conf文件,刚开始还以为只需要修改这个就可以了,结果发现是需要修改hosts.修改完之后要重启网络.具体过程 ...

  4. spring源码分析之freemarker整合

    FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页.电子邮件.配置文件.源代码等)的通用工具. 它不是面向最终用户的,而是一个Java类库,是一款程 ...

  5. ASP.NET Core 中文文档 第五章 测试(5.2)集成测试

    原文: Integration Testing 作者: Steve Smith 翻译: 王健 校对: 孟帅洋(书缘) 集成测试确保应用程序的组件组装在一起时正常工作. ASP.NET Core支持使用 ...

  6. C#各种同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEvent

    看下组织结构: System.Object System.MarshalByRefObject System.Threading.WaitHandle System.Threading.Mutex S ...

  7. ubuntu 下安装scrapy

    1.把Scrapy签名的GPG密钥添加到APT的钥匙环中: sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 6272 ...

  8. 用Java代码实现拦截区域网数据包

    起因: 吃饭的时间在想如果区域网内都是通过路由器上网,那如何实现拦截整个区域网的数据包,从而实现某种窥探欲. 思路:      正常是通过电脑网卡预先设置或分配的IP+网关对路由器进行通讯,比如访问百 ...

  9. 使用git进行源代码管理

    git是一款非常流行的分布式版本控制系统,使用Local Repository追踪代码的修改,通过Push和Pull操作,将代码changes提交到Remote Repository,或从Remote ...

  10. SQL Server中SELECT会真的阻塞SELECT吗?

    在SQL Server中,我们知道一个SELECT语句执行过程中只会申请一些意向共享锁(IS) 与共享锁(S), 例如我使用SQL Profile跟踪会话86执行SELECT * FROM dbo.T ...