Nginx报502 Bad Gateway完整排查思路(PHP-FPM场景)
Nginx 出现 502 Bad Gateway 是最常见的报错之一,90% 的情况都是 PHP-FPM 出了问题。下面是完整的排查思路,按顺序一步步来,基本都能解决。
1. 先看 PHP-FPM 是否在运行
systemctl status php-fpm
如果显示 failed 或 inactive,直接重启:
systemctl restart php-fpm
注意:CentOS 7 一般是
php-fpm,Ubuntu 可能是php7.4-fpm之类的,看你装的版本。
2. 看 Nginx 错误日志(最关键)
tail -f /var/log/nginx/error.log
常见错误信息:
错误1:connect() failed (111: Connection refused) while connecting to upstream
说明 PHP-FPM 没启动,或者监听地址不对。
错误2:upstream timed out
PHP 脚本执行超时了,或者 PHP-FPM 进程不够用。
错误3:Primary script unknown
Nginx 配置里的 SCRIPT_FILENAME 路径不对,找不到 PHP 文件。
3. 检查 Nginx 配置是否正确
nginx -t
如果显示 test is successful 说明配置语法没问题。
重点检查 fastcgi_pass 这一行:
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000; # 或者 unix:/run/php-fpm/www.sock
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
常见问题:
- fastcgi_pass 地址和 PHP-FPM 实际监听地址不一致
- SCRIPT_FILENAME 路径写错了
4. 检查 PHP-FPM 监听地址
netstat -tlnp | grep php-fpm
或者:
ss -tlnp | grep php-fpm
看 PHP-FPM 是监听在 9000 端口还是 unix sock 文件,然后和 Nginx 配置里的 fastcgi_pass 对应上。
5. 检查 PHP-FPM 进程数是否够
看 PHP-FPM 配置:
vi /etc/php-fpm.d/www.conf
重点参数:
pm = dynamic
pm.max_children = 50
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 30
如果访问量大,进程数不够就会 502。
6. 检查 PHP 错误日志
tail -f /var/log/php-fpm/www-error.log
有时候 PHP 代码本身报错,也会导致 502。
7. 其他常见原因
磁盘满了
df -h
内存不够
free -h
如果内存不足,PHP-FPM 进程会被杀掉,也会 502。
SELinux 没关
参考我之前的文章,关掉 SELinux 试试。
排查顺序总结
- 重启 PHP-FPM → 看能不能恢复
- 看 Nginx 错误日志 → 定位具体错误
- 检查 Nginx 配置 → fastcgi_pass 和 SCRIPT_FILENAME
- 检查 PHP-FPM 状态 → 监听地址、进程数
- 检查系统资源 → 磁盘、内存
- 检查 SELinux
遇到服务器、Nginx、MySQL 报错,可以加微信远程协助排查问题。
CentOS7 关闭SELinux详细步骤,临时+永久关闭
刚装完 CentOS 7,部署网站后发现目录权限明明对了,但还是 403 或文件访问被拒绝,十有八九是 SELinux 在搞鬼。这玩意儿对新手极不友好,建议直接关掉。
1. 先查看 SELinux 状态
getenforce
输出有三种可能:
Enforcing:强制模式(正在生效)Permissive:宽容模式(只警告不拦截)Disabled:已关闭
或者用详细命令:
sestatus
2. 临时关闭(重启后失效)
setenforce 0
执行完再 getenforce 确认一下,应该变成 Permissive。
注意:临时关闭不需要重启服务器,但重启后会恢复原来的状态。
3. 永久关闭(重启后依然生效)
编辑配置文件:
vi /etc/selinux/config
找到这一行:
SELINUX=enforcing
改成:
SELINUX=disabled
保存退出后,必须重启服务器才能生效:
reboot
4. 为什么要关 SELinux?
SELinux 是 Linux 的强制访问控制机制,理论上更安全,但对新手来说:
- 权限配对了还是访问不了
- Nginx 代理 PHP 一直 403
- 上传文件后权限不对
- 各种莫名其妙的拒绝访问
生产环境如果不是特别懂,建议直接关掉,省心。真要讲究安全,用防火墙就够了。
5. 常见坑
坑1:改了配置文件没重启
很多人改完 /etc/selinux/config 就以为关了,其实必须重启才生效。
坑2:setenforce 报错
如果 SELinux 本来就是 Disabled 状态,执行 setenforce 0 会报错,正常。
坑3:查看日志
如果想知道 SELinux 拦截了什么,可以看日志:
tail -f /var/log/audit/audit.log
遇到服务器、Nginx、MySQL 报错,可以加微信远程协助排查问题。
Linux查看服务器开放端口,netstat和ss命令使用教程
排查网站问题时,第一步经常是"看看端口到底开没开、哪个进程在监听"。Linux 下最常用的两个命令是 netstat 和 ss,下面整理最实用的用法。
一、netstat 命令(老系统常用)
1. 查看所有监听端口
netstat -tlnp
参数说明:
-t:TCP 端口-l:监听状态的端口-n:数字形式显示(不解析域名)-p:显示进程名和 PID
输出示例:
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1234/nginx
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 5678/sshd
2. 查看某个端口是否被占用
netstat -tlnp | grep 80
如果有输出说明 80 端口正在被占用,后面会显示是什么进程。
3. 查看所有 UDP 端口
netstat -ulnp
二、ss 命令(新系统推荐,更快)
ss 是 netstat 的替代品,在服务器连接数很多时速度快很多。
1. 查看所有监听端口
ss -tlnp
用法和 netstat 几乎一样,参数相同。
2. 查看某个端口
ss -tlnp | grep 443
3. 查看所有已建立的连接
ss -tn state established
三、其他常用排查命令
1. 直接看某个端口的进程
lsof -i:80
这个最直观,直接告诉你 80 端口被哪个 PID 占用了。
2. 查看某个进程监听了哪些端口
lsof -i | grep nginx
四、常见场景
场景1:网站打不开,怀疑端口没开
netstat -tlnp | grep 80
没有输出说明 Nginx 没启动,或者端口配置错了。
场景2:端口被占用,想换端口
lsof -i:3306
kill -9 12345 # 杀掉占用进程
场景3:服务器卡,想看有多少连接
ss -tn state established | wc -l
五、netstat 找不到怎么办?
CentOS 7 最小化安装可能没有 netstat,安装一下:
yum install net-tools -y
或者直接用 ss 命令,系统默认自带。
遇到服务器、Nginx、MySQL 报错,可以加微信远程协助排查问题。
CentOS7 firewalld开放80、443端口永久生效命令
CentOS 7 默认使用 firewalld 作为防火墙管理工具。很多新手搭建网站后发现外网访问不了,90% 都是因为 80、443 端口没开放。下面是完整的操作步骤,照抄就能用。
1. 先确认防火墙状态
systemctl status firewalld
如果显示 active (running) 说明防火墙正在运行,需要继续下面的操作。如果是 inactive 那就是防火墙没开,问题不在这里。
2. 开放 80 端口(HTTP)
firewall-cmd --zone=public --add-port=80/tcp --permanent
参数说明:
--zone=public:作用域为公共区域--add-port=80/tcp:开放 80 端口,TCP 协议--permanent:永久生效,不加这个重启服务器就失效了
3. 开放 443 端口(HTTPS)
firewall-cmd --zone=public --add-port=443/tcp --permanent
4. 一次性开放多个端口
如果需要同时开放多个端口,可以一条命令搞定:
firewall-cmd --zone=public --add-port=80/tcp --add-port=443/tcp --permanent
5. 重载防火墙使配置生效
⚠️ 重点:开放端口后必须重载,否则不生效!
firewall-cmd --reload
6. 查看已开放的端口
确认一下端口有没有真正开放成功:
firewall-cmd --list-ports
正常会输出类似:
80/tcp 443/tcp
7. 常用其他端口开放命令
SSH 端口(默认22,建议改成别的):
firewall-cmd --zone=public --add-port=22/tcp --permanent
MySQL 端口(如果需要远程连接数据库):
firewall-cmd --zone=public --add-port=3306/tcp --permanent
Redis 端口:
firewall-cmd --zone=public --add-port=6379/tcp --permanent
8. 移除/关闭已开放的端口
如果某个端口不需要了,想关掉:
firewall-cmd --zone=public --remove-port=8080/tcp --permanent
firewall-cmd --reload
常见坑
-
加了
--permanent还是不生效?
忘记firewall-cmd --reload了。永久配置写入后必须重载才会加载到运行时。 -
端口开放了还是访问不了?
检查云服务商的安全组(阿里云/腾讯云/华为云都有这个东西),防火墙和安全组是两层,都要开。 -
firewalld 命令找不到?
CentOS 7 最小化安装可能没装,执行yum install firewalld -y安装。 -
临时开放端口(重启就失效)
去掉--permanent参数就是临时开放,重启或 reload 后自动关闭。
遇到服务器、Nginx、MySQL 报错,可以加微信远程协助排查问题。
MySQL 索引优化技巧
MySQL 索引优化技巧
索引是数据库性能的关键,加对了查询快十倍,加错了反而更慢。
1. 索引类型
主键索引:主键自带索引,一张表只能一个。
普通索引:最基本的索引,加速查询。
CREATE INDEX idx_name ON user(name);
唯一索引:字段值不能重复。
CREATE UNIQUE INDEX idx_email ON user(email);
联合索引:多个字段组合的索引。
CREATE INDEX idx_name_age ON user(name, age);
2. 索引失效的情况
1)like 以 % 开头
-- 索引失效
SELECT * FROM user WHERE name LIKE '%张';
-- 索引生效
SELECT * FROM user WHERE name LIKE '张%';
2)对字段做函数运算
-- 索引失效
SELECT * FROM user WHERE LEFT(name,1) = '张';
-- 索引生效
SELECT * FROM user WHERE name LIKE '张%';
3)联合索引不满足最左前缀
联合索引 (name, age):
-- 生效
SELECT * FROM user WHERE name = '张三';
-- 生效
SELECT * FROM user WHERE name = '张三' AND age = 25;
-- 失效
SELECT * FROM user WHERE age = 25;
4)隐式类型转换
-- phone 是 varchar
-- 索引失效
SELECT * FROM user WHERE phone = 13800001111;
-- 索引生效
SELECT * FROM user WHERE phone = '13800001111';
3. 查看索引使用情况
-- 查看执行计划
EXPLAIN SELECT * FROM user WHERE name = '张三';
-- 重点看 type 字段:
-- system > const > eq_ref > ref > range > index > ALL
-- 最差是 ALL(全表扫描)
4. 索引优化原则
- 为高频查询加索引:不要乱加
- 区分度高的字段优先:性别这种区分度低的不加
- 联合索引把最常用的放左边
- 索引不是越多越好:增删改会变慢
- 避免索引冗余:(a,b) 包含了 (a)
5. 慢查询排查
-- 开启慢查询
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2;
-- 查看慢查询日志
SHOW VARIABLES LIKE 'slow_query_log_file';
总结
MySQL 索引优化核心:
- 联合索引满足最左前缀
- like 不要以 % 开头
- 不要对字段做函数运算
- 用 EXPLAIN 看执行计划
- 慢查询找出来再优化
搞懂这些,数据库性能提升一大截。
Git 常用命令汇总
Git 常用命令汇总
Git 是现在开发必备工具,整理一份常用命令清单,收藏备用。
1. 初始化和克隆
# 初始化新仓库
git init
# 克隆远程仓库
git clone https://github.com/user/repo.git
# 克隆指定分支
git clone -b dev https://github.com/user/repo.git
2. 查看状态
# 查看工作区状态
git status
# 查看提交历史
git log
# 查看简洁历史
git log --oneline --graph
# 查看文件改动
git diff
3. 添加和提交
# 添加所有文件到暂存区
git add .
# 添加指定文件
git add file.php
# 提交到本地仓库
git commit -m "修复bug"
# 提交并跳过验证
git commit --no-verify -m "修复bug"
4. 分支操作
# 查看本地分支
git branch
# 查看所有分支(包括远程)
git branch -a
# 创建新分支
git branch dev
# 切换分支
git checkout dev
# 创建并切换分支
git checkout -b dev
# 删除分支
git branch -d dev
# 强制删除分支
git branch -D dev
5. 远程操作
# 拉取远程更新
git pull
# 推送本地提交
git push
# 推送本地新分支到远程
git push -u origin dev
# 删除远程分支
git push origin --delete dev
6. 撤销操作
# 撤销工作区修改
git checkout -- file.php
# 撤销暂存区
git reset HEAD file.php
# 回退到上一个版本
git reset --hard HEAD^
# 回退到指定版本
git reset --hard commit_id
# 撤销上次提交(保留修改)
git reset --soft HEAD^
7. 合并和变基
# 合并分支
git merge dev
# 变基
git rebase master
# 取消合并
git merge --abort
8. 标签操作
# 打标签
git tag v1.0.0
# 查看标签
git tag
# 推送标签
git push origin v1.0.0
# 推送所有标签
git push origin --tags
总结
Git 常用命令核心:
- 查看:status / log / diff
- 提交:add / commit
- 分支:branch / checkout / merge
- 远程:pull / push
- 撤销:reset / checkout
记住这些,日常 Git 操作基本够用了。
Nginx 日志分析技巧
Nginx 日志分析技巧
Nginx 日志是排查问题、分析流量的重要工具。学会看日志,网站问题排查效率翻倍。
1. 日志位置
# 默认访问日志
/var/log/nginx/access.log
# 默认错误日志
/var/log/nginx/error.log
可以在 nginx.conf 里自定义:
access_log /var/log/nginx/www.access.log;
error_log /var/log/nginx/www.error.log;
2. 日志格式
默认日志格式:
127.0.0.1 - - [04/Oct/2026:10:00:00 +0800] "GET / HTTP/1.1" 200 612 "https://www.baidu.com/" "Mozilla/5.0..."
各字段含义:
- IP 地址
- 请求时间
- 请求方法和路径
- 状态码
- 响应大小
- 来源页面
- User-Agent
3. 常用分析命令
# 查看访问量前10的IP
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head 10
# 查看访问量前10的页面
awk '{print $7}' access.log | sort | uniq -c | sort -nr | head 10
# 查看状态码分布
awk '{print $9}' access.log | sort | uniq -c | sort -nr
# 查看 404 请求
grep ' 404 ' access.log
# 查看 502 错误
grep ' 502 ' access.log
4. 排查问题
排查 502 错误:
grep ' 502 ' access.log | tail 20
排查慢请求:
开启慢日志:
slow_request_log /var/log/nginx/slow.log;
slow_request_time 5s;
排查爬虫:
grep 'Baiduspider' access.log
grep 'Googlebot' access.log
5. 流量统计
# 今天总访问量
grep '04/Oct/2026' access.log | wc -l
# 今天总流量
grep '04/Oct/2026' access.log | awk '{sum+=$10} END {print sum/1024/1024 " MB"}'
# 每小时访问量
awk '{print $4}' access.log | cut -d: -f2 | uniq -c
6. 日志切割
日志太大要切割,用 logrotate:
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 30
compress
notifempty
create 0640 nginx adm
postrotate
systemctl reload nginx
endscript
}
7. 常用工具
- GoAccess:实时日志分析工具
- ELK:Elasticsearch + Logstash + Kibana
- Zabbix:监控告警
总结
Nginx 日志分析核心:
- 位置:/var/log/nginx/
- 命令:awk + sort + uniq
- 排查:看状态码、看慢请求
- 切割:用 logrotate
学会这些,日常日志分析基本够用了。
Docker 常用命令大全
Docker 常用命令大全
Docker 命令不多,但记不住还是很麻烦。整理一份常用命令清单,收藏备用。
1. 镜像命令
# 拉取镜像
docker pull nginx:latest
# 查看本地镜像
docker images
# 删除镜像
docker rmi nginx:latest
# 搜索镜像
docker search nginx
# 导出镜像
docker save -o nginx.tar nginx:latest
# 导入镜像
docker load -i nginx.tar
2. 容器命令
# 启动容器(后台运行)
docker run -d --name mynginx -p 80:80 nginx
# 查看运行中的容器
docker ps
# 查看所有容器(包括停止的)
docker ps -a
# 停止容器
docker stop mynginx
# 启动已停止的容器
docker start mynginx
# 重启容器
docker restart mynginx
# 删除容器
docker rm mynginx
# 强制删除运行中的容器
docker rm -f mynginx
3. 进入容器
# 进入容器(bash)
docker exec -it mynginx bash
# 进入容器(sh)
docker exec -it mynginx sh
# 查看容器日志
docker logs mynginx
# 实时查看日志
docker logs -f mynginx
# 查看容器详情
docker inspect mynginx
4. 文件操作
# 从容器复制文件到宿主机
docker cp mynginx:/etc/nginx/nginx.conf ./
# 从宿主机复制文件到容器
docker cp ./index.html mynginx:/usr/share/nginx/html/
5. 端口和网络
# 查看容器端口映射
docker port mynginx
# 查看容器 IP
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mynginx
6. 清理命令
# 停止所有容器
docker stop $(docker ps -q)
# 删除所有停止的容器
docker container prune
# 删除所有未使用的镜像
docker image prune
# 删除所有未使用的资源(危险操作)
docker system prune -a
7. Docker Compose 常用命令
# 启动所有服务
docker-compose up -d
# 停止所有服务
docker-compose down
# 查看服务状态
docker-compose ps
# 查看日志
docker-compose logs -f
# 重启某个服务
docker-compose restart nginx
总结
Docker 常用命令核心:
- 镜像:pull / images / rmi
- 容器:run / ps / stop / start / rm
- 进入:exec -it
- 日志:logs -f
- 清理:prune
记住这些,日常 Docker 操作基本够用了。
PHP Composer 入门指南
PHP Composer 入门指南
Composer 是 PHP 的依赖管理工具,现在做 PHP 开发不用 Composer 几乎不可能。
1. 安装 Composer
# Linux/Mac
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
# Windows 直接下载安装包
# https://getcomposer.org/Composer-Setup.exe
验证安装:
composer -V
2. 换国内镜像(必须)
官方镜像太慢,一定要换国内镜像:
# 阿里云镜像
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
# 腾讯云镜像
composer config -g repo.packagist composer https://mirrors.tencent.com/composer/
3. 初始化项目
# 进入项目目录
cd myproject
# 初始化 composer.json
composer init
或者手动创建 composer.json:
{
"name": "my/project",
"require": {
"monolog/monolog": "^2.0"
}
}
4. 常用命令
# 安装依赖
composer install
# 更新依赖
composer update
# 安装某个包
composer require monolog/monolog
# 卸载某个包
composer remove monolog/monolog
# 自动加载
composer dump-autoload
5. 自动加载
// 引入自动加载文件
require __DIR__ . '/vendor/autoload.php';
// 然后就可以直接用第三方库了
$log = new Monolog\Logger('name');
6. composer.json 详解
{
"name": "项目名",
"description": "项目描述",
"type": "project",
"require": {
"php": ">=7.4",
"monolog/monolog": "^2.0",
"guzzlehttp/guzzle": "^7.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
7. 版本号说明
^2.0:兼容 2.0 到 3.0(不包含 3.0)~2.0:兼容 2.0 到 2.1(不包含 2.1)*:任意版本2.0.*:2.0.x 版本
总结
Composer 核心:
- 安装:一行命令
- 换镜像:必须换国内源
- 常用:install / update / require / remove
- 自动加载:require vendor/autoload.php
搞懂这些,Composer 日常开发就够用了。
MySQL 事务隔离级别
MySQL 事务隔离级别详解
事务是数据库并发控制的基础,搞懂四个隔离级别,才能解决脏读、幻读问题。
1. 事务 ACID 特性
- 原子性(Atomicity):要么全成功,要么全回滚
- 一致性(Consistency):事务前后数据一致
- 隔离性(Isolation):事务之间互不干扰
- 持久性(Durability):提交后永久保存
2. 四种隔离级别
MySQL 有四种隔离级别,从上到下越来越严格:
读未提交(READ UNCOMMITTED)
- 可以读到别人还没提交的数据
- 会出现脏读
- 几乎没人用
读提交(READ COMMITTED)
- 只能读到别人已经提交的数据
- 解决了脏读,但会出现不可重复读
- Oracle、SQL Server 默认级别
可重复读(REPEATABLE READ)
- 同一个事务里,读多次结果都一样
- 解决了脏读、不可重复读
- MySQL InnoDB 默认级别
- InnoDB 用间隙锁解决了幻读
串行化(SERIALIZABLE)
- 事务一个一个排队执行
- 最安全,但性能最差
- 几乎没人用
3. 三种读问题
脏读:读到了别人还没提交的数据,别人回滚了,你读到的就是脏数据。
不可重复读:同一个事务里,两次读同一行数据,结果不一样(别人中间改了并提交了)。
幻读:同一个事务里,两次查同一个范围,行数不一样(别人中间插了新行)。
4. 查看和设置隔离级别
-- 查看当前隔离级别
SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;
-- 设置全局隔离级别
SET GLOBAL tx_isolation = 'READ-COMMITTED';
-- 设置当前会话隔离级别
SET SESSION tx_isolation = 'READ-COMMITTED';
5. 实际应用建议
- 默认就好:MySQL 默认的可重复读已经够用
- 高并发场景:可以降到读提交,提升并发性能
- 金融级场景:用串行化,绝对安全
- 大多数业务:默认级别就行,不用折腾
6. 代码示例
// PDO 事务示例
try {
$pdo->beginTransaction();
$pdo->exec("UPDATE account SET money = money - 100 WHERE id = 1");
$pdo->exec("UPDATE account SET money = money + 100 WHERE id = 2");
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
echo "转账失败:" . $e->getMessage();
}
总结
MySQL 事务隔离级别核心:
- 读未提交:脏读
- 读提交:不可重复读
- 可重复读:默认级别,解决幻读
- 串行化:最安全最慢
大多数业务用默认的可重复读就够了。
PHP 字符串处理技巧
PHP 字符串处理常用技巧
字符串处理是 PHP 开发最常用的操作,掌握这些函数,写代码效率翻倍。
1. 截取字符串
// 截取前 5 个字符
substr("hello world", 0, 5); // hello
// 截取从第 6 个开始到结尾
substr("hello world", 6); // world
// 中文字符串截取(用 mb_)
mb_substr("你好世界", 0, 2, "UTF-8"); // 你好
注意:中文要用 mb_substr,不能用 substr,否则会乱码。
2. 替换字符串
// str_replace(替换所有)
str_replace("world", "PHP", "hello world"); // hello PHP
// str_ireplace(不区分大小写)
str_ireplace("WORLD", "PHP", "hello world");
// preg_replace(正则替换)
preg_replace("/\d/", "", "abc123def"); // abcdef
3. 查找字符串
// 查找位置(找不到返回 false)
strpos("hello world", "world"); // 6
// 查找最后出现位置
strrpos("hello world hello", "hello"); // 12
// 判断是否包含(PHP 8+)
str_contains("hello world", "world"); // true
// 判断开头
str_starts_with("hello world", "hello"); // true
// 判断结尾
str_ends_with("hello world", "world"); // true
4. 格式化字符串
// 去除首尾空格
trim(" hello "); // hello
// 只去左边/右边
ltrim(" hello"); // hello
rtrim("hello "); // hello
// 转小写/大写
strtolower("Hello"); // hello
strtoupper("hello"); // HELLO
// 首字母大写
ucfirst("hello"); // Hello
// 每个单词首字母大写
ucwords("hello world"); // Hello World
5. 字符串和数组转换
// 字符串转数组
explode(",", "a,b,c"); // ["a","b","c"]
// 数组转字符串
implode(",", ["a","b","c"]); // a,b,c
// 重复字符串
str_repeat("ab", 3); // ababab
// 字符串长度
strlen("hello"); // 5
mb_strlen("你好", "UTF-8"); // 2
6. 常用技巧
// 生成随机字符串
substr(md5(time()), 0, 8);
// 脱敏手机号
substr_replace("13812345678", "****", 3, 4); // 138****5678
// 截取摘要(带省略号)
mb_strlen($text) > 10 ? mb_substr($text, 0, 10) . "..." : $text;
// 清除 HTML 标签
strip_tags("<p>hello</p>"); // hello
总结
PHP 字符串处理核心函数:
- 截取:substr / mb_substr
- 替换:str_replace / preg_replace
- 查找:strpos / str_contains
- 格式化:trim / strtolower / ucwords
- 转换:explode / implode
记住这些,日常开发基本够用了。
Linux 防火墙配置技巧
Linux 防火墙配置技巧
服务器安全第一步就是配好防火墙,只开需要的端口,其他全关。
1. firewalld(CentOS 7+ 默认)
# 查看状态
firewall-cmd --state
# 查看已开放端口
firewall-cmd --list-ports
# 开放端口
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp
# 开放多个端口
firewall-cmd --permanent --add-port=8000-9000/tcp
# 开放指定 IP 访问
firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='192.168.1.100' port protocol='tcp' port=3306 accept"
# 重载配置(必须重载才生效)
firewall-cmd --reload
# 删除端口
firewall-cmd --permanent --remove-port=8080/tcp
2. ufw(Ubuntu 默认)
# 查看状态
ufw status
# 开启防火墙
ufw enable
# 允许 SSH
ufw allow 22
# 允许 80 和 443
ufw allow 80
ufw allow 443
# 允许指定 IP
ufw allow from 192.168.1.100 to any port 3306
# 拒绝某个 IP
ufw deny from 1.2.3.4
# 删除规则
ufw delete allow 8080
3. iptables(经典)
# 查看规则
iptables -L -n
# 允许 22 端口
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 允许 80 端口
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# 允许本地回环
iptables -A INPUT -i lo -j ACCEPT
# 允许已建立的连接
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 默认拒绝其他
iptables -P INPUT DROP
# 保存规则(CentOS)
service iptables save
4. 安全建议
- 只开必要端口:SSH、HTTP、HTTPS,其他全关
- SSH 改端口:不要用默认 22,改成其他端口
- 限制 SSH 登录 IP:只允许你的 IP 登录 SSH
- 数据库不对外暴露:MySQL、Redis 只允许本地访问
- 定期查看日志:/var/log/secure 看有没有暴力破解
5. 常用端口速查
- 22:SSH
- 80:HTTP
- 443:HTTPS
- 3306:MySQL
- 6379:Redis
- 8080:应用端口
总结
Linux 防火墙核心:
- CentOS 用 firewalld
- Ubuntu 用 ufw
- 只开必要端口
- 数据库不对外暴露
- SSH 限制 IP 访问
配好防火墙,服务器安全提升一大截。
Nginx 负载均衡配置
Nginx 负载均衡配置实战
网站流量大了,单台服务器扛不住,就得用负载均衡。Nginx 做负载均衡很简单,几行配置就行。
1. 基本配置
http {
upstream backend {
server 192.168.1.101:80;
server 192.168.1.102:80;
server 192.168.1.103:80;
}
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://backend;
}
}
}
默认是轮询(round robin),请求轮流分到三台服务器。
2. 四种分配策略
轮询(默认):一个一个来。
权重(weight):按比例分配,性能好的服务器多分担。
upstream backend {
server 192.168.1.101 weight=3;
server 192.168.1.102 weight=2;
server 192.168.1.103 weight=1;
}
3:2:1 的比例,第一台分一半请求。
ip_hash:同一个 IP 固定分到同一台服务器,解决 session 问题。
upstream backend {
ip_hash;
server 192.168.1.101;
server 192.168.1.102;
}
least_conn:最少连接数优先,分给当前最闲的服务器。
upstream backend {
least_conn;
server 192.168.1.101;
server 192.168.1.102;
}
3. 健康检查
upstream backend {
server 192.168.1.101 max_fails=3 fail_timeout=30s;
server 192.168.1.102 max_fails=3 fail_timeout=30s;
}
一台服务器挂了,自动从列表里摘掉,恢复了再加回来。
4. 常用参数
- max_fails:失败几次算挂了,默认1次
- fail_timeout:挂了多久再重试,默认10秒
- down:标记服务器下线,不分配请求
- backup:备用服务器,其他都挂了才用
5. 完整示例
upstream backend {
server 192.168.1.101 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.102 weight=2 max_fails=3 fail_timeout=30s;
server 192.168.1.103 backup;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
总结
Nginx 负载均衡核心:
- upstream 定义服务器组
- 四种策略:轮询、权重、ip_hash、最少连接
- 健康检查自动摘除故障节点
- 生产环境用权重 + 健康检查就够了
搞懂这些,多台服务器集群就跑起来了。
Redis 数据结构详解
Redis 五种数据结构详解
Redis 不只是缓存,它的五种数据结构各有各的用处,搞懂了才能用对地方。
1. String(字符串)
最基础的数据结构,存字符串、数字都行。
SET name "张三"
GET name
INCR count
SET token "abc123" EX 3600
应用场景:缓存用户信息、计数器、分布式锁、会话存储。
2. Hash(哈希)
存键值对,适合存对象。
HSET user:1 name "张三" age 25 city "北京"
HGET user:1 name
HGETALL user:1
应用场景:存用户信息、商品信息,比 String 存 JSON 更省空间。
3. List(列表)
有序列表,可以从头或尾加元素。
LPUSH messages "hello"
LPUSH messages "world"
LRANGE messages 0 -1
应用场景:消息队列、最新消息列表、朋友圈时间线。
4. Set(集合)
无序不重复集合。
SADD tags "PHP"
SADD tags "MySQL"
SMEMBERS tags
SISMEMBER tags "PHP"
应用场景:标签、共同好友、去重。
5. ZSet(有序集合)
每个元素带分数,按分数排序。
ZADD rank 100 "张三"
ZADD rank 90 "李四"
ZREVRANGE rank 0 9 WITHSCORES
应用场景:排行榜、计分板、优先级队列。
总结
五种数据结构各有用处:
- String:简单缓存、计数器
- Hash:存对象
- List:队列、时间线
- Set:去重、标签
- ZSet:排行榜
选对数据结构,性能才能发挥出来。
PHP 数组操作技巧
PHP 数组操作技巧
数组是 PHP 最常用的数据结构,掌握这些技巧,写代码效率翻倍。
1. 数组合并
// 索引数组合并
$arr1 = [1, 2];
$arr2 = [3, 4];
$result = array_merge($arr1, $arr2); // [1,2,3,4]
// 关联数组合并,后面覆盖前面
$arr1 = ['a' => 1, 'b' => 2];
$arr2 = ['b' => 3, 'c' => 4];
$result = array_merge($arr1, $arr2); // ['a'=>1, 'b'=>3, 'c'=>4]
2. 数组去重
// 一维数组去重
$arr = [1, 2, 2, 3, 3, 3];
$result = array_unique($arr); // [1,2,3]
// 二维数组按字段去重
$users = [
['id' => 1, 'name' => '张三'],
['id' => 2, 'name' => '李四'],
['id' => 1, 'name' => '张三'],
];
$ids = array_column($users, 'id');
$unique = array_unique($ids);
3. 数组排序
// 升序
sort($arr);
// 降序
rsort($arr);
// 按键排序
ksort($arr);
// 按值排序,保留键
asort($arr);
4. 数组过滤
// 过滤空值
$arr = [1, 0, '', null, 'a'];
$result = array_filter($arr); // [1, 'a']
// 自定义过滤
$result = array_filter($arr, function($value) {
return $value > 2;
});
5. 数组取值
// 取某一列
$names = array_column($users, 'name');
// 取交集
$arr1 = [1, 2, 3];
$arr2 = [2, 3, 4];
$result = array_intersect($arr1, $arr2); // [2,3]
// 取差集
$result = array_diff($arr1, $arr2); // [1]
6. 常用技巧
// 判断键是否存在
isset($arr['key']);
array_key_exists('key', $arr);
// 随机取一个
$value = array_rand($arr);
// 打乱数组
shuffle($arr);
// 数组合并成字符串
$str = implode(',', $arr);
// 字符串转数组
$arr = explode(',', $str);
总结
PHP 数组函数非常丰富,常用的就这些:合并、去重、排序、过滤、取值。把这些记熟,日常开发基本够用了。
MySQL 锁机制详解
MySQL 锁机制详解
MySQL 锁是并发控制的核心,搞懂锁,才能避免死锁和性能问题。
1. 表锁 vs 行锁
表锁:锁整个表,开销小,并发低。
- MyISAM 用的就是表锁
- 适合读多写少的场景
行锁:锁单行数据,开销大,并发高。
- InnoDB 用的就是行锁
- 适合写多的场景
2. 共享锁 vs 排他锁
共享锁(S锁):读锁,多个读不互斥。
SELECT ... LOCK IN SHARE MODE
排他锁(X锁):写锁,一个写独占。
SELECT ... FOR UPDATE
3. 间隙锁
InnoDB 用间隙锁防止幻读。锁的是一个范围,不是具体行。
比如你查 id > 10 AND id < 20,间隙锁会把这个区间锁住,别人插不进来。
4. 死锁
两个事务互相等对方的锁,都不放,就死锁了。
避免死锁的方法:
- 按相同顺序访问表
- 事务尽量小,快速提交
- 降低隔离级别
- 加索引,减少行锁范围
5. 查看锁状态
-- 查看当前锁
SHOW OPEN TABLES WHERE In_use > 0;
-- 查看 InnoDB 锁
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
-- 查看锁等待
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
6. 行锁什么时候会变成表锁
行锁失效就会升级成表锁:
- 没用索引
- 索引失效
- 全表扫描
所以加索引不仅是为了查得快,也是为了锁得准。
总结
MySQL 锁的核心:
- InnoDB 默认行锁,MyISAM 是表锁
- 共享锁读、排他锁写
- 间隙锁防幻读
- 死锁要避免:顺序访问、小事务、加索引
搞懂这些,数据库并发问题基本能解决一半。
Linux 文件权限详解
Linux 文件权限详解
Linux 权限是服务器安全的基础,搞懂权限,服务器才不会乱。
1. 查看权限
用 ls -l 查看文件权限:
ls -l file.txt
-rw-r--r-- 1 user group 1024 Oct 4 10:00 file.txt
开头10个字符就是权限:
- 第1个:文件类型(- 普通文件,d 目录)
- 第2-4个:所有者权限
- 第5-7个:所属组权限
- 第8-10个:其他人权限
2. 三种权限
- r(读)= 4
- w(写)= 2
- x(执行)= 1
数字加起来就是权限值:
- 7 = 4+2+1 = rwx
- 6 = 4+2 = rw-
- 5 = 4+1 = r-x
- 4 = 4 = r--
3. 修改权限 chmod
# 给所有者加执行权限
chmod u+x file.txt
# 给所有人只读权限
chmod 444 file.txt
# 所有者读写执行,组和其他人只读
chmod 744 file.txt
4. 修改所有者 chown
# 修改文件所有者
chown user:group file.txt
# 递归修改目录
chown -R user:group /data/www
5. 常用权限
- 普通文件:644(所有者读写,其他人只读)
- 目录:755(所有者读写执行,其他人读执行)
- 脚本文件:755(所有者能执行)
6. 安全建议
- 网站目录不要用 root 所有者
- 上传目录禁止执行权限
- 配置文件权限设为 600,只有所有者能读
总结
Linux 权限核心就是三组(所有者、组、其他人)× 三种(读、写、执行),用数字表示就是 r=4、w=2、x=1。搞懂这个,日常权限管理基本够用了。
Docker 网络模式详解
Docker 四种网络模式详解
Docker 网络是容器通信的基础,搞懂四种网络模式,容器网络就不会乱。
1. bridge 模式(默认)
这是 Docker 默认的网络模式。容器在一个虚拟的 bridge 网络里,每个容器有自己的虚拟 IP,通过 NAT 访问外部网络。
# 启动容器,默认就是 bridge
docker run -d nginx
# 查看容器 IP
docker inspect 容器ID | grep IPAddress
特点:
- 容器之间可以通过 IP 互相访问
- 外部访问需要端口映射
- 适合大多数场景
2. host 模式
容器直接用宿主机的网络,没有独立的网络栈。
docker run -d --net=host nginx
特点:
- 网络性能最好,没有 NAT 开销
- 端口直接用宿主机的,不用映射
- 多个容器不能用同一个端口
适合对性能要求高的场景。
3. none 模式
容器没有网络,完全隔离。
docker run -d --net=none nginx
特点:
- 没有任何网络
- 安全级别最高
- 适合不需要网络的离线计算任务
4. container 模式
新创建的容器和一个已存在的容器共享网络。
docker run -d --net=container:nginx容器ID redis
特点:
- 两个容器共享网络栈
- 可以用 localhost 互相访问
- 适合紧密配合的服务
5. 自定义网络
生产环境推荐用自定义网络,不用默认的 bridge:
# 创建自定义网络
docker network create mynet
# 容器加入自定义网络
docker run -d --net=mynet --name web nginx
docker run -d --net=mynet --name app php
同一个自定义网络里的容器,可以直接用容器名互相访问,不用记 IP。
总结
四种网络模式各有适用场景:
- 日常开发用默认 bridge
- 性能要求高用 host
- 安全要求高用 none
- 容器间通信密集用自定义网络
搞懂这四种模式,Docker 网络基本就够用了。
Redis 缓存穿透解决
Redis 缓存穿透解决方案
缓存穿透是指查询一个数据库里根本不存在的数据,因为缓存里也没有,所以每次请求都打到数据库。如果有人恶意攻击,数据库可能被打挂。
什么是缓存穿透
正常流程:先查缓存,缓存有就直接返回,没有再查数据库,然后写入缓存。
如果查询的数据数据库里也没有,那缓存永远都不会命中,每次请求都会查数据库,这就是缓存穿透。
解决方案一:缓存空值
最简单的办法,数据库查不到也往缓存里写一个空值,设个短过期时间。
$data = $redis->get($key);
if ($data === null) {
$data = $db->query($sql);
if ($data === null) {
// 缓存空值,60秒过期
$redis->setex($key, 60, '');
} else {
$redis->setex($key, 3600, $data);
}
}
优点:简单直接
缺点:会占用更多缓存空间,大量空值缓存会浪费内存
解决方案二:布隆过滤器
在缓存前面加一层布隆过滤器,先判断数据是否存在,不存在直接返回。
布隆过滤器的原理是用一个大的位数组和多个哈希函数,把存在的数据标记为1。查询时如果所有哈希位置都是1,说明可能存在;只要有一个位置是0,说明一定不存在。
优点:内存占用小,效率高
缺点:有一定误判率(说存在可能不存在,说不存在一定不存在)
两种方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 缓存空值 | 简单 | 浪费内存 | 数据量小,穿透不严重 |
| 布隆过滤器 | 内存效率高 | 有误差 | 数据量大,穿透严重 |
总结
缓存穿透是常见的缓存问题,小项目用缓存空值就够了,大项目数据量大就上布隆过滤器。两种方案都能解决问题,根据实际场景选就行。
Nginx 反向代理配置
Nginx 反向代理配置详解
Nginx 反向代理是最常用的服务器配置之一,把请求转发给后端服务。本文讲清楚核心配置。
1. 基本配置
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
2. 传递真实IP
后端服务要拿到用户真实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;
3. 负载均衡
多台后端服务器,用 upstream 做负载均衡:
upstream backend {
server 127.0.0.1:8080 weight=1;
server 127.0.0.1:8081 weight=2;
}
server {
location / {
proxy_pass http://backend;
}
}
4. 超时设置
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
5. 静态文件处理
静态文件直接由 Nginx 处理,不用转发给后端:
location ~* \.(js|css|png|jpg|gif)$ {
root /data/www;
expires 30d;
}
6. HTTPS 反向代理
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:8080;
}
}
总结
Nginx 反向代理的核心就是 proxy_pass,配上 proxy_set_header 传递必要信息,再加上超时和静态文件处理,基本够用了。