第一篇:杂项之pymysql连接池
杂项之pymysql连接池
杂项之pymysql连接池
本节内容
- 本文的诞生
- 连接池及单例模式
- 多线程提升
- 协程提升
- 后记
1.本文的诞生
由于前几天接触了pymysql,在测试数据过程中,使用普通的pymysql插入100W条数据,消耗时间很漫长,实测990s也就是16.5分钟左右才能插完,于是,脑海中诞生了一个想法,能不能造出一个连接池出来,提升数据呢?就像一根管道太小,那就多加几根管道看效果如何呢?于是。。。前前后后折腾了将近一天时间,就有了本文的诞生。。。
2.连接池及单例模式
先说单例模式吧,为什么要在这使用单例模式呢?使用单例模式能够节省资源。
其实单例模式没有什么神秘的,简单的单例模式实现其实就是在类里面定义一个变量,再定义一个类方法,这个类方法用来为调用者提供这个类的实例化对象。(ps:个人对单例模式的一点浅薄理解...)
那么连接池是怎么回事呢?原来使用pymysql创建一个conn对象的时候,就已经和mysql之间创建了一个tcp的长连接,只要不调用这个对象的close方法,这个长连接就不会断开,这样,我们创建了一组conn对象,并将这些conn对象放到队列里面去,这个队列现在就是一个连接池了。
现在,我们先用一个连接,往数据库中插入100W条数据,下面是源码:

1 import pymysql
2 import time
3 start=time.time()
4 conn = pymysql.connect(host="192.168.10.103",port=3306,user="root",passwd="123456",db="sql_example",charset="utf8")
5 conn.autocommit(True) # 设置自动commit
6 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 设置返回的结果集用字典来表示,默认是元祖
7 data=(("男",i,"张小凡%s" %i) for i in range(1000000)) # 伪造数据,data是个生成器
8 cursor.executemany("insert into tb1(gender,class_id,sname) values(%s,%s,%s)",data) # 可以使用executemany执行多条sql
9 # conn.commit()
10 cursor.close()
11 conn.close()
12 print("totol time:",time.time()-start)

执行结果为:
totol time: 978.7649309635162
3.多线程提升
使用多线程,在启动时创建一组线程,每个线程去连接池里面获取一个连接,然后插入数据,这样将会大大提升执行sql的速度,下面是使用多线程实现的连接池源码:

