Python多线程 & 数据库的连接池


Python脚本会交给解释器的GIL机制进行调度,不管CPU有多少个核心,在同一时刻,只有一个线程片段能在解释器中运行。就是说脚本是同步(串行)运行的,除非遇到阻塞,比如I/O作业,此时其他线程会抢到GIL调用CPU等运行非阻塞任务。

Python的多线程意味着可以让多个任务交替运行,这样能够避免执行阻塞任务时,非阻塞任务被阻塞,比如白白让CPU出现大量空闲。

任务交替运行,特别要注意加锁,需要保护特定数据在修改过程中的原子性。Python在改变数据时,过程是漫长的,包括取句柄、读对象、创建对象、赋值到句柄等。但是这个过程是不是上述的I/O作业呢(它在内存中运行)?不知道,但确实是阻塞的。

使用数据库本身进行连接,是真正的建立连接。使用线程池,包括SQLAlchemy的 engine = sqlalchemy.create_engine('...///path'),实际上是建立线程池:conn = engine.connect()是取出一个连接,conn.close()是返还这个连接。

使用线程池,“每次取一个连接---返还”,并非总是非常高效,在并发很少或没有并发的时候,赶不上使用“每次直接连接---关闭”。

而在Python多线程中,当不是借由Web服务器并发的场景时,使用连接池效率很低。

一个连接,只能由一个线程使用,多个线程无法使用同一个连接,至少sqlite3是这样。这种情况可能是因为一个连接“正忙”,无法被其他线程使用。或者引擎不判断忙碌和空闲,或者引擎的设计感到没必要判断。结果就一个线程只能独享一个连接。

如果多线程使用线程池超出了限额,线程池也不再新增线程,按上面的推断,程序会报错。

使用连接池效率低的原因可能是线程数量超出线程池限额,需要等待;而使用直连时可以不断创建新的连接。如果不想再猜测,可以细细研究 sqlalchemy.create_engine()关于连接池的参数说明。

**********

以上纯粹是个人揣摩。