服务器SSH安全加固,改端口和密钥登录

前言

服务器刚买,SSH一直被暴力破解?默认22端口全是黑客在扫。本文讲怎么SSH安全加固:改端口、禁用密码登录、用密钥登录。

一、为什么要加固SSH

默认SSH配置太危险:

  • 端口22是公开的,黑客都扫
  • root密码登录容易被暴力破解
  • 密码可能被盗

二、第一步:改SSH端口

编辑SSH配置

vi /etc/ssh/sshd_config

找到:

#Port 22

改成:

Port 22222

(随便选个1024-65535之间的端口)

防火墙开放新端口

firewalld:

firewall-cmd --permanent --add-port=22222/tcp
firewall-cmd --reload

iptables:

iptables -I INPUT -p tcp --dport 22222 -j ACCEPT

重启SSH

systemctl restart sshd

注意: 改完端口后,连接SSH要用新端口:

ssh -p 22222 root@服务器IP

三、第二步:生成密钥对

在你自己电脑上生成

ssh-keygen -t rsa -b 4096

一路回车,生成两个文件:

  • ~/.ssh/id_rsa (私钥,自己留着)
  • ~/.ssh/id_rsa.pub (公钥,传到服务器)

把公钥传到服务器

ssh-copy-id -p 22222 root@服务器IP

或者手动传:

# 服务器上创建.ssh目录
mkdir -p ~/.ssh
chmod 700 ~/.ssh

# 把公钥内容追加到authorized_keys
vi ~/.ssh/authorized_keys
# 把id_rsa.pub的内容粘进去

chmod 600 ~/.ssh/authorized_keys

测试密钥登录

ssh -p 22222 root@服务器IP

不用输密码就能登录就对了。

四、第三步:禁用密码登录

编辑SSH配置

vi /etc/ssh/sshd_config

找到:

#PasswordAuthentication yes

改成:

PasswordAuthentication no

找到:

#PermitRootLogin yes

改成:

PermitRootLogin prohibit-password

(允许root用密钥登录,但不允许密码登录)

重启SSH

systemctl restart sshd

五、第四步:其他安全设置

限制登录尝试次数

防止暴力破解:

MaxAuthTries 3

登录超时时间

ClientAliveInterval 300
ClientAliveCountMax 2

禁止空密码登录

PermitEmptyPasswords no

只允许特定用户登录

AllowUsers root

六、安装fail2ban

自动封禁暴力破解的IP。

安装

yum install -y epel-release
yum install -y fail2ban

配置

vi /etc/fail2ban/jail.local

加内容:

[sshd]
enabled = true
port = 22222
filter = sshd
logpath = /var/log/secure
maxretry = 3
bantime = 86400

启动

systemctl start fail2ban
systemctl enable fail2ban

七、完整配置文件参考

vi /etc/ssh/sshd_config
Port 22222
PermitRootLogin prohibit-password
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
PermitEmptyPasswords no

改完重启:

systemctl restart sshd

八、常见坑

坑1:改完端口连不上了

原因: 防火墙没开放新端口。

解决:

  • 云服务器还要在控制台安全组里开放端口
  • 先开新窗口测试,别关旧窗口

坑2:密钥登录失败

原因: 权限不对。

检查:

ls -ld ~/.ssh
ls -l ~/.ssh/authorized_keys

正确权限:

  • .ssh目录:700
  • authorized_keys:600

坑3:禁用密码后进不去了

原因: 密钥没配好就禁用密码了。

解决: 一定先测试密钥登录成功,再禁用密码。

坑4:云服务器安全组没开

改了端口,防火墙也开了,但还是连不上。

原因: 云服务商的安全组没开。

解决: 去云控制台(阿里云/腾讯云)安全组里开放端口。

总结

记住:

  1. 改SSH默认端口(22改成别的)
  2. 生成密钥对,传公钥到服务器
  3. 测试密钥登录成功
  4. 禁用密码登录
  5. 安装fail2ban自动封IP
  6. 云服务器记得开安全组

安全加固顺序:

改端口 → 配密钥 → 测试登录 → 禁密码 → 装fail2ban

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:46 

网站被黑了怎么排查,后门查杀方法

前言

网站突然被挂了黑链、被跳转、内容被篡改?别慌,本文讲网站被黑后怎么排查、怎么找后门、怎么清理。

一、怎么判断被黑了

常见现象

  1. 网站打开变成别的页面(赌博、色情)
  2. 网站里多了很多不认识的页面
  3. 百度搜索显示的标题不对
  4. 网站突然变慢、CPU飙升
  5. 后台密码被改了

二、第一步:找最近修改的文件

黑客一般会往网站目录里传后门文件。

找最近24小时修改的文件

find /www/wwwroot/你的站/ -mtime -1 -type f

找最近7天修改的文件

find /www/wwwroot/你的站/ -mtime -7 -type f

找最近修改的PHP文件

find /www/wwwroot/你的站/ -name "*.php" -mtime -7

重点看: 你不认识的、突然冒出来的PHP文件。

三、第二步:找可疑代码

找一句话木马

黑客常用的一句话后门:

<?php @eval($_POST['cmd']); ?>

搜索:

grep -r "eval(" /www/wwwroot/你的站/
grep -r "assert(" /www/wwwroot/你的站/
grep -r "base64_decode(" /www/wwwroot/你的站/
grep -r "str_rot13(" /www/wwwroot/你的站/

找加密的后门

很多后门是加密的:

grep -r "gzinflate(" /www/wwwroot/你的站/
grep -r "gzuncompress(" /www/wwwroot/你的站/
grep -r "strrev(" /www/wwwroot/你的站/

找下载文件的后门

grep -r "file_put_contents(" /www/wwwroot/你的站/
grep -r "fwrite(" /www/wwwroot/你的站/

四、第三步:看Web日志

看Nginx访问日志,找可疑的请求。

看最近的访问

tail -100 /var/log/nginx/access.log

找可疑的POST请求

grep "POST" /var/log/nginx/access.log | tail -50

找访问可疑文件的

grep ".php" /var/log/nginx/access.log | grep "cmd"

五、第四步:看进程

看有没有可疑进程在跑。

ps aux

重点看:

  • 不认识的进程
  • 占CPU很高的进程
  • 从/tmp目录跑的进程

杀掉可疑进程:

kill -9 进程ID

六、第五步:看定时任务

黑客会加定时任务,维持后门。

crontab -l

看有没有不认识的定时任务。

还有系统级的:

cat /etc/crontab
ls /etc/cron.d/

七、第六步:看后门文件特征

常见后门文件名

  • shell.php
  • cmd.php
  • 1.php
  • x.php
  • upload.php
  • webshell.php

看文件权限

后门一般是777权限:

find /www/wwwroot/你的站/ -perm 777

八、清理方法

方法1:删除可疑文件

确认是后门的直接删:

rm -f /www/wwwroot/你的站/可疑文件.php

方法2:重新上传程序

最安全的方法: 备份数据库,重新上传干净的程序,再恢复数据库。

方法3:改所有密码

  • 后台密码
  • 数据库密码
  • FTP密码
  • SSH密码

九、预防措施

1. 程序及时更新

WordPress、ThinkPHP等程序有漏洞要及时更新。

2. 上传目录禁执行

location ~* /upload/.*\.php$ {
    deny all;
}

3. 定期备份

每天自动备份网站文件和数据库。

4. 改后台地址

不要用默认的admin后台地址。

5. 用安全插件

WordPress可以装Wordfence等安全插件。

十、常见坑

坑1:只删文件不改密码

黑客可能已经留了别的后门,删一个没用。

正确: 删文件 + 改所有密码 + 看定时任务。

坑2:备份也被感染了

备份文件里也有后门,恢复后又被黑了。

解决: 备份也要查。

坑3:只查网站目录

黑客可能在系统里留了后门(比如/tmp目录)。

解决: 也要查系统进程、定时任务。

总结

记住:

  1. 先找最近修改的文件
  2. 再找可疑代码(eval、base64_decode)
  3. 看Web日志和进程
  4. 看定时任务
  5. 删后门 + 改所有密码
  6. 最安全是重新上传干净程序

最快排查:

find /www/wwwroot/你的站/ -name "*.php" -mtime -7
grep -r "eval(" /www/wwwroot/你的站/

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:44 

Docker容器数据备份和迁移方法

前言

Docker容器删了数据就没了?想把容器从一台服务器迁到另一台?本文讲Docker容器数据备份和迁移的几种方法,包括容器快照、数据卷备份、镜像导出。

一、Docker数据存哪里

Docker数据有两种存法:

  1. 容器内部:数据存在容器的可写层,容器删了数据就没了
  2. 数据卷(Volume):数据存在宿主机上,容器删了数据还在

重要: 生产环境一定要用数据卷,不然容器一删数据全没。

二、方法1:容器快照(commit)

把容器当前状态打包成一个新镜像。

步骤

第一步:查看容器ID

docker ps

找到要备份的容器ID。

第二步:提交成新镜像

docker commit 容器ID 备份镜像名:标签

例子:

docker commit mynginx nginx-backup:v1

第三步:导出镜像成文件

docker save -o nginx-backup.tar nginx-backup:v1

第四步:传到新服务器

scp nginx-backup.tar root@新服务器IP:/root/

第五步:新服务器导入

docker load -i nginx-backup.tar

第六步:新服务器启动容器

docker run -d --name mynginx -p 80:80 nginx-backup:v1

优缺点

优点: 简单,整个容器都打包了
缺点: 镜像文件大,不适合大数据量

三、方法2:数据卷备份

如果用了数据卷,直接备份数据卷里的文件。

步骤

第一步:看数据卷挂载在哪

docker inspect 容器名 | grep Mounts

输出里找 Source,就是宿主机上的路径。

例子:

"Mounts": [
    {
        "Source": "/data/mysql",
        "Destination": "/var/lib/mysql"
    }
]

数据在宿主机的 /data/mysql 目录。

第二步:打包备份

tar -zcvf mysql-backup.tar.gz /data/mysql

