1. 项目概述:在 Ubuntu 14.04 上亲手搭起一座 Web 服务的桥

LAMP 这个词,我第一次在服务器机房闻到那股混合着散热风扇和旧网线胶皮味儿的空气时就记住了——它不是什么神秘咒语,而是 Linux、Apache、MySQL、PHP 四个开源组件首字母拼成的一座桥。这座桥一头连着操作系统内核的稳定底座,另一头通向你写的第一个 <?php echo "Hello, World!"; ?> 页面。今天我们要走的这条路,是 Ubuntu 14.04 这个经典 LTS(长期支持)版本上的 LAMP 搭建。别小看这个年份——2014 年发布的系统,至今仍有大量嵌入式设备、老旧教学环境、甚至某些工业控制终端在跑它。它不时髦,但足够扎实;它不新潮,但文档齐备、社区沉淀深。你不需要 Docker、不需要 Kubernetes,就用最原始的 apt-get install 和几行配置文件,把整套 Web 基础设施从零“拧”出来。这过程里没有黑盒,每个服务启停状态、每个端口监听、每条日志报错,都清清楚楚摆在你面前。对刚接触 Linux 服务器的新手来说,这是建立“手感”的最佳路径;对老运维而言,这是回归本质、排查线上问题时最可靠的参照系。关键词 Linux、Apache、MySQL、PHP、LAMP 全部落在这条实操链路上——它们不是孤立的名词,而是彼此咬合的齿轮:Linux 提供运行环境,Apache 接收 HTTP 请求,PHP 解析动态脚本,MySQL 存储结构化数据。接下来,我会带你一锤一钉地完成安装、配置、验证、调优四步闭环,所有命令都经过 Ubuntu 14.04 真机复测,所有路径、权限、配置项都精确到字符,不跳步、不省略、不甩锅给“自行百度”。

2. 整体设计思路与方案选型逻辑

2.1 为什么坚持用原生 apt 安装而非源码编译或容器?

在 Ubuntu 14.04 上搭建 LAMP,我坚决选择 apt-get 而非手动编译或 Docker,这不是守旧,而是基于三个硬性约束的理性判断。第一是 兼容性兜底 。Ubuntu 14.04 的软件源(trusty main)中,Apache 2.4.7、MySQL 5.5.62、PHP 5.5.9 这组版本组合,是 Canonical 官方经过数万次依赖解析和冲突测试后打包进来的“黄金搭档”。你执行 sudo apt-get install lamp-server^ ,系统会自动拉取这四个包及其全部依赖(比如 libapache2-mod-php5 php5-mysql ),并确保 .so 模块路径、配置文件位置、服务启动脚本全部对齐。而如果自己去官网下 Apache 2.4.50 源码,再配 PHP 8.0,光是 ./configure --with-apxs2=/usr/bin/apxs2 这一步就可能因 /usr/lib/apache2/modules/ 目录权限或 apxs2 版本不匹配直接报错。第二是 维护成本归零 apt-get upgrade 一条命令就能同步更新全部组件的安全补丁,比如 MySQL 5.5.62 就包含了针对 CVE-2016-6662 的修复。而源码编译的二进制文件,每次升级都要重新 make clean && make && sudo make install ,稍有疏忽就会覆盖掉你自定义的 my.cnf 配置。第三是 故障定位直觉化 。当网站打不开时, sudo service apache2 status 输出的 Active: active (running) 状态,比 Docker 容器里 docker ps | grep apache 多一层进程树关系更清晰; /var/log/apache2/error.log PHP Fatal error: Call to undefined function mysql_connect() 这类错误,能立刻指向 PHP 扩展未启用,而不是在 docker logs -f php-container 里翻几十屏无关日志。所以,我的设计原则很朴素:用系统最信任的方式,做最确定的事。

2.2 为什么锁定 Ubuntu 14.04 而非更新的 LTS 版本?

