SQLAlchemy与数据库连接的QueuePool问题详解

从常见的一种连接错误说起

有关SQLAlchemy与数据库的连接(Connection),最常见的一种runtime error如下所示:

QueuePool limit of size <x> overflow <y> reached, connection timed out, timeout <z>

这个异常的含义是当前系统所需并发数据库连接(对应的sqlalchemy.engine.Connection()sqlalchemy.orm.session.Session(),下文中将这二者统称为连接)超过了当前使用的engine所配置的并发连接数目上限(该上限由两个值组成:pool_sizemax_overflow,这点我们将在下面讨论)。

下面我们总结修复程序中出现的上述错误时需要注意的几点重要情况。

SQLAlchemy连接数据库所使用的Engine对象默认采用一个连接池(a pool of connections)来管理连接

当我们使用Engine对象所对应的SQL数据库连接的资源时,这些对数据库的连接是通过一个连接池(Connection pooling)来管理的。当我们释放(release)一个连接资源时,这个连接并不是被销毁了,而是仍然连接着数据库,只不过其将会被重新存储如一个用于管理连接的连接池(默认为QueuePool)中。放入连接池中的连接可以被复用。事实上总有一定数目的数据库连接被保存在这个连接池中,即使在我们的代码中看起来像是连接被释放了一样。这些连接会在我们的程序结束运行之后自动被销毁,或者当我们显式地调用销毁连接池的代码时被销毁。

连接复用

由于这个连接池的存在,每当我们在代码中调用Engine.connect()方法或者调用ORM对应的Session的时候,往往会得到一个已存在与连接池中的数据库连接,而不是得到了一个全新的连接对象。然而当连接池中没有现成可用的连接对象的时候,在不超过配置所允许的连接上限的条件下,新的连接对象会被创建并返回给调用这些方法的程序。

默认使用的QueuePool

SQLAlchemy默认所使用的连接池为sqlalchemy.pool.QueuePool。当目前总连接数没有超过配置的上限且池中没有现成可用的连接的情况下,一个新的连接会被建立并返回给调用创建新连接的方法的程序。这个上限等于create_engine.pool_sizecreate_engine.max_overflow之和。配置engine的代码如下所示:

engine = create_engine("mysql://user_name:password@host/db", pool_size=x, max_overflow=y, pool_timeout=z)

上文定义的engine同时允许最多x+y个并发且活跃的连接。如果在并发活跃的连接已经有x+y个时,新的对于连接的请求将会被阻塞(block),直到有一个连接对象可用为止。阻塞(block)的时间由create_engine.pool_timeout指定,即z秒(默认情况下为30秒)。其中create_engine.pool_size参数指定的是连接池中最多缓存的连接数目,而create_engine.max_overflow指定的是除连接池中已经缓存的连接对象之外,还允许连接池“上溢(overflow)”多少个连接对象来响应数据库操作的请求。

sqlalchemy.pool.QueuePool外,我们还可用Connection Pooling中提到的其他实现作为传入create_engine.pool的参数。例如使用sqlalchemy.pool.NullPool可以完全禁用连接池。对这一配置的讨论超出了本文的范围,故在此不做进一步的讨论。

可上溢的连接池

如果我们将参数create_engine.max_overflow设置为"-1",那么连接池会允许“上溢”无限多的新连接。在这种情况下,连接池永远不会阻塞一个新的数据库连接请求。相反,每当有新的连接请求且无当前可用的连接对象,连接池就会无条件地创建新的连接对象来返回给这个请求。

然而,即使我们在程序端不限制并发的数据库连接的数目,如果程序无限制的创建新的数据库连接对象,连接的数目最终会到达数据库端的连接数目上限,并且耗尽所有数据库允许的连接,最终同样会造成程序异常。更为需要注意的是,在这种情况下 ,在程序耗尽数据库连接资源之前往往还会耗尽许多其他的资源,并且还很可能会影响运行在同一台服务器上的其他依赖于数据库访问的程序或数据库本身,造成其他程序异常或崩溃。

基于以上讨论,我们可以知道连接池对于并发数据库连接的数目限制可以被看做是一道保证数据库正常运作的安全阀。在限制了连接数目的情况下,即使程序本身出现了错误导致不断请求新建数据库连接,连接池的限制仍然可以保证数据库及其他依赖数据库的程序的安全运行。所以如果我们收到上述的错误信息,最好的解决办法是根据当前程序的需求重新定义连接池允许的数目上限,或者优化程序以减少并发数据库连接的使用,而不是将上限设置为无限多。

导致可用连接被用尽的可能原因

连接池的上限小于程序中需要并发使用连接的请求的数目

这是导致连接被用尽问题最直接的一种原因。如果我们的程序使用一个大小为20的线程池来进行并发处理且每个线程都需要一个单独的数据库连接,而我们定义的连接池大小只有10,那么显然将会出现连接被用尽的问题。这种情况下,就应该通过增加连接池大小或减少并发线程数目的方法来解决问题。一般来说,我们应当保证连接池的大小不小于线程池的数目。

连接没有被释放

另一个常见的导致连接用尽的原因是连接在被使用之后没有被释放,或说没有被归还给连接池。虽然当连接对象由于没有引用而被垃圾收集之后其对应的连接资源仍将被释放还给连接池,但由于垃圾收集的不确定性,这一机制不应当被用来作为释放连接资源的手段。

连接没有被释放一般是因为程序中没有显式地调用相应方法导致的。所以当我们使用完连接对象之后,应当显式地调用连接的释放方法。例如如果我们在使用ORM Session,则应当在合适的地方调用Session.close()方法释放Session对象。或者当我们在使用Core的情况下在合适的位置调用.close()方法释放Connection对象。或者我们也可以使用所谓的context manager帮助我们在使用完毕后自动调用.close()方法释放资源(例如将代码写在“with”代码块中)。

程序试图执行一个运行时间很长的数据库事务(transaction)

数据库的事务是一种非常昂贵的操作,因此不应该用来闲置着等待某些事件发生。例如等待用户点击某个按钮,或者等待一个长时间运行的任务返回结果。对于事务,切记不要一直维持着一个事务而不去结束。如果程序需要进行数据库交互并且和一个事件互动,我们应该当且仅当需要的时候打开一个持续事件很短的事务,并在使用结束后就立即关闭。

当我们编程的时候,切记,如果文章开头的错误信息被抛出,这往往是由于程序本身的逻辑问题导致的,而连接池仅仅是在帮助我们暴露这些问题。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容