第三步:传到新服务器

scp mysql-backup.tar.gz root@新服务器IP:/root/

第四步:新服务器解压

tar -zxvf mysql-backup.tar.gz -C /data/

第五步:新服务器启动容器,挂载数据卷

docker run -d --name mysql -v /data/mysql:/var/lib/mysql -p 3306:3306 mysql:5.7

优缺点

优点: 数据和容器分离,灵活
缺点: 要自己管理数据目录

四、方法3:docker cp拷贝文件

把容器里的文件拷出来。

拷出来

docker cp 容器名:/容器内路径 宿主机路径

例子:

docker cp mynginx:/usr/share/nginx/html /data/backup/html

拷进去

docker cp 宿主机路径 容器名:/容器内路径

例子:

docker cp /data/backup/html/index.html mynginx:/usr/share/nginx/html/

五、MySQL容器备份

备份MySQL数据

docker exec 容器名 mysqldump -uroot -p123456 数据库名 > backup.sql

例子:

docker exec mysql mysqldump -uroot -p123456 test > test.sql

恢复MySQL数据

docker exec -i 容器名 mysql -uroot -p123456 数据库名 < backup.sql

例子:

docker exec -i mysql mysql -uroot -p123456 test < test.sql

六、常见坑

坑1:容器删了数据没了

没用数据卷,容器删了数据全没。

解决: 一定要用 -v 挂载数据卷。

坑2:commit的镜像太大

把整个容器都打包了,包括临时文件。

解决: 尽量用数据卷备份,不要用commit。

坑3:数据权限不对

备份恢复后,容器里读不了数据。

原因: 文件所有者不对。

解决: 改权限:

chown -R 999:999 /data/mysql

(MySQL容器用的是999用户)

坑4:数据库没停就备份

MySQL还在写数据,备份出来不一致。

解决: 用mysqldump逻辑备份,不要直接拷文件。

总结

记住:

  1. 生产环境一定要用数据卷(-v)
  2. 小容器用commit快照备份
  3. 大数据量直接备份数据卷目录
  4. 数据库用mysqldump逻辑备份
  5. 备份完要测试能不能恢复

最常用:

# 备份数据卷
tar -zcvf backup.tar.gz /data/容器数据/

# 恢复数据卷
tar -zxvf backup.tar.gz -C /data/

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:43 

MySQL数据库备份和恢复,mysqldump完整教程

前言

数据库备份是每个站长的基本功。MySQL最常用的备份工具就是mysqldump,简单好用。本文讲怎么用mysqldump备份和恢复数据库,包括完整命令和常见坑。

一、mysqldump是什么

mysqldump是MySQL自带的备份工具,把数据库导出成SQL文件,需要的时候再导入回去。

特点:

  • 免费,MySQL自带
  • 导出的是SQL文本,方便查看和编辑
  • 适合小数据库(几十G以内)

二、备份数据库

备份单个数据库

mysqldump -u用户名 -p密码 数据库名 > 备份文件.sql

例子:

mysqldump -uroot -p123456 test > test.sql

注意: -p和密码之间没有空格。

备份多个数据库

mysqldump -uroot -p123456 --databases db1 db2 db3 > dbs.sql

备份所有数据库

mysqldump -uroot -p123456 --all-databases > all.sql

备份单个表

mysqldump -uroot -p123456 test users > users.sql

备份多个表

mysqldump -uroot -p123456 test users orders > tables.sql

三、常用参数

带删除表语句(--add-drop-table)

导出的SQL里会有 DROP TABLE IF EXISTS,恢复的时候先删表再建表。

mysqldump -uroot -p123456 --add-drop-table test > test.sql

默认就是开的。

不带建表语句(--no-create-info)

只导数据,不导表结构。

mysqldump -uroot -p123456 --no-create-info test > data.sql

只导表结构(--no-data)

只导表结构,不导数据。

mysqldump -uroot -p123456 --no-data test > structure.sql

压缩备份

mysqldump -uroot -p123456 test | gzip > test.sql.gz

解压:

gunzip test.sql.gz

指定端口

mysqldump -uroot -p123456 -P 3307 test > test.sql

指定主机

mysqldump -uroot -p123456 -h 1.2.3.4 test > test.sql

四、恢复数据库

方法1:用mysql命令

mysql -u用户名 -p密码 数据库名 < 备份文件.sql

例子:

mysql -uroot -p123456 test < test.sql

方法2:登录MySQL后source

mysql -uroot -p123456
mysql> use test;
mysql> source /root/test.sql;

恢复压缩备份

gunzip < test.sql.gz | mysql -uroot -p123456 test

五、定时备份脚本

写个备份脚本

#!/bin/bash

# 数据库配置
DB_USER="root"
DB_PASS="123456"
DB_NAME="test"

# 备份目录
BACKUP_DIR="/data/backup"

# 文件名(带日期)
DATE=$(date +%Y%m%d_%H%M%S)
FILE_NAME="${DB_NAME}_${DATE}.sql.gz"

# 备份
mysqldump -u${DB_USER} -p${DB_PASS} ${DB_NAME} | gzip > ${BACKUP_DIR}/${FILE_NAME}

# 删除7天前的备份
find ${BACKUP_DIR} -name "*.sql.gz" -mtime +7 -delete

echo "备份完成:${FILE_NAME}"

加执行权限

chmod +x /root/backup.sh

加到crontab

每天凌晨3点备份:

crontab -e

加一行:

0 3 * * * /root/backup.sh

六、常见坑

坑1:密码写在命令行里不安全

-p123456 会在进程列表里看到密码。

更安全的方法:

mysqldump -uroot -p test > test.sql

然后手动输入密码。

或者用配置文件:

# ~/.my.cnf
[mysqldump]
user=root
password=123456

然后直接:

mysqldump test > test.sql

坑2:大表备份很慢

数据多了以后,mysqldump会锁表,影响业务。

解决: 用 --single-transaction 参数:

mysqldump -uroot -p123456 --single-transaction test > test.sql

这个参数适合InnoDB,不锁表。

坑3:字符集不对

导出的SQL乱码。

加参数:

mysqldump -uroot -p123456 --default-character-set=utf8mb4 test > test.sql

坑4:自增ID不连续

恢复后自增ID变了。

正常现象,不用管。

坑5:备份文件太大

用gzip压缩:

mysqldump -uroot -p123456 test | gzip > test.sql.gz

七、备份验证

备份完了要验证一下能不能恢复。

看文件大小

ls -lh test.sql

不能是0字节。

看文件内容

head -20 test.sql

看开头是不是正常的SQL。

测试恢复

找个测试库恢复一下:

mysql -uroot -p123456 test_restore < test.sql

总结

记住:

  1. 备份:mysqldump -uroot -p123456 数据库 > 备份.sql
  2. 恢复:mysql -uroot -p123456 数据库 < 备份.sql
  3. 大表加 --single-transaction 不锁表
  4. 用gzip压缩备份文件
  5. 定时备份要自动删旧备份
  6. 备份完要验证

最常用:

# 备份
mysqldump -uroot -p123456 --single-transaction test | gzip > test.sql.gz

# 恢复
gunzip < test.sql.gz | mysql -uroot -p123456 test

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:41 

Nginx 502 Bad Gateway常见原因和解决办法

前言

网站突然打不开了,浏览器显示"502 Bad Gateway"?这是Nginx最常见的报错之一。本文讲502是什么意思、常见原因、怎么排查和解决。

一、502是什么意思

502 Bad Gateway = Nginx作为代理,从后端服务器(比如PHP-FPM)拿不到正常的响应。

简单说:Nginx收到了你的请求,然后去问后端"给我数据",后端没理它或者报错了,Nginx就返回502。

二、常见原因

原因1:PHP-FPM没启动

后端PHP服务没跑,Nginx找不到人。

检查:

ps aux | grep php-fpm

没输出就是没启动。

启动:

systemctl start php-fpm

或者:

service php-fpm start

原因2:PHP-FPM端口不对

Nginx配置里写的PHP-FPM地址不对。

比如:

fastcgi_pass 127.0.0.1:9000;

但PHP-FPM实际监听的是 /tmp/php-cgi.sock。

检查PHP-FPM监听:

netstat -tlnp | grep php-fpm

或者:

ss -tlnp | grep php-fpm

原因3:PHP代码报错

PHP代码有致命错误,直接挂了。

看PHP错误日志:

tail -f /var/log/php-fpm/www-error.log

或者:

tail -f /var/log/php/error.log

原因4:PHP超时了

程序跑太慢,超过Nginx或PHP-FPM的超时时间。

改Nginx超时:

fastcgi_read_timeout 300;

改PHP-FPM超时:

request_terminate_timeout = 300

原因5:PHP-FPM进程数不够

请求太多,PHP-FPM忙不过来。

看进程数:

ps aux | grep php-fpm | wc -l

改配置:

pm.max_children = 50
pm.start_servers = 20

原因6:内存不够了

PHP进程占满内存,系统把PHP杀了。

看内存:

free -h

看dmesg:

dmesg | grep oom

三、排查步骤

第一步:看Nginx错误日志

tail -f /var/log/nginx/error.log

常见报错:

报错 含义
connect() failed (111: Connection refused) PHP-FPM没启动
Primary script unknown 文件路径不对
upstream timed out PHP超时了
recv() failed (104: Connection reset by peer) PHP-FPM挂了

第二步:看PHP-FPM状态

systemctl status php-fpm

看是不是running。

第三步:看PHP错误日志

tail -f /var/log/php-fpm/www-error.log

看PHP代码有没有报错。

第四步:看系统资源

top
free -h
df -h

看CPU、内存、磁盘够不够。

四、常见解决方法

方法1:重启PHP-FPM

systemctl restart php-fpm

方法2:改Nginx配置

location ~ \.php$ {
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;

    # 超时时间
    fastcgi_read_timeout 300;
    fastcgi_send_timeout 300;
}

方法3:改PHP-FPM进程数

pm = dynamic
pm.max_children = 50
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 30

