【问题标题】:How would one make Python objects persistent in a web-app?如何让 Python 对象在 Web 应用程序中持久化?
【发布时间】:2010-09-24 17:28:00
【问题描述】:

我正在编写一个相当复杂的 Web 应用程序。 Python 后端运行一种算法,其状态取决于存储在几个相互关联的数据库表中的数据,这些数据表不经常更改,加上用户特定的数据经常更改。当用户使用应用程序时,算法的每个用户状态会发生许多小的变化。在每个用户的工作中经常使用这种算法来做出某些重要的决定。

出于性能原因,对来自(半规范化)数据库数据的每个请求重新初始化状态很快变得不可行。例如,最好以某种方式缓存状态的 Python 对象,以便在必要时可以简单地使用和/或更新它。但是,由于这是一个 Web 应用程序,有多个进程为请求提供服务,因此使用全局变量是不可能的。

我已经尝试序列化相关对象(通过pickle)并将序列化数据保存到数据库,现在正在尝试通过memcached 缓存序列化数据。但是,这仍然具有经常序列化和反序列化对象的巨大开销。

我查看了共享内存解决方案,但我发现唯一相关的是POSH。但是 POSH 似乎没有被广泛使用,我觉得将这样一个实验性组件集成到我的应用程序中并不容易。

我需要一些建议!这是我开发 Web 应用程序的第一次尝试,所以我希望这是一个足够普遍的问题,以便有针对此类问题的众所周知的解决方案。此时假设 Python 后端在单个服务器上运行的解决方案就足够了,但对于扩展到多个服务器的解决方案也可以加分:)

注意事项:

  • 我有这个应用程序正在运行,目前正在运行并且有活跃用户。我一开始没有做任何过早的优化,然后根据需要进行优化。我已经完成了测量和测试,以确保上述问题是实际的瓶颈。我很确定我可以从当前设置中获得更多性能,但我想问问是否有更好的方法。
  • 设置本身仍在进行中;假设系统架构可以是任何适合您的解决方案的架构。

【问题讨论】:

  • 哪个框架?樱桃派?姜戈?
  • 您是否进行过任何测量来证明状态访问是瓶颈? pickle 有多大,unpickle 需要多少时间,整个请求需要多长时间等?
  • @Martin:pickle 大约 2MB 大(协议 2),反序列化和序列化占用了 90% 的请求处理时间。
  • @S.Lott:我特意没有提到我的框架、操作系统等,因为这些都是我愿意改变的,我希望允许使用适合工作的任何工具来解决问题。

标签: python web-applications concurrency persistence


【解决方案1】:

我认为你可以试试 ZODB。

“ZODB 的一个主要特性是透明性。您不需要编写任何代码来显式地从数据库读取对象或从数据库写入对象。您只需将持久对象放入一个像 Python 字典一样工作的容器中。这本字典中的所有内容都保存在数据库中。据说这本字典是数据库的“根”。它就像一个魔术袋;您放入其中的任何 Python 对象都将成为持久性。”

最初它是 Zope 不可分割的一部分,但最近也提供了一个独立的软件包。

它有以下限制:

“实际上,您可以在 ZODB 中存储的内容有一些限制。您可以将任何可以“腌制”的对象存储为标准的跨平台串行格式。列表、字典和数字等对象都可以pickled。文件、套接字和 Python 代码对象等对象不能存储在数据库中,因为它们不能被 pickle。"

我已经阅读了它,但我自己还没有尝试过。

其他可能的事情可能是内存中的 sqlite 数据库,这可能会稍微加快处理速度 - 作为内存数据库,但您仍然需要进行序列化等工作。 注意:内存中的 db 在资源上很昂贵。

这是一个链接:http://www.zope.org/Documentation/Articles/ZODB1

【讨论】:

  • 这或多或少地做了我目前正在做的事情——用pickle序列化对象并将序列化的数据保存到数据库中。由于我试图消除序列化和反序列化过程的开销,ZODB 将无济于事。
  • @taleinat,它只在对象超出缓存并再次请求时进行反序列化和序列化。
【解决方案2】:

小心过早的优化。

补充:“Python 后端运行一个算法,其状态...”是 Web 框架中的会话。而已。让 Django 框架在缓存中维护会话状态。时期。

“当用户使用应用程序时,算法的每个用户状态会发生许多小的变化。”大多数 Web 框架都提供缓存的会话对象。通常它是非常高性能的。请参阅 Django 的session documentation

