MySQL分页查询优化

MySQL分页查询优化

引言

后台列表、文章分页是最常见的SQL场景。数据量小的时候LIMIT偏移量查询很快,数据到几十万、上百万行,深分页会明显变慢,甚至拖垮数据库。

为什么深分页慢

LIMIT 100000,20 会先扫描前100000行再丢弃,只返回20行,扫描行数越多IO开销越大。

优化方案一:子查询定位主键

先查出起始主键,再用主键关联取数据,避免回表扫描:

SELECT a.* FROM article a
INNER JOIN (
  SELECT id FROM article ORDER BY id LIMIT 100000,20
) b ON a.id=b.id;

子查询走覆盖索引,速度明显提升。

优化方案二:游标/记住上一页最大ID

SELECT * FROM article
WHERE id > 100000
ORDER BY id LIMIT 20;

每次只查比上一页最后一条ID大的记录,不走大偏移,适合无限滚动。

其他优化点

  • 分页排序字段必须建索引,避免filesort;
  • 只查询需要的字段,不用SELECT *;
  • 禁止前端跳转到任意深页,后台限制最大页码;
  • 超大表考虑分表、搜索引擎承接列表。

总结

深分页慢的根源是大偏移扫描。优先用主键游标翻页,配合覆盖索引;传统offset分页用子查询优化。列表数据量极大时,交给ES等搜索方案更合理。


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

PHP文件上传校验

PHP文件上传校验

引言

文件上传是网站高频功能,也是最容易出安全问题的地方。只判断后缀名,黑客可以把木马改名为图片绕过,上传到服务器执行。上传必须做多层校验。

必须校验的内容

  1. 检查上传错误码,确认文件确实上传成功;
  2. 限制文件大小,防止超大文件占满磁盘;
  3. 白名单校验扩展名,只允许jpg/png等指定类型;
  4. 读取文件真实MIME类型,不能只信前端传的type;
  5. 重新生成随机文件名,避免原文件名覆盖或路径穿越;
  6. 上传目录禁止执行PHP脚本。

基础校验示例

<?php
$allow = ['jpg','png','gif'];
$ext = strtolower(pathinfo($_FILES['file']['name'],PATHINFO_EXTENSION));
if(!in_array($ext,$allow)){
    exit('类型不允许');
}
if($_FILES['file']['size'] > 2*1024*1024){
    exit('文件过大');
}
//获取真实类型
$info = getimagesize($_FILES['file']['tmp_name']);
if(!$info) exit('不是合法图片');
//随机重命名
$newName = uniqid().'.'.$ext;
move_uploaded_file($_FILES['file']['tmp_name'],'./upload/'.$newName);

服务端加固

  • Nginx/Apache配置上传目录禁止解析php;
  • 图片上传后重新压缩生成,破坏木马脚本;
  • 关闭上传目录的执行权限;
  • 定期扫描上传目录文件。

总结

文件上传安全核心:不信任前端、白名单、校验真实类型、随机改名、目录隔离。多层校验才能防止webshell上传,保护服务器安全。


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

Git分支管理实践

Git分支管理实践

引言

多人协作开发如果所有人直接往master主分支提交,代码很容易混乱、出现问题无法回滚。一套清晰的分支命名和合并规则,是团队开发协作的基础。

常见分支模型

  • master/main:稳定生产分支,只接受合并,不直接开发;
  • develop:日常集成开发分支;
  • feature/xxx:单个功能开发分支,从develop拉出;
  • hotfix/xxx:线上紧急bug修复分支,从master拉出;
  • release/x.x:版本发布前测试分支。

常用命令

# 新建并切换功能分支
git checkout -b feature/login

# 提交代码
git add .
git commit -m "新增登录接口"

# 推送到远程
git push origin feature/login

开发流程

  1. 从develop拉取新功能分支;
  2. 本地开发提交,定期同步develop最新代码;
  3. 测试通过后发起合并请求(MR/PR);
  4. 代码评审通过合并到develop;
  5. 上线前拉出release分支测试,最终合并master打tag。

注意事项

  • 功能分支命名要语义化,带上开发人或需求编号;
  • 每次提交信息写清楚改了什么;
  • 不要在长期分支堆积大量提交,频繁同步主干避免冲突;
  • hotfix修复完成后要同步回develop,防止下次版本再次出现bug。