改完重启:

systemctl restart php-fpm

方法4:开PHP错误显示

display_errors = On
error_reporting = E_ALL

五、常见坑

坑1:只重启Nginx,不重启PHP-FPM

502是后端的问题,重启Nginx没用。

正确: 重启PHP-FPM。

坑2:文件路径不对

fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;

错! 应该是:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

坑3:PHP-FPM版本不对

装了PHP7但Nginx还找PHP5的sock。

检查:

ls -la /var/run/php/

坑4:权限问题

PHP-FPM没权限读网站目录的文件。

解决:

chown -R www:www /www/wwwroot/

总结

记住:

  1. 502 = Nginx拿不到后端响应
  2. 先看Nginx错误日志
  3. 再看PHP-F启没启动
  4. 然后看PHP代码有没有报错
  5. 最后看系统资源够不够

最快排查:

tail -f /var/log/nginx/error.log
systemctl status php-fpm

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:40 

Linux查看系统负载,uptime和w命令详解

前言

服务器卡不卡,先看负载。Linux看系统负载最常用的就是uptime和w命令,一行输出就能看到系统跑了多久、现在有多少人登录、负载高不高。本文讲怎么看这些信息。

一、uptime命令

直接敲

uptime

输出:

 07:39:01 up 35 days,  2:15,  2 users,  load average: 0.08, 0.03, 0.00

逐个解释

字段 含义
07:39:01 当前时间
up 35 days, 2:15 系统运行了35天2小时15分
2 users 当前有2个用户登录
load average: 0.08, 0.03, 0.00 系统负载,分别是1分钟、5分钟、15分钟

负载怎么看

load average三个数:

  • 第一个:最近1分钟的平均负载
  • 第二个:最近5分钟的平均负载
  • 第三个:最近15分钟的平均负载

判断标准:

负载值 状态
0.00-0.70 正常
0.70-1.00 注意一下
1.00-2.00 有点高
2.00以上 很高了,要排查

注意: 负载和CPU核数有关。比如4核CPU,负载4.00才是满。

看CPU核数

nproc
# 4

或者:

cat /proc/cpuinfo | grep processor | wc -l
# 4

判断: 负载小于CPU核数就是正常的。

二、w命令

直接敲

w

输出:

 07:39:01 up 35 days,  2:15,  2 users,  load average: 0.08, 0.03, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/0    1.2.3.4          07:35    5.00s  0.03s  0.00s w
www      pts/1    5.6.7.8          07:30    8:12   0.01s  0.01s -bash

逐个解释

第一行和uptime一样。

下面是登录用户列表:

字段 含义
USER 用户名
TTY 终端类型(pts/0表示远程连接)
FROM 从哪个IP登录的
LOGIN@ 登录时间
IDLE 空闲时间
JCPU 整个终端所有进程用的CPU时间
PCPU 当前进程用的CPU时间
WHAT 当前在跑什么命令

三、其他查看命令

who命令

看谁登录了:

who

输出:

root     pts/0        2026-10-09 07:35 (1.2.3.4)
www      pts/1        2026-10-09 07:30 (5.6.7.8)

last命令

看登录历史:

last

输出最近登录的记录。

lastb命令

看失败的登录:

lastb

四、负载高了怎么排查

第一步:看CPU

top

按P按CPU排序,看哪个进程占CPU多。

第二步:看内存

free -h

看内存够不够,有没有用swap。

第三步:看磁盘

df -h

看磁盘满了没。

第四步:看IO

iostat

看磁盘IO高不高。

五、常见坑

坑1:负载高不代表CPU高

负载包括:

  • 正在用CPU的进程
  • 等着CPU的进程
  • 等着磁盘IO的进程

可能是磁盘IO高,CPU不高,但负载也高。

坑2:负载和核数对比

1核CPU,负载1.0就是满了。
4核CPU,负载4.0才是满了。

别光看数字,要看CPU核数。

坑3:三个负载数的区别

  • 1分钟高,5分钟、15分钟不高:可能是突然有个进程跑了一下
  • 15分钟都高:说明系统一直很忙

总结

记住:

  1. uptime 看系统运行时间和负载
  2. w 看谁登录了、在干嘛
  3. 负载三个数:1分钟、5分钟、15分钟
  4. 负载要和CPU核数对比
  5. 负载高了用top、free、df、iostat排查

最常用:

uptime    # 看负载
w         # 看谁在登录
top       # 看哪个进程占CPU

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:39 

PHP实现简单分页,SQL limit用法

前言

做网站列表页,数据太多一页显示不完,需要分页。PHP+MySQL做分页很简单,就是用SQL的limit。本文写个完整的分页示例,直接拿去用。

一、分页原理

分页就两件事:

  1. 查总条数:看看一共有多少条数据
  2. 查当前页数据:用limit取当前页要显示的那几条

公式:

偏移量 = (当前页 - 1) × 每页条数

比如每页10条,第3页:

偏移量 = (3 - 1) × 10 = 20
SQL: SELECT * FROM users LIMIT 20, 10

二、SQL limit用法

语法

SELECT * FROM 表名 LIMIT 偏移量, 每页条数

例子

-- 取前10条
SELECT * FROM users LIMIT 0, 10;

-- 取第11-20条
SELECT * FROM users LIMIT 10, 10;

-- 取第21-30条
SELECT * FROM users LIMIT 20, 10;

三、完整分页代码

第一步:接收当前页码

<?php
// 当前页,默认第1页
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;

// 每页显示多少条
$pageSize = 10;

// 计算偏移量
$offset = ($page - 1) * $pageSize;

第二步:查总条数

// 连接数据库
$conn = mysqli_connect('127.0.0.1', 'root', '123456', 'test', 3306);
mysqli_set_charset($conn, 'utf8mb4');

// 查总条数
$sql = "SELECT COUNT(*) as total FROM users";
$result = mysqli_query($conn, $sql);
$row = mysqli_fetch_assoc($result);
$total = $row['total'];

// 算总页数
$totalPage = ceil($total / $pageSize);

第三步:查当前页数据

// 查当前页的数据
$sql = "SELECT id, name, age FROM users ORDER BY id DESC LIMIT $offset, $pageSize";
$result = mysqli_query($conn, $sql);

// 取数据
$list = [];
while ($row = mysqli_fetch_assoc($result)) {
    $list[] = $row;
}

第四步:显示数据和分页导航

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>用户列表</title>
</head>
<body>

<h1>用户列表</h1>

<table border="1">
    <tr>
        <th>ID</th>
        <th>姓名</th>
        <th>年龄</th>
    </tr>
    <?php foreach ($list as $item): ?>
    <tr>
        <td><?php echo $item['id']; ?></td>
        <td><?php echo $item['name']; ?></td>
        <td><?php echo $item['age']; ?></td>
    </tr>
    <?php endforeach; ?>
</table>

<p>共 <?php echo $total; ?> 条,第 <?php echo $page; ?> / <?php echo $totalPage; ?> 页</p>

<p>
    <?php if ($page > 1): ?>
    <a href="?page=<?php echo $page - 1; ?>">上一页</a>
    <?php endif; ?>

    <?php for ($i = 1; $i <= $totalPage; $i++): ?>
        <?php if ($i == $page): ?>
            <strong><?php echo $i; ?></strong>
        <?php else: ?>
            <a href="?page=<?php echo $i; ?>"><?php echo $i; ?></a>
        <?php endif; ?>
    <?php endfor; ?>

    <?php if ($page < $totalPage): ?>
    <a href="?page=<?php echo $page + 1; ?>">下一页</a>
    <?php endif; ?>
</p>

</body>
</html>

四、封装成分页函数

function pagination($page, $totalPage, $url = '')
{
    $html = '';

    if ($page > 1) {
        $html .= '<a href="' . $url . '?page=' . ($page - 1) . '">上一页</a> ';
    }

    for ($i = 1; $i <= $totalPage; $i++) {
        if ($i == $page) {
            $html .= '<strong style="margin:0 5px;">' . $i . '</strong> ';
        } else {
            $html .= '<a href="' . $url . '?page=' . $i . '" style="margin:0 5px;">' . $i . '</a> ';
        }
    }

    if ($page < $totalPage) {
        $html .= '<a href="' . $url . '?page=' . ($page + 1) . '">下一页</a>';
    }

    return $html;
}

// 使用
echo pagination($page, $totalPage);

五、常见坑

坑1:页码没做限制

用户输入 ?page=999,结果查出来是空的。

解决: 限制页码范围:

$page = max(1, min($page, $totalPage));

坑2:offset算错了

$offset = $page * $pageSize; // 错!

正确:

$offset = ($page - 1) * $pageSize; // 对!

坑3:总条数查错了

带条件查询的时候,总条数也要带条件。

比如:

// 带条件查数据
$sql = "SELECT * FROM users WHERE status = 1 LIMIT $offset, $pageSize";

// 总条数也要带条件
$sql = "SELECT COUNT(*) FROM users WHERE status = 1";

坑4:大表分页越查越慢

数据多了以后,LIMIT 100000, 10 会很慢。

原因: MySQL要先扫描前面10万条再跳过。

优化:

-- 先查id,再关联
SELECT u.* FROM users u
INNER JOIN (SELECT id FROM users ORDER BY id LIMIT 100000, 10) t
ON u.id = t.id;

六、带搜索条件的分页

// 搜索关键词
$keyword = isset($_GET['keyword']) ? trim($_GET['keyword']) : '';

// 拼WHERE
$where = "WHERE 1=1";
if ($keyword) {
    $where .= " AND name LIKE '%$keyword%'";
}

// 总条数
$sql = "SELECT COUNT(*) as total FROM users $where";

// 当前页数据
$sql = "SELECT * FROM users $where ORDER BY id DESC LIMIT $offset, $pageSize";

总结

记住:

  1. 分页就是 LIMIT 偏移量, 每页条数
  2. 偏移量 = (当前页 - 1) × 每页条数
  3. 先查总条数,再算总页数
  4. 页码要做限制,不能超出范围
  5. 带条件查询时,总条数也要带条件

