You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP8+MySQL8下如何避免客户端每次连接重复执行相同数据库查询?

解决PHP重复执行相同数据库查询的方案

你的核心问题在于:PHP是请求驱动的脚本语言,每个客户端请求都会启动独立的执行环境,静态变量、单例类的作用域仅限当前请求生命周期,请求结束后就会销毁,所以跨请求没法复用查询结果。下面给你几个直接可用的解决方案:

1. 文件缓存(快速上手)

把查询结果序列化后写入本地文件,下次请求优先读取文件,仅当文件不存在或过期时才重新查询数据库。

<?php
$cacheFile = './guest_list_cache.php';
$cacheExpire = 3600; // 缓存有效期1小时

// 优先读取缓存
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $cacheExpire) {
    $resultData = include($cacheFile);
} else {
    // 数据库查询逻辑
    $servername = "localhost";
    $username = "username";
    $password = "password";
    $dbname = "myDB";

    $conn = new mysqli($servername, $username, $password, $dbname);
    if ($conn->connect_error) {
        die("Connection failed: " . $conn->connect_error);
    }

    $sql = "SELECT id, firstname, lastname FROM MyGuests";
    $result = $conn->query($sql);
    $resultData = [];
    if ($result->num_rows > 0) {
        while($row = $result->fetch_assoc()) {
            $resultData[] = $row;
        }
    }
    $conn->close();

    // 将结果写入缓存文件
    file_put_contents($cacheFile, '<?php return ' . var_export($resultData, true) . ';');
}

// 输出数据
if (!empty($resultData)) {
    foreach ($resultData as $row) {
        echo "id: " . $row["id"]. " - Name: " . $row["firstname"]. " " . $row["lastname"]. "<br>";
    }
} else {
    echo "0 results";
}
?>

2. 内存缓存(生产环境推荐)

用Redis/Memcached这类内存缓存工具,比文件缓存性能更高,适合高并发场景。下面是Redis的实现示例(需提前安装php-redis扩展):

<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$cacheKey = 'guest_list';
$cacheExpire = 3600;

// 尝试从Redis获取缓存
$resultData = $redis->get($cacheKey);
if (!$resultData) {
    // 数据库查询逻辑同前
    $servername = "localhost";
    $username = "username";
    $password = "password";
    $dbname = "myDB";

    $conn = new mysqli($servername, $username, $password, $dbname);
    if ($conn->connect_error) {
        die("Connection failed: " . $conn->connect_error);
    }

    $sql = "SELECT id, firstname, lastname FROM MyGuests";
    $result = $conn->query($sql);
    $resultData = [];
    if ($result->num_rows > 0) {
        while($row = $result->fetch_assoc()) {
            $resultData[] = $row;
        }
    }
    $conn->close();

    // 序列化后存入Redis并设置过期时间
    $redis->setex($cacheKey, $cacheExpire, json_encode($resultData));
} else {
    // 反序列化缓存数据
    $resultData = json_decode($resultData, true);
}

// 输出数据
if (!empty($resultData)) {
    foreach ($resultData as $row) {
        echo "id: " . $row["id"]. " - Name: " . $row["firstname"]. " " . $row["lastname"]. "<br>";
    }
} else {
    echo "0 results";
}
?>

3. 进程预加载(常驻进程场景)

如果你的PHP运行在FPM常驻进程模式,或者使用Swoole/Workerman这类框架,可以在进程启动时提前查询数据并存在全局内存里,每个进程只查一次,后续请求直接复用。

示例(FPM环境)

  1. 写一个预加载脚本preload.php:
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";

$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

$sql = "SELECT id, firstname, lastname FROM MyGuests";
$result = $conn->query($sql);
$GLOBALS['guestList'] = [];
if ($result->num_rows > 0) {
    while($row = $result->fetch_assoc()) {
        $GLOBALS['guestList'][] = $row;
    }
}
$conn->close();
?>
  1. 在php-fpm.conf里配置pm.preload = /path/to/preload.php,重启FPM生效。

  2. 业务代码直接调用全局变量:

<?php
$resultData = $GLOBALS['guestList'] ?? [];

// 输出数据
if (!empty($resultData)) {
    foreach ($resultData as $row) {
        echo "id: " . $row["id"]. " - Name: " . $row["firstname"]. " " . $row["lastname"]. "<br>";
    }
} else {
    echo "0 results";
}
?>

补充:为什么单例没用?

PHP每个请求都是独立的执行上下文,静态变量、单例对象仅在当前请求内有效,请求结束后就会被销毁,跨请求无法共享状态,这就是单例对你的场景无效的核心原因。

内容的提问来源于stack exchange,提问作者rog

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 14:13:17