MySQL连接

PHP与MySQL连接概述

PHP提供了多种方式来连接和操作MySQL数据库。主要的连接方式包括:

  1. MySQLi扩展(MySQL Improved)
  2. PDO(PHP Data Objects)
  3. 传统的MySQL扩展(已废弃,不推荐使用)

在现代PHP开发中,推荐使用PDO或MySQLi扩展,因为它们提供了更好的安全性、性能和功能支持。

MySQLi扩展连接

MySQLi扩展提供了面向对象和面向过程两种编程风格。

面向对象方式连接

<?php
// 数据库连接配置
$host = 'localhost';
$username = 'root';
$password = 'password';
$database = 'student_system';
$port = 3306;

// 创建MySQLi对象
$mysqli = new mysqli($host, $username, $password, $database, $port);

// 检查连接是否成功
if ($mysqli->connect_error) {
    die('连接失败: ' . $mysqli->connect_error);
}

echo '数据库连接成功!';

// 设置字符集
$mysqli->set_charset('utf8mb4');

// 关闭连接
$mysqli->close();
?>

面向过程方式连接

<?php
// 数据库连接配置
$host = 'localhost';
$username = 'root';
$password = 'password';
$database = 'student_system';

// 建立连接
$connection = mysqli_connect($host, $username, $password, $database);

// 检查连接
if (!$connection) {
    die('连接失败: ' . mysqli_connect_error());
}

echo '数据库连接成功!';

// 设置字符集
mysqli_set_charset($connection, 'utf8mb4');

// 关闭连接
mysqli_close($connection);
?>

PDO连接

PDO是PHP的数据库抽象层,支持多种数据库系统,包括MySQL、PostgreSQL、SQLite等。

基本PDO连接

<?php
// 数据库连接配置
$host = 'localhost';
$dbname = 'student_system';
$username = 'root';
$password = 'password';
$charset = 'utf8mb4';

// DSN(数据源名称)
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";

// PDO选项
$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,  // 异常模式
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,         // 默认获取模式
    PDO::ATTR_EMULATE_PREPARES   => false,                    // 禁用预处理模拟
];

try {
    // 创建PDO实例
    $pdo = new PDO($dsn, $username, $password, $options);
    echo '数据库连接成功!';
} catch (PDOException $e) {
    // 连接失败时捕获异常
    die('连接失败: ' . $e->getMessage());
}

// PDO对象会在脚本结束时自动关闭连接
?>

使用配置文件的PDO连接

<?php
// config.php
class Database {
    private static $instance = null;
    private $pdo;

    private $host = 'localhost';
    private $dbname = 'student_system';
    private $username = 'root';
    private $password = 'password';
    private $charset = 'utf8mb4';

    private function __construct() {
        $dsn = "mysql:host={$this->host};dbname={$this->dbname};charset={$this->charset}";

        $options = [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES   => false,
            PDO::ATTR_PERSISTENT         => true,  // 持久连接
        ];

        try {
            $this->pdo = new PDO($dsn, $this->username, $this->password, $options);
        } catch (PDOException $e) {
            throw new PDOException('数据库连接失败: ' . $e->getMessage());
        }
    }

    // 单例模式
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function getConnection() {
        return $this->pdo;
    }

    // 防止克隆
    private function __clone() {}

    // 防止反序列化
    public function __wakeup() {
        throw new Exception("Cannot unserialize singleton");
    }
}

// 使用示例
try {
    $db = Database::getInstance();
    $pdo = $db->getConnection();
    echo '数据库连接成功!';
} catch (Exception $e) {
    die('连接失败: ' . $e->getMessage());
}
?>

连接参数详解

常用连接参数

// DSN参数说明
$dsn = "mysql:host=localhost;dbname=test;charset=utf8mb4;port=3306";

// 参数说明:
// host     - 数据库主机名
// dbname   - 数据库名称
// charset  - 字符集
// port     - 端口号(默认3306)
// socket   - Unix套接字路径(可选)

PDO连接选项

$options = [
    // 错误处理模式
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,  // 抛出异常
    // PDO::ERRMODE_WARNING     // 发出警告
    // PDO::ERRMODE_SILENT      // 静默模式

    // 默认获取模式
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,        // 关联数组
    // PDO::FETCH_NUM           // 数字索引数组
    // PDO::FETCH_OBJ           // 对象形式
    // PDO::FETCH_BOTH          // 同时包含关联和数字索引

    // 预处理设置
    PDO::ATTR_EMULATE_PREPARES   => false,                    // 禁用模拟预处理

    // 持久连接
    PDO::ATTR_PERSISTENT         => true,                     // 启用持久连接

    // 自动提交
    PDO::ATTR_AUTOCOMMIT         => true,                     // 自动提交模式

    // 连接超时
    PDO::ATTR_TIMEOUT            => 30,                       // 连接超时时间(秒)

    // 服务器端预处理
    PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,              // 使用缓冲查询
];

