MySQL忘记root密码重置(Linux环境)
MySQL root密码忘了进不去数据库?别慌,按下面步骤操作,5分钟就能重置。
1. 先停止MySQL服务
systemctl stop mysqld
CentOS 7 默认是这个命令,如果是 mariadb 就改成 systemctl stop mariadb。
2. 跳过权限验证启动MySQL
mysqld_safe --skip-grant-tables &
这一步会跳过密码验证,直接进入MySQL,不需要输密码。
3. 无密码登录MySQL
mysql -u root
现在直接就能进去了,不需要密码。
4. 重置root密码
USE mysql;
-- MySQL 5.7 及以上版本
UPDATE user SET authentication_string = PASSWORD('你的新密码') WHERE User = 'root';
-- 如果是 MySQL 5.6 及以下版本
UPDATE user SET password = PASSWORD('你的新密码') WHERE User = 'root';
FLUSH PRIVILEGES;
EXIT;
把 你的新密码 换成你想设置的新密码,注意密码强度。
5. 重启MySQL服务
systemctl start mysqld
6. 用新密码登录测试
mysql -u root -p
输入刚才设置的新密码,能登录就成功了。
常见坑
坑1:mysqld_safe 找不到
有些系统用的是 mysqld,试试:
mysqld --skip-grant-tables &
坑2:改完密码登录还是不行
一定要执行 FLUSH PRIVILEGES;,不然不生效。
坑3:密码太简单报错
MySQL 有密码强度要求,新密码最好是字母+数字+符号,8位以上。
坑4:MySQL 8.0 版本
MySQL 8.0 改密码的语法不一样:
ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码';
遇到问题加QQ23979811 协助处理
Redis 缓存雪崩解决
Redis 缓存雪崩解决
缓存雪崩是指大量缓存同时失效,请求全部打到数据库,数据库直接挂掉。
1. 什么是缓存雪崩
原理:
- 大量 key 同时过期
- 请求都查数据库
- 数据库压力瞬间暴增
- 数据库扛不住挂掉
和缓存穿透的区别:
- 穿透:查询不存在的数据
- 雪崩:大量缓存同时失效
2. 产生原因
1)缓存大面积过期:
- 大量数据设置了相同过期时间
- 到时间一起失效
2)Redis 宕机:
- Redis 服务挂了
- 所有请求都走数据库
3. 解决方案
方案一:过期时间加随机值
// 不要设置相同过期时间
$expire = 3600 + rand(0, 600);
$redis->setex("key", $expire, $value);
加随机值,让 key 分散过期,不会同时失效。
方案二:缓存永不过期
- 热点数据不设置过期时间
- 更新数据时主动更新缓存
- 异步更新缓存
方案三:Redis 高可用
- 搭建 Redis 集群
- 主从复制
- 哨兵模式
- 一台挂了还有其他的
方案四:熔断降级
- 请求量太大时直接返回默认值
- 不让请求打到数据库
- 保护数据库
4. 代码示例
function getData($id) {
global $redis, $db;
$key = "data:".$id;
$data = $redis->get($key);
if ($data) {
return json_decode($data, true);
}
// 查数据库
$data = $db->query("SELECT * FROM table WHERE id=$id");
// 缓存,过期时间加随机值
$expire = 3600 + rand(0, 600);
$redis->setex($key, $expire, json_encode($data));
return $data;
}
5. 最佳实践
- 过期时间加随机值
- 搭建 Redis 集群
- 热点数据永不过期
- 加熔断降级机制
- 数据库做限流
总结
缓存雪崩解决要点:
- 过期时间加随机值,不要同时失效
- Redis 做主从高可用
- 热点数据不设置过期
- 做好降级兜底
做高并发系统,缓存雪崩一定要防。
Nginx HTTPS 配置详解
Nginx HTTPS 配置详解
现在网站都要上 HTTPS,Nginx 配置 SSL 很简单。
1. 准备证书
免费证书可以用:
- Let's Encrypt
- 阿里云免费证书
- 腾讯云免费证书
下载后有两个文件:
- 证书文件:xxx.pem
- 私钥文件:xxx.key
2. 基础配置
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate /etc/nginx/ssl/xxx.pem;
ssl_certificate_key /etc/nginx/ssl/xxx.key;
location / {
root /www/wwwroot/html;
index index.html;
}
}
3. HTTP 跳转 HTTPS
# HTTP 重定向到 HTTPS
server {
listen 80;
server_name www.example.com;
return 301 https://$server_name$request_uri;
}
4. 优化配置
server {
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/nginx/ssl/xxx.pem;
ssl_certificate_key /etc/nginx/ssl/xxx.key;
# SSL 优化
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
root /www/wwwroot/html;
index index.html;
}
}
5. 验证配置
# 检查配置是否正确
nginx -t
# 重载配置
nginx -s reload
6. 常见问题
1)证书不生效:
- 检查证书路径对不对
- 检查证书文件名
- 重载 Nginx
2)浏览器提示不安全:
- 证书域名不对
- 证书过期了
- 缺少中间证书
3)HTTP 打不开:
- 检查 80 端口是否放行
- 检查防火墙
7. Let's Encrypt 免费证书
用 certbot 自动申请:
# 安装
yum install certbot python3-certbot-nginx
# 自动申请并配置
certbot --nginx -d www.example.com
# 自动续期
certbot renew
总结
Nginx HTTPS 配置要点:
- 443 端口监听 SSL
- 配置证书和私钥路径
- HTTP 跳转 HTTPS
- 开启 http2 提升性能
- 定期续期证书
现在做网站,HTTPS 是标配。
MySQL 备份恢复实战
MySQL 备份恢复实战
数据库备份是运维的基本功,数据丢了就找不回来了。
1. 备份方式
1)物理备份:
- 直接拷贝数据文件
- 速度快,适合大数据库
2)逻辑备份:
- 导出 SQL 语句
- 体积小,跨平台
2. mysqldump 备份
备份整个数据库:
mysqldump -u root -p 数据库名 > backup.sql
备份单个表:
mysqldump -u root -p 数据库名 表名 > table.sql
备份所有数据库:
mysqldump -u root -p --all-databases > all.sql
3. 恢复数据
mysql -u root -p 数据库名 < backup.sql
注意: 恢复前要先创建数据库。
4. 压缩备份
# 备份并压缩
mysqldump -u root -p 数据库名 | gzip > backup.sql.gz
# 恢复压缩文件
gunzip < backup.sql.gz | mysql -u root -p 数据库名
5. 定时备份脚本
#!/bin/bash
# 数据库备份脚本
DB_USER="root"
DB_PASS="123456"
DB_NAME="test"
BACKUP_DIR="/data/backup"
DATE=$(date +%Y%m%d)
# 创建备份目录
mkdir -p $BACKUP_DIR
# 备份
mysqldump -u$DB_USER -p$DB_PASS $DB_NAME | gzip > $BACKUP_DIR/$DB_NAME_$DATE.sql.gz
# 删除7天前的备份
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete
加 crontab 定时执行:
# 每天凌晨3点备份
0 3 * * * /bin/bash /root/backup.sh
6. 常见问题
1)备份太大:
- 用 --quick 参数
- 压缩备份
2)备份报错:
- 检查用户名密码
- 检查权限
3)恢复失败:
- 检查 SQL 文件是否完整
- 检查数据库字符集
7. 最佳实践
- 每天定时备份
- 备份文件存到异地
- 定期测试恢复
- 重要数据做双备份
总结
MySQL 备份恢复要点:
- mysqldump 导出 SQL
- gzip 压缩减小体积
- crontab 定时执行
- 定期测试恢复
做运维,数据备份永远是第一位的。
MySQL索引失效常见场景,避免踩坑
明明建了索引,查询还是很慢?十有八九是索引失效了。MySQL 索引失效是开发中最常见的坑,下面整理了最容易导致索引失效的场景,写SQL的时候一定要避开。
1. 对索引字段做函数操作
-- 索引失效
SELECT * FROM user WHERE DATE(create_time) = '2026-10-01';
-- 正确写法,走索引
SELECT * FROM user WHERE create_time >= '2026-10-01' AND create_time < '2026-10-02';
对索引字段用了 DATE()、YEAR()、SUBSTRING() 等函数,索引就失效了。
2. 隐式类型转换
-- 假设 phone 是 varchar 类型
-- 索引失效(数字和字符串比较,MySQL自动转换)
SELECT * FROM user WHERE phone = 13800138000;
-- 正确写法,走索引
SELECT * FROM user WHERE phone = '13800138000';
字符串类型的字段,查询条件一定要加引号。
3. 联合索引不满足最左匹配原则
-- 联合索引:(a, b, c)
-- 走索引
SELECT * FROM t WHERE a = 1 AND b = 2;
-- 不走索引(跳过了a)
SELECT * FROM t WHERE b = 2 AND c = 3;
-- 走部分索引(a走,b和c不走)
SELECT * FROM t WHERE a = 1 AND c = 3;
联合索引必须从最左边的列开始用,不能跳过。
4. 使用了 != 或 <>
-- 索引大概率失效
SELECT * FROM user WHERE status != 1;
-- 可以改成范围查询
SELECT * FROM user WHERE status < 1 OR status > 1;
不等于操作通常会导致全表扫描,因为要返回的结果太多了。
5. LIKE 以 % 开头
-- 索引失效
SELECT * FROM user WHERE name LIKE '%张';
-- 走索引
SELECT * FROM user WHERE name LIKE '张%';
模糊查询左边带 %,索引就用不上了。
6. OR 连接的条件有一个没索引
-- name 有索引,age 没索引 → 整个SQL不走索引
SELECT * FROM user WHERE name = '张三' OR age = 20;
OR 两边只要有一个字段没索引,整个查询就不走索引。
7. 用 explain 验证索引是否生效
EXPLAIN SELECT * FROM user WHERE name = '张三';
看 key 列:
- 显示索引名 → 走索引了
- 显示 NULL → 没走索引
看 type 列:
ref、eq_ref→ 正常ALL→ 全表扫描,索引失效了
常见坑总结
- 不要在索引字段上做运算:
WHERE age + 1 = 20→ 改成WHERE age = 19 - 不要用 NOT IN / NOT EXISTS:大数据量下很慢
- **不要用 SELECT ***:只查需要的字段,覆盖索引更快
- 数据量小的表不要建索引:全表扫描可能比索引还快
遇到问题加QQ23979811 协助处理
PHP 会话管理详解
PHP 会话管理详解
Session 是 PHP 做用户登录状态管理的核心功能,搞懂它很重要。
1. 什么是 Session
- Cookie: 存在客户端(浏览器)
- Session: 存在服务器端
- 通过 SessionID 关联用户
2. 基础用法
// 开启会话(必须放在最前面)
session_start();
// 存数据
$_SESSION['user_id'] = 1;
$_SESSION['username'] = "test";
// 取数据
echo $_SESSION['username'];
// 判断是否登录
if (isset($_SESSION['user_id'])) {
echo "已登录";
} else {
echo "未登录";
}
// 销毁会话
session_destroy();
3. 登录验证示例
// 登录页
session_start();
if ($_POST['password'] == "123456") {
$_SESSION['user'] = $_POST['username'];
header("Location: dashboard.php");
}
// 验证页
session_start();
if (!isset($_SESSION['user'])) {
header("Location: login.php");
exit;
}
4. 常用配置
php.ini 里的配置:
; 会话有效期(分钟)
session.gc_maxlifetime = 1440
; Cookie 有效期(秒)
session.cookie_lifetime = 0
; 是否只允许 HTTP 访问 Cookie
session.cookie_httponly = On
5. 常见问题
1)session 丢失:
- 检查是否有输出(session_start 前不能有空格)
- 检查 Cookie 是否被禁用
2)多服务器共享 Session:
- 存在数据库里
- 存在 Redis 里
- 不要用文件 Session
3)安全问题:
- 登录后重新生成 SessionID
session_regenerate_id(); - 防止 Session 固定攻击
6. Cookie vs Session
| 对比 | Cookie | Session |
|---|---|---|
| 存储位置 | 客户端 | 服务器端 |
| 大小限制 | 4KB | 无限制 |
| 安全性 | 低 | 高 |
| 生命周期 | 可长期保存 | 浏览器关闭就没了 |
7. 登出销毁
session_start();
// 清除所有 session 变量
$_SESSION = array();
// 清除 session cookie
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time()-42000, '/');
}
// 销毁 session
session_destroy();
总结
PHP 会话管理要点:
- session_start() 必须在最前面
- 登录验证用 $_SESSION 判断
- 生产环境用 Redis 存 Session
- 登录后重新生成 SessionID 防攻击
做用户系统,Session 是必须掌握的。
Docker 镜像加速配置
Docker 镜像加速配置
国内拉取 Docker 镜像很慢,配置镜像加速器很有必要。
1. 为什么要加速
Docker Hub 在国外,国内拉镜像经常超时,配置加速器能快很多。
2. 配置方法
Linux(CentOS/Ubuntu):
编辑配置文件:
mkdir -p /etc/docker
vi /etc/docker/daemon.json
写入内容:
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
]
}
重启生效:
systemctl daemon-reload
systemctl restart docker
3. 常用加速器
# 中科大
https://docker.mirrors.ustc.edu.cn
# 网易
https://hub-mirror.c.163.com
# 阿里(需要自己的地址)
https://xxxx.mirror.aliyuncs.com
4. 验证是否生效
docker info
看到 Registry Mirrors 下面有地址就说明成功了。
5. 常见问题
1)拉取还是很慢:
- 换个加速器试试
- 网络问题
2)配置不生效:
- 检查 json 格式对不对
- 重启 docker
3)企业内网:
- 可以自己搭建私有镜像仓库
- 或者用阿里云私有镜像服务
6. 常用镜像源
# 官方
docker pull nginx
docker pull mysql
docker pull redis
# 指定版本
docker pull nginx:1.25
docker pull mysql:8.0
总结
Docker 镜像加速核心:
- 配置 daemon.json
- 用国内加速器地址
- 重启 docker 生效
- docker info 验证
做运维开发,Docker 镜像加速是第一步。
Nginx 反向代理配置
Nginx 反向代理配置
反向代理是 Nginx 最常用的功能,前端请求 Nginx,Nginx 再转发给后端服务。
1. 什么是反向代理
- 正向代理: 代理客户端,比如翻墙
- 反向代理: 代理服务器,客户端不知道真正的后端
2. 基础配置
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
意思是:访问 80 端口的请求,转发到 8080 端口的服务。
3. 常用配置说明
location / {
# 转发地址
proxy_pass http://127.0.0.1:8080;
# 传递真实IP
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 超时设置
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
}
4. 按路径转发不同服务
# 访问 /api/ 转发到后端API服务
location /api/ {
proxy_pass http://127.0.0.1:8080;
}
# 访问 /admin/ 转发到后台管理服务
location /admin/ {
proxy_pass http://127.0.0.1:8081;
}
# 其他访问静态文件
location / {
root /www/wwwroot/html;
}
5. 常见应用场景
1)前后端分离:
- 前端静态文件 Nginx 直接处理
- API 请求转发到后端服务
2)Node.js/Python 后端:
- Nginx 处理 80/443 端口
- 转发到 Node.js 的 3000 端口
3)负载均衡:
- 配合 upstream 转发到多台服务器
6. 常见问题
1)后端获取不到真实 IP:
- 要配置 X-Real-IP 和 X-Forwarded-For
- 后端从请求头里取
2)WebSocket 不支持:
- 需要加特殊头
proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";
3)上传文件大小限制:
client_max_body_size 100m;
总结
Nginx 反向代理核心:
- proxy_pass 转发地址
- proxy_set_header 传递真实信息
- 按 location 转发不同服务
- 注意超时和文件大小限制
做 Web 开发,Nginx 反向代理一定要会配置。
PHP 错误处理机制
PHP 错误处理机制
做开发离不开错误处理,搞懂 PHP 的错误机制才能写出健壮的代码。
1. 错误级别
PHP 错误分几个级别:
1)致命错误(E_ERROR)
- 脚本停止执行
- 比如调用不存在的类、方法
2)警告(E_WARNING)
- 脚本继续执行
- 比如包含不存在的文件
3)注意(E_NOTICE)
- 不影响执行
- 比如使用未定义的变量
2. 错误报告配置
php.ini 里的配置:
; 开发环境:显示所有错误
display_errors = On
error_reporting = E_ALL
; 生产环境:不显示错误,记录日志
display_errors = Off
log_errors = On
error_reporting = E_ALL & ~E_NOTICE
3. 自定义错误处理
// 设置错误报告级别
error_reporting(E_ALL);
// 自定义错误处理函数
set_error_handler(function($errno, $errstr, $errfile, $errline) {
echo "错误:[$errno] $errstr 在 $errfile 第 $errline 行";
return true; // 阻止默认错误处理
});
// 测试
echo $undefined_var;
4. 异常处理
try {
if (!file_exists("test.txt")) {
throw new Exception("文件不存在");
}
$file = fopen("test.txt", "r");
} catch (Exception $e) {
echo "错误:" . $e->getMessage();
} finally {
echo "\n执行完成";
}
5. 错误日志
// 记录错误日志
error_log("这是一条错误日志", 3, "/tmp/php_errors.log");
// 手动触发错误
trigger_error("自定义错误", E_USER_WARNING);
6. 开发 vs 生产
开发环境:
- 显示错误
- 所有错误都报告
- 方便调试
生产环境:
- 不显示错误(防止泄露敏感信息)
- 记录错误日志
- 只报告致命错误和警告
7. 常用技巧
1)错误抑制符 @
// 不推荐,会影响性能
@include("config.php");
2)die/exit
// 简单粗暴
if (! $db) {
die("数据库连接失败");
}
3)抛异常
- 推荐用异常处理
- 代码更清晰,容易维护
总结
PHP 错误处理要点:
- 开发环境显示错误,生产环境记录日志
- 用 try-catch 处理异常
- 错误日志要记录,方便排查
- 不要用 @ 错误抑制符
写代码多做错误处理,线上少踩坑。
MySQL 索引优化技巧
MySQL 索引优化技巧
索引是数据库性能的关键,建对了快10倍,建错了反而更慢。这篇讲实战技巧。
1. 索引类型
普通索引: 最基本的索引,没有限制
唯一索引: 列值不能重复
主键索引: 特殊的唯一索引,一张表只能一个
联合索引: 多个字段组成的索引
2. 建索引原则
1)where 条件经常用的字段建索引
-- 常用查询
SELECT * FROM users WHERE email = 'xxx@qq.com';
-- 给 email 建索引
CREATE INDEX idx_email ON users(email);
2)联合索引最左前缀原则
-- 建联合索引 (name, age)
CREATE INDEX idx_name_age ON users(name, age);
-- 能用索引
WHERE name = '张三'
WHERE name = '张三' AND age = 25
-- 不能用索引(跳过了name)
WHERE age = 25
3)避免建太多索引
- 索引不是越多越好
- 每个索引都占空间,影响写入速度
- 一张表索引不要超过5个
3. 索引失效场景
1)不要在索引字段上做运算
-- 坏(索引失效)
SELECT * FROM users WHERE age + 1 = 26;
-- 好
SELECT * FROM users WHERE age = 25;
2)不要用函数
-- 坏
SELECT * FROM users WHERE DATE(create_time) = '2026-10-01';
-- 好
SELECT * FROM users WHERE create_time >= '2026-10-01' AND create_time < '2026-10-02';
3)like 前导模糊
-- 坏(用不到索引)
SELECT * FROM users WHERE name LIKE '%张%';
-- 好
SELECT * FROM users WHERE name LIKE '张%';
**4)避免 select ***
-- 坏(查所有字段,可能回表)
SELECT * FROM users WHERE name = '张三';
-- 好(覆盖索引)
SELECT id, name FROM users WHERE name = '张三';
4. 查看索引使用
-- 查看表索引
SHOW INDEX FROM users;
-- 用 explain 分析
EXPLAIN SELECT * FROM users WHERE name = '张三';
重点看:
- type:const/ref 好,ALL 全表扫描坏
- key:实际用到的索引
- rows:扫描行数
5. 总结
索引优化记住:
- where 字段建索引
- 联合索引最左前缀
- 别在索引字段上做运算
- 避免前导模糊查询
- 用 explain 检查
索引是数据库优化的第一步,做好了性能提升很大。
Linux 查看系统资源
Linux 查看系统资源
服务器卡了慢了,得知道哪个进程占资源多。整理常用的查看命令。
1. 查看内存使用
# 查看内存使用情况
free -h
# 持续查看
free -h 1
输出说明:
- total:总内存
- used:已使用
- free:空闲
- buff/cache:缓存
- available:可用
2. 查看 CPU 负载
# 查看 CPU 信息
top
# 按 q 退出
top 常用快捷键:
- P:按 CPU 排序
- M:按内存排序
- 1:查看每个核心
3. 查看磁盘使用
# 查看磁盘空间
df -h
# 查看目录大小
du -sh /www/
# 查看当前目录下每个文件大小
du -sh *
4. 查看进程
# 查看所有进程
ps aux
# 查看 PHP 进程
ps aux | grep php
# 查看端口占用
netstat -tlnp
5. 查看系统信息
# 查看系统版本
cat /etc/os-release
# 查看内核版本
uname -a
# 查看 CPU 核心数
nproc
# 查看内存大小
cat /proc/meminfo | head -5
6. 查找大文件
# 查找大于100M的文件
find / -type f -size +100M
# 查找最近7天修改的文件
find /www -type f -mtime -7
7. 常用场景
网站卡了:
- top 看 CPU 和内存
- df -h 看磁盘满没满
- 看日志
内存不够用:
- free -h 看内存
- top 按 M 排序找占内存大的进程
总结
常用命令记这几个:
- 内存:free -h
- CPU:top
- 磁盘:df -h / du -sh
- 进程:ps aux
- 端口:netstat
做运维这些命令天天用,必须熟。
Redis 缓存穿透解决
Redis 缓存穿透解决
缓存穿透是常见问题,大量请求查询不存在的数据,直接打到数据库。这篇讲怎么解决。
1. 什么是缓存穿透
用户查询一个不存在的数据,Redis 里没有,每次都去查数据库。恶意攻击的话,数据库扛不住。
2. 解决方案一:缓存空值
查询数据库也没结果,就缓存一个空值,设置短过期时间。
function getUser($id) {
$key = "user:$id";
// 先查缓存
$user = $redis->get($key);
if ($user !== false) {
return json_decode($user, true);
}
// 查数据库
$user = $db->query("SELECT * FROM users WHERE id = $id");
if ($user) {
// 缓存有数据的结果
$redis->setex($key, 3600, json_encode($user));
} else {
// 缓存空值,短时间过期
$redis->setex($key, 60, '');
}
return $user;
}
优点: 简单粗暴
缺点: 浪费内存,可能导致不一致
3. 解决方案二:布隆过滤器
在缓存前面加一层布隆过滤器,不存在的 key 直接拦截。
// 使用 RedisBloom 模块
// 添加到布隆过滤器
$redis->rawCommand("BF.ADD", "user_filter", "1001");
// 查询是否存在
$exists = $redis->rawCommand("BF.EXISTS", "user_filter", "1001");
if (!$exists) {
return null; // 直接返回,不查数据库
}
原理: 用二进制数组和多个哈希函数,判断一个 key 一定不存在或者可能存在。
优点: 内存占用小,效率高
缺点: 有一定误判率,不支持删除
4. 两种方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 缓存空值 | 简单 | 浪费内存 | 数据量小 |
| 布隆过滤器 | 高效省内存 | 有误差 | 数据量大 |
5. 其他注意事项
- 接口增加参数校验,不合法的 id 直接拒绝
- 增加接口访问限流,防止恶意攻击
- 黑名单机制,频繁查不存在的 IP 封掉
总结
缓存穿透解决思路:
- 缓存空值: 简单,适合小数据量
- 布隆过滤器: 高效,适合大数据量
- 加限流和校验: 从入口防护
生产环境推荐布隆过滤器 + 缓存空值组合使用。
PHP 数组常用函数
PHP 数组常用函数
数组是 PHP 最常用的数据结构,数组函数用熟了开发效率翻倍。整理一份常用清单。
1. 数组创建和遍历
// 索引数组
$arr1 = [1, 2, 3];
// 关联数组
$arr2 = ['name' => '张三', 'age' => 25];
// 遍历
foreach ($arr2 as $key => $value) {
echo "$key: $value\n";
}
2. 数组增删改查
// 追加元素
$arr[] = 4;
array_push($arr, 5, 6);
// 头部插入
array_unshift($arr, 0);
// 删除末尾元素
array_pop($arr);
// 删除头部元素
array_shift($arr);
// 删除指定元素
unset($arr[0]);
3. 数组合并和拆分
// 合并数组
$arr1 = [1, 2];
$arr2 = [3, 4];
$merged = array_merge($arr1, $arr2); // [1,2,3,4]
// 拆分
$slice = array_slice($arr, 0, 2);
4. 查找和过滤
// 判断键是否存在
array_key_exists('name', $arr);
// 判断值是否存在
in_array('张三', $arr);
// 查找值对应的键
array_search('张三', $arr);
// 数组过滤
$filtered = array_filter([1, 2, 3, 4], function($n) {
return $n > 2;
}); // [3,4]
5. 排序
sort($arr); // 升序
rsort($arr); // 降序
asort($arr); // 按值升序,保留键
ksort($arr); // 按键升序
6. 常用操作
// 数组长度
count($arr);
// 去重
array_unique($arr);
// 反转
array_reverse($arr);
// 随机取一个
array_rand($arr);
7. 实际应用
统计数组中出现次数:
$counts = array_count_values(['a', 'b', 'a', 'c', 'a']);
// ['a' => 3, 'b' => 1, 'c' => 1]
二维数组按某列排序:
$users = [
['name' => '张三', 'age' => 25],
['name' => '李四', 'age' => 20]
];
array_multisort(array_column($users, 'age'), SORT_ASC, $users);
总结
PHP 数组函数非常多,日常开发记住这些常用的就够了:
- 增删改查:array_push / array_pop / unset
- 合并拆分:array_merge / array_slice
- 查找过滤:in_array / array_filter
- 排序:sort / asort / ksort
数组是 PHP 的灵魂,多用多练就熟了。
MySQL慢查询开启,定位慢SQL语句方法
前言
网站越来越慢,不知道哪条 SQL 拖后腿。MySQL 自带慢查询日志功能,能把执行慢的 SQL 记录下来,方便优化。
什么是慢查询
就是执行时间超过某个阈值的 SQL 语句。默认阈值是 10 秒,太久了,一般改成 1 秒或者 2 秒。
临时开启(重启不丢)
登录 MySQL:
mysql -u root -p
开启慢查询:
-- 开启慢查询
SET GLOBAL slow_query_log = ON;
-- 设置超过1秒就记录
SET GLOBAL long_query_time = 1;
-- 查看慢查询日志位置
SHOW VARIABLES LIKE 'slow_query_log_file';
永久开启(改配置文件)
改 my.cnf:
vi /etc/my.cnf
加上:
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
重启 MySQL:
systemctl restart mysqld
慢查询日志长什么样
# Time: 2026-10-07T07:00:00.000000Z
# User@Host: root[root] @ localhost [] Id: 1234
# Query_time: 5.234567 Lock_time: 0.000123 Rows_sent: 1000 Rows_examined: 1000000
SET timestamp=1728270000;
SELECT * FROM users WHERE name LIKE '%张%';
重点看:
Query_time:执行时间Rows_examined:扫描了多少行- 最后一行就是慢 SQL 语句
分析慢查询日志
用 mysqldumpslow 工具:
# 找出最慢的10条SQL
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# 找出返回数据最多的10条
mysqldumpslow -s r -t 10 /var/log/mysql/slow.log
参数说明:
| 参数 | 说明 |
|---|---|
-s t |
按时间排序 |
-s r |
按返回行数排序 |
-t 10 |
显示前10条 |
常见坑
坑1:没开启慢查询
不知道怎么开,或者开了忘了。
解决: 用 SHOW VARIABLES LIKE 'slow_query_log'; 确认一下。
坑2:阈值设太大
默认是 10 秒,10 秒以下的慢 SQL 都没记录到。
解决: 改成 1 秒,够用了。
坑3:日志太大占磁盘
慢查询日志一直写,磁盘满了。
解决: 配 logrotate 定期切割。
坑4:没记录没走索引的SQL
有些 SQL 虽然快,但没走索引,数据量一大就慢了。
解决: 加上这个参数,没走索引的也记录:
log_queries_not_using_indexes = 1
总结
三步开启慢查询:
- 开
slow_query_log - 设
long_query_time - 用
mysqldumpslow分析
记住: 慢查询日志是优化数据库的第一步,先开了再说。
遇到问题加QQ23979811 协助处理
Nginx报"no input file specified"错误解决
前言
访问 PHP 页面,直接报 "No input file specified",意思是 PHP-FPM 找不到要执行的 PHP 文件。这是 Nginx 配置里 root 路径不对导致的。
报错长什么样
页面上直接显示:
No input file specified.
没有其他信息,就这么一句话。
常见原因
原因1:root 路径不对
PHP location 里的 root 和 server 里的 root 不一致。
错误示例:
server {
root /var/www/html;
location ~ \.php$ {
fastcgi_param SCRIPT_FILENAME /var/www/php$fastcgi_script_name;
}
}
这样 PHP 找文件的时候就找错目录了。
正确做法:
server {
root /var/www/html;
location ~ \.php$ {
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
用 $document_root 自动取 server 里的 root。
原因2:文件真的不存在
URL 对应的 PHP 文件根本就没有。
检查:
ls /var/www/html/test.php
原因3:文件权限不对
文件存在,但 Nginx/PHP-FPM 读不了。
解决: 确保文件权限是 644,目录是 755,属主是 www。
完整正确的配置
server {
listen 80;
server_name www.example.com;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
排查步骤
第一步:确认文件在不在
ls /var/www/html/test.php
第二步:看 Nginx 错误日志
tail -f /var/log/nginx/error.log
访问一下报错的页面,看日志里写的路径是什么。
第三步:确认 root 路径
grep root /etc/nginx/conf.d/你的站点.conf
常见坑
坑1:fastcgi_param 写错了
写成了 $request_filename 或者其他变量。
解决: 一定要用 $document_root$fastcgi_script_name。
坑2:伪静态和 PHP 冲突
配了伪静态,导致 PHP 请求被重写到别的地方了。
解决: 确保伪静态规则在 PHP location 之前,而且 PHP location 能正常匹配到 .php 请求。
坑3:多站点 root 不一致
A 站点的 PHP 请求跑到 B 站点去了。
原因: 多个站点的 root 配置不对。
解决: 每个站点的 root 都写清楚,别共用。
坑4:PHP-FPM 配置不对
PHP-FPM 的 www.conf 里的 listen 路径和 Nginx 配置的不一致。
解决: 确认两边的 socket 路径一样。
总结
90% 的情况都是 root 路径不对。
检查这三点:
- root 路径写对了没
- 文件真的存在不
- 文件权限对不对
记住: PHP 配置里一定要用 $document_root$fastcgi_script_name,别写死路径。
遇到问题加QQ23979811 协助处理
Nginx配置访问密码,限制后台访问
前言
网站后台地址公开,谁都能访问,容易被别人扫到然后暴力破解。给后台加一层 Nginx 访问密码,多一道防护。
什么是访问密码
就是访问某个目录前,浏览器先弹一个登录框,输入用户名密码才能进去。和网站本身的登录是两回事。
第一步:生成密码文件
用 htpasswd 命令生成密码文件:
htpasswd -c /etc/nginx/.htpasswd admin
第一次加 -c 创建文件,输入两次密码。
不加 -c 是追加新用户:
htpasswd /etc/nginx/.htpasswd user2
如果没有 htpasswd 命令,先装:
yum install -y httpd-tools
第二步:Nginx 配置
给后台目录加密码:
location /admin/ {
auth_basic "Admin Login";
auth_basic_user_file /etc/nginx/.htpasswd;
}
完整配置示例
server {
listen 80;
server_name www.example.com;
root /var/www/html;
index index.php index.html;
# 后台加密码
location /admin/ {
auth_basic "Admin Login";
auth_basic_user_file /etc/nginx/.htpasswd;
# 后台本身的 PHP 配置
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
# 前台正常访问
location / {
try_files $uri $uri/ =404;
}
}
重载生效
nginx -t
nginx -s reload
常见坑
坑1:.htpasswd 文件权限不对
Nginx 读不到密码文件。
解决: 确保文件权限是 644,属主是 www:
chown www:www /etc/nginx/.htpasswd
chmod 644 /etc/nginx/.htpasswd
坑2:PHP 文件不弹密码
访问 /admin/index.php 直接就进了,没弹密码框。
原因: PHP 的 location 把请求拦截了,没走 auth_basic。
解决: 把 auth_basic 配置放到 PHP 的 location 里,或者放到 server 级别。
坑3:忘记密码
自己把密码忘了。
解决: 重新生成:
htpasswd -b /etc/nginx/.htpasswd admin 新密码
坑4:密码文件放网站目录里
把 .htpasswd 放在了网站根目录下,别人能直接下载。
解决: 密码文件放在网站目录外面,比如 /etc/nginx/ 下面。
总结
两步搞定:
htpasswd生成密码文件- Nginx 加
auth_basic配置
记住: 密码文件别放网站目录里,放在外面安全。
遇到问题加QQ23979811 协助处理
Nginx日志切割,按天切割nginx日志
前言
Nginx 的访问日志一直写在一个文件里,越写越大,几百GB都有可能。磁盘满了网站就挂了。本文讲一下怎么用 logrotate 自动按天切割 Nginx 日志。
为什么要切割日志
- 日志太大占磁盘
- 分析大文件慢
- 出问题查日志不方便
方法一:用系统自带的 logrotate
CentOS 自带 logrotate,直接配就行。
第一步:写配置文件
vi /etc/logrotate.d/nginx
内容:
/var/log/nginx/*.log {
daily
missingok
rotate 30
compress
notifempty
create 0644 www www
postrotate
/bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
endscript
}
参数说明:
| 参数 | 说明 |
|---|---|
daily |
按天切割 |
rotate 30 |
保留30天的日志 |
compress |
压缩旧日志 |
notifempty |
空文件不切割 |
missingok |
日志文件不存在不报错 |
第二步:测试一下
logrotate -d /etc/logrotate.d/nginx
-d 是调试模式,不会真执行,看一下有没有报错。
第三步:立即执行一次
logrotate -f /etc/logrotate.d/nginx
方法二:自己写脚本
不想用 logrotate,自己写个 shell 脚本:
vi /root/nginx_log.sh
内容:
#!/bin/bash
LOG_PATH=/var/log/nginx
YESTERDAY=$(date -d "yesterday" +%Y%m%d)
cd $LOG_PATH
mv access.log access_$YESTERDAY.log
mv error.log error_$YESTERDAY.log
nginx -s reopen
加执行权限:
chmod +x /root/nginx_log.sh
加到 crontab,每天凌晨执行:
crontab -e
加一行:
0 0 * * * /root/nginx_log.sh
常见坑
坑1:切割后日志不写了
把日志文件改名了,Nginx 还往旧文件写。
原因: Nginx 打开的是文件句柄,改名后它不知道。
解决: 切割后要通知 Nginx 重新打开日志文件:
nginx -s reopen
坑2:旧日志没删,磁盘还是满的
配了切割,但旧日志还留着。
原因: 没配 rotate 参数。
解决: 加上 rotate 30,自动只保留30天。
坑3:压缩太占CPU
所有日志都压缩,CPU 飙高。
解决: 错峰压缩,比如凌晨3点再压缩,或者用 delaycompress 延迟一天再压缩。
坑4:日志权限不对
切割后新日志文件属主不对,Nginx 写不了。
解决: logrotate 配置里加 create 0644 www www。
总结
推荐用 logrotate,系统自带,不用自己维护脚本。
核心配置就这些:
/var/log/nginx/*.log {
daily
rotate 30
compress
postrotate
/bin/kill -USR1 `cat /run/nginx.pid`
endscript
}
记住: 切割完一定要通知 Nginx 重新打开日志文件,不然新日志不写。
遇到问题加QQ23979811 协助处理
Nginx配置websocket反向代理
前言
做实时聊天、推送、在线游戏,需要用 WebSocket。Nginx 当反向代理的时候,默认不支持 WebSocket 协议,需要额外配置。本文讲一下怎么配。
WebSocket 是什么
WebSocket 是一种全双工通信协议,浏览器和服务器建立连接后,可以一直保持连接,互相实时推送消息。
为什么需要特殊配置
普通 HTTP 请求是短连接,请求完就断了。WebSocket 是长连接,而且用的是 Upgrade 协议头。Nginx 默认的反向代理不支持这种升级,需要加特殊配置。
基础配置
location /ws/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
关键就是这三行:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
完整配置示例
server {
listen 80;
server_name www.example.com;
root /var/www/html;
index index.html;
# WebSocket 代理
location /ws/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 300s;
}
# 普通 HTTP 代理
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
超时配置
WebSocket 是长连接,默认超时时间短了会自动断开。
proxy_read_timeout 300s;
proxy_send_timeout 300s;
意思是:5分钟没有数据传输才断开。
常见坑
坑1:WebSocket 连接失败
前端连 WebSocket 一直失败。
原因: Nginx 没配 Upgrade 和 Connection 头。
解决: 加上那三行关键配置。
坑2:连接几秒就自动断了
连上了,过一会儿就断了。
原因: Nginx 默认超时时间太短(60秒)。
解决: 调大 proxy_read_timeout。
坑3:HTTPS 下 WebSocket 连不上
HTTP 下正常,HTTPS 下连不上。
原因: 前端用了 ws://,但网站是 https 的,浏览器不让。
解决: 前端用 wss://(加密的 WebSocket)。
坑4:连接数太多被拒
很多人同时在线,新的连接被拒了。
原因: Nginx 或后端服务器连接数限制。
解决: 调大 Nginx 和后端的连接数限制。
总结
WebSocket 代理就三行核心配置:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
记住: WebSocket 是长连接,记得调大超时时间,别让它自动断了。
遇到问题加QQ23979811 协助处理
Nginx防盗链配置,防止图片被别人盗用
前言
网站的图片被别人盗链了,他的网站直接用你的图片地址,流量全从你服务器走,白白消耗带宽。Nginx 可以配置防盗链,防止这种情况。
什么是盗链
就是别人在自己网站里直接引用你服务器上的图片。用户看他的网站,实际图片是从你服务器加载的。
防盗链原理
Nginx 检查请求头里的 Referer 字段,看是从哪个页面过来的。如果不是自己网站的 Referer,就拒绝或者返回一张提示图。
基础配置
location ~* \.(jpg|png|gif|jpeg)$ {
valid_referers none blocked www.example.com example.com;
if ($invalid_referer) {
return 403;
}
}
解释:
valid_referers:允许哪些来源访问none:允许直接输入地址访问blocked:允许伪装的 Refererwww.example.com:自己的域名
配置示例
server {
listen 80;
server_name www.example.com;
root /var/www/html;
index index.html;
# 防盗链
location ~* \.(jpg|png|gif|jpeg|webp)$ {
valid_referers none blocked www.example.com example.com *.example.com;
if ($invalid_referer) {
return 403;
}
}
}
替换成提示图
不想直接 403,可以返回一张"禁止盗链"的提示图:
location ~* \.(jpg|png|gif|jpeg)$ {
valid_referers none blocked www.example.com example.com;
if ($invalid_referer) {
rewrite ^/.*\.(jpg|png|gif)$ /no-hotlink.png last;
}
}
这样别人盗链就显示一张提示图。
常见坑
坑1:自己网站图片也加载不出来
配了防盗链,自己网站的图片也打不开了。
原因: valid_referers 里没写全自己的域名。
解决: 把 http 和 https 的域名都写上:
valid_referers none blocked www.example.com example.com;
坑2:搜索引擎图片也被挡了
百度图片搜不到你的图片了。
原因: 防盗链把搜索引擎也挡了。
解决: 加上搜索引擎的 Referer:
valid_referers none blocked www.example.com example.com *.baidu.com *.google.com;
坑3:CDN 后面防盗链不生效
用了 CDN,防盗链配置不生效。
原因: 请求经过 CDN,Referer 是 CDN 的。
解决: CDN 那边配置防盗链,或者把 CDN 的 Referer 加到白名单。
坑4:微信里打开图片不显示
在微信里打开自己的文章,图片显示不出来。
原因: 微信的 Referer 被挡了。
解决: 加上微信的 Referer:
valid_referers none blocked www.example.com example.com *.weixin.qq.com;
总结
防盗链就三行配置:
valid_referers none blocked www.example.com example.com;
if ($invalid_referer) {
return 403;
}
记住: 配完防盗链,先测试一下自己网站图片能不能正常打开。
遇到问题加QQ23979811 协助处理
Nginx安装后找不到配置文件,编译安装路径
前言
编译安装 Nginx 后,想用 nginx 命令提示 command not found,找配置文件也找不到。这是因为编译安装的 Nginx 不在系统默认 PATH 里,路径和包管理器安装的不一样。
编译安装的默认路径
一般编译安装 Nginx,默认装在:
/usr/local/nginx/
目录结构:
/usr/local/nginx/
├── sbin/
│ └── nginx # 主程序
├── conf/
│ ├── nginx.conf # 主配置文件
│ ├── mime.types
│ └── fastcgi_params
├── html/ # 默认网站目录
└── logs/
├── access.log
└── error.log
怎么确认安装路径
方法一:nginx -V
/usr/local/nginx/sbin/nginx -V
输出里 --prefix= 后面就是安装路径。
方法二:ps 命令看进程
ps aux | grep nginx
进程路径就是 Nginx 在哪。
把 nginx 加到 PATH
不想每次都输完整路径,加个软链接:
ln -s /usr/local/nginx/sbin/nginx /usr/local/bin/nginx
之后直接用 nginx 命令就行。
配置文件在哪
编译安装的配置文件在:
/usr/local/nginx/conf/nginx.conf
编辑:
vi /usr/local/nginx/conf/nginx.conf
常用命令
# 启动
/usr/local/nginx/sbin/nginx
# 重载
/usr/local/nginx/sbin/nginx -s reload
# 停止
/usr/local/nginx/sbin/nginx -s stop
# 检查配置
/usr/local/nginx/sbin/nginx -t
开机自启
编译安装的 Nginx 没有 systemd 服务,需要自己写:
vi /etc/systemd/system/nginx.service
内容:
[Unit]
Description=Nginx Web Server
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s stop
[Install]
WantedBy=multi-user.target
然后:
systemctl daemon-reload
systemctl enable nginx
systemctl start nginx
常见坑
坑1:nginx: command not found
执行 nginx 提示找不到命令。
原因: 编译安装的不在 PATH 里。
解决: 加软链接,或者用完整路径。
坑2:改了配置不生效
改了 /etc/nginx/nginx.conf,但重载后还是老配置。
原因: 你改的是包管理器安装的配置文件,实际跑的是编译安装的。
解决: 用 nginx -t 确认一下实际加载的是哪个配置文件。
坑3:多个 nginx 共存
系统里同时有 yum 安装的和编译安装的 Nginx,不知道哪个在跑。
解决: 先停掉一个,再看进程。
坑4:日志找不到
日志不在 /var/log/nginx/。
原因: 编译安装的日志在自己的目录里。
解决: 看 nginx.conf 里的 error_log 和 access_log 配置。
总结
编译安装 Nginx 记住三个路径:
| 内容 | 路径 |
|---|---|
| 主程序 | /usr/local/nginx/sbin/nginx |
| 配置文件 | /usr/local/nginx/conf/nginx.conf |
| 日志 | /usr/local/nginx/logs/ |
记住: 编译安装的和 yum 安装的路径不一样,别搞混了。
遇到问题加QQ23979811 协助处理