1 from gevent import monkey
2 monkey.patch_all()
3
4 import threading
5
6 import pymysql
7 from queue import Queue
8 import time
9
10 class Exec_db:
11
12 __v=None
13
14 def __init__(self,host=None,port=None,user=None,passwd=None,db=None,charset=None,maxconn=5):
15 self.host,self.port,self.user,self.passwd,self.db,self.charset=host,port,user,passwd,db,charset
16 self.maxconn=maxconn
17 self.pool=Queue(maxconn)
18 for i in range(maxconn):
19 try:
20 conn=pymysql.connect(host=self.host,port=self.port,user=self.user,passwd=self.passwd,db=self.db,charset=self.charset)
21 conn.autocommit(True)
22 # self.cursor=self.conn.cursor(cursor=pymysql.cursors.DictCursor)
23 self.pool.put(conn)
24 except Exception as e:
25 raise IOError(e)
26
27 @classmethod
28 def get_instance(cls,*args,**kwargs):
29 if cls.__v:
30 return cls.__v
31 else:
32 cls.__v=Exec_db(*args,**kwargs)
33 return cls.__v
34
35 def exec_sql(self,sql,operation=None):
36 """
37 执行无返回结果集的sql,主要有insert update delete
38 """
39 try:
40 conn=self.pool.get()
41 cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)
42 response=cursor.execute(sql,operation) if operation else cursor.execute(sql)
43 except Exception as e:
44 print(e)
45 cursor.close()
46 self.pool.put(conn)
47 return None
48 else:
49 cursor.close()
50 self.pool.put(conn)
51 return response
52
53
54 def exec_sql_feach(self,sql,operation=None):
55 """
56 执行有返回结果集的sql,主要是select
57 """
58 try:
59 conn=self.pool.get()
60 cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)
61 response=cursor.execute(sql,operation) if operation else cursor.execute(sql)
62 except Exception as e:
63 print(e)
64 cursor.close()
65 self.pool.put(conn)
66 return None,None
67 else:
68 data=cursor.fetchall()
69 cursor.close()
70 self.pool.put(conn)
71 return response,data
72
73 def exec_sql_many(self,sql,operation=None):
74 """
75 执行多个sql,主要是insert into 多条数据的时候
76 """
77 try:
78 conn=self.pool.get()
79 cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)
80 response=cursor.executemany(sql,operation) if operation else cursor.executemany(sql)
81 except Exception as e:
82 print(e)
83 cursor.close()
84 self.pool.put(conn)
85 else:
86 cursor.close()
87 self.pool.put(conn)
88 return response
89
90 def close_conn(self):
91 for i in range(self.maxconn):
92 self.pool.get().close()
93
94 obj=Exec_db.get_instance(host="192.168.10.103",port=3306,user="root",passwd="012615",db="sql_example",charset="utf8",maxconn=10)
95
96 def test_func(num):
97 data=(("男",i,"张小凡%s" %i) for i in range(num))
98 sql="insert into tb1(gender,class_id,sname) values(%s,%s,%s)"
99 print(obj.exec_sql_many(sql,data))
100
101 job_list=[]
102 for i in range(10):
103 t=threading.Thread(target=test_func,args=(100000,))
104 t.start()
105 job_list.append(t)
106 for j in job_list:
107 j.join()
108 obj.close_conn()
109 print("totol time:",time.time()-start)

开启10个连接池插入100W数据的时间:
totol time: 242.81142950057983
开启50个连接池插入100W数据的时间:
totol time: 192.49499201774597
开启100个线程池插入100W数据的时间:
totol time: 191.73923873901367
4.协程提升
使用协程的话,在I/O阻塞时,将会切换到其他任务去执行,这样理论上来说消耗的资源应该会比多线程要少。下面是协程实现的连接池源代码:

1 from gevent import monkey
2 monkey.patch_all()
3 import gevent
4
5 import pymysql
6 from queue import Queue
7 import time
8
9 class Exec_db:
10
11 __v=None
12
13 def __init__(self,host=None,port=None,user=None,passwd=None,db=None,charset=None,maxconn=5):
14 self.host,self.port,self.user,self.passwd,self.db,self.charset=host,port,user,passwd,db,charset
15 self.maxconn=maxconn
16 self.pool=Queue(maxconn)
17 for i in range(maxconn):
18 try:
19 conn=pymysql.connect(host=self.host,port=self.port,user=self.user,passwd=self.passwd,db=self.db,charset=self.charset)
20 conn.autocommit(True)
21 # self.cursor=self.conn.cursor(cursor=pymysql.cursors.DictCursor)
22 self.pool.put(conn)
23 except Exception as e:
24 raise IOError(e)
25
26 @classmethod
27 def get_instance(cls,*args,**kwargs):
28 if cls.__v:
29 return cls.__v
30 else:
31 cls.__v=Exec_db(*args,**kwargs)
32 return cls.__v
33
34 def exec_sql(self,sql,operation=None):
35 """
36 执行无返回结果集的sql,主要有insert update delete
37 """
38 try:
39 conn=self.pool.get()
40 cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)
41 response=cursor.execute(sql,operation) if operation else cursor.execute(sql)
42 except Exception as e:
43 print(e)
44 cursor.close()
45 self.pool.put(conn)
46 return None
47 else:
48 cursor.close()
49 self.pool.put(conn)
50 return response
51
52
53 def exec_sql_feach(self,sql,operation=None):
54 """
55 执行有返回结果集的sql,主要是select
56 """
57 try:
58 conn=self.pool.get()
59 cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)
60 response=cursor.execute(sql,operation) if operation else cursor.execute(sql)
61 except Exception as e:
62 print(e)
63 cursor.close()
64 self.pool.put(conn)
65 return None,None
66 else:
67 data=cursor.fetchall()
68 cursor.close()
69 self.pool.put(conn)
70 return response,data
71
72 def exec_sql_many(self,sql,operation=None):
73 """
74 执行多个sql,主要是insert into 多条数据的时候
75 """
76 try:
77 conn=self.pool.get()
78 cursor=conn.cursor(cursor=pymysql.cursors.DictCursor)
79 response=cursor.executemany(sql,operation) if operation else cursor.executemany(sql)
80 except Exception as e:
81 print(e)
82 cursor.close()
83 self.pool.put(conn)
84 else:
85 cursor.close()
86 self.pool.put(conn)
87 return response
88
89 def close_conn(self):
90 for i in range(self.maxconn):
91 self.pool.get().close()
92
93 obj=Exec_db.get_instance(host="192.168.10.103",port=3306,user="root",passwd="123456",db="sql_example",charset="utf8",maxconn=10)
94
95 def test_func(num):
96 data=(("男",i,"张小凡%s" %i) for i in range(num))
97 sql="insert into tb1(gender,class_id,sname) values(%s,%s,%s)"
98 print(obj.exec_sql_many(sql,data))
99
100 start=time.time()
101 job_list=[]
102 for i in range(10):
103 job_list.append(gevent.spawn(test_func,100000))
104
105 gevent.joinall(job_list)
106
107 obj.close_conn()
108
109 print("totol time:",time.time()-start)