连接管理

持久连接

持久连接可以避免每次脚本执行都重新建立数据库连接,提高性能。

<?php
// PDO持久连接
$options = [
    PDO::ATTR_PERSISTENT => true,
    // 其他选项...
];

$pdo = new PDO($dsn, $username, $password, $options);

// MySQLi持久连接
$mysqli = mysqli_connect('p:' . $host, $username, $password, $database);
?>

连接池

<?php
class ConnectionPool {
    private static $pool = [];
    private static $maxConnections = 10;

    public static function getConnection($config) {
        $key = md5(serialize($config));

        if (!isset(self::$pool[$key]) || count(self::$pool[$key]) === 0) {
            // 创建新连接
            $connection = new PDO(
                $config['dsn'],
                $config['username'],
                $config['password'],
                $config['options']
            );
            return $connection;
        }

        // 从池中获取连接
        return array_pop(self::$pool[$key]);
    }

    public static function releaseConnection($connection, $config) {
        $key = md5(serialize($config));

        if (!isset(self::$pool[$key])) {
            self::$pool[$key] = [];
        }

        if (count(self::$pool[$key]) < self::$maxConnections) {
            array_push(self::$pool[$key], $connection);
        }
    }
}
?>

错误处理

MySQLi错误处理

<?php
// 面向对象方式的错误处理
try {
    $mysqli = new mysqli($host, $username, $password, $database);

    if ($mysqli->connect_error) {
        throw new Exception('连接失败: ' . $mysqli->connect_error);
    }

    // 执行查询
    $result = $mysqli->query("SELECT * FROM students");

    if (!$result) {
        throw new Exception('查询失败: ' . $mysqli->error);
    }

} catch (Exception $e) {
    error_log('数据库错误: ' . $e->getMessage());
    // 用户友好的错误信息
    die('系统错误,请稍后再试');
}

// 面向过程方式的错误处理
$connection = mysqli_connect($host, $username, $password, $database);

if (!$connection) {
    error_log('连接失败: ' . mysqli_connect_error());
    die('数据库连接失败');
}

$result = mysqli_query($connection, "SELECT * FROM students");

if (!$result) {
    error_log('查询失败: ' . mysqli_error($connection));
    die('查询失败');
}
?>

PDO错误处理

<?php
try {
    $pdo = new PDO($dsn, $username, $password, $options);

    // 设置错误模式为异常
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // 执行查询
    $stmt = $pdo->prepare("SELECT * FROM students WHERE age > :age");
    $stmt->execute(['age' => 18]);

    $students = $stmt->fetchAll();

} catch (PDOException $e) {
    // 记录错误日志
    error_log('数据库错误: ' . $e->getMessage());

    // 根据错误类型进行不同处理
    switch ($e->getCode()) {
        case 1045: // 访问被拒绝
            die('数据库认证失败');
        case 1049: // 数据库不存在
            die('数据库不存在');
        case 2002: // 无法连接
            die('无法连接到数据库服务器');
        default:
            die('数据库操作失败');
    }
}
?>

连接安全性

使用环境变量存储敏感信息

<?php
// .env 文件
DB_HOST=localhost
DB_NAME=student_system
DB_USER=root
DB_PASS=password
DB_CHARSET=utf8mb4

// PHP代码
class Config {
    public static function get($key) {
        static $config = null;

        if ($config === null) {
            $config_file = __DIR__ . '/.env';
            if (file_exists($config_file)) {
                $lines = file($config_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
                foreach ($lines as $line) {
                    if (strpos($line, '=') !== false) {
                        list($k, $v) = explode('=', $line, 2);
                        $config[trim($k)] = trim($v);
                    }
                }
            }
        }

        return $config[$key] ?? null;
    }
}

// 使用配置
$host = Config::get('DB_HOST');
$dbname = Config::get('DB_NAME');
$username = Config::get('DB_USER');
$password = Config::get('DB_PASS');
?>

SSL连接

<?php
// SSL连接配置
$ssl_options = [
    PDO::MYSQL_ATTR_SSL_CA => '/path/to/ca.pem',
    PDO::MYSQL_ATTR_SSL_CERT => '/path/to/client-cert.pem',
    PDO::MYSQL_ATTR_SSL_KEY => '/path/to/client-key.pem',
    PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
];

$options = array_merge($ssl_options, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);

try {
    $pdo = new PDO($dsn, $username, $password, $options);
    echo 'SSL连接成功!';
} catch (PDOException $e) {
    die('SSL连接失败: ' . $e->getMessage());
}
?>

性能优化

连接优化建议

