当前位置:Gxlcms > 数据库问题 > Python 数据库连接池DButils

Python 数据库连接池DButils

时间:2021-07-01 10:21:17 帮助过:11人阅读

pymysql def index(): conn = pymysql.connect() cursor = conn.cursor() cursor.execute(select * from tb where id > %s,[5,]) result = cursor.fetchall() cursor.close() conn.close() print(result) def update(): # 第一步:缺点:每次请求反复创建数据库连接,连接数太多 conn = pymysql.connect() cursor = conn.cursor() cursor.execute(update userinfo set username=%s where id > %s,[ctz,5,]) conn.commit() cursor.close() conn.close() return hello

存在问题:每一次请求就得创建数据库链接,可能我们处理数据只需要很少时间,而连接数据库却占了很长时间,每次请求反复创建数据库连接,连接数太多,造成数据库性能的损耗

 

场景二:

  1. 缺点,不能支持并发
  1. <span style="color: #0000ff">import</span><span style="color: #000000"> pymysql
  2. CONN</span>=<span style="color: #000000">pymysql.connect()
  3. </span><span style="color: #0000ff">def</span><span style="color: #000000"> index():
  4. cursor </span>=<span style="color: #000000"> CONN.cursor()
  5. cursor.execute(</span><span style="color: #800000">‘</span><span style="color: #800000">select * from tb where id > %s</span><span style="color: #800000">‘</span>,[5<span style="color: #000000">,])
  6. result </span>=<span style="color: #000000"> cursor.fetchall()
  7. cursor.close()
  8. CONN.close()
  9. </span><span style="color: #0000ff">print</span><span style="color: #000000">(result)
  10. </span><span style="color: #0000ff">def</span><span style="color: #000000"> update():</span><span style="color: #000000">
  11. cursor </span>=<span style="color: #000000"> CONN.cursor()
  12. cursor.execute(</span><span style="color: #800000">‘</span><span style="color: #800000">update userinfo set username=%s where id > %s</span><span style="color: #800000">‘</span>,[<span style="color: #800000">‘</span><span style="color: #800000">ctz</span><span style="color: #800000">‘</span>,5<span style="color: #000000">,])
  13. CONN.commit()
  14. cursor.close()
  15. CONN.close()
  16. </span><span style="color: #0000ff">return</span> <span style="color: #800000">‘</span><span style="color: #800000">hello</span><span style="color: #800000">‘</span>

把数据库链接放到常量中去了,这样就可以保证每次请求都只创建一次数据库链接,但是还是存在问题:不能支持并发

  如果有多个线程进来的话要执行同一个函数,第一个线程还没执行玩,第二个线程进来啦,但是只有一个conn这时就报错;当然我们也可以用下面这种方式解决报错问题:

  1. <span style="color: #0000ff">import</span><span style="color: #000000"> threading
  2. LOCK</span>=<span style="color: #000000">threading.RLock()
  3. CONN</span>=<span style="color: #000000">pymysql.connect()
  4. </span><span style="color: #0000ff">def</span><span style="color: #000000"> index():
  5. with LOCK:
  6. cursor </span>=<span style="color: #000000"> CONN.cursor()
  7. cursor.execute(</span><span style="color: #800000">‘</span><span style="color: #800000">select * from tb where id > %s</span><span style="color: #800000">‘</span>, [5<span style="color: #000000">, ])
  8. result </span>=<span style="color: #000000"> cursor.fetchall()
  9. cursor.close()
  10. </span><span style="color: #0000ff">print</span>(result)

但是 这样虽然解决了多个线程竞争报错的问题,但是程序加锁后变成了串行,运行效率就变低了

 

为了解决上面两个场景出现的问题,我们找到了解决的办法,就是创建数据库连接池

 

数据库链接池

DBUtils是Python的一个用于实现数据库连接池的模块。

此连接池有两种连接模式:

  • 模式一:为每个线程创建一个连接,线程即使调用了close方法,也不会关闭,只是把连接重新放到连接池,供自己线程再次使用。当线程终止时,连接自动关闭。

   实现原理:基于threaing.local实现为每一个线程创建一个链接,该线程关闭时,不是真正关闭;本线程再次调用时,还是使用的最开始创建的连接。直到线程终止,数据库连接才关闭

  1. <span style="color: #800000">"""</span><span style="color: #800000">
  2. 为每个线程创建一个连接,thread.local实现。
  3. </span><span style="color: #800000">"""</span>
  4. <span style="color: #0000ff">from</span> DBUtils.PersistentDB <span style="color: #0000ff">import</span><span style="color: #000000"> PersistentDB
  5. </span><span style="color: #0000ff">import</span><span style="color: #000000"> pymysql
  6. POOL </span>=<span style="color: #000000"> PersistentDB(
  7. creator</span>=pymysql, <span style="color: #008000">#</span><span style="color: #008000"> 使用链接数据库的模块</span>
  8. maxusage=None, <span style="color: #008000">#</span><span style="color: #008000"> 一个链接最多被重复使用的次数,None表示无限制</span>
  9. setsession=[], <span style="color: #008000">#</span><span style="color: #008000"> 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]</span>
  10. ping=<span style="color: #000000">0,
  11. </span><span style="color: #008000">#</span><span style="color: #008000"> 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</span>
  12. closeable=<span style="color: #000000">False,
  13. </span><span style="color: #008000">#</span><span style="color: #008000"> 如果为False时, conn.close() 实际上被忽略,供下次使用,再线程关闭时,才会自动关闭链接。如果为True时, conn.close()则关闭链接,那么再次调用pool.connection时就会报错,因为已经真的关闭了连接(pool.steady_connection()可以获取一个新的链接)</span>
  14. threadlocal=None, <span style="color: #008000">#</span><span style="color: #008000"> 本线程独享值得对象,用于保存链接对象,如果链接对象被重置</span>
  15. host=<span style="color: #800000">‘</span><span style="color: #800000">127.0.0.1</span><span style="color: #800000">‘</span><span style="color: #000000">,
  16. port</span>=3306<span style="color: #000000">,
  17. user</span>=<span style="color: #800000">‘</span><span style="color: #800000">root</span><span style="color: #800000">‘</span><span style="color: #000000">,
  18. password</span>=<span style="color: #800000">‘</span><span style="color: #800000">123</span><span style="color: #800000">‘</span><span style="color: #000000">,
  19. database</span>=<span style="color: #800000">‘</span><span style="color: #800000">pooldb</span><span style="color: #800000">‘</span><span style="color: #000000">,
  20. charset</span>=<span style="color: #800000">‘</span><span style="color: #800000">utf8</span><span style="color: #800000">‘</span><span style="color: #000000">
  21. )
  22. </span><span style="color: #0000ff">def</span><span style="color: #000000"> func():
  23. </span><span style="color: #008000">#</span><span style="color: #008000"> conn = SteadyDBConnection()</span>
  24. conn =<span style="color: #000000"> POOL.connection()
  25. cursor </span>=<span style="color: #000000"> conn.cursor()
  26. cursor.execute(</span><span style="color: #800000">‘</span><span style="color: #800000">select * from tb1</span><span style="color: #800000">‘</span><span style="color: #000000">)
  27. result </span>=<span style="color: #000000"> cursor.fetchall()
  28. cursor.close()
  29. conn.close() </span><span style="color: #008000">#</span><span style="color: #008000"> 不是真的关闭,而是假的关闭。 conn = pymysql.connect() conn.close()</span>
  30. <span style="color: #000000">
  31. conn </span>=<span style="color: #000000"> POOL.connection()
  32. cursor </span>=<span style="color: #000000"> conn.cursor()
  33. cursor.execute(</span><span style="color: #800000">‘</span><span style="color: #800000">select * from tb1</span><span style="color: #800000">‘</span><span style="color: #000000">)
  34. result </span>=<span style="color: #000000"> cursor.fetchall()
  35. cursor.close()
  36. conn.close()
  37. </span><span style="color: #0000ff">import</span><span style="color: #000000"> threading
  38. </span><span style="color: #0000ff">for</span> i <span style="color: #0000ff">in</span> range(10<span style="color: #000000">):
  39. t </span>= threading.Thread(target=<span style="color: #000000">func)
  40. t.start()</span>

模式二:创建一批连接到连接池,供所有线程共享使用,使用时来进行获取,使用完毕后,再次放回到连接池。

PS:由于pymysql、MySQLdb等threadsafety值为1,所以该模式连接池中的线程会被所有线程共享。

  1. <span style="color: #0000ff">import</span><span style="color: #000000"> time
  2. </span><span style="color: #0000ff">import</span><span style="color: #000000"> pymysql
  3. </span><span style="color: #0000ff">import</span><span style="color: #000000"> threading
  4. </span><span style="color: #0000ff">from</span> DBUtils.PooledDB <span style="color: #0000ff">import</span><span style="color: #000000"> PooledDB, SharedDBConnection
  5. POOL </span>=<span style="color: #000000"> PooledDB(
  6. creator</span>=pymysql, <span style="color: #008000">#</span><span style="color: #008000"> 使用链接数据库的模块</span>
  7. maxconnections=6, <span style="color: #008000">#</span><span style="color: #008000"> 连接池允许的最大连接数,0和None表示不限制连接数</span>
  8. mincached=2, <span style="color: #008000">#</span><span style="color: #008000"> 初始化时,链接池中至少创建的空闲的链接,0表示不创建</span>
  9. <span style="color: #000000">
  10. maxcached</span>=5, <span style="color: #008000">#</span><span style="color: #008000"> 链接池中最多闲置的链接,0和None不限制</span>
  11. maxshared=3, <span style="color: #008000">#</span><span style="color: #008000"> 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。</span>
  12. blocking=True, <span style="color: #008000">#</span><span style="color: #008000"> 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错</span>
  13. maxusage=None, <span style="color: #008000">#</span><span style="color: #008000"> 一个链接最多被重复使用的次数,None表示无限制</span>
  14. setsession=[], <span style="color: #008000">#</span><span style="color: #008000"> 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]</span>
  15. ping=<span style="color: #000000">0,
  16. </span><span style="color: #008000">#</span><span style="color: #008000"> 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</span>
  17. host=<span style="color: #800000">‘</span><span style="color: #800000">127.0.0.1</span><span style="color: #800000">‘</span><span style="color: #000000">,
  18. port</span>=3306<span style="color: #000000">,
  19. user</span>=<span style="color: #800000">‘</span><span style="color: #800000">root</span><span style="color: #800000">‘</span><span style="color: #000000">,
  20. password</span>=<span style="color: #800000">‘</span><span style="color: #800000">123</span><span style="color: #800000">‘</span><span style="color: #000000">,
  21. database</span>=<span style="color: #800000">‘</span><span style="color: #800000">pooldb</span><span style="color: #800000">‘</span><span style="color: #000000">,
  22. charset</span>=<span style="color: #800000">‘</span><span style="color: #800000">utf8</span><span style="color: #800000">‘</span><span style="color: #000000">
  23. )
  24. </span><span style="color: #0000ff">def</span><span style="color: #000000"> func():
  25. </span><span style="color: #008000">#</span><span style="color: #008000"> 检测当前正在运行连接数的是否小于最大链接数,如果不小于则:等待或报raise TooManyConnections异常</span>
  26. <span style="color: #008000">#</span><span style="color: #008000"> 否则</span>
  27. <span style="color: #008000">#</span><span style="color: #008000"> 则优先去初始化时创建的链接中获取链接 SteadyDBConnection。</span>
  28. <span style="color: #008000">#</span><span style="color: #008000"> 然后将SteadyDBConnection对象封装到PooledDedicatedDBConnection中并返回。</span>
  29. <span style="color: #008000">#</span><span style="color: #008000"> 如果最开始创建的链接没有链接,则去创建一个SteadyDBConnection对象,再封装到PooledDedicatedDBConnection中并返回。</span>
  30. <span style="color: #008000">#</span><span style="color: #008000"> 一旦关闭链接后,连接就返回到连接池让后续线程继续使用。</span>
  31. <span style="color: #008000">#</span><span style="color: #008000"> PooledDedicatedDBConnection</span>
  32. conn =<span style="color: #000000"> POOL.connection()
  33. </span><span style="color: #008000">#</span><span style="color: #008000"> print(th, ‘链接被拿走了‘, conn1._con)</span>
  34. <span style="color: #008000">#</span><span style="color: #008000"> print(th, ‘池子里目前有‘, pool._idle_cache, ‘\r\n‘)</span>
  35. <span style="color: #000000">
  36. cursor </span>=<span style="color: #000000"> conn.cursor()
  37. cursor.execute(</span><span style="color: #800000">‘</span><span style="color: #800000">select * from tb1</span><span style="color: #800000">‘</span><span style="color: #000000">)
  38. result </span>=<span style="color: #000000"> cursor.fetchall()
  39. conn.close()
  40. conn </span>=<span style="color: #000000"> POOL.connection()
  41. </span><span style="color: #008000">#</span><span style="color: #008000"> print(th, ‘链接被拿走了‘, conn1._con)</span>
  42. <span style="color: #008000">#</span><span style="color: #008000"> print(th, ‘池子里目前有‘, pool._idle_cache, ‘\r\n‘)</span>
  43. <span style="color: #000000">
  44. cursor </span>=<span style="color: #000000"> conn.cursor()
  45. cursor.execute(</span><span style="color: #800000">‘</span><span style="color: #800000">select * from tb1</span><span style="color: #800000">‘</span><span style="color: #000000">)
  46. result </span>=<span style="color: #000000"> cursor.fetchall()
  47. conn.close()
  48. func()</span>

PS:

  1. <span style="color: #000000">如果有三个线程来连接池中获取链接:
  2. 1个链接可以为三个线程提供服务
  3. 2个链接可以为三个线程提供服务
  4. 3个链接可以为三个线程提供服务
  5. maxshared在使用pymysql</span>/<span style="color: #000000">mysqldb时均无用,因为他们的threadsafety都是1,而查看PooledDB源码就可以知道
  6. </span><span style="color: #0000ff">if</span> threadsafety > 1 <span style="color: #0000ff">and</span><span style="color: #000000"> maxshared:
  7. self._maxshared </span>=<span style="color: #000000"> maxshared
  8. self._shared_cache </span>= [] <span style="color: #008000">#</span><span style="color: #008000"> the cache for shared connections</span>
  9. <span style="color: #0000ff">else</span><span style="color: #000000">:
  10. self._maxshared </span>=<span style="color: #000000"> 0
  11. 即使设置了 永远都是0</span>

 

Python 数据库连接池DButils

标签:多个   命令   数据库链接   加锁   mys   blocking   有一个   重置   重复   

人气教程排行