核心公式:

$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
$pageSize = 10;
$offset = ($page - 1) * $pageSize;

$sql = "SELECT * FROM users LIMIT $offset, $pageSize";

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:37 

Redis 缓存穿透解决

Redis 缓存穿透解决

缓存穿透是常见问题,恶意请求能把数据库打挂。

1. 什么是缓存穿透

现象: 请求的数据在缓存和数据库中都不存在,每次请求都直接打到数据库。

危害:

  • 恶意攻击时数据库压力大
  • 正常请求被拖慢
  • 数据库CPU飙升

2. 为什么会穿透

  • 查询一个不存在的ID
  • 缓存没有命中
  • 数据库也查不到
  • 请求直接穿透到数据库

3. 解决方案一:缓存空值

思路: 即使数据库查不到,也在Redis存一个空值,设置短过期时间。

function getUser($id) {
    $key = "user:$id";
    $user = $redis->get($key);

    if ($user !== false) {
        // 空值标记
        if ($user === 'null') {
            return null;
        }
        return json_decode($user, true);
    }

    // 查数据库
    $user = $db->query("SELECT * FROM users WHERE id = $id");

    if ($user) {
        // 存在就缓存1小时
        $redis->setex($key, 3600, json_encode($user));
    } else {
        // 不存在也缓存,5分钟过期
        $redis->setex($key, 300, 'null');
    }

    return $user;
}

优点: 简单有效
缺点: 占内存,可能被恶意key刷爆

4. 解决方案二:布隆过滤器

思路: 在缓存前面加一层布隆过滤器,不存在的key直接拦截。

原理:

  • 所有存在的数据先存到布隆过滤器
  • 请求先过布隆过滤器
  • 不存在直接返回,不查缓存和数据库

PHP 示例(用 Redis 的布隆过滤器模块):

# 加载 RedisBloom 模块
redis-cli BF.ADD user:1
redis-cli BF.EXISTS user:1
function getUser($id) {
    $key = "user:$id";

    // 先过布隆过滤器
    if (!$redis->rawCommand('BF.EXISTS', 'user_filter', $id)) {
        return null; // 一定不存在
    }

    // 布隆说可能存在,再查缓存
    $user = $redis->get($key);
    if ($user) {
        return json_decode($user, true);
    }

    // 查数据库...
}

优点: 内存占用小,效率高
缺点: 有误判率,实现复杂

5. 解决方案三:参数校验

思路: 从源头过滤非法请求。

function getUser($id) {
    // 参数校验
    if ($id <= 0 || !is_numeric($id)) {
        return null;
    }

    // 正常查询逻辑...
}

适用: ID类查询,不合法直接拦截

6. 三种方案对比

方案 优点 缺点 适用场景
缓存空值 简单 占内存 数据量不大
布隆过滤器 高效 误判,复杂 数据量大
参数校验 最直接 只防非法 ID类请求

7. 生产建议

1)组合使用

  • 先做参数校验
  • 再缓存空值
  • 大流量用布隆过滤器

2)空值过期时间要短

  • 5分钟到10分钟
  • 不要太长

3)监控异常请求

  • 发现大量404要警惕
  • 及时加防护

4)接口限流

  • 用 Nginx 或网关限流
  • 防止恶意刷

8. 缓存雪崩和击穿

缓存穿透: 查不存在的数据
缓存击穿: 热点key过期,大量请求打数据库
缓存雪崩: 大量key同时过期

区分清楚: 三个问题现象类似但原因不同,解决方案也不同。

总结

Redis 缓存穿透解决要点:

  • 查不存在的数据穿透到数据库
  • 缓存空值是最简单方案
  • 布隆过滤器适合大数据量
  • 参数校验从源头拦截
  • 空值过期时间要短
  • 生产建议组合使用
  • 注意和雪崩、击穿区分开
  • 监控异常请求
  • 接口加限流

缓存穿透是面试高频题,也是生产常见问题,一定要掌握。


emer 发布于  2026-10-9 07:36 

PHP报错Permission denied权限问题解决

前言

PHP写文件、读文件、上传文件,突然报"Permission denied"?这是Linux文件权限问题,最常见就是文件或目录的所有者不对、权限不够。本文讲怎么排查和解决。

一、报错长啥样

Warning: fopen(/data/test.txt): failed to open stream: Permission denied in /www/test.php on line 5

意思:PHP没权限操作这个文件/目录。

二、常见原因

原因1:文件/目录权限不够

PHP没有读或写的权限。

原因2:文件/目录所有者不对

PHP用www用户跑,但文件是root创建的,www没权限。

原因3:SELinux开着

CentOS默认开SELinux,即使权限对了也不让访问。

原因4:目录权限不够

要写文件的话,目录本身也要有写权限。

三、排查步骤

第一步:看报错的文件路径

报错信息里会告诉你是哪个文件:

Permission denied: /data/upload/test.jpg

就是 /data/upload/ 这个目录的问题。

第二步:看文件权限

ls -l /data/upload/

输出类似:

-rw-r--r-- 1 root root 1024 Oct  9 10:00 test.txt

看第一个字段:

  • -rw-r--r-- 表示所有者能读写,其他人只能读

看所有者:

  • root root 表示所有者和组都是root

第三步:看PHP用什么用户跑

ps aux | grep php-fpm

输出里会看到:

www   1234  0.0  0.5 123456 7890  ?  S  10:00  0:00 php-fpm: pool www

PHP是用 www 用户跑的。

如果文件所有者是root,那www用户就没权限写。

四、解决方法

方法1:改文件所有者

把文件/目录的所有者改成PHP的用户:

# 改单个文件
chown www:www /data/upload/test.txt

# 改整个目录(包括里面的文件)
chown -R www:www /data/upload/

推荐这个方法,最安全。

方法2:改权限

给所有人加权限:

# 文件加写权限
chmod 666 /data/upload/test.txt

# 目录加写权限
chmod 777 /data/upload/

注意: 777太不安全了,尽量用方法1改所有者。

方法3:看SELinux

CentOS开了SELinux的话,即使权限对了也不行。

检查SELinux:

getenforce

输出 Enforcing 就是开着。

临时关掉:

setenforce 0

永久关掉:

vi /etc/selinux/config

把:

SELINUX=enforcing

改成:

SELINUX=disabled

重启服务器生效。

或者只给目录加SELinux权限:

chcon -R -t httpd_sys_rw_content_t /data/upload/

五、常见场景

场景1:上传图片报错

用户上传图片,报Permission denied。

原因: uploads目录所有者不对。

解决:

chown -R www:www /www/wwwroot/你的站/uploads/

场景2:写日志报错

PHP写日志文件,报Permission denied。

原因: 日志目录没权限。

解决:

chown -R www:www /var/log/你的应用/

场景3:session不存

PHP session存不进去。

原因: session目录没权限。

解决:

chown -R www:www /var/lib/php/session/

场景4:缓存目录没权限

ThinkPHP/Laravel缓存写不进去。

原因: runtime目录没权限。

解决:

chown -R www:www runtime/

六、常用命令

看文件权限

ls -l 文件名

看目录权限

ls -ld 目录名

改所有者

chown 用户:组 文件
chown -R 用户:组 目录

改权限

chmod 755 目录
chmod 644 文件

七、权限数字含义

数字 含义
7 读+写+执行 (4+2+1)
6 读+写 (4+2)
5 读+执行 (4+1)
4 只读

常用:

  • 目录:755
  • 文件:644
  • 上传目录:775(或改所有者)

八、常见坑

坑1:只改了文件,没改目录

要写文件的话,目录本身也要有写权限。

解决: 目录也要改所有者。

坑2:递归改权限

chmod -R 777 目录

太危险了,所有人都能写。

解决: 优先改所有者,不要随便777。

坑3:SELinux偷偷开着

CentOS7以后默认开SELinux,权限对了也不行。

检查:

getenforce

坑4:父目录没权限

比如 /data/upload/ 有权限了,但 /data/ 没权限,还是进不去。

解决: 从根开始一层一层检查。

总结

记住:

  1. 报错先看是哪个文件/目录
  2. 看文件所有者是不是PHP的用户(一般是www)
  3. 不是就 chown -R www:www 目录
  4. 还不行就检查SELinux
  5. 尽量改所有者,不要随便777

最常用:

chown -R www:www /你的目录/

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:36 

Nginx 配置HTTPS证书

Nginx 配置HTTPS证书

现在网站都要HTTPS,配置证书是必备技能。

1. 为什么要HTTPS

  • 加密传输,防窃听
  • 浏览器安全标识
  • SEO 排名加分
  • 现在主流浏览器都要求HTTPS

2. 证书文件

一般有两个文件:

  • .crt:证书文件
  • .key:私钥文件
# 证书文件放在这里
/etc/nginx/ssl/
├── example.com.crt
└── example.com.key

3. 基本HTTPS配置

server {
    listen 443 ssl;
    server_name example.com;

    # 证书配置
    ssl_certificate /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    # 其他配置
    location / {
        root /var/www/html;
        index index.html;
    }
}

4. HTTP 跳转 HTTPS

# HTTP 重定向到 HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

5. 优化配置

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    # 优化SSL配置
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;

    # 加密套件
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # HSTS
    add_header Strict-Transport-Security "max-age=63072000" always;
}

6. 检查配置

# 检查配置语法
nginx -t

# 重载配置
nginx -s reload

7. 证书申请

免费证书:

  • Let's Encrypt(用 certbot 申请)
  • 阿里云免费证书
  • 腾讯云免费证书

certbot 申请:

# 安装 certbot
yum install certbot python3-certbot-nginx

# 自动申请并配置
certbot --nginx -d example.com

自动续期:

# 测试续期
certbot renew --dry-run

# 加入定时任务
0 3 * * * certbot renew --quiet

8. 常见问题

1)证书不生效

  • 检查证书路径对不对
  • 检查文件权限
  • 重载 Nginx