  1. 使用持久连接:减少连接建立开销
  2. 合理设置超时时间:避免长时间等待
  3. 使用连接池:管理多个连接
  4. 及时关闭连接:释放资源
  5. 批量操作:减少数据库往返次数

示例:优化的数据库连接类

<?php
class OptimizedDatabase {
    private $pdo;
    private $config;
    private $isConnected = false;

    public function __construct($config) {
        $this->config = $config;
    }

    // 延迟连接
    public function connect() {
        if ($this->isConnected) {
            return $this->pdo;
        }

        $dsn = "mysql:host={$this->config['host']};dbname={$this->config['dbname']};charset={$this->config['charset']}";

        $options = [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES   => false,
            PDO::ATTR_PERSISTENT         => true,
            PDO::ATTR_TIMEOUT            => 30,
            PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false,
        ];

        try {
            $this->pdo = new PDO($dsn, $this->config['username'], $this->config['password'], $options);
            $this->isConnected = true;

            // 设置SQL模式
            $this->pdo->exec("SET sql_mode='STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'");

        } catch (PDOException $e) {
            throw new Exception('数据库连接失败: ' . $e->getMessage());
        }

        return $this->pdo;
    }

    // 检查连接状态
    public function isConnected() {
        return $this->isConnected && $this->pdo !== null;
    }

    // 重新连接
    public function reconnect() {
        $this->disconnect();
        return $this->connect();
    }

    // 断开连接
    public function disconnect() {
        if ($this->isConnected) {
            $this->pdo = null;
            $this->isConnected = false;
        }
    }

    // 获取连接
    public function getConnection() {
        if (!$this->isConnected) {
            $this->connect();
        }
        return $this->pdo;
    }

    // 析构函数
    public function __destruct() {
        $this->disconnect();
    }
}
?>

调试和监控

连接状态检查

<?php
// MySQLi连接状态检查
if ($mysqli->ping()) {
    echo "连接正常";
} else {
    echo "连接已断开,尝试重新连接...";
    if (!$mysqli->real_connect($host, $username, $password, $database)) {
        die("重新连接失败");
    }
}

// PDO连接状态检查
try {
    $stmt = $pdo->query("SELECT 1");
    echo "连接正常";
} catch (PDOException $e) {
    echo "连接已断开: " . $e->getMessage();
}
?>

连接日志记录

<?php
class DatabaseLogger {
    private static $logFile = 'database_connections.log';

    public static function logConnection($host, $username, $status, $message = '') {
        $timestamp = date('Y-m-d H:i:s');
        $ip = $_SERVER['REMOTE_ADDR'] ?? 'CLI';
        $logEntry = sprintf(
            "[%s] IP: %s | Host: %s | User: %s | Status: %s | Message: %s\n",
            $timestamp, $ip, $host, $username, $status, $message
        );

        file_put_contents(self::$logFile, $logEntry, FILE_APPEND | LOCK_EX);
    }
}

// 使用示例
try {
    $pdo = new PDO($dsn, $username, $password, $options);
    DatabaseLogger::logConnection($host, $username, 'SUCCESS');
} catch (PDOException $e) {
    DatabaseLogger::logConnection($host, $username, 'FAILED', $e->getMessage());
    throw $e;
}
?>

总结

在本节中,我们学习了PHP与MySQL数据库连接的各个方面:

  1. 连接方式:MySQLi和PDO两种主要连接方式
  2. 配置管理:数据库连接参数和选项设置
  3. 错误处理:连接和操作中的错误处理机制
  4. 安全性:SSL连接和环境变量管理
  5. 性能优化:持久连接、连接池和延迟连接
  6. 监控调试:连接状态检查和日志记录

选择合适的连接方式和配置对于构建高性能、安全可靠的PHP应用程序至关重要。在现代开发中,推荐使用PDO作为主要的数据库连接方式,因为它提供了更好的抽象性、安全性和可移植性。