之前一直在使用mongo与redis,最近在项目中开始使用mysql数据库,由于现在的项目是全程异步的操作,所以在在网上查了下关于在python中异步的操作mysql,找来找去最后发现aiomysql的是实现最好的,现在简单介绍一下它的使用。

aiomysql的文档地址 https://aiomysql.readthedocs.io/en/latest/

需要根据项目中使用mysql查询的频率来选择是使用单独的connection还是使用连接池,查询较少的可以选择使用connection,使用一次以后就断开,再次使用再次连接,但是对于mysql,每次连接的开销都很高,所以建议还是使用连接池,由于不同的mysql服务对于interactive_timeout的设置时间不同,所以这里还要注意一下这个超时问题,在同步版本中关于mysql主动断开连接的问题可以参考我之前的文章,解决mysql服务器在无操作超时主动断开连接的问题 ,异步版本同样也要注意这个问题。

为了测试,我在docker中启了一个mysql服务,并且设置interactive_timeout为5秒,非常短,这样测试以后,如果一个连接在5秒钟之内都没有任何查询则主动将该连接断开。数据很简单就两条

使用单独的connection

根据官方文档,我对其进行了一点封装,采用单例模式。

这个小脚本执行很顺利,得到的结果

(1, 'yang', 18)

(2, 'fan', 16)

简单说明一个这个脚本,由于aiomysql.connect是异步的,在python里 __init__ 方法不能使用async关键词,也就是在对象的初始化时不能异步,所以我将获取连接的操作单独的使用单例模式来创建一个连接,当然也可以不使用单例,每次进行查询的时候,都重新获取一个新的连接connection。

处理连接无操作超时问题

还是那个老生常谈的问题,如果某个连接在一段时间内无操作,mysql会主动断开这个连接,我这里设置的5秒钟,那么我们看看停顿6秒钟以后再次尝试查询操作会怎样?

sleep了6秒钟以后,当再次使用该connection的cursor对象进行查询操作时,由于mysql服务已经将该连接关闭,所以会得到2013, 'Lost connection to MySQL server during query'错误。

解决方法还是和之前同步版本一样,在进行查询操作之前,先使用connection.ping()方法来检查一下连接是否有效,该方法默认会在连接无效的时候进行重新连接。这里我直接修改Pmysql类的query方法

这样上面的脚本就可以正常的执行了。

异步地执行多个查询

异步的操作的优势在于它可以"同时"的进行多个操作,如果查询只是一个一个的单独查询,那用不用异步其实都无所谓,这里尝试使用异步来同时执行多个操作

这里我准备了两个查询操作,test和test2,并将它们的结果放到另外一个协程querysum中,但是结果却出乎意料,脚本崩了……

我看崩溃信息很多,其中有一条

RuntimeError: readexactly() called while another coroutine is already waiting for incoming data,

这条给我感觉是当一个协程在等待数据的时候突然另外一个协程进来了打断了它的数据读取。

我个人推断应该是我采用了单例,它们共用一个connection然后在异步的处理过程中,当一个查询在进行过程中,在等待协程地数据返回,此时由于用了await,执行权会让出给别的协程,但是此时如果别的协程又在该connection上进行了数据库查询,则会影响到被await协程地数据读取。但是他们是用的同一个connection吗? 我打印一下看看

得到的结果是:

test... 1719190377360

test2.. 1719190377976

惊奇的发现,它们用的居然不是同一个connection…,那么问题是不是出在了单例模式下的初始化connection函数上……

我们返来看一下connection初始化条件if Pmysql.__connection == None:,如果__connection 是None的话则进行初始化操作,据此我又推断,由于我在两个协程中共用的是一个全局的mysqlobj,mysqlobj = Pmysql(),所以在这两个协程运行的一开始在同时调用await Pmysql.getconnection()时,由于此时,这个mysqlobj的__connection是空,所以这两个协程此时的判断都是为空,所以都重新进行了数据库连接操作,然后把各自初始化获取的conn赋给了mysqlobj.connection,但是这就有问题了,同一个对象的某个属性的值就变了,所以在之后使用connection的cursor对象进行数据库查询操作时就会出现问题……

那么我把其中一个协程在获取connection对象之前先暂停一下呢,让另外一个协程先获取到connection,这样当另外的协程再次获取的时候就可以直接获取到之前初始化过的connection了.

我在test2()函数获取connection之前sleep(0.1)秒

此时在test()函数和test2()函数中得到的connection就是相同的了,但是脚本依然报错

脚本报了一个0, 'Not connected'错误,这里是由于在test()和test2()函数执行完以后都执行了mysqlobj.connection.close()操作来关闭这个connection,在异步操作中,不一定谁先执行完,谁先执行完就将connection关闭,但是你关闭了,其它协程可能还会用到,所以这里就报了Not connected错误。

解决方法是将mysqlobj.connection.close()注掉,在脚本全部执行完以后统一对connection进行关闭操作

此时得到正确的执行结果

test... 1732819440584

test2.. 1732819440584

((1, 'yang', 18), (2, 'fan', 16))

((1, 'yang', 18),)

其实这里还可以不使用全局的mysqlobj,在每次查询的时候使用各自独立的对象,使用独立的连接connection

如果使用单独的对象,单独的connection,那么我们其实不需要自己来维护这套连接机制,而是使用下面要介绍的连接池操作。

使用连接池pool

