Redis 持久化机制

Redis 持久化机制

Redis 是内存数据库,断电数据就没了,所以需要持久化。

1. 为什么要持久化

  • Redis 数据在内存中
  • 服务器断电/重启数据丢失
  • 持久化把数据存到磁盘
  • 重启后可以恢复数据

2. RDB 快照

原理: 定时把内存数据全量保存到磁盘。

优点:

  • 恢复速度快
  • 文件小
  • 适合备份

缺点:

  • 可能丢失最后一次快照后的数据
  • fork子进程可能阻塞

配置:

# redis.conf
save 900 1      # 900秒内1个key变化就保存
save 300 10     # 300秒内10个key变化
save 60 10000   # 60秒内10000个key变化

手动触发:

# 前台保存(阻塞)
redis-cli save

# 后台保存(不阻塞)
redis-cli bgsave

3. AOF 日志

原理: 记录每个写命令,重启时重放。

优点:

  • 数据更安全(最多丢1秒)
  • 日志可读

缺点:

  • 文件比 RDB 大
  • 恢复速度慢

配置:

# redis.conf
appendonly yes              # 开启AOF
appendfilename "appendonly.aof"

# 同步策略
appendfsync everysec       # 每秒同步(推荐)
# appendfsync always       # 每次写都同步
# appendfsync no           # 交给操作系统

AOF 重写:

# 手动触发AOF重写
redis-cli bgrewriteaof

4. RDB vs AOF 对比

对比项 RDB AOF
数据安全 可能丢数据 更安全
文件大小 小 大
恢复速度 快 慢
性能影响 fork阻塞 较小
适用场景 备份/容灾 数据安全要求高

5. 混合持久化

Redis 4.0+ 支持混合持久化:

  • AOF 重写时前半段用 RDB 格式
  • 后半段增量用 AOF 格式
  • 结合两者优点

配置:

aof-use-rdb-preamble yes

6. 生产建议

1)数据不敏感用 RDB

  • 缓存场景
  • 丢点数据没关系

2)数据重要用 AOF

  • 每秒同步
  • 最多丢1秒数据

3)生产环境推荐

  • 同时开 RDB + AOF
  • 混合持久化
  • 定期备份 RDB 文件

7. 数据恢复

RDB 恢复:

# 把 dump.rdb 放到数据目录
# 重启 Redis 自动加载

AOF 恢复:

# 开启 appendonly yes
# 重启自动重放AOF日志

8. 常见问题

1)fork 阻塞怎么办

  • 数据量大 fork 慢
  • 用 AOF 代替
  • 减少单实例数据量

2)AOF 文件太大

  • 定期触发重写
  • 设置自动重写阈值

3)恢复慢怎么办

  • 用 RDB 恢复快
  • 混合持久化更优

总结

Redis 持久化要点:

  • RDB 是快照,恢复快但可能丢数据
  • AOF 是日志,数据安全但文件大
  • 生产建议同时开 RDB + AOF
  • 混合持久化结合两者优点
  • 缓存用 RDB,重要数据用 AOF
  • 定期备份持久化文件
  • fork 阻塞要关注数据量

持久化是 Redis 生产必备配置,一定要重视。


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

MySQL大表分页越查越慢,优化方案

数据量小的时候分页很快,一旦表数据上百万条,翻到后面的页就慢得要死?这是MySQL分页最常见的问题,下面说几个实用的优化方案。

1. 先看为什么会慢

SELECT * FROM 表名 ORDER BY id LIMIT 100000, 10;

这个SQL的意思是:扫描前100010条,丢掉前100000条,返回后10条。

数据量越大,扫描的行数越多,就越慢。这就是为什么越往后翻越慢。

2. 方案一:子查询优化(最常用)

SELECT * FROM 表名 
WHERE id >= (SELECT id FROM 表名 ORDER BY id LIMIT 100000, 1)
LIMIT 10;

原理:

  • 子查询先通过索引找到第100001条的id
  • 然后直接从这个id开始往后查
  • 不需要扫描前10万条

速度对比:

  • 原SQL:扫描10万+行,耗时几秒
  • 优化后:走索引,几毫秒就出来

3. 方案二:游标分页(推荐用在后台)

-- 第一页
SELECT * FROM 表名 ORDER BY id LIMIT 10;

-- 第二页(记住上一页最后一条的id)
SELECT * FROM 表名 WHERE id > 100 ORDER BY id LIMIT 10;

-- 第三页
SELECT * FROM 表名 WHERE id > 110 ORDER BY id LIMIT 10;

优点:

  • 永远都是走索引,不管翻到第几页都一样快
  • 适合无限滚动加载

缺点:

  • 不能直接跳到第100页
  • 不适合传统的"上一页/下一页"翻页

4. 方案三:延迟关联

SELECT t.* FROM 表名 t
INNER JOIN (
    SELECT id FROM 表名 ORDER BY id LIMIT 100000, 10
) tmp ON t.id = tmp.id;

原理:

  • 先通过索引查出需要的10个id
  • 再用这10个id去关联原表查完整数据
  • 避免回表太多行

5. 方案四:限制最大翻页深度

如果不是必须让用户翻到很后面,可以直接限制:

-- 最多只允许翻100页(1000条数据)
IF 页码 > 100 THEN
    -- 提示用户"请搜索更精确的条件"
END IF;

一般用户也不会翻到很后面的页,限制一下性能就好很多。

6. 方案五:用ES做搜索

如果是文章、商品之类的内容,直接用Elasticsearch做全文搜索和分页,MySQL只存原始数据。

ES天生适合做大数据量的分页搜索,MySQL不是干这个的。

常见坑

坑1:ORDER BY 字段没加索引
ORDER BY 的字段一定要加索引,不然不管怎么优化都是全表扫描。

坑2:OFFSET 太大了
OFFSET 100万,不管怎么优化都慢,直接用游标分页。

*坑3:SELECT 不要用**
只查需要的字段,减少回表数据量。


遇到问题加QQ23979811 协助处理


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

PHP session不生效,cookie配置检查

前言

PHP用session存用户登录状态,结果刷新页面就掉了?session不生效太常见了,一般就是cookie配置不对、路径不对、或者目录没权限。本文讲怎么排查和解决。

一、session工作原理

简单说:

  1. 浏览器访问网站
  2. PHP创建session,生成session_id
  3. session_id通过cookie发给浏览器
  4. 浏览器每次请求带上这个cookie
  5. PHP根据cookie里的session_id找到对应数据

核心: session靠cookie传session_id,cookie不行session就没了。

二、常见原因

原因1:cookie没设置对

session.cookie_path、session.cookie_domain配置不对。

原因2:session保存目录没权限

session文件存不进去。

原因3:session.auto_start没开

或者开了但和代码冲突。

原因4:跨域问题

主域和子域之间cookie不共享。

原因5:HTTPS下cookie的secure问题

cookie设了secure,但用http访问。