2)浏览器提示不安全

  • 证书过期了
  • 域名不匹配
  • 中间证书没装全

3)HTTPS 访问慢

  • 开启 HTTP/2
  • 优化 SSL 会话缓存

4)证书续期失败

  • 检查域名解析
  • 检查80端口是否通
  • 手动运行 certbot 看报错

9. 证书有效期

  • 免费证书一般 3 个月
  • 要设置自动续期
  • 不然到期网站就挂了

10. 生产建议

1)强制 HTTPS

  • HTTP 全部 301 跳转
  • 开启 HSTS

2)开启 HTTP/2

  • listen 443 ssl http2
  • 提升访问速度

3)定期检查证书

  • 监控证书到期时间
  • 提前续期

4)中间证书

  • 有些证书要把中间证书合并
  • 不然浏览器不信任

总结

Nginx 配置HTTPS要点:

  • 配置证书和私钥路径
  • HTTP 跳转 HTTPS
  • 开启 HTTP/2
  • 优化 SSL 会话缓存
  • 用 certbot 免费申请
  • 设置自动续期
  • 定期检查证书有效期
  • 开启 HSTS
  • 注意中间证书问题

HTTPS 是网站标配,一定要配置好。


emer 发布于  2026-10-9 07:35 

PHP file_get_contents获取https内容失败

前言

PHP用file_get_contents获取http内容没问题,换成https就报错了?这是最常见的问题,一般就是openssl扩展没开,或者SSL证书验证没过。本文讲怎么解决。

一、问题现象

<?php
$url = 'https://api.example.com/data';
$result = file_get_contents($url);

echo $result;

结果报错:

Warning: file_get_contents(): failed to open stream: Unable to find the wrapper "https"

或者:

Warning: file_get_contents(): SSL operation failed with code 1

二、常见原因

原因1:openssl扩展没装

PHP默认不开openssl扩展,file_get_contents不支持https。

原因2:allow_url_open没开

php.ini里禁了远程打开。

原因3:SSL证书验证失败

对方网站证书有问题,PHP验证不通过。

原因4:网络问题

服务器出不去,或者DNS解析不了。

三、排查步骤

第一步:看openssl扩展装了没

php -m | grep openssl

有输出就是装了,没输出就是没装。

或者写个phpinfo:

<?php
phpinfo();

搜"openssl",看有没有。

第二步:看allow_url_open开了没

<?php
echo ini_get('allow_url_fopen');

输出1就是开了,空就是没开。

第三步:用curl测试

curl https://api.example.com/data

curl能通就是PHP的问题,curl也不通就是网络问题。

四、解决方法

方法1:装openssl扩展(推荐)

CentOS

yum install php-openssl

或者:

yum install php7x-openssl

(x是你的PHP版本)

Ubuntu

apt install php-openssl

装完重启

systemctl restart php-fpm

方法2:改php.ini配置

打开php.ini,找到:

;extension=openssl

去掉前面的分号:

extension=openssl

然后重启php-fpm。

方法3:关掉SSL验证(临时)

不想改配置,可以在代码里关验证:

<?php
$context = stream_context_create([
    'ssl' => [
        'verify_peer' => false,
        'verify_peer_name' => false
    ]
]);

$result = file_get_contents('https://api.example.com/data', false, $context);

echo $result;

注意: 这是临时方案,生产环境建议把证书配好。

方法4:改用curl(推荐)

file_get_contents不好用,直接用curl:

<?php
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/data');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);

$result = curl_exec($ch);

if (curl_errno($ch)) {
    echo '错误: ' . curl_error($ch);
}

curl_close($ch);

echo $result;

curl比file_get_contents好用多了,推荐用curl。

五、常见坑

坑1:CLI和FPM配置不一样

命令行里openssl装了,网页上还是不行。

原因: CLI和FPM用不同的php.ini。

解决: 看phpinfo里的配置路径,改对应的那个。

坑2:装了扩展没重启

装完忘了重启php-fpm。

解决:

systemctl restart php-fpm

坑3:对方网站证书过期

对方网站SSL证书过期了,PHP验证不通过。

解决: 关验证,或者让对方更新证书。

坑4:allow_url_fopen没开

php.ini里禁了远程打开。

解决:

allow_url_fopen = On

坑5:防火墙挡了出站

服务器防火墙挡了443端口出站。

解决:

# 看能不能出去
curl -I https://www.baidu.com

# 不行的话开防火墙
firewall-cmd --add-port=443/tcp --permanent
firewall-cmd --reload

六、POST请求示例

file_get_contents发POST:

<?php
$url = 'https://api.example.com/submit';
$data = http_build_query([
    'name' => '张三',
    'age' => 20
]);

$context = stream_context_create([
    'http' => [
        'method' => 'POST',
        'header' => "Content-type: application/x-www-form-urlencoded\r\n",
        'content' => $data,
        'timeout' => 30
    ],
    'ssl' => [
        'verify_peer' => false,
        'verify_peer_name' => false
    ]
]);

$result = file_get_contents($url, false, $context);

echo $result;

还是建议用curl,更灵活。

七、封装函数

function http_get($url, $timeout = 30)
{
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);

    $result = curl_exec($ch);

    if (curl_errno($ch)) {
        $error = curl_error($ch);
        curl_close($ch);
        return ['code' => -1, 'msg' => $error];
    }

    curl_close($ch);
    return ['code' => 0, 'data' => $result];
}

// 使用
$res = http_get('https://api.example.com/data');
if ($res['code'] == 0) {
    echo $res['data'];
} else {
    echo '请求失败: ' . $res['msg'];
}

总结

记住:

  1. file_get_contents不支持https,是openssl扩展没装
  2. 装openssl扩展,重启php-fpm
  3. 或者关SSL验证临时解决
  4. 最推荐:改用curl,比file_get_contents好用多了

推荐方案:

直接用curl,别用file_get_contents搞https。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:34 

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 index.php

提交:

git commit -m "修复bug"

添加并提交:

git commit -am "修复bug"

4. 远程操作

查看远程仓库:

git remote -v

拉取远程更新:

git pull

推送代码:

git push

推送并设置上游分支:

git push -u origin dev

5. 分支操作

查看本地分支:

git branch

查看所有分支:

git branch -a

创建分支:

git branch dev

切换分支:

git checkout dev

创建并切换:

git checkout -b dev

合并分支:

# 先切换到master
git checkout master
# 合并dev分支
git merge dev

删除分支:

git branch -d dev

强制删除:

git branch -D dev

6. 撤销操作

撤销工作区修改:

git checkout -- index.php

撤销暂存区:

git reset HEAD index.php

回退到上个版本:

git reset --hard HEAD^

回退到指定版本:

git reset --hard commit_id

7. 标签操作

打标签:

git tag v1.0.0

查看标签:

git tag

推送标签:

git push origin v1.0.0

推送所有标签:

git push origin --tags

8. 暂存操作

暂存当前修改:

git stash

查看暂存列表:

git stash list

恢复暂存:

git stash pop

删除暂存:

git stash drop

9. 其他常用

配置用户名:

git config --global user.name "张三"

配置邮箱:

git config --global user.email "zhangsan@example.com"

查看配置:

git config --list

查看当前分支:

git branch --show-current

10. 常用工作流

开发新功能:

# 拉最新代码
git pull

# 创建功能分支
git checkout -b feature/user

# 开发完提交
git add .
git commit -m "添加用户功能"

# 推送
git push -u origin feature/user

总结

Git 常用命令要点:

  • git init 初始化仓库
  • git clone 克隆仓库
  • git status 看状态
  • git add 添加修改
  • git commit 提交
  • git push 推送
  • git pull 拉取
  • git branch 分支操作
  • git checkout 切换分支
  • git merge 合并分支
  • git reset 回退
  • git stash 暂存

Git 熟练了,团队协作效率提升很多。


emer 发布于  2026-10-9 07:34 

Docker 常用命令大全

Docker 常用命令大全

Docker 现在开发必备,常用命令一定要熟练。

1. 镜像命令

查看本地镜像:

docker images

搜索镜像:

docker search nginx

拉取镜像:

docker pull nginx:latest

删除镜像:

docker rmi nginx:latest

删除所有镜像:

docker rmi $(docker images -q)

构建镜像:

# 在Dockerfile目录下
docker build -t myapp:1.0 .

2. 容器命令

查看运行中的容器:

docker ps

查看所有容器(包括停止的):

docker ps -a

启动容器:

docker run -d --name mynginx -p 80:80 nginx

参数说明:

  • -d:后台运行
  • --name:容器名
  • -p:端口映射(主机:容器)
  • -v:目录挂载

停止容器:

docker stop mynginx

启动已停止的容器:

docker start mynginx

重启容器:

docker restart mynginx

删除容器:

docker rm mynginx

强制删除运行中的容器:

docker rm -f mynginx

删除所有停止的容器:

docker container prune

3. 进入容器

进入容器命令行:

docker exec -it mynginx /bin/bash

退出容器:

exit

4. 日志查看

查看容器日志:

docker logs mynginx

实时查看日志:

docker logs -f mynginx

查看最近100行日志:

docker logs --tail 100 mynginx

5. 文件操作

从主机复制到容器:

docker cp index.html mynginx:/usr/share/nginx/html/

从容器复制到主机:

docker cp mynginx:/etc/nginx/nginx.conf ./

6. 容器信息

查看容器详情:

docker inspect mynginx

查看容器端口映射:

docker port mynginx

查看容器资源使用:

docker stats

7. 数据卷

创建数据卷:

docker volume create mydata

查看数据卷:

docker volume ls

使用数据卷:

docker run -d -v mydata:/data nginx

8. 网络

查看网络:

docker network ls

创建网络:

docker network create mynet

容器加入网络:

docker network connect mynet mynginx

9. 其他常用

查看Docker版本:

docker version

查看Docker信息:

docker info

清理无用资源:

docker system prune

10. 常用组合

启动一个PHP环境:

docker run -d --name php \
  -p 8080:80 \
  -v /www:/var/www/html \
  php:apache

