跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

Web 开发技巧: 将 antipool.py 用于线程化的 Python 数据库访问

共享和保留数据库连接

David Mertz, Ph.D (mertz@gnosis.cx), 撰稿人, Gnosis Software, Inc.
Photo of David Mertz
David Mertz 是一个并发的狂热者,但常为连接伤脑筋。您可以通过 mertz@gnosis.cx 和 David 联系,他的生活点滴记录在 http://g nosis.cx/publish/。他的书可以在 http://gnosis.cx/TPiP/ 买到。

简介: 数据库可以很好地处理并行请求(数据库的定义中就包含这一点)。然而,活动进程(threaded 或 porked)几乎不可避免地都会消耗一种宝贵资源:数据库连接。Martin Blais antiorm 库中的 antipool.py 模块可以使连接的池化和重用对程序员透明,而且还是以一种 RDBMS 未知的方式。

发布日期: 2007 年 9 月 04 日
级别: 初级
访问情况 : 856 次浏览
评论: 


简介

长时间运行的 Web 服务器应用程序常常会将后端 RDBMS 用于很多不同的目的,这一点在事务性应用程序中尤为突出。实际上,我认为,对比实际的使用情况,应该 有更多的 Web 服务去使用数据库 ,因为开发人员在配置 RDBMS 时经常会感觉到一种不必要的负担(不过那是另一篇文章的主题)。在其众多的优势中,数据库访问至少存在一个瓶颈:连接。

连接产生瓶颈的方式有两个。简单地说,它们需要占用一些带宽以便在网络上传输,并会占用一点 CPU 和 内存资源以便在 RDBMS 机上分配每一个连接。占用资源虽不多,但却不能忽视。然而,更重要的是 RDBMS 只提供有限 数量的连接。如果 RDBMS 的主客户机是一个 Web 应用程序,那么这个数量限制是对每个客户机而言还是对总体而言将不会有什么实际上的差别,原因是数据库客户机(Web 服务器)会创建大批动作。

在本文介绍的这个技巧里,我使用的是优秀的 psycopg2 适配器,但任何使用了 DBAPI 的 Python 数据库也会得到同样的效果。这里需要特别注意的是对象关系映射(ORM),在我看来,它隐藏了太多数据库访问真实情况的细节,强加了一些人为限制 ,而且 “插手” 的事情太多,远远超出了简化编程的初衷。

没有池

在理想的情况下(没有连接池),每个线程或进程(Web 服务器应用程序在其中连接到数据库)都会很快获得连接并创建游标,然后执行一些相对快速的操作、提交或回滚这些操作,然后关闭此连接。如果一切顺利,这会非常简单。例如:


清单 1. Web 服务器写入 RDBMS
def AddData(foo, bar):
    "This function is generally called in its own thread"
    from psycopg import connect
    conn = connect("dbname=transact user=web host=server.mine")
    cur = conn.cursor()
    cur.execute("INSERT INTO userdata VALUES (%r, %r)" % (foo, bar))
    conn.commit()
    conn.close()

上述示例代码还不是最佳的代码,因为我完全可以围绕 connect() 调用再放上一个 try/'except',也可以再用一个 try/finally 来确保 conn.close() 被调用。我本来也可以使用 DBAPI 值转义。尽管如此,AddData() 基本上已经具备了典型的数据库调用的 “形态”。

问题出在何处(以及如何修正)

与上述类似的代码中很可能会出现问题。一个问题就是当很多线程被同时启用时,即使还没有达到该数据库的限值,其中的一些线程实际上也不能获得连接。从理论上讲,这个问题不应该 发生,但实际它却发生了。另一个问题是,如果达到 了该数据库池限值(通常是因为这些线程的完成用了比预期更长的时间或是这些线程在关闭连接前引起了异常),获取连接还是有可能失败。

通过创建一个本地线程池,这两个问题都能得到解决。连接池是一个预分配数据库连接的集合,这些连接可以由每个 “虚拟连接” 线程重用或释放。也就是说,真正 的数据库连接会相对较少关闭,但它们的池代理则会随着每个事务获取或释放。

一方面,从一个本地池中获取预分配连接是个内存内操作,除非连接已耗尽,否则这种操作不太可能失败,因为不会有网络或数据库延时问题出现。另一方面,使用池可以让您精确地本地管理连接限值。如果依靠数据库来限制连接,将不能直接判断在应用程序层面有多少连接已用和还有多少连接可用;已打开线程的数量虽可以作为粗略的近似数字,但却不是一个可靠的方法。在这种情况下,antipool.py 模块会在后台增减池化了的(实际的)连接以免将实际连接保存过长。然而,对用户来说,除了池中连接的可用性是本地已知的,且其最大值是连接池的参数而非数据库的参数这两点外,池化了的连接看上去与实际的数据库连接没什么区别。

即使使用 antipool.py,我仍然发现进一步对池进行些许包装会很方便,而不必时时都使用 ConnectionPool 的所有选项。此外,对于大多数达到了实际连接限值的常见情况而言,一个简单的 sleep() 通常足以最终获取一个新的连接。如果大量线程都运行不正常(一直不能关闭),虽然仍存在问题,但至少调试起来会更加容易。如下所示的是一个我使用的包装器:


清单 2. webapp_pool.py
"""USAGE:
from webapp_pool import get_connection
conn, cur = get_connection()   # Might hang, but never raises
cur.execute(SQL)
conn.commit()
conn.release()    # 'conn.release()' not 'conn.close()'
"""
import psycopg2
from time import sleep
from antipool import ConnectionPool
from database import host, database, user, MAXCONNECTIONS
conn_pool = ConnectionPool(psycopg2,
                           host=host,
                           database=database,
                           user=user,
                           options={'maxconn':MAXCONNECTIONS})
def get_connection():
    got_connection = False
    while not got_connection:
        try:
            conn = conn_pool.connection()
            cur = conn.cursor()
            got_connection = True
        except psycopg2.OperationalError, mess:
            # Might log exception here
            sleep(1)
        except AttributeError, mess:
            # Might log exception here
            sleep(1)
    return conn, cur

结束语

在本文所介绍的这个技巧里,我使用了数据库未知的 antipool.py 模块。我发现 antipool.py 非常灵活;在这篇简短的文章中,它的许多其他优势特性未能一一论及。但如果您打算使用内置到 psycopg2 或其他工具的池化功能,那么使用连接池化的这些常见原则仍适用。


参考资料

学习

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文

  • 查看 Martin Blais 的全部 antiorm 包

  • IBM 的 DB2 RDBMS 也有一个 Python 适配器,称为 PyDB2(如您所想)。该模块不包含连接池化,但可与 antipool.py 完美协作。

  • 若想使用 IBM 先进的 DB2 RDBMS,一定要访问可免费下载的 beta of DB2/Viper

  • 要获得 psycopg2,可借助其 Trac 主页。请务必使用 2.x 系列,而非备受争议的 1.x。

  • 参看 developerWorks 特色试用软件下载

  • 订阅 developerWorks Web development 新闻。

  • Web development 专区技术库 获取更多指导文章。

讨论

关于作者

Photo of David Mertz

David Mertz 是一个并发的狂热者,但常为连接伤脑筋。您可以通过 mertz@gnosis.cx 和 David 联系,他的生活点滴记录在 http://g nosis.cx/publish/。他的书可以在 http://gnosis.cx/TPiP/ 买到。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=252864
ArticleTitle=Web 开发技巧: 将 antipool.py 用于线程化的 Python 数据库访问
publish-date=09042007
author1-email=mertz@gnosis.cx
author1-email-cc=dwxed@us.ibm.com

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。