三、排查步骤

第一步:检查session有没有启动

<?php
session_start();

// 存个值
$_SESSION['test'] = 'hello';

// 看有没有
echo '<pre>';
print_r($_SESSION);
echo '</pre>';

刷新页面,看test还在不在。

第二步:看cookie里有没有PHPSESSID

按F12打开浏览器开发者工具 → Application → Cookies。

看有没有 PHPSESSID 这个cookie。

没有的话: 就是cookie没设对。

有的话: 那就是session保存目录的问题。

四、php.ini配置检查

找到php.ini位置

php --ini

关键配置

;session保存目录
session.save_path = "/var/lib/php/session"

;是否自动启动
session.auto_start = 0

;cookie路径
session.cookie_path = /

;cookie域名
session.cookie_domain =

;cookie有效期(秒)
session.cookie_lifetime = 0

;是否仅https
session.cookie_secure = 0

;是否仅http(防XSS)
session.cookie_httponly = 1

五、常见坑和解决

坑1:session目录没权限

session文件写不进去。

检查:

ls -ld /var/lib/php/session

解决:

chmod 777 /var/lib/php/session
chown apache:apache /var/lib/php/session

或者看php-fpm用的哪个用户。

坑2:cookie_path不对

比如网站装在子目录,但cookie_path设成了别的。

解决:

ini_set('session.cookie_path', '/');
session_start();

坑3:cookie_domain不对

多域名访问时,cookie只在一个域名下有效。

解决:

ini_set('session.cookie_domain', '.example.com');

注意前面有个点,表示所有子域名都生效。

坑4:HTTPS下设了secure

cookie_secure=1表示只在https下传cookie,用http访问就没了。

解决:

session.cookie_secure = 0

或者全站用https。

坑5:session.auto_start冲突

代码里已经session_start()了,但php.ini里也开了auto_start,冲突。

解决:

session.auto_start = 0

六、代码里动态设置

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

<?php
// 必须在session_start()之前

ini_set('session.cookie_path', '/');
ini_set('session.cookie_domain', '.example.com');
ini_set('session.cookie_lifetime', 0);

session_start();

$_SESSION['user_id'] = 1;

七、检查session是否生效

写个测试页:

<?php
session_start();

// 第一次访问,设置
if (!isset($_SESSION['count'])) {
    $_SESSION['count'] = 1;
} else {
    $_SESSION['count']++;
}

echo "访问次数: " . $_SESSION['count'];
echo "<br>";
echo "Session ID: " . session_id();
echo "<br>";
echo "Session 文件: " . session_save_path() . "/sess_" . session_id();

刷新页面,数字应该一直涨。

如果不涨: 就是session没存住。

八、常见场景

场景1:登录后刷新就掉

原因: cookie_path或domain不对。

解决: 检查cookie配置。

场景2:换了个子域名就掉

比如 www.example.com 登录了,跳到 bbs.example.com 就没登录。

原因: cookie_domain没设成 .example.com。

解决:

ini_set('session.cookie_domain', '.example.com');

场景3:从http跳到https就掉

原因: cookie_secure=1,但http不带cookie。

解决: 全站用https,或者关掉secure。

场景4:session目录满了

原因: session文件太多。

解决: 清理旧session,或者设gc_maxlifetime。

session.gc_maxlifetime = 1440

九、快速排查清单

  1. F12看cookie里有没有PHPSESSID
  2. 没有 → 查cookie_path、cookie_domain、cookie_secure
  3. 有 → 查session目录权限
  4. 都没问题 → 看session.auto_start有没有冲突
  5. 跨域问题 → cookie_domain设成 .主域名

总结

记住:

  1. session靠cookie传session_id
  2. 先看cookie里有没有PHPSESSID
  3. 没有就查cookie配置
  4. 有就查session目录权限
  5. 跨域名记得设cookie_domain=.主域名

最常用:

ini_set('session.cookie_path', '/');
session_start();

遇到问题加QQ23979811 协助处理


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

PHP 过滤和验证数据

PHP 过滤和验证数据

用户输入不可信,必须过滤验证,这是Web开发的基本功。

1. 为什么要过滤验证

  • 防止 SQL 注入
  • 防止 XSS 攻击
  • 保证数据格式正确
  • 防止脏数据入库

2. filter_var 函数

PHP 内置过滤器,很好用。

验证邮箱

$email = 'test@example.com';
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
    echo '邮箱格式正确';
} else {
    echo '邮箱格式错误';
}

验证数字

$age = '20abc';
if (filter_var($age, FILTER_VALIDATE_INT)) {
    echo '是整数';
}

验证URL

$url = 'https://example.com';
if (filter_var($url, FILTER_VALIDATE_URL)) {
    echo 'URL格式正确';
}

验证IP

$ip = '192.168.1.1';
if (filter_var($ip, FILTER_VALIDATE_IP)) {
    echo 'IP格式正确';
}

3. filter_input 获取并过滤

从 GET/POST 获取并过滤:

// 获取并过滤GET参数
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);

// 获取并过滤POST参数
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);

4. 过滤输入

过滤HTML标签

$str = '<script>alert(1)</script>你好';
// 去除HTML标签
echo strip_tags($str);

// HTML转义(安全输出)
echo htmlspecialchars($str);

过滤特殊字符

$str = "hello ' world";
// 转义单引号
echo addslashes($str);

5. 常用验证场景

验证手机号

function checkPhone($phone) {
    return preg_match('/^1[3-9]\d{9}$/', $phone);
}

if (checkPhone('13800138000')) {
    echo '手机号正确';
}

验证身份证

function checkIdCard($idcard) {
    return preg_match('/^\d{17}[\dXx]$/', $idcard);
}

验证用户名

// 4-16位,字母数字下划线
function checkUsername($name) {
    return preg_match('/^\w{4,16}$/', $name);
}

6. 安全建议

1)所有外部输入都要验证

  • GET、POST、COOKIE
  • 不要相信任何用户输入

2)用预处理防SQL注入

  • 不要自己拼接SQL
  • 用 PDO 预处理

3)输出时转义

  • 输出到HTML要 htmlspecialchars
  • 防止 XSS 攻击

4)白名单验证

  • 只接受允许的值
  • 不要用黑名单

7. 完整示例

// 接收用户注册信息
$username = trim($_POST['username']);
$email = trim($_POST['email']);
$age = (int)$_POST['age'];

$errors = [];

// 验证用户名
if (!preg_match('/^\w{4,16}$/', $username)) {
    $errors[] = '用户名4-16位字母数字下划线';
}

// 验证邮箱
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $errors[] = '邮箱格式错误';
}

// 验证年龄
if ($age < 18 || $age > 100) {
    $errors[] = '年龄必须18-100岁';
}

if (empty($errors)) {
    // 验证通过,入库
    echo '注册成功';
} else {
    print_r($errors);
}

总结