启动MySQL:

docker run -d --name mysql \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -v /data/mysql:/var/lib/mysql \
  mysql:5.7

启动Redis:

docker run -d --name redis \
  -p 6379:6379 \
  redis:latest

总结

Docker 常用命令要点:

  • docker images 看镜像
  • docker ps 看容器
  • docker run 启动容器
  • docker exec 进入容器
  • docker logs 看日志
  • docker cp 复制文件
  • docker stats 看资源
  • -d 后台运行
  • -p 端口映射
  • -v 目录挂载

Docker 命令熟练了,开发部署效率提升很多。


emer 发布于  2026-10-9 07:33 

PHP字符串处理常用函数,substr、explode

前言

PHP字符串处理天天用,截取、拼接、查找、替换,每个项目都离不开。本文整理最常用的字符串函数,收藏随用随查。

一、字符串长度

strlen()

字节长度(中文算3个字节):

echo strlen('hello'); // 5
echo strlen('你好');   // 6(UTF-8每个中文3字节)

mb_strlen()

字符长度(中文算1个字符):

echo mb_strlen('你好', 'utf-8'); // 2

区别: strlen看字节,mb_strlen看字符。

二、截取字符串

substr()

$str = 'hello world';

// 从第0位取5个
echo substr($str, 0, 5); // hello

// 从第6位取到最后
echo substr($str, 6); // world

// 从倒数第5位取
echo substr($str, -5); // world

mb_substr()(中文截取)

$str = '我是中国人';

// 从第0个字符取2个
echo mb_substr($str, 0, 2, 'utf-8'); // 我是

注意: 中文一定要用mb_substr,不然会乱码。

三、查找字符串

strpos()

找位置(从左找,找到返回位置,没找到返回false):

$str = 'hello world';

echo strpos($str, 'world'); // 6

// 没找到
var_dump(strpos($str, 'abc')); // bool(false)

strrpos()

从右找:

echo strrpos('a/b/c', '/'); // 3

strstr()

找并截取:

$str = 'user@example.com';

echo strstr($str, '@'); // @example.com

str_contains()(PHP8)

var_dump(str_contains('hello world', 'world')); // bool(true)

注意: PHP8以上才有。

四、替换字符串

str_replace()

$str = 'hello world';

echo str_replace('world', 'php', $str); // hello php

str_ireplace()

不区分大小写:

echo str_ireplace('WORLD', 'php', $str); // hello php

preg_replace()

正则替换:

$str = '2026-10-09';

// 把横线换成点
echo preg_replace('/-/g', '.', $str); // 2026.10.09

五、拼接和拆分

implode()(数组转字符串)

$arr = ['a', 'b', 'c'];

echo implode(',', $arr); // a,b,c
echo implode('-', $arr); // a-b-c

explode()(字符串转数组)

$str = 'a,b,c';

$arr = explode(',', $str);
// ['a', 'b', 'c']

限制拆分次数

$str = 'a,b,c,d';

// 只拆成2段
$arr = explode(',', $str, 2);
// ['a', 'b,c,d']

六、去除空白

trim()

去首尾空格:

$str = '  hello  ';

echo trim($str); // hello

ltrim() / rtrim()

ltrim($str); // 去左边
rtrim($str); // 去右边

七、大小写转换

strtoupper('hello'); // HELLO
strtolower('HELLO'); // hello
ucwords('hello world'); // Hello World
ucfirst('hello'); // Hello

八、重复和填充

str_repeat()

echo str_repeat('ab', 3); // ababab

str_pad()

// 补到10位,右边补*
echo str_pad('123', 10, '*', STR_PAD_RIGHT); // 123*******

// 左边补
echo str_pad('123', 10, '0', STR_PAD_LEFT); // 0000000123

九、常见场景

场景1:手机号中间打星号

$phone = '13812345678';

echo substr($phone, 0, 3) . '****' . substr($phone, -4);
// 138****5678

场景2:截取文件名后缀

$file = 'photo.jpg';

echo strrchr($file, '.'); // .jpg

场景3:邮箱拆分

$email = 'user@example.com';

$name = strstr($email, '@', true); // user
$domain = strstr($email, '@');    // @example.com

场景4:URL参数解析

$url = 'https://example.com/index.php?id=1&name=zhangsan';

// 拿问号后面的
$query = parse_url($url, PHP_URL_QUERY);
// id=1&name=zhangsan

parse_str($query, $params);
// ['id' => '1', 'name' => 'zhangsan']

场景5:HTML标签转义

$str = '<script>alert(1)</script>';

echo htmlspecialchars($str);
// &lt;script&gt;alert(1)&lt;/script&gt;

十、常见坑

坑1:中文截取乱码

用substr截中文,结果乱码了。

原因: substr按字节截,中文一个字3个字节,容易截到半个字。

解决: 用mb_substr:

mb_substr($str, 0, 10, 'utf-8');

坑2:strpos===0的问题

$str = 'abc';
if (strpos($str, 'a') == false) {
    echo '没找到';
}

问题: strpos找到a在位置0,0==false是true。

解决:

if (strpos($str, 'a') === false) {
    echo '没找到';
}

注意: 一定要用===,不是==。

坑3:trim只去空格

trim默认只去空格和换行。

要去指定字符:

trim($str, "/"); // 去首尾的/

坑4:explode空字符串

$str = '';
$arr = explode(',', $str);
// [''] 不是空数组!

解决:

if ($str === '') {
    $arr = [];
} else {
    $arr = explode(',', $str);
}

总结

最常用的10个:

  1. strlen() 字符串长度(字节)
  2. mb_strlen() 字符长度(中文友好)
  3. substr() 截取字符串
  4. strpos() 查找位置
  5. str_replace() 替换
  6. explode() 字符串转数组
  7. implode() 数组转字符串
  8. trim() 去空格
  9. strtoupper() / strtolower() 大小写
  10. str_pad() 填充

记住:

  • 中文用mb_*系列函数
  • 查找用=== false判断
  • 数组字符串互转用explode/implode

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:33 

MySQL 索引优化技巧

MySQL 索引优化技巧

索引是提升查询性能的关键,但用不好反而拖慢速度。

1. 为什么要建索引

  • 没有索引要全表扫描
  • 数据量大时查询很慢
  • 索引相当于书的目录
  • 合适的索引能提升百倍速度

2. 索引类型

主键索引:

  • 主键自动创建
  • 唯一且非空

普通索引:

  • 最基本的索引
  • 加速查询

唯一索引:

  • 值不能重复
  • 比如手机号

联合索引:

  • 多个字段组成
  • 遵循最左前缀原则

3. 建索引原则

1)最频繁查询的字段建索引

-- 经常按name查
CREATE INDEX idx_name ON users(name);

2)区分度高的字段建索引

  • 性别这种区分度低的不要建
  • 手机号、身份证这种适合

3)联合索引注意顺序

-- 联合索引 (a, b, c)
-- 最左前缀:
-- a 能用
-- a,b 能用
-- a,b,c 能用
-- 单独b,c 用不了

4. 索引失效情况

1)where 里对索引字段用函数

-- 失效
SELECT * FROM users WHERE DATE(create_time) = '2026-01-01';

-- 有效
SELECT * FROM users WHERE create_time >= '2026-01-01' AND create_time < '2026-01-02';

2)where 里对索引字段做运算

-- 失效
SELECT * FROM users WHERE age + 1 = 20;

-- 有效
SELECT * FROM users WHERE age = 19;

3)like 以 % 开头

-- 失效
SELECT * FROM users WHERE name LIKE '%张';

-- 有效
SELECT * FROM users WHERE name LIKE '张%';

4)隐式类型转换

-- phone是字符串
-- 失效(数字转字符串)
SELECT * FROM users WHERE phone = 13800138000;

-- 有效
SELECT * FROM users WHERE phone = '13800138000';

5. EXPLAIN 查看执行计划

EXPLAIN SELECT * FROM users WHERE name = '张三';

重点看:

  • type: 访问类型,至少要 range
  • key: 实际用到的索引
  • rows: 扫描行数
  • Extra: 额外信息

6. 索引不要建太多

  • 每个索引都占空间
  • 插入更新时要维护索引
  • 不是越多越好
  • 一张表索引建议不超过5个

7. 覆盖索引

避免回表:

-- 索引包含查询的所有字段
SELECT name, age FROM users WHERE name = '张三';

-- 索引 (name, age) 就是覆盖索引
-- 不用回表查主键

Extra 显示 Using index 就是覆盖索引。

8. 慢查询定位

-- 开启慢查询
slow_query_log = 1
long_query_time = 1  -- 超过1秒记录

-- 查看慢查询日志
-- 找到慢SQL,加索引优化

9. 常见优化场景

1)分页查询优化

-- 慢(深分页)
SELECT * FROM orders LIMIT 100000, 10;

-- 快(子查询)
SELECT * FROM orders 
WHERE id >= (SELECT id FROM orders LIMIT 100000, 1)
LIMIT 10;

2)JOIN 字段建索引

  • JOIN 的两边都要建索引
  • 类型要一致

3)ORDER BY 用索引

  • 排序字段建索引
  • 避免 filesort

10. 索引维护

查看索引:

SHOW INDEX FROM users;

删除多余索引:

DROP INDEX idx_name ON users;

分析表:

ANALYZE TABLE users;

总结

MySQL 索引优化要点:

  • 区分度高的字段建索引
  • 联合索引遵循最左前缀
  • 避免索引失效(函数、运算、like%)
  • 用 EXPLAIN 分析执行计划
  • 索引不要建太多
  • 覆盖索引避免回表
  • 慢查询日志找问题SQL
  • 深分页要优化
  • JOIN 两边都建索引
  • 定期维护索引

索引优化是MySQL性能优化的核心,一定要多实践。


emer 发布于  2026-10-9 07:32 

PHP数组常用函数大全,array操作

前言

PHP数组用得最多,几乎每个脚本都要操作数组。本文整理最常用的数组函数,收藏起来随用随查。

一、创建和遍历

数组定义