总结

Git分支模型不追求复杂,适合团队规模即可。核心原则:主干保持稳定,功能独立分支,小步提交,评审合并,线上问题走hotfix单独修复。


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

Redis哈希结构场景

Redis哈希结构场景

引言

Redis Hash(哈希)适合存储对象类数据,类似数据库单行记录。相比把对象序列化为JSON字符串存储,Hash可以单独读写某个字段,不用整体读取修改,适合用户资料、商品基础信息这类场景。

Hash基础命令

# 设置单个字段
HSET user:100 name "test" age 20

# 获取单个字段
HGET user:100 name

# 获取全部字段
HGETALL user:100

# 删除字段
HDEL user:100 age

PHP操作示例

<?php
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$key = 'user:100';
$redis->hMSet($key,[
    'name'=>'admin',
    'phone'=>'13800138000'
]);
$info = $redis->hGetAll($key);
print_r($info);

适用与不适用场景

✅ 适合:用户资料、商品基础属性,需要局部更新字段

❌ 不适合:字段数量超大、海量对象;大量独立统计计数,推荐单独key

注意事项

Redis Hash有底层ziplist编码,字段少、值小的时候内存占用极低。当字段数量或者value超过阈值,会转为hash表,内存占用上升。不要在单个hash里面存放成千上万的字段。

总结

Hash是Redis高频数据结构,适合存储结构化小对象,可以局部读写字段,避免JSON字符串每次全量读写。根据业务字段数量合理选择,不要滥用。


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

Docker多阶段镜像构建

Docker多阶段镜像构建

引言

直接打包项目代码、编译工具到镜像,镜像体积巨大,部署传输慢。Docker多阶段构建可以在第一阶段编译代码,第二阶段只复制编译后的成品,剔除编译器、源码,大幅度减小镜像大小。

原理

一个Dockerfile可以写多个FROM,每一个FROM代表一个阶段。阶段之间可以复制文件,最终镜像只保留最后一个阶段内容,前面阶段的编译环境不会打包进最终镜像。

示例Dockerfile(PHP项目)

#阶段1:构建
FROM php:8.2-cli AS builder
WORKDIR /app
COPY . .
RUN composer install --no-dev

#阶段2:运行环境
FROM php:8.2-cli-alpine
WORKDIR /app
COPY --from=builder /app .
CMD ["php","index.php"]

最终镜像只有php运行环境加业务代码,没有composer、源码缓存文件。

优势

  • 镜像体积大幅缩小,传输更快;
  • 减少攻击面,镜像内没有编译工具,安全性更高;
  • 只维护单个Dockerfile,构建流程简单。

注意事项

  • 多阶段命名便于阅读;
  • 不要复制不需要的文件,.dockerignore忽略日志、缓存;
  • 区分开发镜像与生产镜像,生产镜像精简。

总结

多阶段构建是容器化部署最佳实践,适合PHP、Go、前端打包等场景,生产环境镜像尽量使用多阶段构建,减小体积提升部署效率。


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

Web XSS攻击分类

Web XSS攻击分类

引言

XSS跨站脚本是最常见的Web漏洞,黑客注入JS代码,在用户浏览器执行,盗取cookie、劫持页面。建站开发必须了解XSS三种类型,针对性做好防护。

XSS三大类型

  1. 存储型XSS:代码存入数据库,所有访问页面的用户都会触发,危害最大。例如评论区没有过滤,提交恶意js存入数据库。
  2. 反射型XSS:恶意代码放在URL参数,用户点击链接,页面直接输出参数触发,不会存入数据库。
  3. DOM型XSS:前端JS直接读取URL参数修改DOM,不经过后端服务,后端日志看不到攻击载荷。

基础防护方案

  • 后端输出页面使用htmlspecialchars转义;
  • 前端渲染数据不要直接innerHtml,优先textContent;
  • 设置Cookie HttpOnly,防止JS读取cookie;
  • 配置CSP内容安全策略。

简单示例漏洞代码

