大型 PHP 会话减慢 Web 应用程序

Posted

技术标签:

【中文标题】大型 PHP 会话减慢 Web 应用程序【英文标题】:Large PHP session slowing down web application 【发布时间】:2011-11-24 09:34:42 【问题描述】:

我有一个 Web 应用程序,其中复杂的权限决定了用户是否可以访问数千个不同文件中的每一个。用户可以查看所有文件,但有一个指示器可以打开他们有权访问的文件。如果组织中的其他人有权访问该文件,或者与他们合作的某个人具有对该文件的共享访问权限,则用户有权访问该文件。

现在,我有一个复杂的 php 函数,它通过构建用户在其组织或协作中可以访问的文件的数组并合并这些访问数组来生成一个大型 PHP 会话。当这些文件显示给用户时,PHP 会检查此数组以查看他们是否有权访问,如果有,则添加打开文件的按钮。我这样做是因为在显示长文件列表时运行查询来检查每个文件的访问权限最终会花费太长时间,而 PHP 的 in_array() 速度要快得多。

问题是……

php 会话变得如此之大,以至于它似乎正在减慢简单的网站功能,我需要想出一种新的方法来做到这一点。

我的问题是……

替换 PHP 会话以存储用户可以访问的数千个文件的文件权限和文件位置的最佳方法是什么,以便在显示文件列表时,PHP 可以快速检索此信息,而无需为每个单独的文件运行查询?

【问题讨论】:

你考虑过使用memcached来存储数组吗?另外,您是否确定了访问设置的哪一部分实际上导致了僵局? 有时显示的文件列表可能长达数百个。当显示这些文件时,对于每个文件,PHP 需要运行查询以查看他们是否有权访问它,他们的组织是否有权访问它,然后查询该用户的每个协作以查看他们是否有访问它。这对于许多单独的查询来说太远了,而且需要很长时间。拥有一大堆预先生成的访问权限要快得多。可能不是最好的方法,但它比为每个文件运行多个查询要快。 不可能将查询减少到更少(一个?)查询吗?另外,请注意您回复的评论我已删除(在您回复之前),但无论如何它都会有所帮助。此外,分页结果可能会有所帮助,或者将其卸载到另一个进程并在它准备好后加载响应(例如旅游网站,尽管这有点极端)。 Chuck 的方法似乎可以做到这一点......将会话存储在数据库中并查询用户在显示文件列表时可以访问的所有文章,然后检查每个文件是否在该结果数组中。这种方法听起来是不是最有效? 我想,在你做任何事情之前,首先要找出僵局发生在哪里(xdebug.org),然后从根本上解决问题。将当前基于会话的数组卸载到数据库字段中,至少从表面上看,在我看来并不是一种更有效的方法,但我也不确定。首先找出实际问题出在哪里。 【参考方案1】:

嗯,在不了解问题的全部范围的情况下,我建议在您的数据库中添加一个 Sessions 表,并包括一个 FilePermissions 字段和一个 UserId 字段。

此字段将存储您的权限结构的 json 表示。这只需要对数据库进行一次调用,并且大部分处理将在解析 json 数据服务器端时进行(这根本不应该有太多开销)。

这是减少客户端会话信息大小的标准方法。一个好的经验法则是在 Sessions 表中放置任何公开应用程序逻辑的内容。

更新

我只会将他们确实可以访问的文件存储在 json 字段中。可以假设不存在禁止他们访问文件。这将再次减少性能占用。

这只有在没有复杂的权限结构时才有效(比如每个文件都有读写权限)。如果没有,我会说你很清楚。

【讨论】:

因此,为了澄清您的建议,我将在数据库会话表中查询用户在显示每个文件列表时可以访问的所有文件,然后查询列表中的每个文件,检查该结果数组是否可以访问? 是的,没错。我在动态表单生成中使用了类似的东西,效果很好。请参阅我的帖子以减少资源占用。 这与会话没有什么不同,不是吗?会话也可以只包含用户有权访问的文件,这可能比获取 JSON 条目的数据库访问以及 JSON 条目的解码更快。 @IvanVučica PHP 会话运行得更快,但我的应用程序所需的会话的绝对大小导致整个系统陷入困境,因此需要另一种方法。我需要一次存储每个文件的权限,因为动态生成这些权限成本太高,因为它们会变得相当复杂。 @Chuck - 我实现了你的方法,但似乎对序列化权限的单个数据库查询然后对数据进行反序列化大约需要 8 秒,或多或少。这解决了应用程序陷入困境的问题,但似乎在加载文件列表时引入了一个初始延迟 8 秒以上的新问题。你知道解决这个问题的可能方法吗?【参考方案2】:

我不确定你能做多少。也许memcached 可以提供帮助,但我没有使用它(尽管据我所知,这就是它的用途)。

您可以将数组保存在文件中,但据我所知,这正是会话所做的。

您也可以尝试使用shared memory 在脚本启动之间将用户数据保存在内存中。

您真的需要一个数组中的整个用户权限列表吗?也就是说,您是否总是向用户显示数千个文件?如果是这样,为什么?是否可以使用 AJAX 重新设计系统以延迟获取部分文件?


更新:另一个想法。

您还可以预先计算用户对每个文件的权限并将其存储在数据库中。表可以称为FilesPermittedPerUser,并具有两列主键userID / fileID。这将创建一个首先按userID 排序的索引,然后按fileID 排序。两列键也将强制条目的唯一性。

因为它会被用户索引,你可以简单地ORDER BY userIDLIMIT 10, 10 只列出文件 10-20。通过 AJAX 仅获取列表的一部分意味着您永远不会导致脚本当前导致的可怕内存负载。

只需要每当更新文件的权限(例如,创建文件、删除文件、更改组权限、更改用户的组成员身份、更改文件的组成员身份...)您将不得不更新表。我怀疑这应该不会太难。只需确保在事务中进行缓存更新,以保持操作的原子性。

您可能还想在文件夹中组织文件系统。仅仅向用户扔大量文件并且必须始终维护它们是没有意义的。尝试在 Explorer/Finder/Nautilus 上扔 10.000 个文件,看看打开该文件夹时会发生什么。没什么好说的,而且他们可以保留内存——而 PHP 则没有。

最终想法(尽管您可能不必走这些极端):用非 PHP 的东西重写文件系统 API,并且可以保留权限数据。仅使用 PHP 将请求转发到在不同服务器上运行的自定义服务器。

【讨论】:

整个系统都在使用 AJAX 来检索文件列表,而 PHP 正在确定他们可以访问哪些文件。它们不需要一次全部显示,但需要在显示列表时快速确定访问权限。我从来没有使用过memcached,我得研究一下! 我根据此页面上其他人的答案和 cmets 以及您的 cmets 更新了答案。

以上是关于大型 PHP 会话减慢 Web 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

大型列表上单击事件的 jQuery 委托性能 - 如果动态添加更多元素会减慢速度?

在 iPhone 上的 Web 应用程序中维护 PHP 会话

大型Web应用运行时 PHP负载均衡指南

当计算相同时,为啥 R 会随着时间的推移而减慢?

大型 SQL Server 数据库超时 PHP Web 应用程序

使用 sqlalchemy 会话执行 sql 会大大减慢执行时间