PHP连接只读MySQL的核心前提是使用仅授予SELECT权限的数据库用户,而非PHP端控制;需在MySQL中创建用户并精确授权指定库的SELECT权限,禁用所有写权限,PHP通过该用户连接后任何写操作均由MySQL服务端拒绝。

PHP 连接只读 MySQL 的核心前提
MySQL 只读权限不是 PHP 侧控制的,而是由数据库用户权限决定。PHP 本身没有“只读连接”开关,mysqli 或 PDO 都会忠实地执行你发的任何 SQL —— 即使是 UPDATE 或 DROP。真正起作用的是你用哪个数据库账号连接:这个账号必须在 MySQL 里被显式授予 SELECT 权限,且**不授予** INSERT、UPDATE、DELETE、CREATE 等写权限。
创建只读数据库用户的正确步骤
在 MySQL 中(用 root 或高权限账号执行):
- 先创建用户:
CREATE USER 'app_reader'@'%' IDENTIFIED BY 'strong_password';
- 只授权查询:
GRANT SELECT ON `mydb`.* TO 'app_reader'@'%';
- 刷新权限:
FLUSH PRIVILEGES;
⚠️ 注意:GRANT SELECT ON *.* 是全局只读,风险高;生产环境应限定到具体库(如 `mydb`.*)。如果用 localhost 连接,记得把 '%' 换成 'localhost',否则可能连不上。
PHP 中使用该只读用户连接(PDO 示例)$dsn = 'mysql:host=127.0.0.1;dbname=mydb;charset=utf8mb4';
$pdo = new PDO($dsn, 'app_reader', 'strong_password', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
- 先创建用户:
CREATE USER 'app_reader'@'%' IDENTIFIED BY 'strong_password'; - 只授权查询:
GRANT SELECT ON `mydb`.* TO 'app_reader'@'%'; - 刷新权限:
FLUSH PRIVILEGES;
GRANT SELECT ON *.* 是全局只读,风险高;生产环境应限定到具体库(如 `mydb`.*)。如果用 localhost 连接,记得把 '%' 换成 'localhost',否则可能连不上。
PHP 中使用该只读用户连接(PDO 示例)$dsn = 'mysql:host=127.0.0.1;dbname=mydb;charset=utf8mb4';
$pdo = new PDO($dsn, 'app_reader', 'strong_password', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
连接成功后,任何写操作都会立刻报错:SQLSTATE[42000]: Syntax error or access rule violation: 1142 INSERT command denied to user...。这不是 PHP 拦截的,是 MySQL 服务端拒绝执行。
为什么不能靠 PHP 层“模拟只读”?
有人试图在代码里封装一个“只读 DB 类”,禁用 executeUpdate() 方法 —— 这毫无安全意义。只要底层连接用的是有写权限的账号,攻击者或误操作仍可通过原生 $pdo->exec('UPDATE ...') 绕过。真正的只读必须从 MySQL 用户权限源头卡死。另外,某些 ORM(如 Laravel 的 DB::statement())或原始 mysqli_query() 调用,完全不受上层封装约束。
只读权限的可靠性,取决于 MySQL 用户配置是否干净、最小化。哪怕漏授了一个 TRIGGER 或 ALTER 权限,都可能成为意外写入的入口。











