Flask 的 数据库连接 与 DBUtils 数据库连接池
1本地线程:thread_local 2 3 为每个线程创建存储数据的空间,用于线程之间的数据隔离 4 否则多个线程同时访问,会使得数据混乱
<br> <br>
1 Flask 连接数据库 的方法
<br>1 pymysql 2 3 依赖 SQLArchemy
数据库连接的方案
<br> <br>11 每次请求反复的创建数据库连接 (可以并发请求 -- 但是每次都会创建连接) 2 # 连接数太多 3 42 将连接放在全局变量 单线程(没有问题) pymysql 只能支持单线程 5 # 不能支持并发 6 7 83 import threading 9 LOCK = threading.RLock() 10 with LOCK: 11 connect --- 12 ... 13 # 这样控制每次只能有一个线程连接 (不能并发操作) 14 # 不加锁 ,多线程状况下 会报错 15 16 174 基于 DBUtils 实现 数据库连接池 18 19 -- 为每一个线程创建一个连接,当连接关闭的时候,不是真正的关闭; 20 当本线程再次使用的时候,还是使用最开始的连接, 21 只有当线程终止的时候,连接才会关闭 22 23 -- 创建一个连接池(列表),为所有的线程 提供连接,使用完毕 再放回连接池 24 所有的连接都可以被重复使用,共享 25 26 -- 基于实现本地线程: 线程间的数据隔离
2 为什么要使用数据库连接池
如果没有连接池,使用pymysql来连接数据库时,单线程应用完全没有问题,但如果涉及到多线程应用那么就需要加锁,一旦加锁那么连接势必就会排队等待,当请求比较多时,性能就会降低了
实现并发,避免无限制的连接
<br>
<br>
3 使用DBUtils 数据库连接池
DBUtils是Python的一个用于实现数据库连接池的模块
<br>
模式一:为每个线程创建一个连接,线程即使调用了close方法,也不会关闭,只是把连接重新放到连接池,供自己线程再次使用。当线程终止时,连接自动关闭
<br>1基于本地线程 每个线程创建一个本地线程 2 3POOL = PersistentDB( 4 creator=pymysql, # 使用链接数据库的模块 5 maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制 6 setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."] 7 ping=0, 8 # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always 9 closeable=False, 10 # 如果为False时, conn.close() 实际上被忽略,供下次使用,再线程关闭时,才会自动关闭链接。如果为True时, conn.close()则关闭链接,那么再次调用pool.connection时就会报错,因为已经真的关闭了连接(pool.steady_connection()可以获取一个新的链接) 11 threadlocal=None, # 本线程独享值得对象,用于保存链接对象,如果链接对象被重置 12 host='127.0.0.1', 13 port=3306, 14 user='root', 15 password='123', 16 database='pooldb', 17 charset='utf8' 18) 19 20def func(): 21 conn = POOL.connection(shareable=False) 22 cursor = conn.cursor() 23 cursor.execute('select * from tb1') 24 result = cursor.fetchall() 25 cursor.close() 26 conn.close() # 结束本次连接,供本线程再次使用 27 28func()
模式二:创建一批连接到连接池,供所有线程共享使用
1先 创建本地连接,让所有线程公用 2 3PS:由于pymysql、MySQLdb等threadsafety值为1,所以该模式连接池中的线程会被所有线程共享。 4所以 最大共享数 无用的。
这种模式是最常使用的
<br>1db.py: 2 3 import time 4 import pymysql 5 import threading 6 from DBUtils.PooledDB import PooledDB, SharedDBConnection 7 POOL = PooledDB( 8 creator=pymysql, # 使用链接数据库的模块 9 maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数 10 mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建 11 maxcached=5, # 链接池中最多闲置的链接,0和None不限制 12 maxshared=3, # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。 13 blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错 14 maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制 15 setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."] 16 ping=0, 17 # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always 18 host='127.0.0.1', 19 port=3306, 20 user='root', 21 password='123', 22 database='pooldb', 23 charset='utf8' 24 ) 25 26 27使用的时候: 28 29 from db import POOL 30 31 def func(): 32 # 检测当前正在运行连接数的是否小于最大链接数,如果不小于则:等待或报raise TooManyConnections异常 33 # 否则 34 # 则优先去初始化时创建的链接中获取链接 SteadyDBConnection。 35 # 然后将SteadyDBConnection对象封装到PooledDedicatedDBConnection中并返回。 36 # 如果最开始创建的链接没有链接,则去创建一个SteadyDBConnection对象,再封装到PooledDedicatedDBConnection中并返回。 37 # 一旦关闭链接后,连接就返回到连接池让后续线程继续使用。 38 39 conn = POOL.connection() 40 41 # print(th, '链接被拿走了', conn1._con) 42 # print(th, '池子里目前有', pool._idle_cache, '\r\n') 43 44 cursor = conn.cursor() 45 cursor.execute('select * from tb1') 46 result = cursor.fetchall() 47 conn.close() # 结束本次连接,把连接放回连接池 48 49 func()
小结
11 每次操作都要连接 --- 不允许 2 32 公用一个连接,多线程会出问题, 加锁-->> 变成了串行 4 53 DBUtils 数据库连接池: 6 (1) 基于treading.local 实现为每一个线程 创建一个连接,当前线程可以反复使用本线程的连接,线程终止的时候连接关闭 7 8 # treading.local 为每一个线程创建一份独立的值,相当于一个字典(每个线程的唯一标识为 键) 9 10 (2) 设置最大连接数 11 默认启动时,连接池中创建的连接 12 13 # 1个连接可以为3个线程服务(可能中途返还)