全局还是单例用于数据库连接?
在PHP中使用单例而不是全局的数据库连接有什么好处?我觉得使用单例而不是全局会使代码不必要地变得复杂。
全局编码
$conn = new PDO(...);
function getSomething()
{
global $conn;
.
.
.
}
用Singleton编码
class DB_Instance
{
private static $db;
public static function getDBO()
{
if (!self::$db)
self::$db = new PDO(...);
return self::$db;
}
}
function getSomething()
{
$conn = DB_Instance::getDBO();
.
.
.
}
如果除了全局或者单例之外,还有一种更好的初始化数据库连接的方法,请提及它并描述它比全局或者单例具有的优势。
解决方案
这很简单。切勿使用全局或者单例。
如果我们不打算使用持久性连接,并且在某些情况下不这样做,那么我发现从概念上讲,单例比面向对象设计中的全局性更可口。
在真正的OO体系结构中,单例比每次创建对象的新实例都更有效。
我不确定我能否回答特定问题,但想建议如果对于基于Web的系统,全局/单例连接对象可能不是最好的主意。 DBMS通常设计为以有效的方式管理大量唯一连接。如果我们使用的是全局连接对象,那么我们需要做几件事:
- 强制页面按顺序进行所有数据库连接,并终止异步页面加载的任何尝试。
- 可能在数据库元素上保持打开锁的时间超过了必要的时间,从而降低了整体数据库的性能。
- 最大化数据库可以支持的并发连接总数,并阻止新用户访问资源。
我相信还有其他潜在后果。请记住,此方法将尝试为每个访问该站点的用户维持数据库连接。如果我们只有一个或者两个用户,则不成问题。如果这是一个公共网站,并且我们想要流量,那么可伸缩性将成为一个问题。
[编辑]
在较大规模的情况下,每次碰到数据集时创建新连接都是不好的。但是,答案不是创建全局连接并将其重新用于所有内容。答案是连接池。
使用连接池,可以维护许多不同的连接。当应用程序需要连接时,将检索池中的第一个可用连接,然后在完成其工作后将其返回到池中。如果请求连接而没有可用连接,则将发生以下两种情况之一:a)如果未达到允许的最大连接数,则打开新连接,或者b)强制应用程序等待连接可用。
注意:在.Net语言中,默认情况下,连接池由ADO.Net对象处理(连接字符串设置所有必需的信息)。
感谢Crad对此发表评论。
在给定的示例中,我认为没有理由使用单例。根据经验,如果我唯一关心的是允许对象的单个实例,如果语言允许,我更喜欢使用全局变量
通常,我会使用单例进行数据库连接...我们不想在每次需要与数据库进行交互时都创建新的连接...这可能会损害网络的性能和带宽...新的,当有一个可用的...我的2美分...
温迪
两种模式都达到相同的最终效果,为数据库调用提供了一个单一的访问点。
在特定实现方面,单例具有一个小的优点,即只有在其他方法中的至少一个请求它之前,才启动数据库连接。实际上,在我编写的大多数应用程序中,这并没有太大的区别,但是如果我们有一些页面/执行路径根本不进行任何数据库调用,则这是潜在的优势,因为这些页面不会曾经请求连接到数据库。
另一个较小的区别是,全局实现可能会无意间践踏了应用程序中的其他变量名。我们不太可能会意外地声明另一个全局$ db引用,尽管我们可能会意外地覆盖它(例如,当我们打算编写if($ db == null时,我们写了if($ db = null)。单例对象可以防止这种情况。
我知道这很旧,但是Dr8k的答案几乎就在那里。
当我们考虑编写一段代码时,假设它会发生变化。这并不意味着我们要假设它会在将来的某个时刻进行各种更改,而是要进行某种形式的更改。
将其设定为目标可以减轻将来进行更改的痛苦:全球性组织很危险,因为很难在一个地方进行管理。如果将来我想让该数据库连接上下文知道怎么办?如果我希望它每使用5次就关闭并重新打开一次,该怎么办?如果我决定为了扩展我的应用程序而要使用10个连接池,该怎么办?还是可配置数量的连接?
单件工厂为我们提供了这种灵活性。我以很少的额外复杂性进行设置,并且获得的不仅仅是访问同一连接。稍后,我将能够以一种简单的方式更改该连接传递给我的方式。
请注意,我说的是单例工厂,而不是单例工厂。单例和全局之间几乎没有什么区别,真实的。因此,没有理由建立单例连接:为什么当我们可以创建常规全局变量时,为什么要花时间进行设置呢?
工厂能为我们带来什么是获得连接的原因,并且是一个单独的位置来决定我们将获得的连接(或者连接)。
例子
class ConnectionFactory
{
private static $factory;
private $db;
public static function getFactory()
{
if (!self::$factory)
self::$factory = new ConnectionFactory(...);
return self::$factory;
}
public function getConnection() {
if (!$this->db)
$this->db = new PDO(...);
return $this->db;
}
}
function getSomething()
{
$conn = ConnectionFactory::getFactory()->getConnection();
.
.
.
}
然后,在6个月内,当应用程序非常出名,并且变得笨拙和断线,并且我们决定需要的连接不止一个时,我们要做的就是在getConnection()方法中实现一些池化。或者,如果我们决定要实现SQL日志记录的包装器,则可以传递PDO子类。或者,如果我们决定要在每次调用时都建立一个新的连接,则可以这样做。它是灵活的,而不是僵化的。
16行代码,包括花括号,可以节省我们数小时的时间,也可以节省数小时的重构工作。
请注意,我不考虑这种"功能蠕变",因为在第一轮测试中我没有进行任何功能实现。它是边界线" Future Creep",但是在某些时候,"为今天的明天编码"始终是一件坏事的想法对我来说并不成立。