开启10个连接池插入100W数据的时间:
totol time: 240.16892313957214
开启50个连接池插入100W数据的时间:
totol time: 202.82087111473083
开启100个线程池插入100W数据的时间:
totol time: 196.1710569858551
5.后记
统计结果如下:
单线程一个连接使用时间:978.76s
| 10个连接池 | 50个连接池 | 100个连接池 | |
| 多线程版 | 242.81s | 192.49s | 191.74s |
| 协程版 | 240.17s | 202.82s | 196.17s |
通过统计结果显示,通过协程和多线程操作连接池插入相同数据,相对一个连接提升速度明显,但是在将连接池开到50以及100时,性能提升并没有想象中那么大,这时候,瓶颈已经不在网络I/O上了,而在数据库中,mysql在大量连接写入数据时,也会有锁的产生,这时候就需要优化数据库的相关设置了。
在对比中显示多线程利用线程池和协程利用线程池的性能差不多,但是多线程的开销比协程要大。
和大神讨论过,在项目开发中需要考虑到不同情况使用不同的技术,多线程适合使用在连接量较大,但每个连接处理时间很短的情况下,而协程适用于处理大量连接,但同时活跃的链接比较少,并且每个连接的时间量比较大的情况下。
在实际生产应用中,创建连接池可以按需分配,当连接不够用时,在连接池没达到上限的情况下,在连接池里面加入新的连接,在连接池比较空闲的情况下,关闭一些连接,实现这一个操作的原理是通过queue里面的超时时间来控制,当等待时间超过了超时时间时,说明连接不够用了,需要加入新的连接。
第一篇:杂项之pymysql连接池的更多相关文章
- 杂项之pymysql连接池
杂项之pymysql连接池 本节内容 本文的诞生 连接池及单例模式 多线程提升 协程提升 后记 1.本文的诞生 由于前几天接触了pymysql,在测试数据过程中,使用普通的pymysql插入100W条 ...
- pymysql 连接池
pymysql连接池 import pymysql from DBUtils.PooledDB import PooledDB, SharedDBConnection ''' 连接池 ''' clas ...
- Hibernate游记——装备篇《三》(连接池的使用)
这里介绍几种最常见的连接池配置: [说明:在hibernate3.0中,已经不再支持dbcp了,hibernate的作者在hibernate.org中,明确指出在实践中发现dbcp有BUG,在某些种情 ...
- spring 5.x 系列第6篇 —— 整合 mybatis + druid 连接池 (代码配置方式)
源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 项目目录结构 1.创建maven工程,除了Spring基本依赖外,还需要导 ...
- spring 5.x 系列第5篇 —— 整合 mybatis + druid 连接池 (xml配置方式)
源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 项目目录结构 1.创建maven工程,除了Spring基本依赖外,还需要导 ...
- Swoole Redis 连接池的实现
概述 这是关于 Swoole 入门学习的第九篇文章:Swoole Redis 连接池的实现. 第八篇:Swoole MySQL 连接池的实现 第七篇:Swoole RPC 的实现 第六篇:Swoole ...
- 【JAVAWEB学习笔记】10_JDBC连接池&DBUtils
使用连接池改造JDBC的工具类: 1.1.1 需求: 传统JDBC的操作,对连接的对象销毁不是特别好.每次创建和销毁连接都是需要花费时间.可以使用连接池优化的程序. * 在程序开始的 ...
- 通过连接池和字段索引,提升单点登录cas的性能
cas是多个系统的中心认证,认证的过程就是用户的登录信息和数据库中的信息匹对的过程,假设某一时刻登录的人数非常多,须要频繁的读取数据库,数据库连接的管理就是问题. 前天測试评教时无意之中把单点登录的问 ...
- 【JDBC&Dbutils】JDBC&JDBC连接池&DBUtils使用方法(重要)
-----------------------JDBC---------- 0. db.properties文件 driver=com.mysql.jdbc.Driver url=jdbc: ...
随机推荐
- gdb调试高级用法
Linux下进程崩溃时定位源代码位置 如何在调试内核时,同时可以调试应用程序的做法: (cskygdb) c Continuing. ^C Program received signal SIGINT ...
- hadoop2.2编程:hadoop性能测试
<hadoop the definitive way>(third version)中的Benchmarking a Hadoop Cluster Test Cases 的class在新的 ...
- GridControl 列中显示图片 z
如何在 DevExpress.XtraGrid.GridControl 显示图片列. 方法很多,我把它们逐一写在附言中,方便大家分情况合理使用. 附言1 附言2 附言3 第 1 条附言 · ...
- Keepass TAN 记录的使用
之前一直不知道 Keepass 软件中这个“TAN”是做什么用的,今天看了帮助文档,然后通过维基百科,有了一些大致的了解. 简单来说,一个 TAN 记录用于某些网站增加安全操作使用的.常规下只要用户名 ...
- HTML5 移动应用开发环境搭建及原理分析
开发环境搭建: 一.Android 开发平台搭建 安装java jdk:\\10.194.151.132\Mewfile\tmp\ADT 配置java jdk 1) 新建系统变量,JAVA_HOME ...
- 一段SQL代码的压缩:从974行到96行,十倍压缩
涉及到数据库拆分,需要将相关表的JOIN SQL剥离. 剥离过程中,发现了这么个SQL XML.整个SQL 974 行,分为6个函数.读起来费劲无比. <?xml version="1 ...
- Spock Proxy
Spock Proxy undefined 服务网关_百度搜索 undefined 8月2日,深圳 | DockOne技术沙龙开始报名啦 - DockOne.io undefined 知道创宇爬虫题- ...
- 如何使用chown?
以test为例,目前test归root用户拥有,也归root组拥有 [root@localhost home]# ls -al total 36 drwxr-xr-x 6 root ro ...
- STM32 IAP 在线更新程序 为什么有时行 有时又不行 感觉不可靠 问题解决
搞了一天才发现 在MDK设置选项中有个"linker"选项卡 , 需要在Project->Options->Linker中将Use Memory Layout from ...
- mysql 插入默认值的问题 sql-mode
刚好碰到如果不给默认值mysql数据就插入不成功的问题,后来百度了很多,试了下结果 把my.ini里面的[mysqld]的sql-mode 换成下面的一行,如果没有则添加 sql-mode=&quo ...