<?php
//危险代码,直接输出参数,存在反射XSS
echo $_GET['name'];
//安全写法
echo htmlspecialchars($_GET['name'],ENT_QUOTES);
?>

总结

XSS核心成因:把外部可控数据当成页面代码执行。存储型风险最高,DOM型容易被忽略。前后端配合做输出转义、CSP、HttpOnly Cookie,多层防护抵御XSS。


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

MySQL慢查询定位

MySQL慢查询定位

引言

网站访问缓慢很多根源是执行时间过长的SQL。开启MySQL慢查询日志,可以捕获执行超时SQL,找到需要优化的语句,是数据库性能排查的核心手段。

慢查询基础配置

慢查询日志会记录执行时长超过指定阈值的SQL语句。

-- 查看慢查询是否开启
SHOW VARIABLES LIKE 'slow_query_log';

-- 设置阈值,单位秒,超过1秒就记录
SET GLOBAL long_query_time =1;

常用分析工具

  1. mysqldumpslow:mysql自带工具,统计慢日志
    mysqldumpslow -s t /var/lib/mysql/slow.log
  2. pt-query-digest:percona工具,分析效果更强,适合大量日志。

优化排查流程

  1. 开启慢查询,收集慢SQL;
  2. 使用explain查看执行计划,判断是否缺少索引;
  3. 优化where条件,添加合适索引,改写SQL逻辑;
  4. 优化完成后,持续观察慢日志确认效果。

示例

EXPLAIN SELECT * FROM article WHERE title like '%PHP%';

看type字段,ALL代表全表扫描,是需要优化的信号。

总结

慢查询日志用来抓取低效SQL,但是开启会少量增加服务器IO压力,生产环境可以按需开启。定位到慢SQL后优先检查索引,再调整SQL写法,解决数据库卡顿问题。


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

PHP请求参数过滤

PHP请求参数过滤

引言

Web接口接收GET、POST参数,如果不做过滤校验,容易出现SQL注入、XSS、业务逻辑漏洞。PHP开发接口时,参数过滤是安全第一道防线,所有外部输入都不可信。

基础过滤思路

  1. 类型强制转换:数字参数直接转为int;
  2. 长度限制,防止传入超大字符串;
  3. 正则匹配手机号、邮箱等固定格式;
  4. 输出到页面时做html转义,防止XSS;
  5. 入库前转义或者使用PDO预编译防止注入。

代码示例

<?php
// 获取GET参数
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
$name = trim($_GET['name'] ?? '');

// 长度校验
if(mb_strlen($name)>50){
    exit("参数过长");
}

// 输出到页面,防XSS
echo htmlspecialchars($name,ENT_QUOTES);

框架层面最佳实践

TP6、Laravel等框架自带验证器,推荐优先使用框架验证,不用手写大量if判断。

// ThinkPHP6验证示例
$validate = new \think\Validate([
    'id'=>'require|integer',
    'name'=>'require|max:50'
]);

避坑点

不要简单依靠addslashes防御注入,推荐PDO预处理语句;参数过滤不要只在前端校验,后端必须二次校验,前端校验可被绕过。

总结

任何外部传入的数据都要校验,区分类型校验、长度校验、格式校验、输出转义四层防护,降低Web安全漏洞风险,是PHP项目开发的基础规范。


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

Linux查看系统负载

Linux查看系统负载

引言

服务器CPU、内存压力过大,会造成网站接口响应缓慢、502报错。运维排查服务器性能问题第一步就是查看系统负载,了解服务器当前资源压力,及时扩容或者优化程序。

负载概念

uptime输出的三个数字:1分钟、5分钟、15分钟平均负载。代表等待CPU处理的任务数量。

多核CPU参考标准:4核服务器,负载接近4说明CPU打满;超过代表任务排队,系统过载。

常用命令

# 快速查看负载
uptime

# 实时监控
top

# 更轻量化监控
htop

# 查看CPU核心数量
nproc

top命令内可以按1查看每个核心负载;P按CPU使用率排序,快速找到耗资源进程。

排查思路

  1. 负载高,CPU使用率高:程序代码循环、大量计算,定位进程优化代码;
  2. 负载高,CPU使用率低:大量IO等待(磁盘读写慢、数据库阻塞),查看磁盘io iostat。