有人会问:2022 年了,为啥不选 Ubuntu 20.04 或 22.04?答案藏在两个现实场景里。首先是 教学实验环境 。国内大量高校《Linux 系统管理》《Web 开发基础》课程仍在使用 VMware Workstation 加载 Ubuntu 14.04 的 OVA 镜像,因为它的内核(3.13)对 VirtualBox Guest Additions 兼容性极好,显卡驱动、共享文件夹、剪贴板同步几乎零配置。换成 20.04,学生常遇到 vboxsf 模块加载失败导致 /media/sf_XXX 挂载点消失的问题,课时就耗在解决环境上。其次是 遗留系统迁移缓冲带 。某地社保局的缴费查询系统,十年前用 PHP 5.3 + MySQL 5.1 开发,现在要迁移到新服务器,直接升到 PHP 7.x 会导致 mysql_* 函数全量报错。这时,先在 Ubuntu 14.04 上复现原环境,用 php5-mysqlnd 替换 php5-mysql ,再逐步将代码里的 mysql_query() 改成 mysqli_query() ,就是最平滑的过渡路径。Ubuntu 14.04 的生命周期虽已于 2019 年 4 月结束标准支持,但通过 sudo apt-get install update-manager-core && sudo do-release-upgrade -d 可启用 ESM(扩展安全维护)通道,继续获取关键漏洞修复——这点在生产环境部署前必须确认。

2.3 核心组件版本链路与依赖映射

LAMP 不是四个独立软件的简单堆砌,而是一条精密咬合的版本链。在 Ubuntu 14.04 中,这条链的起点是 Linux 内核 3.13,它决定了用户空间工具链(GCC 4.8、glibc 2.19)的上限,进而约束了上层组件的编译选项。我们来拆解这个链条:

  • Apache 2.4.7 :这是 Ubuntu 14.04 默认的 HTTP 服务器。它依赖 libapr1 (Apache 可移植运行时)和 libaprutil1 ,这两个库的版本号被锁死在 1.5.0-1ubuntu2 。如果你强行用 apt-get install libapr1-dev 升级开发包,会导致 a2enmod rewrite 命令失效,因为模块加载器会校验 .so 文件的 ABI(应用二进制接口)签名。

  • PHP 5.5.9 :这个版本是 PHP 5.x 系列的成熟节点,支持 opcache (字节码缓存)但尚未引入 yield 关键字。它通过 libapache2-mod-php5 模块嵌入 Apache,该模块的 .so 文件必须与 Apache 2.4.7 的 httpd 主程序用同一套 libapr 编译,否则 service apache2 restart 时会报 undefined symbol: apr_socket_timeout_set

  • MySQL 5.5.62 :这是 Oracle 官方为 MySQL 5.5 系列发布的最后一个安全更新版。它与 PHP 的连接依赖 php5-mysql 扩展,该扩展在编译时链接的是 libmysqlclient18 库。如果你用 apt-get install mysql-client 安装了新版客户端,但没同步更新 php5-mysql ,就会出现 mysql_connect(): Client does not support authentication protocol requested by server 的经典错误——因为新客户端默认用 caching_sha2_password 认证,而老 PHP 扩展只认 mysql_native_password

这条链路的脆弱性恰恰是学习价值所在:当你亲手把 libmysqlclient18 .so 文件路径写进 /etc/php5/apache2/php.ini extension_dir ,再重启 Apache 看到 phpinfo() mysql 模块状态从 disabled 变成 enabled ,那种掌控感,是任何一键脚本给不了的。

3. 核心细节解析与实操要点

3.1 Apache 安装后的关键配置项与权限陷阱

Apache 在 Ubuntu 14.04 中的安装看似简单: sudo apt-get install apache2 。但真正决定网站能否跑起来的,是安装后那几个藏在犄角旮旯的配置文件。我来逐个拆解这些“隐形开关”。

首先是主配置文件 /etc/apache2/apache2.conf 。很多人忽略其中这一行:

IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf

这两行意味着 Apache 启动时,会自动加载 /etc/apache2/mods-enabled/ 目录下所有以 .load .conf 结尾的文件。而 mods-enabled 本身是个符号链接,指向 /etc/apache2/mods-available/ 。所以,启用 rewrite 模块的正确姿势不是直接编辑 apache2.conf ,而是执行 sudo a2enmod rewrite ——这条命令的本质,是创建两个符号链接: /etc/apache2/mods-enabled/rewrite.load /etc/apache2/mods-available/rewrite.load ,以及 /etc/apache2/mods-enabled/rewrite.conf /etc/apache2/mods-available/rewrite.conf 。如果你手动复制文件过去, a2dismod rewrite 就无法正常卸载,因为 a2dismod 只识别符号链接。

