全局还是单例用于数据库连接?

2022-08-30 08:17:58

在 PHP 中对数据库连接使用单例而不是全局有什么好处?我觉得使用单例而不是全局会使代码变得不必要地复杂。

使用全局代码

$conn = new PDO(...);

function getSomething()
{
    global $conn;
    .
    .
    .
}

使用单例的代码

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();
    .
    .
    .
}

如果有除全局或单例之外的更好的方法来初始化数据库连接,请提及它并描述它相对于全局或单例的优势。


答案 1

我知道这已经很老了,但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 行代码,包括大括号,这将为您节省数小时和数小时的重构,使其大致相似。

请注意,我不考虑这种“功能蠕变”,因为我在第一轮中没有进行任何功能实现。这是“未来蠕变”的边界线,但在某些时候,“今天为明天编码”的想法总是一件坏事,这对我来说并不受欢迎。


答案 2

我不确定我能回答您的具体问题,但想建议全局/单例连接对象可能不是最好的主意,如果这是基于Web的系统。DBMS通常设计为以有效的方式管理大量唯一连接。如果您使用的是全局连接对象,则您正在执行以下几项操作:

  1. 强制页按顺序执行所有数据库连接,并终止异步页加载的任何尝试。

  2. 在数据库元素上保持打开锁的时间可能会超过所需的时间,从而降低整体数据库性能。

  3. 最大限度地增加数据库可以支持的同时连接总数,并阻止新用户访问资源。

我相信还有其他潜在的后果。请记住,此方法将尝试为访问站点的每个用户维持数据库连接。如果您只有一个或两个用户,则没有问题。如果这是一个公共网站,并且您想要流量,那么可扩展性将成为一个问题。

[编辑]

在规模较大的情况下,每次命中 datase 时创建新连接都可能很糟糕。但是,答案不是创建全局连接并将其重用于所有内容。答案是连接池。

使用连接池,可以维护许多不同的连接。当应用程序需要连接时,将检索池中的第一个可用连接,并在其工作完成后返回到池中。如果请求连接并且没有可用的连接,则会发生以下两种情况之一:a)如果未达到允许的最大连接数,则打开新连接,或b)强制应用程序等待连接变为可用。

注意:在 .Net 语言中,默认情况下,连接池由 ADO.Net 对象处理(连接字符串设置所有必需的信息)。

感谢Crad对此的评论。


推荐