实操示例

# 安装iostat
yum install -y sysstat

# 持续查看io状态
iostat -x 1

总结

平均负载不只代表CPU,还包含IO等待。看到高负载不能直接判定CPU瓶颈,结合top、iostat综合定位,区分是计算密集还是IO密集导致服务器卡顿。


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

Nginx防盗链原理

Nginx防盗链原理

引言

网站图片、视频、静态资源经常被其他站点直接引用,消耗服务器流量与带宽。Nginx防盗链通过校验请求来源Referer,阻止第三方网站直接嵌入你的资源链接,降低服务器流量损耗。

原理说明

浏览器访问页面加载图片时,请求头会携带Referer,代表来源页面地址。Nginx读取该请求头,判断域名是否属于本站,非信任域名直接返回403拒绝访问。

局限:Referer可被客户端伪造,防盗链属于基础防护,不是100%安全,重要资源建议搭配token鉴权。

Nginx配置示例

location ~* \.(jpg|png|gif|mp4)$ {
    valid_referers none blocked *.0454.wang;
    if ($invalid_referer) {
        return 403;
    }
}

配置解释:

  • valid_referers配置允许的来源;
  • none允许直接浏览器地址栏访问资源;
  • 匹配不到白名单,$invalid_referer生效,返回403。

进阶优化

  • 重要资源可使用url签名,生成带时效token的资源地址;
  • CDN环境,需要同步在CDN配置防盗链;
  • 部分爬虫、APP请求不带Referer,按需调整规则。

总结

Nginx Referer防盗链是低成本的静态资源保护手段,适合图片博客、素材站点。适合作为第一层防护,高安全场景需要结合URL签名二次校验。


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

Redis分布式锁实战

Redis分布式锁实战

引言

多服务器部署项目时,多个服务实例同时操作同一资源,会出现并发争抢问题。Redis分布式锁可以保证同一时间只有一个请求执行临界代码,常用于订单扣库存、定时任务防重复执行场景。

实现原理

利用RedisSET key value NX EX命令:NX仅当key不存在才设置,EX设置过期时间。原子命令可以防止锁成功后服务宕机导致死锁。解锁时需要校验锁的值,防止误删除别人的锁。

PHP代码示例

<?php
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$lockKey = 'order:lock:1001';
$lockVal = uniqid();
// 获取锁,过期10秒
$ok = $redis->set($lockKey,$lockVal,['NX','EX'=>10]);
if($ok){
    try{
        // 临界区业务代码
        echo "获取锁,执行业务";
    }finally{
        // lua脚本原子解锁,判断value一致才删除
        $lua = 'if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end';
        $redis->eval($lua,[$lockKey,$lockVal],1);
    }
}else{
    echo "获取锁失败,资源被占用";
}

注意事项

  1. 锁必须设置过期时间,防止程序崩溃死锁;
  2. 解锁必须用Lua脚本,保证判断和删除原子操作;
  3. 业务执行时间不能超过锁过期时间,可增加锁续期逻辑;
  4. 单机Redis存在单点故障,高可用场景用Redis集群或Redlock。

总结

Redis分布式锁简单易用,适合中小型Web项目并发控制。理解原子操作、锁过期、安全解锁三个关键点,避免网上简易版本带来的并发漏洞。


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

MySQL索引设计原则

MySQL索引设计原则

引言

MySQL索引相当于书籍目录,快速定位数据,避免全表扫描。不合理的索引不仅无法提升查询速度,还会降低写入性能。索引设计是数据库优化最基础、最高效的手段,建站、接口开发都需要掌握。

索引核心原理

InnoDB引擎索引基于B+树实现,主键索引叶子节点存储完整行数据;二级索引叶子节点存储主键值,查询会触发回表。索引字段越大,占用磁盘越多,内存缓存能存放的索引越少,查询越慢。

索引设计核心原则

  1. 选择性优先:优先给区分度高的字段建索引,如手机号、ID;性别、状态这类只有少量枚举值字段不适合单独建索引。
  2. 联合索引遵循最左匹配原则,字段顺序:等值查询在前,范围查询放后面。
  3. 避免在索引字段做函数运算,where YEAR(create_time)=2026会直接失效索引。
  4. 不要过度建索引,每次新增、更新、删除数据,所有索引都需要同步更新,拖累写入。