<?php
// 索引数组
$arr = [1, 2, 3, 4, 5];

// 关联数组
$user = [
    'name' => '张三',
    'age' => 20,
    'email' => 'zhangsan@qq.com'
];

遍历

// foreach遍历
foreach ($arr as $value) {
    echo $value;
}

// 带key
foreach ($user as $key => $value) {
    echo "$key: $value";
}

二、增删改查

添加元素

// 末尾加
$arr[] = 6;
array_push($arr, 7, 8);

// 开头加
array_unshift($arr, 0);

// 关联数组加
$user['phone'] = '13800138000';

删除元素

// 删最后一个
array_pop($arr);

// 删第一个
array_shift($arr);

// 删指定key
unset($user['age']);

// 删多个
unset($arr[0], $arr[1]);

修改元素

$arr[0] = 100;
$user['name'] = '李四';

查找元素

// 值是否在数组里
in_array(3, $arr); // true

// 找key
array_key_exists('name', $user); // true

// 找值的key
array_search(2, $arr); // 1

三、数组统计

长度

count($arr);
sizeof($arr); // 和count一样

求和、最大、最小

array_sum($arr);      // 求和
max($arr);            // 最大
min($arr);            // 最小

去重

$unique = array_unique($arr);

打乱

shuffle($arr);

四、排序

索引数组排序

sort($arr);    // 升序
rsort($arr);   // 降序

关联数组排序

asort($user);  // 按值升序
arsort($user); // 按值降序
ksort($user);  // 按key升序
krsort($user); // 按key降序

五、拆分和合并

取一段

// 从第2个开始取3个
$slice = array_slice($arr, 1, 3);

合并数组

$arr1 = [1, 2];
$arr2 = [3, 4];

$merge = array_merge($arr1, $arr2); // [1,2,3,4]

交差

$arr1 = [1, 2, 3];
$arr2 = [2, 3, 4];

array_intersect($arr1, $arr2); // [2,3] 交集
array_diff($arr1, $arr2);      // [1] 差集

六、key和value操作

取所有key

$keys = array_keys($user);
// ['name', 'age', 'email']

取所有value

$values = array_values($user);
// ['张三', 20, 'zhangsan@qq.com']

翻转key和value

$flipped = array_flip($user);

填充数组

// 5个元素,值都是0
$arr = array_fill(0, 5, 0);

七、数组和字符串互转

数组转字符串

$arr = ['a', 'b', 'c'];
$str = implode(',', $arr); // 'a,b,c'

字符串转数组

$str = 'a,b,c';
$arr = explode(',', $str); // ['a', 'b', 'c']

八、常用场景

场景1:数组去重

$arr = [1, 2, 2, 3, 3, 3];
$unique = array_unique($arr);
// [1, 2, 3]

场景2:查值在不在

$fruits = ['apple', 'banana', 'orange'];

if (in_array('apple', $fruits)) {
    echo '有苹果';
}

场景3:二维数组按key排序

$users = [
    ['name' => '张三', 'age' => 20],
    ['name' => '李四', 'age' => 25],
    ['name' => '王五', 'age' => 18]
];

// 按age排序
array_multisort(array_column($users, 'age'), SORT_ASC, $users);

场景4:数组分页

$page = 1;
$pageSize = 10;
$offset = ($page - 1) * $pageSize;

$pageData = array_slice($allData, $offset, $pageSize);

场景5:过滤空值

$arr = ['a', '', null, 'b', 0, 'c'];
$filtered = array_filter($arr);
// ['a', 'b', 'c'] 空字符串、null、0都被过滤了

九、常见坑

坑1:count(null)报错

PHP7.2以后,count(null)会警告。

解决:

$count = is_array($arr) ? count($arr) : 0;

坑2:==和===的区别

in_array('123', [123]); // true(==)
in_array('123', [123], true); // false(===)

注意: 第三个参数true才是严格比较。

坑3:array_merge和+的区别

$arr1 = ['a' => 1, 'b' => 2];
$arr2 = ['b' => 3, 'c' => 4];

array_merge($arr1, $arr2); // ['a'=>1, 'b'=>3, 'c'=>4]
$arr1 + $arr2;             // ['a'=>1, 'b'=>2, 'c'=>4]

区别: array_merge后者覆盖前者,+前者保留。

坑4:引用传递

foreach ($arr as &$value) {
    $value = $value * 2;
}
unset($value); // 一定要unset,不然后面会出问题

总结

最常用的10个:

  1. count() 数组长度
  2. in_array() 值是否在数组里
  3. array_key_exists() key是否存在
  4. array_unique() 去重
  5. array_merge() 合并
  6. array_slice() 取一段
  7. array_sum() 求和
  8. explode() 字符串转数组
  9. implode() 数组转字符串
  10. array_column() 取二维数组某一列

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:31 

Nginx 反向代理配置

Nginx 反向代理配置

反向代理是 Nginx 最常用的功能之一,配置很简单。

1. 什么是反向代理

反向代理就是:用户访问 Nginx,Nginx 把请求转发给后端服务器,再把结果返回给用户。

作用:

  • 隐藏后端服务器
  • 负载均衡
  • 统一入口
  • 缓存静态内容

2. 基本配置

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;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

说明:

  • proxy_pass:后端地址
  • proxy_set_header:传递请求头

3. 常用请求头

location / {
    proxy_pass http://127.0.0.1:8080;

    # 传递域名
    proxy_set_header Host $host;

    # 传递真实IP
    proxy_set_header X-Real-IP $remote_addr;

    # 传递完整请求链
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # 传递协议
    proxy_set_header X-Forwarded-Proto $scheme;
}

4. 超时配置

location / {
    proxy_pass http://127.0.0.1:8080;

    # 连接超时
    proxy_connect_timeout 60s;

    # 发送超时
    proxy_send_timeout 60s;

    # 读取超时
    proxy_read_timeout 60s;
}

5. WebSocket 代理

location /ws {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;

    # WebSocket 升级
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

6. 负载均衡

# 定义后端服务器组
upstream backend {
    server 127.0.0.1:8080 weight=1;
    server 127.0.0.1:8081 weight=2;
    server 127.0.0.1:8082 max_fails=3 fail_timeout=30s;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
    }
}

7. 静态文件直接返回

server {
    listen 80;
    server_name example.com;

    # 静态文件直接处理
    location ~* \.(js|css|png|jpg|gif)$ {
        root /var/www/static;
        expires 30d;
    }

    # 其他请求转发后端
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
    }
}

8. 配置生效

# 检查配置
nginx -t

# 重载配置
nginx -s reload

9. 常见问题

1)502 Bad Gateway

  • 后端服务没启动
  • 后端端口不对
  • 后端超时

2)504 Gateway Timeout

  • 后端处理太慢
  • 调整超时时间

3)获取不到真实IP

  • 没配置 X-Real-IP
  • 后端没读取请求头

4)WebSocket 连不上

  • 没配置 Upgrade 和 Connection 头

10. 生产建议

1)一定要传请求头

  • Host
  • X-Real-IP
  • X-Forwarded-For

2)合理设置超时

  • 根据业务调整
  • 不要太长也不要太短

3)静态文件 Nginx 直接处理

  • 减轻后端压力
  • 提高性能

4)开启 gzip 压缩

gzip on;
gzip_types text/css application/javascript;

总结

Nginx 反向代理配置要点:

  • proxy_pass 指定后端地址
  • 一定要传请求头
  • 常用超时配置
  • WebSocket 要升级头
  • upstream 做负载均衡
  • 静态文件直接返回
  • 配置完 nginx -t 检查
  • 502 检查后端是否启动
  • 生产环境传全请求头

反向代理是 Nginx 基本功,一定要熟练掌握。


emer 发布于  2026-10-9 07:30 

PHP cURL请求超时,超时时间设置

前言

PHP用cURL请求接口,结果一直卡住或者超时?cURL默认没有超时时间,对方服务器慢的话能卡你几分钟。必须设超时,不然整个脚本都堵死了。本文讲怎么设。

一、为什么要设超时

不设超时的后果:

  1. 对方服务器挂了,你的脚本一直等着
  2. 对方服务器慢,你的脚本卡很久
  3. 高并发下,进程都堵在cURL上,服务器卡死

一定要设!

二、两个超时参数

1. CURLOPT_CONNECTTIMEOUT

连接超时,单位秒。

就是TCP连接花多久。

推荐: 5-10秒

2. CURLOPT_TIMEOUT

总超时,单位秒。

整个请求(连接+发送+接收)花多久。

推荐: 30-60秒

区别

参数 含义 推荐值
CURLOPT_CONNECTTIMEOUT 连接超时 5秒
CURLOPT_TIMEOUT 总超时 30秒

三、完整示例

<?php
$url = 'https://api.example.com/data';

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// 连接超时(秒)
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);

// 总超时(秒)
curl_setopt($ch, CURLOPT_TIMEOUT, 30);

// 毫秒级超时(可选)
// curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 5000);
// curl_setopt($ch, CURLOPT_TIMEOUT_MS, 30000);

$result = curl_exec($ch);

if (curl_errno($ch)) {
    echo 'cURL错误: ' . curl_error($ch);
}

curl_close($ch);

echo $result;

四、超时错误码

常见错误码

错误码 说明
28 超时(CURLE_OPERATION_TIMEDOUT)
7 连接失败(CURLE_COULDNT_CONNECT)
6 域名解析失败(CURLE_COULDNT_RESOLVE_HOST)

检查错误码

if (curl_errno($ch) == 28) {
    echo "请求超时了";
}

五、常见坑

坑1:timeout设成了毫秒

CURLOPT_TIMEOUT的单位是秒,不是毫秒!

错误:

curl_setopt($ch, CURLOPT_TIMEOUT, 5000); // 5000秒,不是5秒!

正确:

curl_setopt($ch, CURLOPT_TIMEOUT, 5); // 5秒

或者用毫秒版:

curl_setopt($ch, CURLOPT_TIMEOUT_MS, 5000); // 5000毫秒 = 5秒