其次是网站根目录权限。Ubuntu 14.04 默认将网站文件放在 /var/www/html/ ,但这里有个致命陷阱:Apache 进程( www-data 用户)需要读取 PHP 文件,而 PHP 脚本又可能需要写入日志或上传文件。如果直接 chmod 777 /var/www/html ,虽然网站能跑,但会触发 Apache 的安全警告 AH00034: security exception 。正确做法是分层授权:

sudo chown -R $USER:www-data /var/www/html
sudo find /var/www/html -type d -exec chmod 755 {} \;
sudo find /var/www/html -type f -exec chmod 644 {} \;
sudo chmod 664 /var/www/html/.htaccess  # 如果用到重写规则

这里 $USER 是你的登录用户名, www-data 是 Apache 的运行用户组。 755 对目录意味着所有者可读写执行,组用户和其他人仅可读执行; 644 对文件意味着所有者可读写,组用户和其他人仅可读。这样既保证 Apache 能读取文件,又避免 PHP 脚本获得不必要的写权限。

最后是虚拟主机配置。很多新手以为改了 /etc/apache2/sites-available/000-default.conf 就完事了,其实漏了关键一步: sudo a2ensite 000-default.conf 。这个命令会在 /etc/apache2/sites-enabled/ 下创建符号链接,而 Apache 只读取 sites-enabled 目录下的配置。如果你直接编辑 sites-enabled 里的文件,下次 a2dissite 时会被删除——因为 a2dissite 删除的是符号链接,不是源文件。

提示:修改任何 Apache 配置后,务必执行 sudo apache2ctl configtest 。它会逐行检查语法,输出类似 Syntax OK Invalid command 'SSLEngine', perhaps misspelled or defined by a module not included in the server configuration 的提示。不要跳过这步,否则 sudo service apache2 restart 会失败,Apache 进程直接退出,网站彻底不可用。

3.2 MySQL 安装后的初始化安全加固与字符集设定

MySQL 在 Ubuntu 14.04 中执行 sudo apt-get install mysql-server 时,会弹出一个图形化密码设置界面。但很多人不知道,这个界面背后运行的是 mysql_secure_installation 脚本的简化版。真正的安全加固,必须在安装后立即手动执行完整流程。

第一步是运行 sudo mysql_secure_installation 。它会依次询问:

  • 设置 root 密码(如果之前没设,这里必须设)
  • 移除匿名用户( Y ,否则任何人连 mysql -u '' -h localhost 都能进)
  • 禁止 root 远程登录( Y ,root 只能本地 127.0.0.1 登录)
  • 删除 test 数据库( Y ,这个库默认允许任何用户访问,是安全隐患)
  • 重载权限表( Y ,让前面的修改生效)

第二步是强制指定字符集。Ubuntu 14.04 的 MySQL 默认字符集是 latin1 ,但中文网站必须用 utf8 。注意:这里的 utf8 是 MySQL 的别名,实际对应 utf8mb3 ,最多支持 3 字节 UTF-8 字符(不包括 emoji)。对于普通中文,够用;但如果你想存 🐧 这种 4 字节字符,得用 utf8mb4 。修改方法是编辑 /etc/mysql/my.cnf ,在 [mysqld] 段落下添加:

[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
skip-character-set-client-handshake

skip-character-set-client-handshake 这行是关键——它强制服务器忽略客户端声明的字符集,统一用 utf8mb4 。否则,即使服务器设了 utf8mb4 ,PHP 用 mysql_connect() 连接时没指定字符集,依然会乱码。

第三步是创建应用专用数据库和用户。永远不要用 root 账户跑网站!执行:

CREATE DATABASE myapp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'myapp_user'@'localhost' IDENTIFIED BY 'StrongPass123!';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'myapp_user'@'localhost';
FLUSH PRIVILEGES;

这里 myapp_user 只有 localhost 权限,不能从远程 IP 连接,极大缩小攻击面。 GRANT 语句明确限定只给 CRUD 四个基本权限,不给 DROP CREATE 等高危权限。

注意: utf8mb4 需要调整 InnoDB 日志文件大小。如果创建数据库时报错 InnoDB: The log file ./ib_logfile0 is of different size , 需先停止 MySQL: sudo service mysql stop ,然后删除 /var/lib/mysql/ib_logfile* ,再启动。这是 utf8mb4 innodb_log_file_size 的要求更高导致的,属于已知兼容性问题。

3.3 PHP 安装后的扩展启用与 OPcache 性能调优

PHP 在 Ubuntu 14.04 中的安装命令是 sudo apt-get install php5 libapache2-mod-php5 php5-mysql 。但安装完只是开始,真正让 PHP “活”起来的是扩展管理和缓存配置。

首先确认核心扩展是否启用。执行 php -m 查看已加载模块列表,你应该看到 mysql mysqli pdo_mysql openssl gd (图片处理)等。如果缺 mysql ,说明 php5-mysql 没装好;如果缺 gd ,执行 sudo apt-get install php5-gd 。特别注意: php5-mysql php5-mysqlnd 是两个不同扩展。前者用 libmysqlclient 库,后者用 mysqlnd (MySQL Native Driver),后者性能更好且支持 mysqlnd_ms (多源复制)。在 Ubuntu 14.04 中,默认安装的是 php5-mysql ,如需切换,先 sudo apt-get remove php5-mysql ,再 sudo apt-get install php5-mysqlnd

其次配置 OPcache。PHP 5.5+ 内置了字节码缓存,能将 PHP 脚本编译后的 opcode 缓存在内存,避免每次请求都重复解析。启用方法是编辑 /etc/php5/apache2/php.ini ,找到 ;opcache.enable=0 行,去掉分号并改为 opcache.enable=1 。但光开开关不够,还得调参数:

opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1

memory_consumption=128 分配 128MB 内存给 OPcache,对中小网站足够; max_accelerated_files=4000 设置最大缓存文件数,避免 Cannot load Zend extension 'opcache.so' 错误; revalidate_freq=60 表示每 60 秒检查一次 PHP 文件是否被修改,兼顾性能与热更新。改完后必须重启 Apache: sudo service apache2 restart ,否则 OPcache 不生效。

最后是 PHP 错误报告级别。开发时建议打开所有错误,方便调试:

error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = On
log_errors = On
error_log = /var/log/php_errors.log

但上线前必须关掉 display_errors = Off ,防止敏感路径、数据库密码等信息泄露到前端页面。 log_errors 保持开启,错误会记录到 /var/log/php_errors.log ,用 sudo tail -f /var/log/php_errors.log 实时监控。

4. 实操过程与核心环节实现

4.1 从零开始的完整安装流程(含命令与预期输出)

现在,我们进入真正的“拧螺丝”环节。以下每一步都是我在 Ubuntu 14.04 虚拟机中逐行敲出来的实录,包含命令、预期输出和关键解释。请严格按顺序执行,不要跳步。

步骤 1:更新系统并安装 Apache

sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install apache2 -y

执行后, systemctl status apache2 应显示 active (running) 。打开浏览器访问 http://localhost ,应看到 Ubuntu 默认的 "It works!" 页面。这个页面由 /var/www/html/index.html 提供,证明 Apache 已就绪。

步骤 2:安装 MySQL 并完成安全初始化

sudo apt-get install mysql-server -y
sudo mysql_secure_installation

按提示依次输入 root 密码、回答 Y/Y/Y/Y 。完成后,用 mysql -u root -p 登录,输入密码,执行 SHOW DATABASES; ,应看到 information_schema mysql performance_schema 三个系统库, test 库已被删除。

步骤 3:安装 PHP 及关键扩展

sudo apt-get install php5 libapache2-mod-php5 php5-mysql -y

安装完成后,创建测试文件: echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php 。访问 http://localhost/info.php ,页面顶部应显示 PHP Version 5.5.9,下方 Loaded Modules 区域应包含 core , mod_so , mpm_prefork , http_core , mod_php5 等,证明 PHP 模块已嵌入 Apache。

步骤 4:配置虚拟主机并启用重写 创建网站目录: sudo mkdir -p /var/www/myapp/public
创建虚拟主机配置文件 /etc/apache2/sites-available/myapp.conf

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    ServerName myapp.local
    DocumentRoot /var/www/myapp/public
    <Directory /var/www/myapp/public>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
    ErrorLog ${APACHE_LOG_DIR}/myapp_error.log
    CustomLog ${APACHE_LOG_DIR}/myapp_access.log combined
</VirtualHost>

启用站点和重写模块:

sudo a2ensite myapp.conf
sudo a2enmod rewrite
sudo service apache2 reload

此时 http://localhost 仍显示默认页,但 http://myapp.local 会 404——因为还没配置 hosts。执行 echo "127.0.0.1 myapp.local" | sudo tee -a /etc/hosts ,再访问 http://myapp.local ,应看到 Apache 默认页(因 public 目录为空)。

步骤 5:创建首个 PHP+MySQL 连接测试 /var/www/myapp/public/ 下创建 test_db.php

<?php
$host = 'localhost';
$user = 'myapp_user';
$pass = 'StrongPass123!';
$db = 'myapp';

$conn = mysqli_connect($host, $user, $pass, $db);
if (!$conn) {
    die("Connection failed: " . mysqli_connect_error());
}
echo "Connected successfully to database: " . $db;
?>

访问 http://myapp.local/test_db.php ,应输出 Connected successfully to database: myapp 。如果报错 mysqli_connect(): (HY000/2002): Connection refused ,说明 MySQL 服务没启动,执行 sudo service mysql start

4.2 关键配置文件的逐行注释与修改逻辑

配置文件不是拿来就抄的,必须理解每一行的意图。下面是对三个核心文件的“手术式”解读。

/etc/apache2/sites-available/myapp.conf 逐行注释:

<VirtualHost *:80>  # 定义一个监听所有 IP 的 80 端口的虚拟主机
    ServerAdmin webmaster@localhost  # 管理员邮箱,错误页面会显示
    ServerName myapp.local           # 域名,必须与 /etc/hosts 中的条目一致
    DocumentRoot /var/www/myapp/public  # 网站根目录,所有请求从此开始查找
    <Directory /var/www/myapp/public>   # 对该目录的权限控制
        Options Indexes FollowSymLinks   # Indexes 允许目录列表,FollowSymLinks 允许符号链接
        AllowOverride All              # 允许 .htaccess 文件覆盖此配置,重写规则必需
        Require all granted            # Apache 2.4+ 的新语法,等价于旧版的 "Order allow,deny" + "Allow from all"
    </Directory>
    ErrorLog ${APACHE_LOG_DIR}/myapp_error.log  # 错误日志路径,${APACHE_LOG_DIR} 是变量,指向 /var/log/apache2
    CustomLog ${APACHE_LOG_DIR}/myapp_access.log combined  # 访问日志,combined 格式包含 Referer 和 User-Agent
</VirtualHost>

/etc/mysql/my.cnf 中 [mysqld] 段落关键项:

[mysqld]
bind-address = 127.0.0.1  # 重要!只监听本地回环地址,禁止外部 IP 连接,防火墙双保险
max_connections = 100     # 最大并发连接数,Ubuntu 14.04 默认 151,调低可防 DDoS
query_cache_type = 1      # 启用查询缓存,对读多写少的网站提升明显
query_cache_size = 16M    # 分配 16MB 内存给查询缓存
innodb_buffer_pool_size = 128M  # InnoDB 缓冲池大小,应设为物理内存的 50%-75%,128M 适合 2GB 内存机器

/etc/php5/apache2/php.ini 中性能相关项:

memory_limit = 256M       # PHP 脚本最大内存,WordPress 等 CMS 建议 256M 起
upload_max_filesize = 64M # 单文件上传上限,配合 post_max_size 使用
post_max_size = 64M       # POST 数据总大小上限,必须 >= upload_max_filesize
max_execution_time = 300  # 脚本最大执行时间(秒),长任务如导入数据需调高
date.timezone = Asia/Shanghai  # 必须设置时区,否则 date() 函数返回 UTC 时间

4.3 环境验证与连通性测试脚本编写

光靠浏览器点点点不够,我们需要自动化脚本验证整个链路。以下是一个 Bash 脚本 lamp-check.sh ,它会检查每个环节的状态:

#!/bin/bash
echo "=== LAMP 环境健康检查 ==="

# 检查 Apache 状态
echo -n "Apache 服务状态: "
if systemctl is-active --quiet apache2; then
    echo "✅ 运行中"
else
    echo "❌ 已停止"
    exit 1
fi

# 检查 MySQL 状态
echo -n "MySQL 服务状态: "
if systemctl is-active --quiet mysql; then
    echo "✅ 运行中"
else
    echo "❌ 已停止"
    exit 1
fi

# 检查 PHP 解析
echo -n "PHP 解析能力: "
if curl -s http://localhost/info.php | grep -q "PHP Version"; then
    echo "✅ 正常"
else
    echo "❌ 失败"
    exit 1
fi

# 检查 MySQL 连接(用 PHP 脚本)
echo -n "PHP-MySQL 连接: "
if curl -s http://localhost/test_db.php | grep -q "Connected successfully"; then
    echo "✅ 成功"
else
    echo "❌ 失败"
    exit 1
fi

# 检查端口监听
echo -n "80 端口监听: "
if ss -tln | grep -q ":80"; then
    echo "✅ 已监听"
else
    echo "❌ 未监听"
    exit 1
fi

echo "=== 全部检查通过!LAMP 环境就绪 ==="

保存为 lamp-check.sh ,执行 chmod +x lamp-check.sh && sudo ./lamp-check.sh 。脚本会逐项输出 ✅ 或 ❌,任何一项失败都会 exit 1 并终止。这种“断言式”检查,比人工肉眼确认可靠十倍。

5. 常见问题与排查技巧实录

5.1 Apache 启动失败的五大高频原因与修复

Apache 启动失败是新手最常遇到的拦路虎。根据我处理过的上百个案例,以下是五个最高频原因及对应解法,按发生概率排序:

问题 1:端口被占用(占比 42%)
现象: sudo service apache2 start 后无响应, systemctl status apache2 显示 failed ,日志里有 Address already in use: AH00072: make_sock: could not bind to address [::]:80
原因:Nginx、Skype 或其他程序占用了 80 端口。
排查: sudo ss -tlnp | grep ':80' ,输出类似 LISTEN 0 128 *:80 *:* users:(("nginx",pid=1234,fd=6))
修复: sudo kill -9 1234 杀掉进程,或改 Apache 端口:编辑 /etc/apache2/ports.conf ,将 Listen 80 改为 Listen 8080 ,再 sudo service apache2 restart

问题 2:配置语法错误(占比 28%)
现象: sudo apache2ctl configtest 报错,如 AH00526: Syntax error on line 12 of /etc/apache2/sites-enabled/myapp.conf: Invalid command 'SSLEngine', perhaps misspelled...
原因:启用了 ssl 模块但没加载,或配置了不存在的指令。
排查: sudo a2enmod ssl 启用 SSL 模块,再 sudo apache2ctl configtest
修复:如果只是临时禁用 SSL,注释掉 myapp.conf SSLEngine on 及相关行;如果是正式环境,执行 sudo a2enmod ssl && sudo a2ensite default-ssl

问题 3:DocumentRoot 目录不存在(占比 15%)
现象:访问网站时显示 Forbidden You don't have permission to access / on this server.
原因: DocumentRoot 指向的目录 /var/www/myapp/public 不存在,或权限不足。
排查: ls -ld /var/www/myapp/public ,输出应为 drwxr-xr-x 2 root root 4096 ...
修复: sudo mkdir -p /var/www/myapp/public && sudo chown -R $USER:www-data /var/www/myapp

问题 4:.htaccess 权限被禁(占比 10%)
现象:重写规则(如 WordPress 的伪静态)不生效,URL 仍是 ?p=123 形式。
原因: <Directory> 块中 AllowOverride None 禁止了 .htaccess 覆盖。
排查: grep -r "AllowOverride" /etc/apache2/ ,找到对应配置文件。
修复:将 AllowOverride None 改为 AllowOverride All ,再 sudo service apache2 reload

问题 5:SELinux 或 AppArmor 干预(占比 5%)
现象:所有配置都对,但 Apache 就是打不开, /var/log/apache2/error.log 里有 Permission denied
原因:Ubuntu 14.04 默认启用 AppArmor,其配置文件 /etc/apparmor.d/usr.sbin.apache2 可能限制了访问。
排查: sudo aa-status | grep apache ,若显示 apache2 在 enforce 模式,则是它在作怪。
修复:临时禁用 sudo aa-disable /usr/sbin/apache2 ,或永久修改 AppArmor 配置,添加 /var/www/** rw, 规则。

5.2 PHP 无法连接 MySQL 的七种典型场景

PHP 连不上 MySQL,错误信息往往模糊,但根源就那么几个。我整理了一张速查表:

错误信息( mysqli_connect_error() 根本原因 快速验证命令 修复方案
Access denied for user 'xxx'@'localhost' 用户密码错误或权限不足 mysql -u xxx -p -h localhost GRANT 语句授权,或重置密码 SET PASSWORD FOR 'xxx'@'localhost' = PASSWORD('newpass');
Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' MySQL 服务未启动或 sock 文件路径错 sudo service mysql status ls -l /var/run/mysqld/mysqld.sock sudo service mysql start ;或在 PHP 中指定 mysqli_connect('localhost', 'u', 'p', 'db', 3306) 强制走 TCP
Client does not support authentication protocol requested by server MySQL 用新认证协议,PHP 扩展太老 mysql --version php -i | grep mysqlnd 升级 php5-mysqlnd ,或降级 MySQL 认证: ALTER USER 'xxx'@'localhost' IDENTIFIED WITH mysql_native_password BY 'pass';
No such file or directory mysqli 扩展未启用 php -m | grep mysqli sudo phpenmod mysqli (Ubuntu 14.04 中 phpenmod 是启用 PHP 扩展的命令)
Connection refused MySQL 绑定地址不是 127.0.0.1 sudo grep "bind-address" /etc/mysql/my.cnf bind-address = 127.0.0.1 取消注释, sudo service mysql restart
Too many connections 连接数超限 mysql -u root -p -e "SHOW STATUS LIKE 'Threads_connected';" 增加 max_connections = 200 my.cnf ,重启 MySQL
Unknown database 'xxx' 数据库不存在 mysql -u root -p -e "SHOW DATABASES;" CREATE DATABASE xxx CHARACTER SET utf8mb4;

实操心得:我习惯在 PHP 连接代码前加一行 error_log("Connecting to MySQL with host={$host}, user={$user}"); ,把连接参数写入 Apache 错误日志。这样当连接失败时,至少知道 PHP 传了什么参数给 MySQL,排除是代码拼写错误还是配置问题。

5.3 MySQL 表碎片处理与性能优化实战

标题里提到的“php mysql 某个表有碎片,一般怎么处理”,这确实是线上环境的高频痛点。碎片(Fragmentation)指 InnoDB 表的物理存储不连续,导致磁盘 I/O 增加、查询变慢。Ubuntu 14.04 的 MySQL 5.5 默认用 InnoDB,碎片产生于频繁的 DELETE UPDATE 操作。

如何检测碎片?
执行 SQL:

SELECT 
    table_schema AS 'Database',
    table_name AS 'Table',
    ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'Size (MB)',
    ROUND((data_free / 1024 / 1024), 2) AS 'Free (MB)',
    ROUND((data_free / (data_length + index_length)) * 100, 2) AS 'Fragmentation (%)'
FROM information_schema.TABLES 
WHERE table_schema NOT IN ('information_schema','mysql','performance_schema') 
    AND data_free > 0 
ORDER BY data_free DESC;

如果 Fragmentation (%) 超过 20%,或 Free (MB) 大于 10MB,就需要处理。

处理方案有三种,按风险排序:

  1. ** OPTIMIZE TABLE (推荐新手

更多推荐