Redis 缓存穿透解决
Redis 缓存穿透解决方案
缓存穿透是指查询一个数据库里根本不存在的数据,因为缓存里也没有,所以每次请求都打到数据库。如果有人恶意攻击,数据库可能被打挂。
什么是缓存穿透
正常流程:先查缓存,缓存有就直接返回,没有再查数据库,然后写入缓存。
如果查询的数据数据库里也没有,那缓存永远都不会命中,每次请求都会查数据库,这就是缓存穿透。
解决方案一:缓存空值
最简单的办法,数据库查不到也往缓存里写一个空值,设个短过期时间。
$data = $redis->get($key);
if ($data === null) {
$data = $db->query($sql);
if ($data === null) {
// 缓存空值,60秒过期
$redis->setex($key, 60, '');
} else {
$redis->setex($key, 3600, $data);
}
}
优点:简单直接
缺点:会占用更多缓存空间,大量空值缓存会浪费内存
解决方案二:布隆过滤器
在缓存前面加一层布隆过滤器,先判断数据是否存在,不存在直接返回。
布隆过滤器的原理是用一个大的位数组和多个哈希函数,把存在的数据标记为1。查询时如果所有哈希位置都是1,说明可能存在;只要有一个位置是0,说明一定不存在。
优点:内存占用小,效率高
缺点:有一定误判率(说存在可能不存在,说不存在一定不存在)
两种方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 缓存空值 | 简单 | 浪费内存 | 数据量小,穿透不严重 |
| 布隆过滤器 | 内存效率高 | 有误差 | 数据量大,穿透严重 |
总结
缓存穿透是常见的缓存问题,小项目用缓存空值就够了,大项目数据量大就上布隆过滤器。两种方案都能解决问题,根据实际场景选就行。
Nginx 反向代理配置
Nginx 反向代理配置详解
Nginx 反向代理是最常用的服务器配置之一,把请求转发给后端服务。本文讲清楚核心配置。
1. 基本配置
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
2. 传递真实IP
后端服务要拿到用户真实IP,需要配置:
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
3. 负载均衡
多台后端服务器,用 upstream 做负载均衡:
upstream backend {
server 127.0.0.1:8080 weight=1;
server 127.0.0.1:8081 weight=2;
}
server {
location / {
proxy_pass http://backend;
}
}
4. 超时设置
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
5. 静态文件处理
静态文件直接由 Nginx 处理,不用转发给后端:
location ~* \.(js|css|png|jpg|gif)$ {
root /data/www;
expires 30d;
}
6. HTTPS 反向代理
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:8080;
}
}
总结
Nginx 反向代理的核心就是 proxy_pass,配上 proxy_set_header 传递必要信息,再加上超时和静态文件处理,基本够用了。
MySQL 慢查询优化技巧
MySQL 慢查询优化实战技巧
数据库慢查询是生产环境最常见的性能问题之一。掌握慢查询优化技巧,能让系统性能提升数倍。
1. 开启慢查询日志
先开启慢查询日志,找出哪些 SQL 慢:
slow_query_log = 1
long_query_time = 1
slow_query_log_file = /var/log/mysql/slow.log
超过1秒的查询就会被记录。
2. 用 Explain 分析
慢 SQL 拿到后,用 EXPLAIN 分析执行计划:
EXPLAIN SELECT * FROM users WHERE name = '张三';
重点看:
- type:访问类型,ALL 是全表扫描,要优化
- key:实际用到的索引
- rows:扫描行数
- Extra:额外信息,Using filesort、Using temporary 要警惕
3. 加索引
最直接的优化就是加索引:
ALTER TABLE users ADD INDEX idx_name(name);
加索引原则:
- WHERE、JOIN、ORDER BY 字段优先
- 区分度高的字段效果好
- 联合索引遵循最左前缀
4. 避免 SELECT *
只查需要的字段:
-- 不好
SELECT * FROM users WHERE id = 1;
-- 好
SELECT id, name FROM users WHERE id = 1;
5. 深分页优化
LIMIT 100000, 10 很慢,因为要扫描10万行:
-- 不好
SELECT * FROM users LIMIT 100000, 10;
-- 好,用子查询
SELECT * FROM users
WHERE id >= (SELECT id FROM users LIMIT 100000, 1)
LIMIT 10;
6. 大表拆小
单表数据量超过千万行,性能会下降。可以:
- 按时间分表
- 按用户ID分库
- 归档历史数据
7. 总结
慢查询优化的核心思路:先找慢 SQL,再分析执行计划,然后加索引或改写 SQL。优化完记得对比前后效果,确保真的变快了。
Linux 进程管理命令
Linux 进程管理常用命令
服务器上跑了多少进程?哪个进程占内存多?怎么杀进程?这些命令必须会。
1. 查看进程
# 查看所有进程
ps aux
# 查找指定进程
ps aux | grep nginx
# 实时查看
top
2. top 命令详解
top 是最常用的进程查看工具:
- 第一行:系统负载
- 第二行:任务数
- 第三行:CPU 使用率
- 第四行:内存使用
- 第五行:Swap 使用
按 q 退出,按 k 杀进程。
3. 杀进程
# 按 PID 杀
kill 1234
# 强制杀
kill -9 1234
# 按名字杀
pkill nginx
# 杀掉所有同名进程
killall nginx
4. 查看端口占用
# 查看端口被谁占了
netstat -tlnp | grep 80
# 或者用 ss
ss -tlnp | grep 80
5. 查看进程树
# 查看进程父子关系
pstree
# 查看某个进程的子进程
pstree -p 1234
6. 后台运行
# 后台运行
nohup ./script.sh &
# 查看后台任务
jobs
# 把任务调到前台
fg %1
7. 查看资源占用
# 内存占用排行
ps aux --sort=-%mem | head
# CPU 占用排行
ps aux --sort=-%cpu | head
总结
日常运维就这几个命令:ps、top、kill、netstat、nohup。学会这些,服务器进程管理基本够用了。
PHP 安全最佳实践
PHP 开发必须注意的安全问题
PHP 开发最容易出安全问题,几个常见坑一定要避开。
1. SQL 注入
最常见的安全漏洞。用户输入拼接到 SQL 里,被注入恶意 SQL。
解决方案:用预处理语句
// 不要这样
$sql = "SELECT * FROM users WHERE id = " . $_GET['id'];
// 要这样
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]);
2. XSS 跨站脚本
用户输入直接输出到页面,被注入恶意 JS。
解决方案:输出时转义
echo htmlspecialchars($user_input);
3. CSRF 跨站请求伪造
别人伪造你的请求操作。
解决方案:加 Token 验证
// 表单里加 Token
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['token']; ?>">
// 后端验证
if ($_POST['csrf_token'] !== $_SESSION['token']) {
die('CSRF 攻击');
}
4. 文件上传漏洞
用户上传 PHP 文件,直接拿到 webshell。
解决方案:
- 限制文件类型
- 重命名文件
- 上传目录不执行 PHP
5. 密码明文存储
数据库被拖库,密码全泄露。
解决方案:用 password_hash
$hash = password_hash($password, PASSWORD_DEFAULT);
// 验证
password_verify($password, $hash);
6. 错误信息泄露
生产环境把报错直接展示给用户,暴露路径和代码。
解决方案:关闭 display_errors
display_errors = Off
log_errors = On
总结
PHP 安全的核心:用户输入都不可信,所有输出都要转义,数据库操作用预处理。安全开发意识比工具更重要。
MySQL 索引原理详解
MySQL 索引为什么能提速
索引是数据库性能优化的核心,搞懂原理才能用对。
1. 为什么慢查询慢
没有索引,MySQL 就得全表扫描,一行一行找。100万行数据就得扫100万次,当然慢。
有了索引,就像书有目录,直接定位到位置,查几百万行也很快。
2. B+ 树索引
MySQL InnoDB 默认用 B+ 树做索引。
- 树的高度低,一般3-4层
- 一次查询只需3-4次磁盘IO
- 叶子节点存数据,非叶子节点存索引
3. 聚簇索引 vs 二级索引
聚簇索引:数据和索引放在一起,按主键组织。一张表只有一个聚簇索引。
二级索引:索引和数据分开,叶子节点存主键值。查二级索引需要回表查主键。
4. 覆盖索引
如果查询的字段都在索引里,就不用回表了,叫覆盖索引。
-- name有索引
SELECT name FROM users WHERE name = '张三';
-- 覆盖索引,不用回表
5. 最左前缀
联合索引 (a, b, c),必须从最左列开始用:
WHERE a = 1能用WHERE a = 1 AND b = 2能用WHERE b = 2用不了
6. 索引失效场景
- 索引字段做函数运算
- 隐式类型转换
- like 以 % 开头
- 联合索引不满足最左前缀
总结
索引是空间换时间。不是越多越好,太多索引会影响写入性能,占用额外存储空间。
PHP 8.0 新特性
PHP 8.0 有哪些重磅更新
PHP 8.0 是一个大版本更新,带来了很多革命性的特性。
1. JIT 编译
PHP 8.0 引入了 JIT(即时编译),性能大幅提升,计算密集型场景性能翻倍。
2. 命名参数
array_filter(array: $array, callback: fn($x) => $x > 0);
不用按顺序传参,写参数名就行,可读性更好。
3. 枚举 Enum
enum Status: string
{
case Pending = 'pending';
case Active = 'active';
case Rejected = 'rejected';
}
4. 构造函数属性提升
class User {
public function __construct(
public int $id,
public string $name
) {}
}
不用再写 $this->id = $id 了,构造函数直接定义属性。
5. 空安全运算符
$user?->getAddress()?->city;
不用层层判断 null,有 null 直接返回 null。
6. Match 表达式
$result = match($status) {
200 => '成功',
404 => '未找到',
default => '未知'
};
比 switch 更简洁,还能直接赋值。
7. 联合类型
function foo(int|string $input) {}
参数可以是多种类型。
总结
PHP 8.0 是现代 PHP 的起点,类型系统更完善,语法更现代。新项目强烈推荐用 PHP 8.0+。
Git 常用命令汇总
Git 常用命令速查
Git 是开发必备工具,掌握常用命令就行,不用背太多。
1. 初始化与克隆
# 初始化仓库
git init
# 克隆远程仓库
git clone https://github.com/user/repo.git
2. 提交相关
# 查看状态
git status
# 添加到暂存区
git add .
git add file.php
# 提交
git commit -m "提交说明"
# 查看提交历史
git log --oneline
3. 分支操作
# 查看分支
git branch
# 创建分支
git branch feature
# 切换分支
git checkout feature
# 创建并切换
git checkout -b feature
# 合并分支
git merge feature
# 删除分支
git branch -d feature
4. 远程操作
# 拉取远程更新
git pull
# 推送本地提交
git push origin main
# 查看远程仓库
git remote -v
5. 撤销操作
# 撤销工作区修改
git checkout -- file.php
# 撤销暂存区
git reset HEAD file.php
# 回退到上个版本
git reset --hard HEAD~1
6. 暂存操作
# 暂存当前修改
git stash
# 恢复暂存
git stash pop
7. 标签
# 打标签
git tag v1.0.0
# 推送标签
git push origin v1.0.0
总结
日常开发就这几个命令:add、commit、push、pull、branch、merge。其他的用到再查就行,不用全背。
Docker Compose 实战
Docker Compose 一键部署多容器
Docker Compose 可以用一个 YAML 文件定义和运行多个 Docker 容器,开发环境部署特别方便。
1. 基本结构
version: '3'
services:
web:
image: nginx
ports:
- "80:80"
db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: 123456
2. 常用命令
# 启动所有服务
docker-compose up -d
# 查看服务状态
docker-compose ps
# 查看日志
docker-compose logs -f
# 停止并删除容器
docker-compose down
# 重启某个服务
docker-compose restart web
3. PHP 开发环境示例
version: '3'
services:
php:
build: .
ports:
- "8000:80"
volumes:
- ./:/var/www/html
depends_on:
- mysql
- redis
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:alpine
volumes:
mysql_data:
4. 数据卷
数据卷用来持久化数据,容器删了数据还在:
volumes:
- ./data:/var/lib/mysql
5. 网络
默认同一 Compose 里的服务在同一个网络,可以直接用服务名互相访问。比如 PHP 容器里连数据库用 mysql:3306,不用写 IP。
总结
Docker Compose 适合本地开发和测试环境。生产环境部署建议用 K8s 或者更专业的编排工具。
Nginx 日志分析技巧
Nginx 日志分析常用命令
Nginx 日志是排查问题、分析流量的重要工具。掌握几个常用命令,线上排查效率翻倍。
1. 查看访问量
# 总请求数
wc -l access.log
# 今天的请求数
grep "04/Oct/2026" access.log | wc -l
2. 分析 IP 访问量
# 访问最多的前10个IP
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -10
3. 分析 URL 访问量
# 访问最多的前10个URL
awk '{print $7}' access.log | sort | uniq -c | sort -nr | head -10
4. 分析状态码
# 统计各状态码数量
awk '{print $9}' access.log | sort | uniq -c | sort -nr
# 404错误数量
grep " 404 " access.log | wc -l
5. 分析流量
# 总流量
awk '{sum += $10} END {print sum/1024/1024 " MB"}' access.log
6. 实时监控
# 实时查看最新日志
tail -f access.log
# 实时查看502错误
tail -f access.log | grep " 502 "
7. 查找慢请求
# 响应时间超过1秒的请求
awk '$NF > 1' access.log | head -10
8. 排查爬虫
# 百度爬虫访问量
grep "Baiduspider" access.log | wc -l
总结
Nginx 日志分析主要靠 awk + sort + uniq 组合,掌握这几个命令,日常排查基本够用。复杂分析可以用 GoAccess 等可视化工具。
Redis 持久化详解
Redis 两种持久化机制对比
Redis 是内存数据库,重启数据就没了,所以持久化很重要。Redis 有两种持久化方式:RDB 和 AOF。
1. RDB 快照
RDB 就是把整个内存数据快照存成文件。
优点:
- 文件小,恢复快
- 适合备份和灾难恢复
缺点:
- 可能丢数据,两次快照之间的写入会丢
配置:
save 900 1 # 900秒内至少1个key变化就快照
save 300 10 # 300秒内至少10个key变化
save 60 10000 # 60秒内至少10000个key变化
2. AOF 日志
AOF 把每个写操作都记录到日志文件,重启时重放日志恢复数据。
优点:
- 数据更安全,最多丢1秒
- 日志可读,误删可以手动修复
缺点:
- 文件比 RDB 大
- 恢复慢
配置:
appendonly yes
appendfsync everysec # 每秒刷盘一次
3. 混合持久化
Redis 4.0 支持混合持久化,RDB 做全量,AOF 做增量。
aof-use-rdb-preamble yes
生产环境推荐用这个,兼顾性能和数据安全。
4. 怎么选
- 纯缓存场景:不要持久化,性能最好
- 数据不能丢:用 AOF 或混合持久化
- 需要快速恢复:用 RDB
总结
Redis 持久化没有完美方案,根据业务场景选。生产环境建议混合持久化,兼顾性能和数据安全。
MySQL 8.0 新特性
MySQL 8.0 有哪些重要更新
MySQL 8.0 是目前最主流的大版本,相比 5.7 提升很大。
1. 性能提升
8.0 的性能比 5.7 提升约 2 倍,尤其是读取和写入并发场景下提升明显。
2. 原子 DDL
8.0 支持原子 DDL,建表、删表操作要么全成功要么全失败,不会出现一半成功一半失败的情况。
3. 窗口函数
支持窗口函数,比如 ROW_NUMBER()、RANK(),做排名统计很方便:
SELECT name, score,
RANK() OVER (ORDER BY score DESC) as rank
FROM students;
4. CTE 公共表表达式
支持 WITH 语法,复杂 SQL 写起来更清晰:
WITH top_users AS (
SELECT id, name FROM users WHERE vip = 1
)
SELECT * FROM top_users;
5. JSON 增强
JSON 操作函数更丰富,性能也更好,基本可以替代 MongoDB 的简单场景。
6. 隐藏索引
可以把索引设为隐藏状态,不删除但暂时不用,用来测试索引是否真的有用。
7. 字符集变化
默认字符集从 latin1 改成了 utf8mb4,支持 Emoji 表情。
总结
MySQL 8.0 是目前新项目的首选,性能好、特性全。老项目从 5.7 升级要注意兼容性,尤其是 SQL 模式和函数变化。
PHP 7.4 新特性
PHP 7.4 有哪些实用新特性
PHP 7.4 是 7.x 系列的最后一个版本,带来了不少语法糖和性能优化,至今还有很多项目在用。
1. 类型属性
类的属性可以声明类型了:
class User {
public int $id;
public string $name;
public ?string $email;
}
之前属性不能写类型,只能靠 PHPDoc 注释,现在直接写在代码里,IDE 提示更准确。
2. 箭头函数
支持短闭包:
$users = array_map(fn($user) => $user['name'], $users);
比传统的 function ($user) use (...) {} 简洁多了,自动捕获外层变量。
3. 空合并赋值
$data['user'] ??= [];
相当于:
if (!isset($data['user'])) {
$data['user'] = [];
}
4. 数组展开
$arr1 = [1, 2, 3];
$arr2 = [4, 5, 6];
$merged = [...$arr1, ...$arr2];
5. 数字字面量分隔
$amount = 1_000_000;
$price = 9_999.99;
大数字用下划线分隔,可读性提升。
6. 性能提升
PHP 7.4 比 7.3 性能提升约 10%,内存占用也更少。
总结
PHP 7.4 虽然不是大版本,但实用特性很多,尤其是类型属性和箭头函数,写起来舒服不少。新项目建议直接上 PHP 8+,老项目升级到 7.4 也很稳妥。
PHP Session 与 Cookie:会话管理实战指南
前言
HTTP 是无状态的,你每次请求服务器都不认识你。Session 和 Cookie 就是用来解决这个问题的。本文讲清楚 PHP 中 Session 和 Cookie 的用法。
一、Cookie 是什么
Cookie 是存在浏览器里的小数据,每次请求自动带给服务器。
设置 Cookie
setcookie("username", "张三", time() + 3600, "/");
参数说明:
- 第一个参数:名称
- 第二个参数:值
- 第三个参数:过期时间(时间戳)
- 第四个参数:路径
读取 Cookie
echo $_COOKIE['username'];
删除 Cookie
setcookie("username", "", time() - 3600, "/");
把过期时间设成过去就行。
二、Session 是什么
Session 是存在服务器上的数据,通过 Session ID 识别用户。
启动 Session
session_start();
注意:必须在输出之前调用,不然会报错。
存数据
$_SESSION['user_id'] = 1;
$_SESSION['username'] = '张三';
读数据
echo $_SESSION['username'];
删除单个数据
unset($_SESSION['user_id']);
销毁 Session
session_destroy();
三、Session 和 Cookie 的区别
| 对比 | Cookie | Session |
|---|---|---|
| 存储位置 | 浏览器 | 服务器 |
| 大小限制 | 4KB | 无限制 |
| 安全性 | 低(用户能改) | 高 |
| 生命周期 | 可设置 | 默认浏览器关闭就没 |
四、登录功能示例
登录页
<?php
session_start();
if ($_POST) {
$username = $_POST['username'];
$password = $_POST['password'];
// 验证用户名密码
if ($username == 'admin' && $password == '123456') {
$_SESSION['user_id'] = 1;
$_SESSION['username'] = $username;
header('Location: dashboard.php');
exit;
} else {
echo "用户名或密码错误";
}
}
?>
判断是否登录
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
?>
退出登录
<?php
session_start();
session_destroy();
header('Location: login.php');
exit;
?>
五、Session 配置
修改 Session 有效期
ini_set('session.gc_maxlifetime', 3600); // 1小时
session_start();
修改 Session 存储路径
session_save_path('/tmp/sessions');
session_start();
用 Redis 存储 Session
生产环境推荐用 Redis 存 Session,默认文件存储性能差。
php.ini 配置:
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379"
六、安全问题
1. Session 劫持
别人拿到你的 Session ID,就能冒充你。
防护:
- 用 HTTPS
- Session ID 定期更换
- 绑定 IP(不太推荐,IP 会变)
2. Cookie 劫持
别人拿到你的 Cookie,就能冒充你。
防护:
- 设置 HttpOnly,JS 读不到
- 设置 Secure,只在 HTTPS 下传
- 设置 SameSite,防止 CSRF
setcookie("token", $token, [
'expires' => time() + 3600,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict'
]);
3. CSRF 攻击
别人伪造你的请求。
防护:
- 表单加 CSRF Token
- 验证 Referer
七、常见坑
- session_start() 在输出之后:报错 headers already sent
- Session 没销毁:退出登录后还能访问
- Cookie 大小限制:别存太多东西,超过 4KB 就丢
- Session 文件太多:默认文件存储,大了要清理
八、最佳实践
- 敏感数据用 Session,别存 Cookie
- Cookie 加 HttpOnly 和 Secure
- 生产环境用 Redis 存 Session
- 登录成功后重新生成 Session ID,防止固定攻击
// 登录成功后
session_regenerate_id(true);
总结
Session 和 Cookie 的核心要点:
- Cookie 存在浏览器,Session 存在服务器
- session_start() 必须在输出之前调用
- Session 更安全,Cookie 更轻量
- 生产环境用 Redis 存 Session
- Cookie 加 HttpOnly 和 Secure,防劫持
登录、权限、购物车,都离不开 Session 和 Cookie。搞懂这俩,PHP 后端才算入门。
PHP Composer 入门:依赖管理完全指南
前言
Composer 是 PHP 的依赖管理工具,就像 Node 的 npm、Python 的 pip。现在写 PHP 项目,不会 Composer 根本没法干活。本文讲清楚 Composer 的核心用法。
一、安装 Composer
Linux / Mac
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
Windows
去官网下载安装包,一路下一步就行。
验证安装
composer -V
二、初始化项目
# 在项目目录下
composer init
会问你几个问题,一路回车就行,然后会生成 composer.json 文件。
composer.json 长什么样
{
"name": "yourname/project",
"description": "项目描述",
"require": {
"monolog/monolog": "^2.0"
}
}
三、安装依赖
1. 安装所有依赖
composer install
会根据 composer.lock 文件安装指定版本的依赖。
2. 添加新依赖
composer require monolog/monolog
会自动加到 composer.json 里,然后安装。
3. 安装指定版本
composer require monolog/monolog:^2.0
版本号说明
^2.0:2.0 到 3.0 之前(不包括 3.0)~2.0:2.0 到 2.1 之前(不包括 2.1)*:任意版本2.0.*:2.0 开头的版本
四、自动加载
Composer 最爽的就是自动加载,不用自己写 require。
引入自动加载
<?php
require 'vendor/autoload.php';
// 直接用类,不用 require
$log = new Monolog\Logger('name');
PSR-4 自动加载
在 composer.json 里配置:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
然后执行:
composer dump-autoload
这样 App\User 类就会从 src/User.php 加载。
五、常用命令
| 命令 | 作用 |
|---|---|
composer install |
安装所有依赖 |
composer update |
更新所有依赖 |
composer require 包名 |
添加依赖 |
composer remove 包名 |
删除依赖 |
composer dump-autoload |
重新生成自动加载 |
composer show |
查看已安装的包 |
composer self-update |
更新 Composer 本身 |
六、开发环境依赖
有些包只在开发时用,比如测试工具:
composer require --dev phpunit/phpunit
线上部署的时候,不安装开发依赖:
composer install --no-dev
七、中国镜像
国外源太慢,换中国镜像:
# 阿里云镜像
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
# 腾讯云镜像
composer config -g repo.packagist composer https://mirrors.tencent.com/composer/
换完之后速度飞起来。
八、composer.lock 的作用
- 记录当前安装的依赖版本
- 团队成员 install 的时候,装的版本一模一样
- 一定要提交到 Git
git add composer.json composer.lock
git commit -m "update composer"
九、常见坑
- 没装 composer.lock:团队成员装的版本不一样,出 bug
- 自动加载不生效:改了 autoload 配置,忘了 dump-autoload
- 版本冲突:两个包依赖同一个库的不同版本
- 国内太慢:不换镜像,下载半天
十、最佳实践
- composer.json 和 composer.lock 都提交 Git
- 线上用 install --no-dev,不要用 update
- 用中文镜像,加速下载
- 改了自动加载配置,记得 dump-autoload
总结
Composer 的核心要点:
- require 添加依赖,install 安装依赖
- vendor/autoload.php 是自动加载入口
- composer.lock 要提交 Git,保证版本一致
- 换国内镜像,速度快很多
- 开发依赖用 --dev,线上不装
学会 Composer,PHP 开发效率提升一倍。
MySQL 备份与恢复:mysqldump 实战指南
前言
数据库备份是生产环境的生命线。很多人没出事的时候不备份,出事了才后悔。本文讲清楚 MySQL 最常用的备份工具 mysqldump。
一、为什么要备份
- 误操作:手滑删了表、删了数据
- 服务器挂了:硬盘坏了、机器报废
- 被攻击: ransomware 加密了数据库
- 升级失败:新版本不兼容,数据回退
记住:没有备份的数据库,就是在裸奔。
二、mysqldump 是什么
mysqldump 是 MySQL 自带的逻辑备份工具,把数据导出成 SQL 文件。
优点:简单、通用、跨版本
缺点:大数据量慢
适合:中小数据库(< 50G)
三、备份数据库
1. 备份整个数据库
mysqldump -u root -p 数据库名 > backup.sql
输入密码后,就会生成 backup.sql 文件。
2. 备份所有数据库
mysqldump -u root -p --all-databases > all_backup.sql
3. 备份单个表
mysqldump -u root -p 数据库名 表名 > table_backup.sql
4. 备份多个表
mysqldump -u root -p 数据库名 表1 表2 > multi_backup.sql
四、恢复数据库
1. 恢复整个数据库
mysql -u root -p 数据库名 < backup.sql
注意:数据库要先存在。
CREATE DATABASE 数据库名;
2. 恢复所有数据库
mysql -u root -p < all_backup.sql
五、常用参数
--single-transaction
InnoDB 引擎下,保证备份一致性,不锁表:
mysqldump -u root -p --single-transaction 数据库名 > backup.sql
生产环境一定要加这个!
--routines
备份存储过程和函数:
mysqldump -u root -p --routines 数据库名 > backup.sql
--triggers
备份触发器:
mysqldump -u root -p --triggers 数据库名 > backup.sql
--events
备份事件调度器:
mysqldump -u root -p --events 数据库名 > backup.sql
--no-data
只备份表结构,不备份数据:
mysqldump -u root -p --no-data 数据库名 > schema.sql
--where
按条件备份:
mysqldump -u root -p 数据库名 表名 --where="id > 100" > partial.sql
六、完整备份命令
生产环境推荐用这个:
mysqldump -u root -p \
--single-transaction \
--routines \
--triggers \
--events \
--default-character-set=utf8mb4 \
数据库名 > backup.sql
七、压缩备份
备份文件太大?压缩一下:
# 备份并压缩
mysqldump -u root -p 数据库名 | gzip > backup.sql.gz
# 解压并恢复
gunzip < backup.sql.gz | mysql -u root -p 数据库名
八、定时备份
写个脚本每天自动备份:
#!/bin/bash
# 备份目录
BACKUP_DIR="/data/backup/mysql"
DATE=$(date +%Y%m%d)
# 备份
mysqldump -u root -p密码 --single-transaction 数据库名 | gzip > $BACKUP_DIR/db_$DATE.sql.gz
# 删除 7 天前的备份
find $BACKUP_DIR -name "db_*.sql.gz" -mtime +7 -delete
加到 crontab:
# 每天凌晨 2 点备份
0 2 * * * /bin/bash /path/to/backup.sh
九、常见坑
- 备份不锁表:大表备份会锁表,一定要加 --single-transaction
- 字符集不对:备份文件乱码,加 --default-character-set=utf8mb4
- 没备份存储过程:导出后丢了,加 --routines
- 备份没验证:备份完一定要试恢复,不然真出事的时候哭都来不及
十、备份策略
- 每天全量备份:小数据库
- 每周全量 + 每天增量:大数据库
- 异地备份:备份文件要存到别的服务器
- 定期演练恢复:每个月测一次能不能恢复
总结
MySQL 备份的核心要点:
- mysqldump 是最常用的备份工具
- 生产环境加 --single-transaction,不锁表
- 备份要压缩,节省空间
- 定时备份,自动删除旧备份
- 备份一定要验证,能恢复才算备份成功
数据是公司最值钱的资产,备份这事儿千万别偷懒。
Linux 常用命令:从文件操作到进程管理的完整指南
前言
Linux 是后端开发的必修课。很多新手面对黑窗口就发怵,其实只要掌握常用命令,日常开发就够用了。本文整理最常用的 Linux 命令。
一、文件操作
1. 查看目录
# 列出当前目录文件
ls
# 详细信息
ls -l
# 包括隐藏文件
ls -la
# 按时间排序
ls -lt
2. 切换目录
# 进入目录
cd /home/user
# 回到上一级
cd ..
# 回到用户主目录
cd ~
# 回到上次目录
cd -
3. 创建目录
# 创建目录
mkdir test
# 递归创建多级目录
mkdir -p a/b/c
4. 创建文件
# 创建空文件
touch test.txt
# 写入内容
echo "hello" > test.txt
# 追加内容
echo "world" >> test.txt
5. 复制文件
# 复制文件
cp test.txt test2.txt
# 复制目录
cp -r dir1 dir2
6. 移动/重命名
# 移动文件
mv test.txt /tmp/
# 重命名
mv old.txt new.txt
7. 删除文件
# 删除文件
rm test.txt
# 删除目录
rm -rf dir1
警告:rm -rf 很危险,一定要确认路径!
二、查看文件内容
1. cat
# 查看整个文件
cat test.txt
2. less
# 分页查看
less test.log
# 快捷键:
# 空格:下一页
# b:上一页
# /关键字:搜索
# q:退出
3. head / tail
# 看前 10 行
head -10 test.txt
# 看后 10 行
tail -10 test.txt
# 实时看日志(最常用)
tail -f /var/log/nginx/access.log
三、查找文件
1. find
# 按文件名查找
find / -name "nginx.conf"
# 按大小查找(大于 100M)
find / -size +100M
# 按修改时间查找(7 天内修改的)
find / -mtime -7
2. grep
# 在文件里搜索
grep "error" test.log
# 不区分大小写
grep -i "error" test.log
# 显示行号
grep -n "error" test.log
# 递归搜索目录
grep -r "function" /var/www/
# 配合管道使用
ps aux | grep nginx
四、系统状态
1. 查看进程
# 所有进程
ps aux
# 查找进程
ps aux | grep nginx
2. 杀死进程
# 按 PID 杀
kill 1234
# 强制杀
kill -9 1234
# 按名字杀
pkill nginx
3. 查看端口
# 查看所有监听端口
netstat -tlnp
# 查看某个端口
netstat -tlnp | grep 80
4. 查看磁盘
# 磁盘使用情况
df -h
# 当前目录大小
du -sh .
# 每个子目录大小
du -sh *
5. 查看内存
free -h
五、权限管理
1. chmod
# 给文件加执行权限
chmod +x test.sh
# 755:所有者可读写执行,其他人可读执行
chmod 755 test.sh
# 644:所有者可读写,其他人只读
chmod 644 test.txt
2. chown
# 修改文件所有者
chown www:www test.txt
六、压缩解压
1. tar
# 压缩
tar -zcvf website.tar.gz /var/www/html
# 解压
tar -zxvf website.tar.gz
2. zip
# 压缩
zip -r website.zip /var/www/html
# 解压
unzip website.zip
七、网络相关
1. curl
# 访问网页
curl https://www.baidu.com
# 查看响应头
curl -I https://www.baidu.com
# 发 POST 请求
curl -X POST -d "name=张三" https://api.example.com
2. wget
# 下载文件
wget https://example.com/file.zip
八、常用快捷键
| 快捷键 | 作用 |
|---|---|
| Ctrl + C | 终止当前命令 |
| Ctrl + Z | 暂停当前命令 |
| Ctrl + D | 退出终端 |
| Tab | 自动补全 |
| Ctrl + R | 搜索历史命令 |
| !命令 | 执行最近一次的某命令 |
九、常用操作速查
| 操作 | 命令 |
|---|---|
| 看文件 | cat / less / tail -f |
| 找文件 | find / grep |
| 看进程 | ps aux / top |
| 看端口 | netstat -tlnp |
| 看磁盘 | df -h / du -sh |
| 改权限 | chmod / chown |
总结
Linux 常用命令的核心要点:
- 文件操作:ls / cd / mkdir / cp / mv / rm
- 查看内容:cat / less / tail -f
- 查找搜索:find / grep
- 进程管理:ps / kill / netstat
- 系统状态:df / du / free
不用背,用多了自然就记住了。先把这几个常用的练熟,日常开发基本够用。
PHP 数组常用操作:从排序到过滤的完整指南
前言
数组是 PHP 里最常用的数据结构,没有之一。PHP 提供了 100 多个数组函数,很多人只知道其中几个。本文整理最常用的数组操作。
一、创建数组
<?php
// 索引数组
$arr1 = [1, 2, 3, 4, 5];
// 关联数组
$arr2 = ['name' => '张三', 'age' => 18];
// 空数组
$arr3 = [];
二、添加元素
<?php
$arr = [1, 2, 3];
// 末尾添加
$arr[] = 4; // [1, 2, 3, 4]
array_push($arr, 5); // [1, 2, 3, 4, 5]
// 开头添加
array_unshift($arr, 0); // [0, 1, 2, 3, 4, 5]
// 关联数组添加
$user = ['name' => '张三'];
$user['age'] = 18; // ['name' => '张三', 'age' => 18]
三、删除元素
<?php
$arr = [1, 2, 3, 4, 5];
// 删除末尾元素
array_pop($arr); // [1, 2, 3, 4]
// 删除开头元素
array_shift($arr); // [2, 3, 4]
// 删除指定元素
unset($arr[1]); // [0 => 2, 2 => 4],注意索引不连续
四、查找元素
<?php
$arr = ['a', 'b', 'c'];
// 值是否存在
in_array('b', $arr); // true
// 键是否存在
$user = ['name' => '张三', 'age' => 18];
array_key_exists('name', $user); // true
// 查找值的键
array_search('b', $arr); // 1
五、数组长度
<?php
$arr = [1, 2, 3];
count($arr); // 3
sizeof($arr); // 3(count 的别名)
六、排序
索引数组排序
<?php
$arr = [3, 1, 2, 5, 4];
sort($arr); // [1, 2, 3, 4, 5] 升序
rsort($arr); // [5, 4, 3, 2, 1] 降序
关联数组排序
<?php
$age = ['张三' => 20, '李四' => 18, '王五' => 25];
asort($age); // 按值升序,保持键名
ksort($age); // 按键升序
arsort($age); // 按值降序
krsort($age); // 按键降序
自定义排序
<?php
$users = [
['name' => '张三', 'age' => 20],
['name' => '李四', 'age' => 18],
['name' => '王五', 'age' => 25],
];
// 按 age 升序
usort($users, function ($a, $b) {
return $a['age'] <=> $b['age'];
});
七、数组过滤
array_filter()
<?php
$arr = [1, 2, 3, 4, 5, 6];
// 过滤出偶数
$even = array_filter($arr, function ($n) {
return $n % 2 == 0;
});
// [2, 4, 6]
array_map()
<?php
$arr = [1, 2, 3, 4, 5];
// 每个元素乘以 2
$doubled = array_map(function ($n) {
return $n * 2;
}, $arr);
// [2, 4, 6, 8, 10]
array_walk()
<?php
$arr = ['a' => 1, 'b' => 2];
array_walk($arr, function (&$value, $key) {
$value = $value * 10;
});
// ['a' => 10, 'b' => 20]
八、数组合并
<?php
$arr1 = [1, 2];
$arr2 = [3, 4];
// 数组合并
$merged = array_merge($arr1, $arr2); // [1, 2, 3, 4]
// + 运算符(保留第一个数组的键)
$result = $arr1 + $arr2;
九、数组切片
<?php
$arr = [1, 2, 3, 4, 5];
// 从第 2 个开始取 3 个
$slice = array_slice($arr, 1, 3); // [2, 3, 4]
// 从末尾取
$last = array_slice($arr, -2); // [4, 5]
十、键值操作
<?php
$user = ['name' => '张三', 'age' => 18];
// 所有键
array_keys($user); // ['name', 'age']
// 所有值
array_values($user); // ['张三', 18]
// 翻转键值
array_flip($user); // ['张三' => 'name', 18 => 'age']
十一、常用操作速查表
| 操作 | 函数 |
|---|---|
| 数组长度 | count() |
| 查找值 | in_array() |
| 查找键 | array_key_exists() |
| 排序 | sort() / asort() / ksort() |
| 过滤 | array_filter() |
| 映射 | array_map() |
| 合并 | array_merge() |
| 切片 | array_slice() |
十二、常见坑
- unset 后索引不连续:用 array_values() 重新索引
- array_merge 和 + 的区别:数字键 array_merge 会重新索引,+ 不会
- in_array 严格模式:加第三个参数 true 防止类型转换
- sort 会改变原数组:不是返回新数组
总结
PHP 数组操作的核心要点:
- 增删改查:array_push/pop/unshift/shift/unset
- 查找判断:in_array/array_key_exists/array_search
- 排序:sort/asort/ksort/usort
- 处理:array_filter/array_map/array_walk
- 合并切片:array_merge/array_slice
数组用好了,PHP 开发效率能提升一倍。
Redis 数据结构:五种常用类型和应用场景实战
前言
Redis 不只是简单的缓存,它有 5 种常用数据结构,每种都有特定的应用场景。用对了能解决很多问题,用错了就是浪费内存。本文讲清楚这 5 种数据结构。
一、String(字符串)
是什么
最简单的 key-value 结构,value 可以是字符串、数字、二进制。
常用命令
# 设置值
SET name "张三"
# 获取值
GET name
# 数字自增
INCR count
INCRBY count 10
# 设置过期时间
SETEX session:token 3600 "abc123"
# 批量操作
MSET name "张三" age 18 city "北京"
MGET name age city
应用场景
- 缓存:缓存用户信息、配置
- 计数器:文章阅读数、点赞数
- 分布式锁:SETNX 命令
- Session 存储:用户登录状态
二、Hash(哈希)
是什么
一个 key 对应一个 map,适合存储对象。
常用命令
# 设置单个字段
HSET user:1 name "张三"
HSET user:1 age 18
# 获取单个字段
HGET user:1 name
# 获取所有字段
HGETALL user:1
# 批量设置
HMSET user:1 name "张三" age 18 city "北京"
# 字段自增
HINCRBY user:1 age 1
应用场景
- 存储对象:用户信息、商品信息
- 购物车:key 是用户 ID,field 是商品 ID,value 是数量
# 购物车
HSET cart:1001 商品A 2
HSET cart:1001 商品B 1
三、List(列表)
是什么
有序列表,底层是双向链表,可以从左右两边 push/pop。
常用命令
# 左边插入
LPUSH messages "hello"
LPUSH messages "world"
# 右边插入
RPUSH messages "end"
# 左边弹出
LPOP messages
# 右边弹出
RPOP messages
# 查看列表范围
LRANGE messages 0 -1
# 列表长度
LLEN messages
应用场景
- 消息队列:LPUSH + RPOP
- 最新消息:比如最新评论、最新文章
- 朋友圈时间线:LPUSH 新内容,LRANGE 分页查看
四、Set(集合)
是什么
无序且不重复的集合,自动去重。
常用命令
# 添加元素
SADD tags "PHP" "MySQL" "Redis"
# 查看所有元素
SMEMBERS tags
# 判断元素是否存在
SISMEMBER tags "PHP"
# 集合运算
SINTER set1 set2 # 交集
SUNION set1 set2 # 并集
SDIFF set1 set2 # 差集
# 集合大小
SCARD tags
应用场景
- 标签系统:一篇文章的标签
- 共同好友:SINTER 算交集
- 去重:UV 统计、防止重复提交
- 抽奖:SRANDMEMBER 随机抽取
五、ZSet(有序集合)
是什么
每个元素都有一个 score,按 score 排序,不重复。
常用命令
# 添加元素
ZADD rank 100 "张三"
ZADD rank 90 "李四"
ZADD rank 95 "王五"
# 按分数从低到高排序
ZRANGE rank 0 -1 WITHSCORES
# 按分数从高到低排序
ZREVRANGE rank 0 -1 WITHSCORES
# 查看元素排名
ZREVRANK rank "张三"
# 查看元素分数
ZSCORE rank "张三"
# 分数范围查询
ZRANGEBYSCORE rank 90 100
应用场景
- 排行榜:直播间送礼排行、游戏排行
- 延迟队列:score 存时间戳,到点的任务处理
- 加权排序:搜索结果排序
六、怎么选数据结构
| 场景 | 用什么 |
|---|---|
| 简单 key-value | String |
| 存对象 | Hash |
| 有序列表/队列 | List |
| 去重/标签 | Set |
| 排序/排行 | ZSet |
七、常见坑
- List 太多元素:链表太长,范围查询慢
- Hash 字段太多:小对象还好,大对象不适合
- Set 当 List 用:Set 无序,要顺序用 List 或 ZSet
- 大 key:一个 key 存几百万数据,会阻塞
总结
Redis 5 种数据结构的核心要点:
- String:最简单,缓存、计数器
- Hash:存对象,用户信息、购物车
- List:有序队列,消息、时间线
- Set:去重集合,标签、共同好友
- ZSet:排序集合,排行榜、延迟队列
选对数据结构,Redis 才能发挥最大威力。
PHP 字符串处理:常用函数和最佳实践指南
前言
字符串处理是 PHP 开发中最常用的操作之一。很多人写了很多年代码,却还是用错字符串函数。本文整理最常用的字符串处理函数和最佳实践。
一、字符串长度
strlen()
<?php
$str = "hello";
echo strlen($str); // 5
注意:strlen 按字节算,中文会出问题。
mb_strlen()
<?php
$str = "你好";
echo mb_strlen($str, 'UTF-8'); // 2
echo strlen($str); // 6(3个字节一个汉字)
记住:处理中文一定要用 mb_ 系列函数。
二、字符串查找
strpos()
查找字符串第一次出现的位置:
<?php
$str = "hello world";
$pos = strpos($str, "world");
echo $pos; // 6
坑: 如果没找到返回 false,如果找到的位置是 0,也返回 0。
<?php
// 错误写法
if (strpos($str, "hello")) {
// 如果 hello 在第 0 位,这里不会执行
}
// 正确写法
if (strpos($str, "hello") !== false) {
// 这样才对
}
strrpos()
查找字符串最后一次出现的位置。
stripos() / strripos()
不区分大小写的查找。
三、字符串替换
str_replace()
<?php
$str = "hello world";
echo str_replace("world", "PHP", $str); // hello PHP
数组替换:
<?php
echo str_replace(
["hello", "world"],
["hi", "PHP"],
"hello world"
); // hi PHP
substr_replace()
替换字符串的一部分:
<?php
echo substr_replace("hello world", "PHP", 6); // hello PHP
preg_replace()
正则替换:
<?php
// 把多个空格变成一个
echo preg_replace('/\s+/', ' ', "hello world"); // hello world
四、字符串截取
substr()
<?php
$str = "hello world";
echo substr($str, 0, 5); // hello
echo substr($str, 6); // world
中文截取要用 mb_substr():
<?php
$str = "你好世界";
echo mb_substr($str, 0, 2, 'UTF-8'); // 你好
五、字符串分割与连接
explode()
<?php
$str = "a,b,c,d";
$arr = explode(",", $str);
print_r($arr); // ['a', 'b', 'c', 'd']
implode() / join()
<?php
$arr = ['a', 'b', 'c'];
echo implode(",", $arr); // a,b,c
六、字符串去空格
trim()
<?php
$str = " hello ";
echo trim($str); // "hello",去掉两边空格
echo ltrim($str); // "hello ",去掉左边空格
echo rtrim($str); // " hello",去掉右边空格
七、大小写转换
<?php
$str = "Hello World";
echo strtolower($str); // hello world
echo strtoupper($str); // HELLO WORLD
echo ucfirst($str); // Hello world
echo ucwords($str); // Hello World
八、HTML 转义
htmlspecialchars()
输出到 HTML 时一定要转义,防止 XSS:
<?php
$str = "<script>alert('xss')</script>";
echo htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
// 输出 <script>alert('xss')</script>
htmlentities()
更彻底的转义,把所有 HTML 都转义。
九、JSON 处理
json_encode()
<?php
$arr = ['name' => '张三', 'age' => 18];
echo json_encode($arr, JSON_UNESCAPED_UNICODE);
// {"name":"张三","age":18}
记住:加 JSON_UNESCAPED_UNICODE,不然中文会变成 \uXXXX。
json_decode()
<?php
$json = '{"name":"张三","age":18}';
$obj = json_decode($json); // 对象
$arr = json_decode($json, true); // 数组
十、常见坑
- strpos 用 == 判断:位置 0 和 false 会混淆
- 中文用 strlen/substr:按字节算,中文会乱码
- 输出 HTML 不转义:XSS 漏洞
- json_encode 不加 JSON_UNESCAPED_UNICODE:中文变 \uXXXX
- 字符串拼接用 . 不用 +:PHP 是 . 号
总结
PHP 字符串处理的核心要点:
- 中文用 mb_ 系列函数,不要用 strlen/substr
- strpos 用 !== false 判断,不要用 ==
- 输出 HTML 一定要 htmlspecialchars 转义
- json_encode 加 JSON_UNESCAPED_UNICODE
- 字符串拼接用 . 号,不要用 + 号
掌握这些,字符串处理就不会出大问题了。