首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 單例模式資料庫類實戰:統一連線管理、防止重複連接與效能優化

     編輯:PHP 相關知識

高流量下頻繁 `mysql_connect` 會耗盡資料庫連線數。單例模式確保**全域唯一連線實例**,配合延遲連線、字元集設定、錯誤處理,構建穩健的資料庫存取層基石。

核心實現:nmdb 類

<?php
class Nmdb {
    private $link;
    static private $_instance;

    // 私有建構子:建立連線、設定字元集
    private function __construct(string $host, string $user, string $pass) {
        $this->link = @mysqli_connect($host, $user, $pass); // 改用 mysqli
        if (!$this->link) throw new Exception("連線失敗: " . mysqli_connect_error());
        mysqli_set_charset($this->link, 'utf8mb4');
    }

    private function __clone() {} // 禁止克隆
    public function __wakeup() { throw new Exception("不可反序列化"); } // 禁止反序列化

    // 靜態工廠:取得唯一實例
    public static function getInstance(string $host, string $user, string $pass): self {
        if (false === (self::$_instance instanceof self)) {
            self::$_instance = new self($host, $user, $pass);
        }
        return self::$_instance;
    }

    // 選擇資料庫
    public function selectDb(string $db): bool {
        return mysqli_select_db($this->link, $db);
    }

    // 執行查詢 (自動重連邏輯可加在這裡)
    public function query(string $sql) {
        $result = mysqli_query($this->link, $sql);
        if (!$result) throw new Exception("SQL錯誤: " . mysqli_error($this->link) . " | SQL: $sql");
        return $result;
    }

    // 取單行關聯陣列
    public function fetchAssoc($result): ?array {
        return mysqli_fetch_assoc($result);
    }

    // 取所有行
    public function fetchAll($result): array {
        $rows = [];
        while ($row = mysqli_fetch_assoc($result)) $rows[] = $row;
        return $rows;
    }

    // 關閉連線 (通常由腳本結束自動回收,長運行腳本可手動呼叫)
    public function close(): bool {
        return mysqli_close($this->link);
    }
}
?>

使用範例

<?php
$db = Nmdb::getInstance('localhost', 'user', 'pass');
$db->selectDb('my_db');

$rows = $db->fetchAll($db->query("SELECT * FROM articles WHERE status=1 LIMIT 10"));
foreach ($rows as $row) {
    echo $row['title'] . "
"; } // 驗證單例 $db2 = Nmdb::getInstance('localhost', 'user', 'pass'); var_dump($db === $db2); // true ?>

進階版:支援多資料庫連線的單例容器

class DbManager {
    private static $instances = [];

    public static function get(string $name, array $config): mysqli {
        if (!isset(self::$instances[$name])) {
            $mysqli = new mysqli($config['host'], $config['user'], $config['pass'], $config['db']);
            if ($mysqli->connect_errno) throw new Exception($mysqli->connect_error);
            $mysqli->set_charset($config['charset'] ?? 'utf8mb4');
            self::$instances[$name] = $mysqli;
        }
        return self::$instances[$name];
    }
}

// 使用
$mainDb = DbManager::get('main', ['host'=>'...', 'user'=>'...', 'pass'=>'...', 'db'=>'main']);
$logDb  = DbManager::get('log',  ['host'=>'...', 'user'=>'...', 'pass'=>'...', 'db'=>'log']);

單例模式優缺點與替代方案

優點缺點現代替代
全域唯一、節省連線全域狀態難測試、隱性耦合**依賴注入 (DI) 容器** 管理連線生命週期
延遲初始化多資料庫需改容器模式**連線池** (ProxySQL, PgBouncer, Swoole 協程池)
實現簡單單點故障 (連線斷無自動重連)**ORM** (Eloquent, Doctrine) 內建連線管理

關鍵加固建議

  1. **改用 `mysqli` / `PDO`** (範例已更新),`mysql_*` 已移除。
  2. **加入自動重連**:`mysqli_ping()` 失敗時重新連線。
  3. **參數化查詢** 防注入:`$stmt = $link->prepare("SELECT * FROM t WHERE id=?"); $stmt->bind_param('i',$id);`。
  4. **慢查詢日誌**:`mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)`。
  5. **連線池化**:Swoole/RoadRunner 協程環境下用 `Swoole\Coroutine\MySQL` 連線池,QPS 提升數倍。

單例資料庫類是從「每頁一連線」邁向「專業連線管理」的關鍵一步,理解其本質與局限,才能在架構演進中做出正確選擇。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved