将用户数据存储在$_SESSION中vs.重复访问数据库


Storing userdata in $_SESSION vs. repeated DB accesses

从(MySQL)数据库中读取的某些用户数据将在随后的页面请求中需要,例如用户名或一些首选项。

是否有利于将这些数据存储在$_SESSION变量中以节省数据库查找?

我们谈论的是(潜在的)大量用户。我认为存储在$_SESSION中会增加RAM的使用(非常少的数量乘以非常多的用户),而在每次请求相同数据的页面上访问数据库应该会增加磁盘活动。

你的问题的讽刺之处在于,对于大多数系统来说,一旦你有了大量的用户,你就需要找到一种方法把你的会话从默认的磁盘存储中取出来,放到一个单独的持久化层中(即数据库、内存缓存等)。这是因为在某些情况下,您需要多个应用服务器,而不必在应用服务器本身上维护状态通常要容易得多。

许多大型系统使用内存缓存(memcached或类似的)来实现会话持久化,因为它可以为多个前端服务器提供公共持久化层,并且不需要数据的长时间持久化(磁盘存储)。

设计良好的数据库表或其他基于磁盘的键值存储也可以成功使用,尽管它们的性能可能不如内存存储。然而,它们的操作可能更便宜,这取决于您希望在每个会话键中存储多少数据(在RAM中保存大量数据通常比在磁盘上存储更昂贵)。

了解会话数据的大小(平均大小和最大大小)、期望支持的并发会话的数量以及需要访问会话数据的频率对于帮助您决定哪种解决方案最适合您的情况非常重要。

在PHP中,会话数据可以使用多个存储后端。默认情况下保存到文件中。一个文件对应一个会话。你也可以使用数据库作为会话后端或任何你想通过实现自己的会话保存处理程序

如果你想让你的应用程序最具可伸缩性,我会在文件系统上使用而不是会话。想象一下,您有多个web服务器,所有这些服务器都将您的站点作为一个农场。当在文件系统上使用会话时,每个请求都必须将用户重定向到同一服务器,因为会话数据仅在该服务器的文件系统上可用。如果您不在文件系统上使用会话,那么请求使用哪个服务器就无关紧要了。这使得负载平衡更加容易。

我建议在文件系统上使用会话,而不是

  • 使用cookie
  • 在多个请求中使用请求变量

或(如果数据是安全关键)

  • 使用会话与数据库保存处理程序。因此数据将可用于从数据库(或集群)读取的每个web服务器。

使用会话有一个主要缺点:如果用户都试图启动会话来访问数据,则无法为用户提供并发请求。这是因为一旦脚本启动会话,PHP就会锁定会话,以防止数据被另一个脚本覆盖。在使用会话数据时,通常的想法是在调用session_start()之后,数据在$_SESSION中可用,并且在脚本结束后将被写回存储中。在此之前,您可以随意读写会话数组。PHP通过锁定来确保不会破坏或覆盖数据。

如果您想做一个Web2.0站点,并且对服务器进行大量的Ajax调用,那么锁定会话将会降低性能,因为每个需要会话的请求都将被串行执行。如果您可以避免使用会话,这将有利于用户的感知性能。

有一些技巧可以解决这个问题:

  1. 你可以尝试通过调用session_write_close()来尽快释放锁,但是你必须处理在调用之后无法写入会话的问题。
  2. 如果您知道一些脚本调用将只从会话中读取数据,您可能会尝试实现只读取会话数据而不调用session_start()的代码,并完全避免锁。
  3. 如果I/O有问题,使用Memcache服务器进行存储可能会提高性能,但无助于解决锁定问题。
  4. 请注意,数据库在任何表中存储的所有数据也存在这种锁定问题。如果你没有明智地选择数据库存储引擎(比如MyISAM而不是InnoDB),你失去的性能会比避免会话带来的好处更多。

如果你现在没有任何性能问题,所有这些讨论都是没有意义的。做最符合你意图的事。无论您以后会遇到什么性能问题,我们今天都无法知道,试图避免这些问题是不成熟的优化(这是罪恶的根源)。

始终遵守优化的第一条规则:测量它,看看一个更改是否改进了它。