PHP 过滤和验证数据要点:

  • 所有用户输入都不可信
  • 用 filter_var 验证格式
  • 用 filter_input 获取并过滤
  • 输出用 htmlspecialchars 防XSS
  • 用预处理防SQL注入
  • 用正则验证手机号、用户名
  • 白名单验证比黑名单好
  • 不要自己拼接SQL字符串

做Web开发,安全永远第一位。


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

MySQL数据库导入sql文件报错解决

导入sql文件的时候各种报错?下面整理了最常见的导入报错和解决办法,照着排查就行。

1. 先看最常见的导入命令

mysql -u root -p 数据库名 < /path/to/你的文件.sql

或者进入MySQL后用source:

mysql -u root -p
USE 数据库名;
source /path/to/你的文件.sql;

2. 报错1:Unknown database

原因: 数据库不存在,你直接导入了。

解决: 先创建数据库再导入:

CREATE DATABASE 数据库名 DEFAULT CHARACTER SET utf8mb4;

然后再执行导入命令。

3. 报错2:Access denied

原因: 权限不够,或者密码错了。

解决: 确认用户名密码对不对,给用户授权:

GRANT ALL PRIVILEGES ON *.* TO '用户名'@'localhost' IDENTIFIED BY '密码';
FLUSH PRIVILEGES;

4. 报错3:File not found

原因: 文件路径不对,或者文件没权限读。

解决:

  • 用绝对路径,不要用相对路径
  • 确认文件存在:ls -l /path/to/file.sql
  • 给文件加读权限:chmod 644 file.sql

5. 报错4:Duplicate entry

原因: 导入的表已经有数据了,主键冲突。

解决:

  • 先清空表再导入:TRUNCATE TABLE 表名;
  • 或者导入SQL文件开头加上 DROP TABLE IF EXISTS 表名;

6. 报错5:Unknown collation

原因: 字符集排序规则不一致,比如导出用的是 utf8mb4_general_ci,导入的数据库是 utf8_general_ci。

解决: 导入前设置字符集:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- 导入你的SQL
SET FOREIGN_KEY_CHECKS = 1;

7. 报错6:MySQL server has gone away

原因: SQL文件太大,导入超时了。

解决: 修改MySQL配置:

# /etc/my.cnf
max_allowed_packet = 64M
wait_timeout = 300
interactive_timeout = 300

然后重启MySQL:

systemctl restart mysqld

8. 报错7:Syntax error

原因: SQL文件格式不对,或者MySQL版本不兼容。

解决:

  • 导出的时候选择兼容版本
  • 去掉注释行(-- 开头的)
  • 用文本编辑器打开检查一下有没有乱码

9. 大文件导入更快的方法

如果SQL文件几百MB,用mysql命令行比phpMyAdmin快10倍:

mysql -u root -p 数据库名 < big_file.sql

还可以开事务加速:

SET autocommit = 0;
SET unique_checks = 0;
SET foreign_key_checks = 0;
-- 导入完成后
SET autocommit = 1;
SET unique_checks = 1;
SET foreign_key_checks = 1;
COMMIT;

遇到问题加QQ23979811 协助处理


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

MySQL 主键和自增

MySQL 主键和自增

主键是表的重要概念,搞懂怎么用很有必要。

1. 什么是主键

主键(Primary Key)是表中唯一标识一行记录的字段。

特点:

  • 唯一性: 每行数据主键不能重复
  • 非空性: 主键不能为 NULL
  • 一张表只能有一个主键

2. 创建主键

建表时指定

CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50)
);

自增主键(最常用)

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50)
);

3. 自增主键特点

  • 自动增长: 插入数据不用指定ID,自动+1
  • 从1开始: 默认从1开始
  • 不重复: 保证唯一
-- 插入数据不用写id
INSERT INTO users (name) VALUES ('张三');

-- id自动变成1
-- 再插入自动变成2

4. 查看自增值

-- 查看表当前自增值
SHOW TABLE STATUS LIKE 'users';

-- 查看建表语句
SHOW CREATE TABLE users;

5. 修改自增值

-- 设置下一次自增值为100
ALTER TABLE users AUTO_INCREMENT = 100;

6. 联合主键

多个字段组成主键:

CREATE TABLE user_role (
    user_id INT,
    role_id INT,
    PRIMARY KEY (user_id, role_id)
);

7. 主键设计建议

1)用自增整数做主键

  • INT 或 BIGINT
  • 查询快,占用空间小
  • 不要用 UUID 做主键

2)为什么不用字符串做主键

  • 占空间大
  • 查询慢
  • 自增效率高

3)什么时候用联合主键

  • 中间表(如用户角色关联表)
  • 没有合适的单字段做主键

4)BIGINT 还是 INT

  • 一般 INT 够用(约21亿)
  • 数据量大用 BIGINT

8. 常见问题

1)删除数据后ID不连续

  • 正常现象,自增只增不减
  • 不要依赖ID连续

2)自增值跳号

  • 删除了中间数据
  • 插入失败也会占用ID
  • 正常现象

3)为什么不用手机号做主键

  • 可能会变
  • 字符串查询慢
  • 用自增ID更稳妥

总结

MySQL 主键和自增要点:

  • 主键唯一且非空
  • 一张表只能一个主键
  • 自增主键最常用
  • 用 INT 或 BIGINT
  • 不要用 UUID 做主键
  • 中间表可以用联合主键
  • ID不连续是正常现象

主键设计对表性能影响很大,一定要重视。


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

Nginx 访问日志配置

Nginx 访问日志配置

访问日志是排查问题和分析流量的重要依据,配置好很有必要。

1. 默认日志位置

# 访问日志
/var/log/nginx/access.log

# 错误日志
/var/log/nginx/error.log

2. 日志格式

Nginx 默认有两种日志格式:

combined(默认)

log_format combined '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent"';

common

log_format common '$remote_addr [$time_local] "$request" $status $body_bytes_sent';

3. 自定义日志格式

在 nginx.conf 的 http 块添加:

http {
    log_format main '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" '
                    '$request_time $upstream_response_time';

    access_log /var/log/nginx/access.log main;
}

4. 常用日志变量

  • $remote_addr: 客户端IP
  • $time_local: 本地时间
  • $request: 请求行
  • $status: 状态码
  • $body_bytes_sent: 发送字节数
  • $http_referer: 来源页面
  • $http_user_agent: 浏览器信息
  • $request_time: 请求总耗时
  • $upstream_response_time: 后端响应时间

5. 按站点分开日志

不同站点用不同日志文件:

server {
    listen 80;
    server_name a.com;
    access_log /var/log/nginx/a.access.log;
}

server {
    listen 80;
    server_name b.com;
    access_log /var/log/nginx/b.access.log;
}

6. 关闭日志

静态资源不记日志:

location ~* \.(jpg|jpeg|png|gif|css|js)$ {
    access_log off;
}

7. 日志切割

Nginx 自带日志切割:

http {
    # 每天切割,保留30天
    access_log /var/log/nginx/access.log main;
    log_not_found off;
}

也可以用 logrotate:

# 配置文件
/etc/logrotate.d/nginx

8. 分析日志

统计访问量

# 统计总请求数
wc -l /var/log/nginx/access.log

# 统计独立IP
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

统计状态码

awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c

统计访问最多的URL

awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10

总结

Nginx 访问日志配置要点:

  • 默认日志在 /var/log/nginx/
  • combined 是默认格式
  • 自定义格式添加 $request_time 等变量
  • 不同站点分开日志文件
  • 静态资源可以关闭日志
  • 日志要定期切割
  • 用 awk 分析日志很方便

做好日志配置,排查问题和分析流量都有用。


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

PHP 时间日期处理

PHP 时间日期处理

时间日期是开发常用功能,这些函数要记住。

1. 获取当前时间

// 当前时间戳
echo time();

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

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

2. 时间戳和日期互转

时间戳转日期

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

日期转时间戳

$date = '2024-01-01 12:00:00';
$time = strtotime($date);
echo $time;

3. 常用日期格式

// 2024-01-01
echo date('Y-m-d');

// 2024-01-01 12:00:00
echo date('Y-m-d H:i:s');

// 2024年01月01日
echo date('Y年m月d日');

// 2024/01/01
echo date('Y/m/d');

// 星期几
echo date('l');  // Sunday

4. 常用格式字符

  • Y: 4位年
  • m: 2位月
  • d: 2位日
  • H: 24小时制
  • i: 分钟
  • s: 秒

5. 日期加减

// 明天
echo date('Y-m-d', strtotime('+1 day'));

// 昨天
echo date('Y-m-d', strtotime('-1 day'));

// 一周后
echo date('Y-m-d', strtotime('+1 week'));

// 一个月后
echo date('Y-m-d', strtotime('+1 month'));

// 一年后
echo date('Y-m-d', strtotime('+1 year'));

6. 计算时间差

$start = strtotime('2024-01-01');
$end = strtotime('2024-01-10');

// 相差天数
$diff = ($end - $start) / 86400;
echo $diff . '天';

7. DateTime 类(推荐)

// 创建日期对象
$date = new DateTime('2024-01-01');

// 格式化输出
echo $date->format('Y-m-d H:i:s');

// 加一天
$date->add(new DateInterval('P1D'));
echo $date->format('Y-m-d');

// 计算两个日期差
$start = new DateTime('2024-01-01');
$end = new DateTime('2024-01-10');
$diff = $start->diff($end);
echo $diff->days . '天';

8. 常用场景

1)几天前

function timeAgo($time) {
    $diff = time() - $time;
    if ($diff < 60) return '刚刚';
    if ($diff < 3600) return floor($diff/60) . '分钟前';
    if ($diff < 86400) return floor($diff/3600) . '小时前';
    return floor($diff/86400) . '天前';
}

2)生日计算

$birthday = '1990-01-01';
$age = floor((time() - strtotime($birthday)) / 31536000);
echo $age . '岁';

总结

PHP 时间日期处理要点:

  • time() 获取时间戳
  • date() 格式化日期
  • strtotime() 日期转时间戳
  • 用 +1 day 等做日期加减
  • 推荐用 DateTime 类
  • 时间差除以86400得天数
  • 注意时区设置

做开发时间日期天天用,这些函数必须熟。


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

Linux 查看磁盘空间

Linux 查看磁盘空间

服务器磁盘满了怎么办?这些命令帮你快速排查。

1. df 查看整体磁盘

基本用法

# 查看磁盘使用情况
df

# 人性化显示(带M、G单位)
df -h

常用参数

  • -h: 人性化显示大小
  • -T: 显示文件系统类型
  • -i: 查看 inode 使用情况
# 常用组合
df -hT

输出说明:

  • Filesystem: 磁盘分区
  • Size: 总大小
  • Used: 已用
  • Avail: 可用
  • Use%: 使用率
  • Mounted on: 挂载点

2. du 查看目录大小

查看当前目录大小

# 当前目录总大小
du -sh

# 查看指定目录
du -sh /var/log

查看子目录大小

# 查看当前目录下每个子目录大小
du -sh *

# 按大小排序
du -sh * | sort -rh

常用参数

  • -s: 只显示总计
  • -h: 人性化显示
  • --max-depth=1: 只看一层