使用连接池的意义在于,有一个池子,它里保持着指定数量的可用连接,当一个查询结执行之前从这个池子里取一个连接,查询结束以后将连接放回池子中,这样可以避免频繁的连接数据库,节省大量的资源。

该脚本在test方法在数据库查询操作分成了两部分,中间停了6秒钟来让mysql服务主动断开连接,当进行第二次查询的时候,并没有报2013, 'Lost connection to MySQL server during query'error,这里是由于

在getCurosr方法中是从连接池中重新获取了一个可用的连接。

异步处理多任务

和单连接一样,我们这里尝试异步的处理多个任务看看情况如何

注意这里在运行test()和test2()放入的是同一个mysqlobj,但是它们在进行查询的时候都重新通过pool.acquire()重新获取连接和游标,这样它们相互之间不互影响,可以各自进行各自的查询。

aiomysql 的使用初步就讲到这里,之后我会介绍一下在tornado中如何异步的使用aiomysql进行查询。

如果觉得文章对您有帮助欢迎评论与转发,由于今日头条上发的文章对于代码排版不太方便,所以我将代码片段都使用了截图的方式,想要复制代码请点击 "了解更多"来查看原文或者微信搜索公众号"序语程言",如果有问题也可以在下方评论

python协程池操作mysql_在python中使用aiomysql异步操作mysql相关推荐

  1. python协程池操作mysql_python_协程方式操作数据库

    #!/usr/bin/python3 # -*- coding: utf-8 -*- import requests import gevent import pymysql from gevent ...

  2. python 协程池

    python 协程池 一.问题描述 现在有一段代码,需要扫描一个网段内的ip地址,是否可以ping通. 执行起来效率太慢,需要使用协程. #!/usr/bin/env python # -*- cod ...

  3. python 协程池和pool.map用法

    一.问题描述 现在有一段代码,需要扫描一个网段内的ip地址,是否可以ping通. 执行起来效率太慢,需要使用协程. #!/usr/bin/env python # -*- coding: utf-8 ...

  4. python 协程池gevent.pool_进程池\线程池,协程,gevent

    目录 1. 进程池与线程池 2. 协程 3. gevent 4. 单线程下实现并发的套接字通信 首先写一个基于多线程的套接字 服务端: from socket import * from thread ...

  5. python协程池爬虫_Python之协程爬虫 小说网协程爬虫案例

    在Gevent协程的使用中我们已经学会简单的使用协程,这篇文章我们通过协程爬虫来测试一下具体的效果.Gevent遇到IO阻塞时会自动切换任务: from gevent import monkey mo ...

  6. python协程池_python3下multiprocessing、threading和gevent性能对比—-暨进程池、线程池和协程池性能对比 | 学步园...

    目前计算机程序一般会遇到两类I/O:硬盘I/O和网络I/O.我就针对网络I/O的场景分析下python3下进程.线程.协程效率的对比.进程采用multiprocessing.Pool进程池,线程是自己 ...

  7. python 协程、进程、线程_Python 中的进程、线程、协程

    1. 进程 进程是正在运行的程序实例,是内核分配资源的最基本的单元.进程拥有自己独立的堆和栈,独立的地址空间,资源句柄.进程由 OS 调度,调度开销较大,在并发的切换过程效率较低. Python 提供 ...

  8. python协程怎么做数据同步_Python 中的进程、线程、协程、同步、异步、回调

    进程和线程究竟是什么东西?传统网络服务模型是如何工作的?协程和线程的关系和区别有哪些?IO过程在什么时间发生? 一.上下文切换技术 简述 在进一步之前,让我们先回顾一下各种上下文切换技术. 不过首先说 ...

  9. python协程和线程区别_python中的线程和协程之间有什么区别

    一.首先我们来了解一下线程和协程的概念1.线程线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源( ...

最新文章

  1. jQuery实现王者手风琴案例
  2. php 服务器运行状态,检查服务器各种服务的运行状态
  3. Java项目 常用包的命名及理解【dao包、domain包、service包、utils包、web包、impl包】
  4. c#与java_C#与Java的区别
  5. MySQL主从同步校验与重新同步
  6. html5 txt文件上传,JavaScript html5利用FileReader实现上传功能
  7. 服务器实际显示内存,服务器实际显示内存
  8. [.net 面向对象程序设计进阶] (2) 正则表达式 (一) 快速入门
  9. PyTorch入门-词向量
  10. 大数据之-Hadoop3.x_MapReduce_ReduceTask工作机制并行度---大数据之hadoop3.x工作笔记0125
  11. 【干货】腾讯人力资源与组织管理体系.pptx(附下载链接)
  12. 关于random的多种用法
  13. 第二轮冲次会议第八次
  14. jQuery 知识点大纲
  15. java定时器quartz
  16. Python微信、QQ自动发消息
  17. 007-绘制三角函数图像(一)
  18. C、C++、java的区别
  19. 血战上海滩寻找英雄血量地址 实现无敌效果
  20. python测试培训 马哥

热门文章

  1. JavaScript判断是否是手机mobile登录
  2. LINUX DNS服务的配置(一)
  3. 很好的FireFox addin
  4. pycharm中python解释器的配置
  5. 未比对上的bam reads 处理
  6. 最小生成树(kruskal+prime)
  7. springMvc的执行流程(源码分析)
  8. 【调试工具】tcpdump
  9. 北京集训:20180323
  10. linux shell中文显示