示例SQL

-- 联合索引示例,适合 where uid=100 and create_time>xxx
CREATE INDEX idx_uid_ct ON user(uid,create_time);

-- 查看sql是否命中索引
EXPLAIN SELECT * FROM user WHERE uid=100;

常见踩坑

  • 字符串字段不加引号,隐式类型转换,索引失效;
  • 模糊查询%关键词,前置百分号不走索引;
  • 联合索引跳过左侧字段,无法触发索引。

总结

索引不是越多越好,按需创建。建索引前先用explain分析执行计划,优先优化高频查询SQL,控制索引数量,平衡查询与写入性能。


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

PHP trait复用代码方案

PHP trait复用代码方案

引言

在PHP面向对象开发中,类只支持单继承,一个子类只能继承一个父类。当多个不同类需要共用一段逻辑,又不想强行继承同一父类时,Trait特性就是最佳解决方案。Trait可以把可复用的方法抽离出来,在多个类中引入,打破单继承限制,广泛用于项目公共方法封装。

Trait基础原理

Trait本质不是类,不能实例化,仅用于封装方法。使用use关键字在类内部引入,引入后,类可以直接调用Trait内定义的方法。

优先级规则:本类方法 > trait方法 > 父类方法。当方法重名时,优先执行当前类定义的代码。

代码示例

<?php
trait LogTrait {
    public function writeLog(string $msg) {
        $log = date('Y-m-d H:i:s') . " : " . $msg . PHP_EOL;
        file_put_contents('./run.log', $log, FILE_APPEND);
    }
}

class UserService {
    use LogTrait;
    public function addUser() {
        $this->writeLog("新增用户数据");
    }
}

$user = new UserService();
$user->addUser();
?>

多Trait冲突处理

一个类可以同时use多个Trait。多个Trait存在同名方法会直接报错,这时使用insteadof指定优先使用哪个Trait的方法,as可以给方法起别名。

trait A{ public function test(){echo "A";}}
trait B{ public function test(){echo "B";}}
class Demo{
    use A,B{
        A::test insteadof B;
        B::test as bTest;
    }
}

适用场景与坑点

✅ 适用场景:多类共用日志、验证码、文件上传等公共逻辑

⚠️ 注意:Trait不能定义静态常量;尽量不要在Trait写大量业务逻辑,只放通用工具方法,避免代码难以维护。

总结

Trait弥补了PHP单继承短板,适合项目中抽取通用能力。合理使用可以减少大量重复代码,但不能滥用,否则会造成代码逻辑分散,增加后期维护成本。


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

JavaScript this指向详解常见问题汇总

项目赶进度的时候,this指向详解这类基础问题最容易浪费调试时间。提前掌握本文的内容,可以少走很多弯路。


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

JavaScript 全屏API使用最佳实践(附代码示例)

JavaScript的生态非常丰富,但全屏API使用依然是最常用的功能点之一。本文整理了最实用的几种实现方式。


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

Java 面向对象三大特性入门教程(附代码示例)

做Java开发这几年,面向对象三大特性是被身边同事问得最多的问题之一。今天把答案整理出来,供大家参考。


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

Git alias别名设置常见问题汇总(完整版)

如果你正在学习Git,那么alias别名设置几乎是绕不开的一个环节。本文从原理到实践,带你快速上手。


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

Git reflog恢复误操作案例讲解(新手必看)

如果你正在学习Git,那么reflog恢复误操作几乎是绕不开的一个环节。本文从原理到实践,带你快速上手。


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

PHP 批量导入CSV数据完整指南(完整版)

最近在项目里又遇到了批量导入CSV数据的典型问题,索性把解决方法整理成这篇文章,希望能帮到遇到同样问题的同学。


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

Python 爬虫获取网页标题进阶技巧分享(完整版)

很多初学者在学习Python时,都会遇到爬虫获取网页标题相关的问题。网上的资料零散且质量参差不齐,本文尝试用一篇文章把爬虫获取网页标题讲清楚。


emer 发布于  2026-10-14 07:09 
'); })();