建议。 [修订]

看来你有一些有用的东西。利用杠杆来学习你的框架,学习工具,并了解你可以毫不费力地转动哪些旋钮。具体来说,使用会话状态。

其次,摆弄缓存、会话管理和易于调整的东西,看看你是否有足够的速度。通过尝试找出 MySQL 套接字或命名管道是否更快。这些是无编程优化。

第三,衡量性能以找到您的实际瓶颈。准备好提供(并捍卫)测量值,使其足够细粒度,足够有用且足够稳定,以提供对备选方案的有意义的比较。

例如,显示持久会话和缓存会话之间的性能差异。

【讨论】:

  • 我有它的工作,目前生活和活跃用户。我已经摆弄好了。我已经完成了测量和测试,以确保这是实际的瓶颈。我敢肯定还有很多事情要做,但我想问问是否有更好的方法。
  • @taleinat:请用这些事实更新问题。此外,确定您的框架、操作系统、数据库。
  • @S.Lott:我特意没有提到我的框架、操作系统等,因为我可以改变这些,我希望允许使用适合工作的任何工具的解决方案。
  • 对于会话,通常这些存储在 Cookie 和/或缓存和/或数据库中。使用其中任何一个都需要序列化我的数据,这是我试图避免的。
  • @S.Lott:你说得对,它们通常也保存在文件中。但是,同样,保存到文件需要先序列化数据,然后在从文件加载时反序列化它。我不介意必须为解决方案编程;我正在寻找可以解决我的问题的新概念方法。
【解决方案3】:

另一种选择是查看状态要求,听起来如果序列化是瓶颈,那么对象就非常大。你真的需要这么大的物体吗?

我知道在 Stackoverflow 播客 27 中,reddit 的人讨论了他们用于状态的内容,所以听听可能有用。

【讨论】:

  • 我听了你提到的大部分播客。 reddit 的人并没有真正深入,他们大多说他们使用 memcached 来同步一些东西而不是“粘性会话”,但粘性会话更简单;他们仍然会再次使用 memcached,因为它的效果非常好。
【解决方案4】:

我认为multiprocessing 框架有可能适用于这里 - 即共享 ctypes 模块。

多处理对 Python 来说是相当新的,所以它可能有一些奇怪的地方。我不太确定该解决方案是否适用于不是通过multiprocessing 生成的进程。

【讨论】:

  • 谢谢!我没有注意到多处理模块功能的那一部分。这可能正是我所需要的!
  • 多处理中的共享内存功能要求共享对象是特殊类的实例,因此它不适用于普通的 Python 对象。但是,这是迄今为止最好的建议,也是最接近我的问题的答案,所以我接受了。
【解决方案5】:

首先,您的方法不是一种常见的 Web 开发实践。即使正在使用多线程,Web 应用程序也被设计为能够运行多处理环境,以实现可扩展性和更轻松的部署。

如果您只需要初始化一个大对象,并且以后不需要更改,您可以通过使用在创建 WSGI 应用程序时初始化的全局变量轻松完成,或者包含正在创建的对象的模块加载等,多处理对你来说很好。

如果您需要更改对象并从每个线程访问它,您需要确保您的对象是线程安全的,使用锁来确保这一点。并使用单个服务器上下文,一个进程。任何多线程 python 服务器都可以很好地为您服务,FCGI 也是这种设计的不错选择。

但是,如果多个线程正在访问和更改您的对象,则锁定可能会对您的性能提升产生非常糟糕的影响,这可能会使所有好处都消失。

【讨论】:

    【解决方案6】:

    这是 Durus,一个用 Python 编写的应用程序的持久对象系统 编程语言。 Durus 提供了一种简单的方法来使用和保持一致 一个或多个进程使用的对象实例的集合。访问和更改 持久实例通过缓存的 Connection 实例进行管理,其中包括 commit() 和 abort() 方法,以便更改是事务性的。

    http://www.mems-exchange.org/software/durus/

    我以前在一些研究代码中使用过它,我想保留某些计算的结果。我最终切换到pytables,因为它更好地满足了我的需求。

    【讨论】:

    • 这看起来很有趣。但是,Durus 提供了自己的基类,其中持久对象必须是实例,因此它不适用于普通的 Python 对象。
    • 即使多重继承也不能改成使用Persistent的子类等?
    猜你喜欢
    • 1970-01-01
    • 2019-02-03
    • 2012-03-29
    • 1970-01-01
    • 1970-01-01
    • 2013-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多