# 查看根目录下各目录大小
du -sh /* --max-depth=1

3. 查找大文件

查找大于100M的文件

find / -type f -size +100M -exec ls -lh {} \;

查找最近修改的大文件

find /var -type f -size +50M -mtime -7

4. 常见排查场景

1)磁盘满了

# 先看哪个分区满了
df -h

# 找到大目录
du -sh /* | sort -rh

2)日志占空间

# 查看日志目录大小
du -sh /var/log

# 清空大日志文件
> /var/log/large.log

3)inode满了

# 查看inode使用
df -i

5. 清理磁盘

清理日志文件

# 清空文件(不删除文件)
> /var/log/messages

# 删除旧日志
rm /var/log/*.old

清理缓存

# 清理包缓存
yum clean all

清理 Docker 没用的镜像

docker system prune -a

总结

Linux 查看磁盘空间要点:

  • df -h 查看整体磁盘使用
  • du -sh 查看目录大小
  • find 查找大文件
  • 磁盘满了先看分区,再找大目录
  • 日志文件要定期清理
  • 注意 inode 也可能满
  • 用 sort -rh 按大小排序

运维排查磁盘问题,这些命令天天用。


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

PHP 生成随机密码

PHP 生成随机密码

注册、重置密码功能经常要生成随机密码,这几种方法要会。

1. 简单随机字符串

function generatePassword($length = 8) {
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    $password = '';
    for ($i = 0; $i < $length; $i++) {
        $password .= $chars[mt_rand(0, strlen($chars) - 1)];
    }
    return $password;
}

// 生成8位密码
echo generatePassword(8);

2. 带特殊字符的密码

function generateStrongPassword($length = 12) {
    $lower = 'abcdefghijklmnopqrstuvwxyz';
    $upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $numbers = '0123456789';
    $special = '!@#$%^&*()';

    $all = $lower . $upper . $numbers . $special;

    $password = '';
    // 确保至少包含每种字符
    $password .= $lower[mt_rand(0, strlen($lower) - 1)];
    $password .= $upper[mt_rand(0, strlen($upper) - 1)];
    $password .= $numbers[mt_rand(0, strlen($numbers) - 1)];
    $password .= $special[mt_rand(0, strlen($special) - 1)];

    // 剩余随机
    for ($i = 4; $i < $length; $i++) {
        $password .= $all[mt_rand(0, strlen($all) - 1)];
    }

    // 打乱顺序
    return str_shuffle($password);
}

echo generateStrongPassword(12);

3. 更安全的 random_bytes

PHP 7+ 推荐用 random_bytes,更安全:

function generateSecurePassword($length = 12) {
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*';
    $max = strlen($chars) - 1;
    $password = '';

    for ($i = 0; $i < $length; $i++) {
        $password .= $chars[random_int(0, $max)];
    }

    return $password;
}

echo generateSecurePassword(16);

4. 生成验证码

function generateCode($length = 6) {
    $chars = '0123456789';
    $code = '';
    for ($i = 0; $i < $length; $i++) {
        $code .= $chars[random_int(0, 9)];
    }
    return $code;
}

// 6位数字验证码
echo generateCode(6);

5. 生成随机 Token

function generateToken($length = 32) {
    return bin2hex(random_bytes($length / 2));
}

// 32位随机token
echo generateToken();

6. 安全建议

1)用 random_int 不用 mt_rand

  • random_int 是密码安全随机数
  • mt_rand 不够安全

2)密码长度

  • 普通密码:8-12位
  • 强密码:12位以上
  • Token:32位以上

3)密码强度

  • 大小写字母+数字+特殊字符
  • 避免纯数字或纯字母

4)不要自己写随机算法

  • 用 PHP 内置函数
  • 不要用时间戳做随机种子

总结

PHP 生成随机密码要点:

  • 简单密码用 mt_rand 拼接
  • 强密码包含大小写+数字+特殊字符
  • 安全密码用 random_int
  • Token 用 bin2hex(random_bytes())
  • 验证码用纯数字
  • 推荐用 PHP 7+ 的 random_int
  • 不要自己写随机算法

做用户系统,生成密码和验证码是必备功能。


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

MySQL 常用数据类型

MySQL 常用数据类型

建表选对数据类型很重要,这些常用类型要记住。

1. 数值类型

整数类型

  • TINYINT: 1字节,范围 -128~127
  • SMALLINT: 2字节,范围 -32768~32767
  • INT: 4字节,常用,范围约 ±21亿
  • BIGINT: 8字节,大数值

小数类型

  • FLOAT: 单精度浮点数
  • DOUBLE: 双精度浮点数
  • DECIMAL: 精确小数,存金额用这个
-- 金额用 DECIMAL(总位数, 小数位数)
price DECIMAL(10,2)  -- 最多10位,2位小数

2. 字符串类型

CHAR: 定长字符串,比如 CHAR(10) 存不满补空格

  • 适合固定长度,比如手机号、性别

VARCHAR: 变长字符串,最常用

  • VARCHAR(255),存多少占多少
  • 适合变长内容,比如用户名、标题

TEXT: 长文本

  • TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT
  • 存文章内容、备注等

常用选择:

  • 短文本:VARCHAR(50)
  • 长文本:TEXT
  • 固定长度:CHAR

3. 日期时间类型

DATE: 日期,格式 YYYY-MM-DD
TIME: 时间,格式 HH:MM:SS
DATETIME: 日期+时间,范围 1000-9999年
TIMESTAMP: 时间戳,范围 1970-2038年,自动更新

create_time DATETIME DEFAULT CURRENT_TIMESTAMP
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

4. 常用字段设计示例

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL COMMENT '用户名',
    age TINYINT UNSIGNED COMMENT '年龄',
    phone CHAR(11) COMMENT '手机号',
    money DECIMAL(10,2) DEFAULT 0 COMMENT '余额',
    status TINYINT DEFAULT 1 COMMENT '状态:1正常 0禁用',
    content TEXT COMMENT '简介',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

5. 选择建议

1)主键ID

  • 用 INT 或 BIGINT
  • 不要用 UUID 做主键

2)金额

  • 用 DECIMAL,不要用 FLOAT/DOUBLE
  • 避免精度丢失

3)状态字段

  • 用 TINYINT,不要用 VARCHAR 存 "正常/禁用"

4)时间

  • 创建时间用 DATETIME
  • 更新时间用 TIMESTAMP 自动更新

5)字符串

  • 短文本用 VARCHAR
  • 长文本用 TEXT
  • 固定长度用 CHAR

6. 注意事项

  • 不要过度设计: 够用就行,不要字段太大
  • 能用数字不用字符串: 排序、计算都快
  • 避免 NULL: 尽量设默认值,NULL 不好查询
  • 加注释: 每个字段都加 COMMENT

总结

MySQL 常用数据类型要点:

  • INT/BIGINT:整数
  • DECIMAL:金额
  • VARCHAR:字符串
  • TEXT:长文本
  • DATETIME/TIMESTAMP:时间
  • 主键用 INT 自增
  • 金额用 DECIMAL
  • 状态用 TINYINT
  • 字段加注释

建表选对类型,性能和存储都好。


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

Docker 查看容器日志

Docker 查看容器日志

容器出问题怎么排查?先看日志,这些命令要会。

1. 查看容器日志

基本命令

# 查看全部日志
docker logs 容器名

# 查看容器ID
docker logs 容器ID

2. 常用参数

看最后几行

# 最后10行
docker logs --tail 10 容器名

# 最后20行
docker logs --tail 20 容器名

实时跟踪日志(最常用)

docker logs -f 容器名

带时间戳

docker logs -t 容器名

组合使用

# 实时跟踪 + 时间戳 + 最后10行
docker logs -f -t --tail 10 容器名

3. 按时间查看

# 最近10分钟的日志
docker logs --since 10m 容器名

# 最近1小时的日志
docker logs --since 1h 容器名

# 指定时间开始
docker logs --since "2024-01-01T00:00:00" 容器名

4. 多容器日志

docker-compose 查看日志

# 查看所有服务日志
docker-compose logs

# 实时跟踪
docker-compose logs -f

# 查看指定服务
docker-compose logs nginx

# 看最后50行
docker-compose logs --tail 50 nginx

5. 日志持久化

json-file 日志驱动(默认)

  • 日志存在 /var/lib/docker/containers/
  • 单个容器日志文件

配置日志大小限制

# docker-compose.yml
services:
  app:
    image: nginx
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

6. 常见排查场景

1)容器启动失败

docker logs 容器名

看最后几行报错信息。

2)接口报错

docker logs -f --tail 100 容器名

实时看请求日志。

3)查某个时间点的错误

docker logs --since "2024-01-01T10:00:00" 容器名 | grep "error"

7. 日志清理

清理单个容器日志

# 找到日志文件
sudo find /var/lib/docker/containers/ -name "*.log"

# 清空日志
sudo sh -c "echo > /var/lib/docker/containers/容器ID/容器ID-json.log"

重启容器自动清理

docker restart 容器名

总结

Docker 查看容器日志要点:

  • docker logs 查看日志
  • -f 实时跟踪最常用
  • --tail 看最后几行
  • -t 带时间戳
  • --since 按时间查
  • docker-compose logs 多容器
  • 配置日志大小防止占满磁盘
  • 排查问题先看日志

容器运维,看日志是第一步。


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

PHP PDO 连接数据库

PHP PDO 连接数据库

PDO 是 PHP 推荐的数据库连接方式,支持多种数据库,防注入。

1. 什么是 PDO

PDO(PHP Data Objects)是 PHP 数据库抽象层,统一接口操作多种数据库。

2. 为什么用 PDO

  • 防SQL注入: 预处理语句
  • 支持多数据库: MySQL、PostgreSQL、SQLite 等
  • 面向对象: 写法更规范
  • 错误处理: 统一的错误模式

3. 连接数据库

// 数据库配置
$host = '127.0.0.1';
$dbname = 'test';
$user = 'root';
$pass = 'root';

try {
    // 连接数据库
    $pdo = new PDO(
        "mysql:host=$host;dbname=$dbname;charset=utf8mb4",
        $user,
        $pass,
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
        ]
    );
    echo '连接成功';
} catch(PDOException $e) {
    die('连接失败:' . $e->getMessage());
}

4. 查询数据

查询多条

$stmt = $pdo->query("SELECT * FROM users");
$users = $stmt->fetchAll();

查询单条

$stmt = $pdo->query("SELECT * FROM users WHERE id = 1");
$user = $stmt->fetch();

5. 预处理语句(防注入)

查询

// 问号占位符
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([1]);
$user = $stmt->fetch();

// 命名占位符
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute([':id' => 1]);
$user = $stmt->fetch();

插入

$stmt = $pdo->prepare("INSERT INTO users (name, age) VALUES (:name, :age)");
$stmt->execute([
    ':name' => '张三',
    ':age' => 20
]);

更新

$stmt = $pdo->prepare("UPDATE users SET name = :name WHERE id = :id");
$stmt->execute([
    ':name' => '李四',
    ':id' => 1
]);

删除

$stmt = $pdo->prepare("DELETE FROM users WHERE id = :id");
$stmt->execute([':id' => 1]);

6. 获取自增ID

$pdo->exec("INSERT INTO users (name) VALUES ('王五')");
$id = $pdo->lastInsertId();

7. 事务处理

try {
    $pdo->beginTransaction();

    $pdo->exec("INSERT INTO orders ...");
    $pdo->exec("UPDATE stock ...");

    $pdo->commit();
    echo '事务成功';
} catch(Exception $e) {
    $pdo->rollBack();
    echo '事务失败:' . $e->getMessage();
}

8. 常用配置

// 设置返回关联数组
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC

// 错误模式
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION

// 关闭预处理模拟(真正服务端预处理)
PDO::ATTR_EMULATE_PREPARES => false

总结

PHP PDO 连接数据库要点:

  • PDO 是统一数据库接口
  • 预处理语句防 SQL 注入
  • 支持事务操作
  • 配置错误模式方便调试
  • 推荐用命名占位符
  • 生产环境关闭预处理模拟
  • 记得设置 utf8mb4 字符集

做数据库开发,PDO 比 mysqli 更好用。


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

Linux 查看系统日志

Linux 查看系统日志

排查问题先看日志,这些命令要记住。

1. 系统日志位置

/var/log/messages    # 系统主日志
/var/log/syslog      # Debian/Ubuntu 系统日志
/var/log/boot.log    # 启动日志
/var/log/cron        # 定时任务日志
/var/log/secure      # 安全日志(登录、sudo等)
/var/log/nginx/      # Nginx 日志
/var/log/httpd/      # Apache 日志
/var/log/mysql/      # MySQL 日志

2. 基本查看命令

cat:查看全部

cat /var/log/messages

tail:看末尾几行(最常用)

# 看最后10行
tail /var/log/messages

# 看最后20行
tail -n 20 /var/log/messages

# 实时跟踪日志(最常用)
tail -f /var/log/messages

head:看开头几行

head -n 10 /var/log/messages

3. 搜索日志

grep:搜索关键词

# 搜索错误信息
grep "error" /var/log/messages

# 不区分大小写
grep -i "error" /var/log/messages

# 显示行号
grep -n "error" /var/log/messages

# 反向匹配(不包含error)
grep -v "debug" /var/log/messages

结合管道

# 实时日志里搜索错误
tail -f /var/log/messages | grep "error"

# 搜索最近的nginx错误
tail -n 1000 /var/log/nginx/error.log | grep "500"

4. journalctl 查看 systemd 日志

# 查看全部日志
journalctl

# 看最后100行
journalctl -n 100

# 实时跟踪
journalctl -f

# 按服务查看
journalctl -u nginx

# 按时间查看
journalctl --since "1 hour ago"
journalctl --since "2024-01-01" --until "2024-01-02"

5. 常用排查场景

1)服务器登录失败

grep "Failed password" /var/log/secure

2)服务起不来

systemctl status nginx
journalctl -u nginx -n 50

3)磁盘满了

df -h
du -sh /var/log/*

4)定时任务没执行

cat /var/log/cron

6. 日志清理

手动删除大日志

# 查看日志大小
du -sh /var/log/*

# 清空日志(不删除文件)
> /var/log/messages

# 删除旧日志
rm /var/log/messages.1

logrotate 自动切割

  • Linux 自带日志切割工具
  • 配置文件在 /etc/logrotate.conf
  • 一般不用管,自动轮转

总结

Linux 查看日志要点:

  • 日志都在 /var/log/ 目录
  • tail -f 实时看日志最常用
  • grep 搜索关键词
  • journalctl 查 systemd 服务日志
  • 排查问题先看错误日志
  • 日志太大要及时清理
  • 系统自动轮转不用手动管

运维排查,看日志是第一步。


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

MySQL 视图的使用

MySQL 视图的使用

视图是虚拟表,用好了能简化查询、保护数据。

1. 什么是视图

视图(View)是基于 SQL 查询结果的虚拟表,本身不存数据,数据来自真实表。

2. 为什么用视图

1)简化查询

  • 复杂查询封装成视图
  • 直接查视图就行

2)权限控制

  • 只暴露部分字段给用户
  • 敏感字段不暴露

3)数据独立

  • 表结构变了,视图不变
  • 上层应用不用改

3. 创建视图

-- 创建视图
CREATE VIEW v_user_order AS
SELECT u.id, u.name, o.order_no, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id;

-- 使用视图
SELECT * FROM v_user_order WHERE id = 1;

4. 修改和删除视图

-- 修改视图
CREATE OR REPLACE VIEW v_user_order AS
SELECT u.id, u.name, o.order_no, o.amount, o.create_time
FROM users u
JOIN orders o ON u.id = o.user_id;

-- 删除视图
DROP VIEW IF EXISTS v_user_order;

5. 查看视图

-- 查看所有视图
SHOW FULL TABLES WHERE Table_type = 'VIEW';

-- 查看视图结构
DESC v_user_order;

-- 查看创建语句
SHOW CREATE VIEW v_user_order;

6. 视图的限制

1)不能用 ORDER BY 在视图里

  • 外层查询可以排序
  • 视图里排序意义不大

2)某些视图不能更新

  • 包含 GROUP BY、DISTINCT、JOIN 等的视图
  • 不能通过视图 INSERT/UPDATE/DELETE

3)视图不存数据

  • 每次查询都执行 SQL
  • 复杂视图查询可能慢

7. 实际使用建议

1)视图不是越多越好

  • 复杂业务逻辑用视图
  • 简单查询直接写 SQL

2)视图不要嵌套太多层

  • 视图套视图性能差
  • 最多一层就够了

3)敏感数据保护

  • 密码、手机号等敏感字段
  • 用视图隐藏或脱敏

8. 视图和表的区别

对比项 表 视图
存数据 是 否(虚拟表)
占空间 是 否
查询速度 快 取决于SQL
安全性 低 高

总结

MySQL 视图使用要点:

  • 视图是虚拟表,不存数据
  • 简化复杂查询
  • 保护敏感数据
  • 用 CREATE VIEW 创建
  • 简单视图可以更新
  • 复杂视图查询可能慢
  • 不要过度使用视图

视图是好东西,但别滥用。


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

PHP 上传文件处理

PHP 上传文件处理

文件上传是 Web 开发常见功能,安全处理很重要。

1. HTML 表单

<form method="post" action="upload.php" enctype="multipart/form-data">
    <input type="file" name="file">
    <button type="submit">上传</button>
</form>

注意:必须加 enctype="multipart/form-data"。

2. PHP 接收文件

// $_FILES 接收上传文件
$file = $_FILES['file'];

// 文件信息
$name = $file['name'];        // 文件名
$tmp = $file['tmp_name'];     // 临时路径
$size = $file['size'];        // 文件大小
$error = $file['error'];      // 错误码
$type = $file['type'];        // MIME类型

3. 错误码说明

  • UPLOAD_ERR_OK (0): 上传成功
  • UPLOAD_ERR_INI_SIZE (1): 超过 php.ini 限制
  • UPLOAD_ERR_FORM_SIZE (2): 超过表单限制
  • UPLOAD_ERR_PARTIAL (3): 部分上传
  • UPLOAD_ERR_NO_FILE (4): 没有文件

4. 完整上传代码

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $file = $_FILES['file'];

    // 检查错误
    if ($file['error'] !== UPLOAD_ERR_OK) {
        exit('上传失败');
    }

    // 检查大小(限制2M)
    if ($file['size'] > 2 * 1024 * 1024) {
        exit('文件太大');
    }

    // 允许的扩展名
    $allowExt = ['jpg', 'png', 'gif'];
    $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
    if (!in_array($ext, $allowExt)) {
        exit('不允许的文件类型');
    }

    // 生成新文件名
    $newName = uniqid() . '.' . $ext;
    $uploadDir = 'uploads/';

    // 移动文件
    if (move_uploaded_file($file['tmp_name'], $uploadDir . $newName)) {
        echo '上传成功';
    } else {
        echo '保存失败';
    }
}

5. php.ini 配置

; 上传最大文件大小
upload_max_filesize = 2M

; 表单最大提交大小
post_max_size = 8M

; 允许上传
file_uploads = On

; 临时目录
upload_tmp_dir = /tmp

6. 安全注意事项

1)不要用原文件名

  • 防止覆盖文件和路径穿越
  • 用 uniqid 生成新文件名

2)验证文件类型

  • 不要只看扩展名
  • 用 finfo 检查 MIME 类型
$finfo = new Finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);

3)上传目录不可执行

  • 上传目录关闭 PHP 执行
  • 防止上传木马

4)重命名文件

  • 不要保留用户上传的文件名
  • 防止覆盖和目录穿越攻击

7. 常见问题

1)上传失败,$_FILES 为空

  • 检查表单是否有 enctype
  • 检查 post_max_size 够不够大

2)move_uploaded_file 失败

  • 检查目录权限
  • 检查目录是否存在

3)大文件上传超时

  • 修改 max_execution_time
  • 修改 memory_limit

总结

PHP 文件上传要点:

  • 表单必须加 enctype
  • 用 $_FILES 接收文件
  • 检查错误码和文件大小
  • 验证文件类型
  • 重命名文件防止安全问题
  • 上传目录关闭执行权限
  • 注意 php.ini 配置

做上传功能,安全永远第一位。


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

Nginx 反向代理原理

Nginx 反向代理原理

反向代理是 Nginx 最常用的功能,搞懂原理很有必要。

1. 什么是反向代理

正向代理:代理客户端,比如翻墙工具。
反向代理:代理服务器,客户端不知道真实服务器。

用户请求 Nginx,Nginx 再转发给后端服务器,用户以为自己直接访问的就是 Nginx。

2. 为什么用反向代理

1)隐藏真实服务器

  • 后端服务器 IP 不暴露
  • 更安全

2)负载均衡

  • 多个后端服务器分摊请求
  • 提高并发能力

3)统一入口

  • 多个服务用一个域名
  • 不同路径转发到不同服务

4)SSL 卸载

  • HTTPS 证书在 Nginx 配置
  • 后端用 HTTP 就行

5)缓存静态内容

  • Nginx 直接返回静态资源
  • 减轻后端压力

3. 基本配置

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;
    }
}

4. 常用代理头

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_set_header X-Forwarded-Proto $scheme;

后端通过这些头获取真实客户端信息。

5. 路径转发示例

# 根路径转发到PHP服务
location / {
    proxy_pass http://127.0.0.1:8080;
}

# /api 转发到后端接口
location /api/ {
    proxy_pass http://127.0.0.1:3000/;
}

# /static 直接读本地文件
location /static/ {
    root /var/www;
}

6. 超时设置

# 连接超时
proxy_connect_timeout 60s;

# 读取超时
proxy_read_timeout 60s;

# 发送超时
proxy_send_timeout 60s;

7. 常见问题

1)后端获取不到真实IP

  • 检查 X-Real-IP 头是否设置
  • 后端代码是否读取这个头

2)WebSocket 连接失败

  • 需要添加 Upgrade 头
  • 配置 HTTP/1.1
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;

3)大文件上传失败

  • 修改 client_max_body_size
  • 增加 proxy_read_timeout

总结

Nginx 反向代理要点:

  • 代理服务器,隐藏真实后端
  • 实现负载均衡、统一入口
  • 配置 proxy_pass 转发
  • 记得设置常用代理头
  • 后端通过请求头获取真实客户端信息
  • 注意超时和上传大小限制
  • WebSocket 需要特殊配置

做 Web 开发,反向代理是必备技能。


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

Redis 过期策略详解

Redis 过期策略详解

Redis 设置了过期时间后,怎么删除过期的 key?这就涉及过期策略。

1. 设置过期时间

# 设置过期时间(秒)
set name "张三" ex 60

# 给已有key设置过期时间
expire name 60

# 查看剩余时间
ttl name

# 移除过期时间
persist name

2. 两种过期策略

1)惰性删除

  • 等到访问这个 key 的时候才检查是否过期
  • 过期了就删除,返回 nil
  • 优点:对 CPU 友好
  • 缺点:对内存不友好,很多过期 key 没被访问就占着内存

2)定期删除

  • 每隔一段时间,随机检查一些 key
  • 发现过期的就删除
  • 优点:定期清理,控制内存
  • 缺点:随机抽查可能漏

Redis 实际是两种策略结合用。

3. 内存淘汰策略

当内存满了,写入新数据时怎么办?

# 查看当前淘汰策略
config get maxmemory-policy

常用淘汰策略:

  • noeviction: 不淘汰,写入报错(默认)
  • allkeys-lru: 所有 key 中淘汰最久没用的
  • allkeys-random: 所有 key 中随机淘汰
  • volatile-lru: 只在设置了过期时间的 key 中淘汰最久没用的
  • volatile-random: 只在设置了过期时间的 key 中随机淘汰
  • volatile-ttl: 淘汰即将过期的 key

生产推荐:allkeys-lru

4. LRU 和 LFU

  • LRU(最近最少使用): 淘汰最久没访问的
  • LFU(最不经常使用): 淘汰访问次数最少的

Redis 4.0 开始支持 LFU。

5. 实际使用建议

  1. 设置合理的 maxmemory

    • 不要让 Redis 把内存占满
    • 一般设为服务器内存的 70%
  2. 选择合适的淘汰策略

    • 纯缓存场景:allkeys-lru
    • 有持久化数据:volatile-lru
  3. key 一定要设过期时间

    • 重要数据不要设过期
    • 缓存数据都要设过期时间
  4. 监控内存使用

    • 用 info memory 查看
    • 关注 used_memory 指标

6. 查看内存信息

# 查看内存使用
info memory

# 查看key数量
dbsize

# 查看大key
--bigkeys

总结

Redis 过期策略要点:

  • 惰性删除:访问时才删
  • 定期删除:定期抽查清理
  • 两种策略结合使用
  • 内存满了要配置淘汰策略
  • 生产推荐 allkeys-lru
  • 缓存数据都要设过期时间
  • 监控内存使用情况

搞懂过期策略,Redis 用起来才不踩坑。


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

MySQL 字符集设置

MySQL 字符集设置

中文乱码是数据库开发常见问题,字符集设置一定要搞对。

1. 常用字符集

  • utf8: 最多3字节,不支持 emoji
  • utf8mb4: 最多4字节,支持 emoji,推荐用这个
  • gbk: 中文编码,不推荐

2. 查看当前字符集

-- 查看数据库字符集
SHOW VARIABLES LIKE 'character_set%';

-- 查看排序规则
SHOW VARIABLES LIKE 'collation%';

3. 创建数据库时指定

CREATE DATABASE mydb 
DEFAULT CHARACTER SET utf8mb4 
COLLATE utf8mb4_general_ci;

4. 创建表时指定

CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

5. 修改已有表字符集

-- 修改表字符集
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4;

-- 修改字段字符集
ALTER TABLE users MODIFY name VARCHAR(50) CHARACTER SET utf8mb4;

6. 连接时设置字符集

PHP 连接数据库后一定要设置:

$conn = mysqli_connect('localhost', 'user', 'pass', 'db');
mysqli_set_charset($conn, 'utf8mb4');

或者 SQL 语句:

SET NAMES utf8mb4;

7. 中文乱码排查

出现中文乱码,按顺序检查:

  1. 数据库字符集是不是 utf8mb4
  2. 表字符集是不是 utf8mb4
  3. 字段字符集是不是 utf8mb4
  4. 连接字符集是不是 utf8mb4
  5. 前端页面编码是不是 utf-8

8. 常用排序规则

  • utf8mb4_general_ci: 不区分大小写,常用
  • utf8mb4_unicode_ci: 支持 Unicode,更准确
  • utf8mb4_bin: 区分大小写,按二进制比较

总结

MySQL 字符集设置要点:

  • 统一用 utf8mb4,不要用 utf8
  • 数据库、表、字段都要设置
  • 连接后要设置字符集
  • 中文乱码逐层排查
  • 推荐 utf8mb4_general_ci 排序

字符集统一,才能避免乱码问题。


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

PHP 超全局变量详解

PHP 超全局变量详解

PHP 超全局变量在所有作用域都能用,写接口和表单开发天天用到。

1. 什么是超全局变量

超全局变量是 PHP 预定义的数组变量,不需要 global 声明,在函数、类、文件里都能直接访问。

2. 常用超全局变量

1)$_GET
获取 URL 查询参数。

// 访问 index.php?id=1&name=张三
$id = $_GET['id'];       // 1
$name = $_GET['name'];   // 张三

2)$_POST
获取表单 POST 提交的数据。

// 表单 method="post"
$username = $_POST['username'];
$password = $_POST['password'];

3)$_REQUEST
同时获取 GET、POST、COOKIE 数据。

$id = $_REQUEST['id'];
// 不推荐,安全隐患大

4)$_SESSION
会话数据,跨页面共享。

session_start();
$_SESSION['user_id'] = 1;
$_SESSION['username'] = '张三';

5)$_COOKIE
获取客户端 Cookie。

$token = $_COOKIE['token'];

6)$_SERVER
服务器和执行环境信息。

// 获取请求方法
$method = $_SERVER['REQUEST_METHOD'];

// 获取当前URL
$uri = $_SERVER['REQUEST_URI'];

// 获取客户端IP
$ip = $_SERVER['REMOTE_ADDR'];

// 获取HTTP头
$host = $_SERVER['HTTP_HOST'];

3. 文件上传变量

$_FILES
处理文件上传。

// 表单 enctype="multipart/form-data"
$file = $_FILES['upload'];
$name = $file['name'];        // 文件名
$tmp = $file['tmp_name'];     // 临时路径
$size = $file['size'];        // 文件大小
$error = $file['error'];      // 错误码

4. 注意事项

  • 安全问题: 所有超全局变量都不可信,必须做过滤和验证
  • isset 判断: 取值前用 isset 判断是否存在,避免报错
  • 类型转换: 数字参数记得转 int
  • 不要用 $_REQUEST: 安全隐患大,明确用 GET 还是 POST

5. 安全取值示例

// 安全获取GET参数
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;

// 安全获取POST参数
$username = isset($_POST['username']) ? trim($_POST['username']) : '';

// 判断请求方法
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 处理POST请求
}

总结

PHP 超全局变量要点:

  • $_GET:获取 URL 参数
  • $_POST:获取表单数据
  • $_SESSION:会话数据
  • $_COOKIE:客户端 Cookie
  • $_SERVER:服务器信息
  • $_FILES:文件上传
  • 所有外部输入都要过滤验证,不能直接用
  • 取值前先 isset 判断

做 PHP 开发,超全局变量是基础中的基础。


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