到上一节为止,服务器已经「会算」了,但它一关机就什么都忘了。本节装上数据库,让它能把东西「记下来」——用户、留言、订单,都存在这里,重启也不丢。 这是 LAMP 里的第三块,也是本节最容易踩坑的一块:新版数据库的登录方式跟老教程完全不一样。
5-1 装 Apache(接待)、5-2 装 PHP(现算),本节装数据库(记账)——三块凑齐,就是一个完整的动态网站后台。 这门课的第 15 门《完全图解数据库》讲过库、表、记录、SQL 四类语句——本节是那门课第一次「真的装上一个数据库」,把纸上的概念变成机器里的东西。 本节是全课第一个「要会算」的 ⭐⭐⭐:SQL 语句得动手敲,权限得亲手授。
一、为什么服务器需要数据库
你可能会想:存数据用文件不就行了? 4-2 学的 > 重定向、cat 都在啊。
文件能存,但有四件事做不到:
| 需求 | 文件 | 数据库 |
|---|---|---|
| 多人同时写(两个用户同时下单) | 会互相覆盖、写坏 | 有锁与事务,保证不打架 |
| 快速查找(10 万条里找一个用户) | 只能从头翻到尾 | 有索引,几步就定位 |
| 结构约束(年龄不许填「哈哈」) | 管不了 | 可以定义类型与规则(4-2 那门课的「避免非法数据」) |
| 断电不丢(正写着断电) | 可能写一半,文件坏了 | 有日志,能恢复到一致状态 |
文件是「笔记本」,数据库是「档案室」。 记一两句用笔记本挺好;要管几万份材料、还要多人同时借还、还得能查、能追责——就得有档案室。
二、先搞清一件事:你会装到的是 MariaDB
这是本节第一个反直觉的地方。
sudo apt install mysql-server # 在 Debian / 树莓派上,这句装的是 MariaDB
MariaDB 是什么? 简单说:MySQL 被 Oracle 收购后,原作者带团队另开的分支,跟 MySQL 几乎完全兼容——同样的 SQL、同样的协议、同样的客户端。Debian 系(包括 Raspberry Pi OS)默认仓库里就是它。
| MySQL | MariaDB | |
|---|---|---|
| 谁维护 | Oracle | 原 MySQL 团队 + 社区 |
| 在 Debian 仓库里 | 不是默认(要另加官方源) | 是默认 |
| 命令基本一样吗 | —— | 基本一样,日常使用感觉不到差别 |
统一装 mariadb-server(这就是 Debian 上「装数据库」的正路)。
后面正文里的命令,绝大多数和你查到的 MySQL 教程是通用的——只有「登录方式」和「命令名字」两处不同,而这两处正是本节的防坑重点。
三、装:三行命令
sudo apt update
sudo apt install mariadb-server -y
systemctl status mariadb --no-pager
注意服务名是 mariadb,不是 mysql(老教程里的 systemctl restart mysql 在这会报 Unit mysql.service not found)。
期望看到 Active: active (running)。
再确认一下版本:
mariadb --version
期望输出类似 mariadb from 11.8.x-MariaDB(具体版本以你机器为准)。
四、最大的坑:root 不用密码,也「不能」用密码
这一条是本节最值钱的内容。 老教程会让你这样登录:
mysql -u root -p # 然后输入你设的密码
在当前版本上,这会失败——而且失败得很奇怪:你从来没设过密码,因为安装时根本没让你设。
真相是:MariaDB 从 10.4 起,root 默认改用 unix_socket 认证。
它不看密码,看你是谁——准确地说,看你的 Linux 系统用户身份。
如果你是以系统 root(sudo)身份连过来的,数据库就认为「你是 root,放行」。
这是更安全的做法:密码可能被猜、被撞库,但「你已经通过系统认证成为 root」这件事没法伪造。
所以正确的登录方式是:
sudo mariadb
期望结果:直接进入 MariaDB [(none)]> 提示符,不问你密码。
-- 在数据库里查一下 root 用的是哪种认证
SELECT user, host, plugin FROM mysql.user WHERE user = 'root';
期望看到 plugin 那一列是 unix_socket。看到它就说明「我不是遇到 bug,而是碰到了新版本的正常设计」。
mysql -u root -p登不进 —— 因为没密码可输,改用sudo mariadb;- 教程里的「设置 root 密码」步骤对你没用 —— 除非你确实要用密码方式(比如给某个程序用),那得显式改认证方式,见下一节;
mysql_secure_installation这个名字变了 —— 现在叫mariadb-secure-installation(旧名大多还留着兼容,但新资料里一律是新名字)。
五、跑一遍安全脚本(会问你几个问题)
sudo mariadb-secure-installation
它会依次问几件事,几个关键问句怎么答(这几个答错会给自己挖坑):
| 它会问 | 建议答 | 为什么 |
|---|---|---|
是否切换到 unix_socket 认证 |
Y(一般默认就是它) | 保持「用 sudo 才能管数据库」这种最安全的状态 |
| 设置 root 密码 | 若上一问答 Y,则跳过 | socket 认证本身不用密码 |
| 删除匿名用户 | Y | 匿名账号是纯粹的后门 |
| 禁止 root 远程登录 | Y | root 只该在机器本机用 |
| 删除 test 数据库 | Y | 无用的默认库 |
| 重新加载权限表 | Y | 让刚才的改动生效 |
跑完看到 Thanks for using MariaDB! 就算成功。
六、给它建一个「专用账号」——不要用 root 跑网站
这是本节最该带走的习惯(4-3 那门课讲的「最小权限原则」在这里落地):
网站程序永远不要用 root 连数据库。 一旦网站被攻破,攻击者用 root 的权限就能删掉整个数据库服务器上的一切。给它一个只能在「某一个库」里干活的账号——这样最多损失一个库。
四步走(sudo mariadb 进去后执行):
-- ① 建一个库
CREATE DATABASE myapp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- ② 建一个专用用户(注意:密码要换成你自己的,别照抄)
CREATE USER 'webuser'@'localhost' IDENTIFIED BY 'ChangeThisPass123';
-- ③ 只把 myapp 这个库的权限给它
GRANT ALL PRIVILEGES ON myapp.* TO 'webuser'@'localhost';
-- ④ 让权限生效
FLUSH PRIVILEGES;
几个要点:
CHARACTER SET utf8mb4—— 建库时就要指定编码,否则中文容易乱码。utf8mb4才是「完整的 UTF-8」(能存 emoji),老教程里的utf8是不完整的。'webuser'@'localhost'—— 含义是「只允许从本机连接」。这很重要:PHP 和数据库跑在同一台树莓派上,本来就不需要远程连——别写成'%'(任何人)。GRANT ... ON myapp.*—— 只给这一个库,不是*.*(所有库)。
验证一下这个账号能进去(不用 sudo,走密码):
mariadb -u webuser -p myapp
输入密码后能进 MariaDB [myapp]>,说明专用账号建对了。
七、建一张表,塞点数据(回收第 15 门的 SQL)
USE myapp;
CREATE TABLE visitors (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
visited_at DATETIME
);
INSERT INTO visitors (name, visited_at) VALUES ('波仔', NOW());
INSERT INTO visitors (name, visited_at) VALUES ('小明', NOW());
SELECT * FROM visitors;
期望输出:两行记录,id 从 1 开始自增。
几条常用的查看命令:
SHOW DATABASES; -- 有哪些库
USE myapp; -- 切到某个库
SHOW TABLES; -- 这个库有哪些表
DESCRIBE visitors; -- 看表结构(列名、类型、是否可空)
SELECT COUNT(*) FROM visitors; -- 数一数有几条
退出数据库:输入 exit 或按 Ctrl + D。
八、让 PHP 把数据显示出来(三块拼在一起)
这一步做完,你就真的把 LAMP 串起来了。
新建 /var/www/html/db.php:
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>数据库测试</title></head>
<body>
<h1>从数据库里读出来的记录</h1>
<?php
$conn = new mysqli("localhost", "webuser", "ChangeThisPass123", "myapp");
if ($conn->connect_error) {
die("连接失败:" . $conn->connect_error);
}
$conn->set_charset("utf8mb4"); // 不给它设,中文还是可能乱码
$result = $conn->query("SELECT name, visited_at FROM visitors ORDER BY id DESC LIMIT 5");
while ($row = $result->fetch_assoc()) {
echo "<p>" . htmlspecialchars($row["name"]) . " —— " . $row["visited_at"] . "</p>";
}
$conn->close();
?>
</body>
</html>
浏览器打开 http://树莓派的IP/db.php,期望看到刚才插进去的那两条记录。
set_charset("utf8mb4")—— 建库时指定了编码,连接时还要再声明一次,两头都对上中文才稳;htmlspecialchars()—— 把用户输入里的特殊字符转义掉,防的是「用户输入被当成代码执行」(网页安全的第一道门)。养成习惯,凡是往页面上输出的用户数据,都套一层。
九、配置与文件都在哪儿
几张「地图」要认,出问题时知道去哪儿看:
| 位置 | 是什么 |
|---|---|
/etc/mysql/mariadb.conf.d/50-server.cnf |
主配置(端口、监听地址都在这) |
/var/lib/mysql/ |
数据目录——所有库和表都躺在这(备份时就备份它) |
/var/log/mysql/error.log |
数据库自己的错误日志 |
/etc/mysql/debian.cnf |
系统维护脚本用的凭据(别乱改) |
~/.my.cnf |
你自己的登录凭据(可选,省得每次输密码) |
其中一个设置要特别说:
grep -r "bind-address" /etc/mysql/mariadb.conf.d/
# 默认:bind-address = 127.0.0.1
127.0.0.1 的含义是「只听本机」——别的机器连不上它。这是默认的安全设置,别去改。
网上大量教程教你把它改成 0.0.0.0、再建个 '%' 的用户,好让别的机器连。
本课场景完全不需要(PHP 和数据库在同一台树莓派上),改了只是凭空开了一扇门。
确实需要远程管数据库时,正确做法是走 SSH 隧道,而不是把数据库端口暴露出去。
十、动手 Demo:一条数据从「表单」走进「数据库」
目标:把本节所有零件串成一条完整链路——你在浏览器填、PHP 收、数据库存、再读回来显示。
sudo nano /var/www/html/add.php
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>登记</title></head>
<body>
<h1>登记一位访客</h1>
<?php
$conn = new mysqli("localhost", "webuser", "ChangeThisPass123", "myapp");
if ($conn->connect_error) { die("连接失败:" . $conn->connect_error); }
$conn->set_charset("utf8mb4");
if ($_SERVER["REQUEST_METHOD"] === "POST" && !empty($_POST["name"])) {
$stmt = $conn->prepare("INSERT INTO visitors (name, visited_at) VALUES (?, NOW())");
$stmt->bind_param("s", $_POST["name"]);
$stmt->execute();
echo "<p>已登记:" . htmlspecialchars($_POST["name"]) . "</p>";
}
?>
<form method="post">
姓名:<input type="text" name="name">
<button type="submit">提交</button>
</form>
<hr>
<?php
$result = $conn->query("SELECT name, visited_at FROM visitors ORDER BY id DESC LIMIT 10");
while ($row = $result->fetch_assoc()) {
echo "<p>" . htmlspecialchars($row["name"]) . " —— " . $row["visited_at"] . "</p>";
}
$conn->close();
?>
</body>
</html>
验收四步:
- 浏览器打开
add.php,看到输入框和已有的记录; - 输入一个名字,点提交,页面上出现「已登记:xxx」;
- 刷新页面,那条记录出现在下面列表里(说明真的写进数据库了);
- 回到终端验证:
bash sudo mariadb -e "SELECT * FROM myapp.visitors;"能看到你刚提交的那条就算彻底成功。
读结果:
- 提交后记录没出现 → 去
/var/log/apache2/error.log看 PHP 报错; - 中文显示成
????或乱码 → 三处编码没对齐(建库 / 连接 / 页面 meta 都要是 utf8mb4); - 提示
Access denied for user→ 用户名密码写错,或忘了FLUSH PRIVILEGES。
代码里没直接把用户输入拼进 SQL,而是用了参数化查询。
这是为了防SQL 注入(用户输入被当成 SQL 代码执行)——这是所有数据库教程里最重要的一条安全习惯。现在只要记住「有用户输入就用 prepare」这个动作,原理以后细讲。
十一、30 秒实验:看见「数据真的存在磁盘上」
# 看一眼数据目录(需要权限)
sudo ls -lh /var/lib/mysql/myapp/
期望看到 visitors.ibd 之类的文件——这就是你刚才那两条记录在磁盘上的物理形态。
# 看一眼数据库服务的状态和启动时间
systemctl status mariadb --no-pager | head -5
再做个「重启也不丢」的验证:
sudo systemctl restart mariadb
sudo mariadb -e "SELECT COUNT(*) FROM myapp.visitors;"
记录数不变 —— 这就回答了本节开头那个问题:为什么需要数据库。文件可能写坏,数据库能保证「写下去了就是写下去了」。
十二、可选:装个图形界面管数据库(phpMyAdmin)
不想老敲 SQL 的话,可以装网页版的数据库管理工具:
sudo apt install phpmyadmin -y
安装过程中会问你「用哪个 Web 服务器」→ 选 apache2;再问「要不要用 dbconfig 配置数据库」→ 选 Yes,然后设一个管理密码。
装完访问:http://树莓派的IP/phpmyadmin,用 webuser 或 root(socket 方式可能需另设密码)登录。
phpMyAdmin 是直连数据库的管理面板——挂在网上等于把数据库钥匙插在门上。
本地学习用没问题,但只要这台机器有半点外网暴露(哪怕路由器转了个端口),先把 phpmyadmin 目录删掉或用 Apache 的访问控制锁住。这是 5-1 那条红线(绝不暴露公网)的具体应用。
十三、常见误区
- 「
apt install mysql-server装的是 MySQL」——在 Debian / 树莓派上,它给你的是 MariaDB。这是官方仓库的选择,不是 bug。 - 「
mysql -u root -p登不进去,说明装坏了」——没坏,是新版本的正常设计。root 默认走unix_socket,用sudo mariadb登。(见第四节) - 「网站用 root 连数据库比较省事」——这是最危险的一条。永远建专用账号,只授必要权限(第六节)。
- 「
utf8就是 UTF-8」——不是。MySQL/MariaDB 里的utf8是不完整的旧编码,存 emoji 会出错。一律用utf8mb4。 - 「改
bind-address成0.0.0.0才能用」——本课不需要。PHP 和数据库同机,走localhost就够。改了只是无谓地开门。 - 「SQL 里把用户输入拼进语句没关系」——这是 SQL 注入。有用户输入就用
prepare+bind_param。 - 「重启一下数据库服务,数据就没了吧」——不会。数据在
/var/lib/mysql/的磁盘文件里,服务重启不影响它。(这也正是数据库存在的意义)
mysql_secure_installation—— 现在叫mariadb-secure-installation(旧名多为兼容保留,新资料用新名);systemctl restart mysql—— 服务名是mariadb;mysql/mysqladmin/mysqldump命令 —— 新版提供mariadb/mariadb-admin/mariadb-dump一套新名字(老名字多数还留了别名,但新教程一律新名字);- 安装时让你设 root 密码 —— 现在安装过程根本不问,因为默认就是 socket 认证;
- 用
mysql_install_db初始化 —— 老命令,新版已移除,包安装时自动完成初始化。
自测三问(点开对答案)
1. 为什么 `mysql -u root -p` 在这台机器上登不进去?正确的登录方式是什么?(root 默认使用 **`unix_socket` 认证**,它不是靠密码、而是靠「你是不是系统 root」。正确方式:**`sudo mariadb`**) 2. 为什么网站程序不该用 root 连数据库?(**最小权限原则**:网站一旦被攻破,root 权限会连累整台数据库服务器;专用账号只授某一个库的权限,损失可控) 3. 建库时为什么写 `utf8mb4` 而不是 `utf8`?(MariaDB/MySQL 里的 **`utf8` 是不完整的旧编码**;`utf8mb4` 才是完整的 UTF-8,能正确存中文与 emoji。而且编码要在**建库、连接、页面 meta** 三处都对齐)- ☐ 下一节(5-4 架设 FTP 服务器)
相关笔记
- 安装 PHP 开发环境 —— 上一节:本节 PHP 连数据库,用的就是它装的
php-mysql - 架设 Web 服务器 —— LAMP 的第一块,网页的家在
/var/www/html - Linux 的用户与文件权限指令 —— 「最小权限」原则在数据库用户上的落地
- Linux 的常用指令 —— 排错要用的日志查看与
grep - 🍓 树莓派 · 索引 —— 全课路线图
- 🗄️ 数据库基础 · 索引 —— 库、表、记录、SQL 四类语句的系统复习