坑2:set_time_limit不够

PHP脚本本身有最大执行时间。

如果cURL超时30秒,但PHP脚本只允许10秒,PHP先超时了。

解决:

set_time_limit(0); // 不限制
// 或者
set_time_limit(60); // 60秒

坑3:DNS解析慢

域名解析也要时间,算在连接超时里。

解决: 连接超时设长一点,比如10秒。

坑4:https证书问题

https请求如果证书有问题,也会卡住或报错。

临时关闭验证:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

注意: 生产环境不建议关,最好把证书配好。

坑5:重定向太多

对方返回301/302重定向,cURL默认不自动跳。

开启自动跳转:

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 3); // 最多跳3次

六、POST请求示例

<?php
$url = 'https://api.example.com/submit';
$data = [
    'name' => '张三',
    'age' => 20
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);

$result = curl_exec($ch);

if (curl_errno($ch)) {
    echo '错误: ' . curl_error($ch);
}

curl_close($ch);

echo $result;

七、封装成函数

function http_curl($url, $post = false, $data = [], $timeout = 30)
{
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

    if ($post) {
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, is_array($data) ? http_build_query($data) : $data);
    }

    $result = curl_exec($ch);

    if (curl_errno($ch)) {
        $error = curl_error($ch);
        curl_close($ch);
        return ['code' => -1, 'msg' => $error];
    }

    curl_close($ch);
    return ['code' => 0, 'data' => $result];
}

// 使用
$res = http_curl('https://api.example.com/data');
if ($res['code'] == 0) {
    echo $res['data'];
} else {
    echo '请求失败: ' . $res['msg'];
}

八、推荐配置

普通接口请求

curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);  // 5秒连接
curl_setopt($ch, CURLOPT_TIMEOUT, 30);        // 30秒总超时

快速接口(查个数据)

curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);  // 3秒连接
curl_setopt($ch, CURLOPT_TIMEOUT, 10);        // 10秒总超时

慢接口(上传大文件)

curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // 10秒连接
curl_setopt($ch, CURLOPT_TIMEOUT, 120);       // 2分钟总超时

总结

记住:

  1. cURL一定要设超时,不然卡死
  2. CURLOPT_CONNECTTIMEOUT 连接超时(秒)
  3. CURLOPT_TIMEOUT 总超时(秒)
  4. 单位是秒,不是毫秒
  5. 超时了用 curl_errno 判断是不是28

最常用:

curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:30 

Linux 查看内存使用

Linux 查看内存使用

服务器内存不够用的时候,得知道谁在吃内存。

1. free 命令

最常用的查看内存命令:

free -h

输出说明:

  • total: 总内存
  • used: 已用内存
  • free: 空闲内存
  • shared: 共享内存
  • buff/cache: 缓存
  • available: 可用内存

注意: 不要只看 free,要看 available。Linux 会用空闲内存做缓存,这是好事。

2. top 命令

实时查看内存占用:

top

常用操作:

  • 按 M 按内存排序
  • 按 P 按CPU排序
  • 按 q 退出

重点看:

  • %MEM:进程内存占用百分比
  • RES:进程实际占用物理内存

3. ps 命令

查看进程内存占用:

# 按内存占用排序,前10
ps aux --sort=-%mem | head -10

输出说明:

  • USER:进程用户
  • %MEM:内存占用
  • RSS:实际物理内存(KB)
  • COMMAND:进程命令

4. vmstat 命令

查看整体内存和CPU:

# 每秒刷新一次
vmstat 1

重点列:

  • swpd:交换分区使用
  • free:空闲内存
  • buff:缓冲区
  • cache:缓存
  • si:换入内存
  • so:换出内存

注意: si 和 so 大于0说明内存不够,在用swap。

5. /proc/meminfo

查看详细内存信息:

cat /proc/meminfo

重点看:

  • MemTotal:总内存
  • MemFree:空闲内存
  • MemAvailable:可用内存
  • Buffers:缓冲区
  • Cached:缓存
  • SwapTotal:交换分区总量
  • SwapFree:交换分区空闲

6. 缓存说明

Linux 内存缓存机制:

  • 空闲内存用来做文件缓存
  • 程序需要时会自动释放
  • cached 高不是坏事
  • available 才是真正可用的

7. 内存不够怎么办

1)找出吃内存的进程

ps aux --sort=-%mem | head

2)重启大内存进程

  • 确认是哪个进程
  • 重启释放内存

3)清理缓存

# 清理页面缓存
echo 1 > /proc/sys/vm/drop_caches

# 清理目录项和inode
echo 2 > /proc/sys/vm/drop_caches

# 全部清理
echo 3 > /proc/sys/vm/drop_caches

4)加交换分区

  • 临时方案,治标不治本
  • 最好加物理内存

8. 常用组合命令

# 查看内存占用前5的进程
ps aux --sort=-%mem | head -6

# 查看swap使用最多的进程
for i in $(ls /proc | grep -E '^[0-9]+'); do
    awk '/VmSwap/{print $2/1024 " MB", $0}' /proc/$i/status 2>/dev/null
done | sort -nr | head

9. 监控建议

1)设置内存告警

  • 超过80%告警
  • 及时处理

2)定期检查

  • 每天看一次内存
  • 发现异常及时处理

3)不要盲目清缓存

  • 缓存是好事
  • 清缓存会降低性能

总结

Linux 查看内存使用要点:

  • free -h 看整体内存
  • top 按M看内存占用
  • ps aux 排序找大进程
  • vmstat 看swap使用
  • /proc/meminfo 看详细信息
  • 不要只看 free,要看 available
  • cached 高是好事
  • 内存不够先找大进程
  • 清缓存要谨慎
  • 最好加物理内存

学会看内存,服务器问题就好排查了。


emer 发布于  2026-10-9 07:29 

PHP获取当前时间不对,时区设置date.timezone

前言

PHP用date()获取当前时间,结果比实际时间差了8个小时?这是最常见的时区问题。PHP默认用UTC时区,北京时间是东八区,所以差了8小时。本文讲怎么改。

一、问题现象

<?php
echo date('Y-m-d H:i:s');

输出:

2026-10-09 00:30:00

但实际北京时间是:

2026-10-09 08:30:00

差了8个小时。

二、原因

PHP默认时区是UTC(世界标准时间),不是北京时间。

北京时间是 UTC+8,所以差了8小时。

三、解决方法

方法1:改php.ini(推荐)

第一步:找到php.ini

php --ini

找到Loaded Configuration File那一行:

Loaded Configuration File: /etc/php.ini

第二步:修改date.timezone

打开php.ini,找到这行:

;date.timezone =

改成:

date.timezone = Asia/Shanghai

第三步:重启php-fpm

systemctl restart php-fpm

方法2:代码里设置(临时)

不想改php.ini,可以在代码里设:

<?php
date_default_timezone_set('Asia/Shanghai');

echo date('Y-m-d H:i:s');

注意: 要在所有日期函数之前设。

方法3:.htaccess里设

Apache的话,可以在.htaccess里设:

php_value date.timezone Asia/Shanghai

四、常用时区对照

时区 说明
Asia/Shanghai 北京时间(东八区)
Asia/Chongqing 重庆时间
Asia/Urumqi 乌鲁木齐时间
Asia/Hong_Kong 香港时间
Asia/Taipei 台北时间
Asia/Tokyo 东京时间(东九区)
America/New_York 纽约时间
Europe/London 伦敦时间
UTC 世界标准时间

国内用: Asia/Shanghai

五、验证时区

看当前时区

<?php
echo date_default_timezone_get();
// 输出:Asia/Shanghai

看服务器时间

date

看PHP时间

<?php
echo date('Y-m-d H:i:s');

对比一下,应该一样了。

六、常见坑

坑1:CLI和FPM时区不一样

命令行里改了php.ini,但网页上还是不对。

原因: CLI和FPM用不同的php.ini。

解决: 看phpinfo()里的配置路径,改对应的那个。

<?php
phpinfo();

搜"Loaded Configuration File",看用的哪个文件。

坑2:改了没重启

改了php.ini忘了重启php-fpm。

解决:

systemctl restart php-fpm

坑3:代码里又改回去了

某个框架或库里又调了 date_default_timezone_set('UTC')。

解决: 全局搜一下,看哪里改的。

坑4:用了gmdate()

gmdate()总是用UTC时区,不管你设什么。

比如:

echo gmdate('Y-m-d H:i:s');

这个输出的是UTC时间,不是北京时间。

解决: 用date()不要用gmdate(),或者自己加8小时。

坑5:数据库时间不对

PHP时区对了,但MySQL里的时间不对。

原因: MySQL自己也有时区设置。

解决: MySQL连接后设时区:

SET time_zone = '+8:00';

或者改my.cnf:

default-time-zone = '+8:00'

七、常用日期函数

获取当前时间

date_default_timezone_set('Asia/Shanghai');

// 当前日期时间
echo date('Y-m-d H:i:s');

// 今天
echo date('Y-m-d');

// 现在几点
echo date('H:i:s');

// 时间戳
echo time();

时间戳转日期

$time = time();
echo date('Y-m-d H:i:s', $time);

日期转时间戳

$str = '2026-10-09 08:30:00';
echo strtotime($str);

几天以后

// 3天后
echo date('Y-m-d', strtotime('+3 days'));

// 下周
echo date('Y-m-d', strtotime('next week'));

八、框架里的设置

ThinkPHP

在config/app.php里:

'default_timezone' => 'Asia/Shanghai',

Laravel

在config/app.php里:

'timezone' => 'Asia/Shanghai',

总结

记住:

  1. PHP默认UTC时区,差8小时
  2. 改php.ini里的 date.timezone = Asia/Shanghai
  3. 改完重启php-fpm
  4. 或者代码里 date_default_timezone_set('Asia/Shanghai')
  5. CLI和FPM可能用不同的php.ini

最常用:

date_default_timezone_set('Asia/Shanghai');
echo date('Y-m-d H:i:s');

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:28