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 服务日志
- 排查问题先看错误日志
- 日志太大要及时清理
- 系统自动轮转不用手动管
运维排查,看日志是第一步。
Linux 文件权限详解
Linux 文件权限详解
Linux 权限是基础,搞不懂经常出问题。
1. 查看权限
ls -l
输出:
-rw-r--r-- 1 root root 1000 Oct 8 10:00 test.txt
2. 权限含义
第一位:文件类型
-
- 普通文件
- d 目录
- l 软链接
后面9位:分3组
- 第1组:所有者权限
- 第2组:所属组权限
- 第3组:其他人权限
3个字母:
- r 读(4)
- w 写(2)
- x 执行(1)
比如 rw-r--r--:
- 所有者:读写(6)
- 组:只读(4)
- 其他人:只读(4)
3. 修改权限 chmod
数字方式:
# 所有者读写执行,组读执行,其他人读执行
chmod 755 test.sh
# 所有人只读
chmod 444 test.txt
字母方式:
# 给所有者加执行权限
chmod u+x test.sh
# 给组加写权限
chmod g+w test.txt
# 去掉其他人的读权限
chmod o-r test.txt
4. 修改所有者 chown
# 修改所有者
chown www:www test.txt
# 递归修改目录
chown -R www:www /www/wwwroot
5. 常用权限
- 目录:755(所有者读写执行,其他人读执行)
- 普通文件:644(所有者读写,其他人只读)
- 脚本文件:755
6. 常见问题
1)网站打不开:
- 检查目录权限是不是 755
- 检查文件权限是不是 644
- 检查所有者是不是 www
2)上传不了文件:
- 检查目录有没有写权限
3)脚本执行不了:
- 检查有没有执行权限
总结
Linux 文件权限要点:
- r=4, w=2, x=1
- chmod 修改权限
- chown 修改所有者
- 目录 755,文件 644
- 权限不对经常出问题
做运维,权限一定要搞懂。
Nginx 负载均衡配置
Nginx 负载均衡配置
多台服务器分流,提高网站并发能力,Nginx 负载均衡很常用。
1. 什么是负载均衡
- 多台服务器做同样的服务
- Nginx 把请求分发给不同服务器
- 提高并发能力,一台挂了其他还能用
2. 基础配置
upstream myserver {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
server 127.0.0.1:8082;
}
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://myserver;
}
}
3. 常用分发策略
1)轮询(默认)
- 一个接一个轮流分
upstream myserver { server 127.0.0.1:8080; server 127.0.0.1:8081; }
2)加权轮询
- 性能好的服务器多分点
upstream myserver { server 127.0.0.1:8080 weight=3; server 127.0.0.1:8081 weight=1; }
3)ip_hash
- 同一个IP固定访问同一台
upstream myserver { ip_hash; server 127.0.0.1:8080; server 127.0.0.1:8081; }
4. 健康检查
upstream myserver {
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8081 max_fails=3 fail_timeout=30s;
}
- max_fails:失败几次标记为挂掉
- fail_timeout:多久后重试
5. 常用参数
upstream myserver {
# 备用服务器,其他都挂了才用
server 127.0.0.1:8080 backup;
# 不参与负载均衡
server 127.0.0.1:8081 down;
}
6. 注意事项
- 多台服务器数据要同步(Session、数据库)
- Session 共享用 Redis
- 文件上传用共享存储
总结
Nginx 负载均衡要点:
- upstream 定义服务器组
- proxy_pass 转发到组
- 轮询、加权、ip_hash 策略
- 健康检查自动剔除挂掉的服务
- 注意 Session 共享
做大并发网站,负载均衡是必备的。
Linux 防火墙配置详解
Linux 防火墙配置详解
服务器安全第一步就是防火墙,学会配置很重要。
1. firewalld(CentOS 7+)
查看状态:
systemctl status firewalld
开启/关闭:
# 开启
systemctl start firewalld
# 关闭
systemctl stop firewalld
# 开机自启
systemctl enable firewalld
2. 开放端口
开放单个端口:
firewall-cmd --zone=public --add-port=80/tcp --permanent
开放多个端口:
firewall-cmd --zone=public --add-port=80-90/tcp --permanent
重新加载生效:
firewall-cmd --reload
3. 查看端口
# 查看所有开放端口
firewall-cmd --list-ports
# 查看某个端口是否开放
firewall-cmd --query-port=80/tcp
4. 关闭端口
firewall-cmd --zone=public --remove-port=80/tcp --permanent
firewall-cmd --reload
5. iptables(旧版)
查看规则:
iptables -L -n
开放端口:
iptables -I INPUT -p tcp --dport 80 -j ACCEPT
保存规则:
service iptables save
6. 常用端口
# 常用端口
22 SSH
80 HTTP
443 HTTPS
3306 MySQL
6379 Redis
8080 Tomcat
7. 常见问题
1)端口开了还是访问不了:
- 检查云服务商安全组
- 检查服务器内部防火墙
2)远程连接不上:
- 检查 22 端口是否开放
- 检查 sshd 是否启动
总结
Linux 防火墙要点:
- firewalld 管理端口
- --permanent 永久生效
- --reload 重新加载
- 记得开放安全组
- 常用端口按需开放
做运维,防火墙是基本功。
Nginx 日志分析技巧
Nginx 日志分析技巧
Nginx 日志是排查问题的重要工具,学会分析日志很重要。
1. 日志位置
默认日志路径:
# 访问日志
/var/log/nginx/access.log
# 错误日志
/var/log/nginx/error.log
2. 日志格式
默认日志格式:
log_format access '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';
3. 常用分析命令
1)查看访问量最多的IP:
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head 10
2)查看访问最多的页面:
awk '{print $7}' access.log | sort | uniq -c | sort -nr | head 10
3)查看状态码分布:
awk '{print $9}' access.log | sort | uniq -c | sort -nr
4)查看404错误:
awk '$9 == 404 {print $7}' access.log | sort | uniq -c | sort -nr
4. 排查常用
1)查看哪个IP访问频繁:
grep "127.0.0.1" access.log
2)查看某个时间段日志:
grep "08/Oct/2026:07" access.log
3)查看错误日志:
tail -f error.log
5. 日志切割
用 logrotate 自动切割:
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 30
compress
notifempty
create 0640 nginx adm
postrotate
systemctl reload nginx
endscript
}
6. 常用场景
- 被攻击了:查访问量最多的IP
- 页面报错:查404/500
- 流量突增:查访问最多的页面
- 爬虫多:查 User-Agent
总结
Nginx 日志分析要点:
- 访问日志查访问情况
- 错误日志排查问题
- awk 统计数据
- 日志自动切割
- 定期分析日志
做运维,看日志是基本功。
MySQL 备份恢复实战
MySQL 备份恢复实战
数据库备份是运维的基本功,数据丢了就找不回来了。
1. 备份方式
1)物理备份:
- 直接拷贝数据文件
- 速度快,适合大数据库
2)逻辑备份:
- 导出 SQL 语句
- 体积小,跨平台
2. mysqldump 备份
备份整个数据库:
mysqldump -u root -p 数据库名 > backup.sql
备份单个表:
mysqldump -u root -p 数据库名 表名 > table.sql
备份所有数据库:
mysqldump -u root -p --all-databases > all.sql
3. 恢复数据
mysql -u root -p 数据库名 < backup.sql
注意: 恢复前要先创建数据库。
4. 压缩备份
# 备份并压缩
mysqldump -u root -p 数据库名 | gzip > backup.sql.gz
# 恢复压缩文件
gunzip < backup.sql.gz | mysql -u root -p 数据库名
5. 定时备份脚本
#!/bin/bash
# 数据库备份脚本
DB_USER="root"
DB_PASS="123456"
DB_NAME="test"
BACKUP_DIR="/data/backup"
DATE=$(date +%Y%m%d)
# 创建备份目录
mkdir -p $BACKUP_DIR
# 备份
mysqldump -u$DB_USER -p$DB_PASS $DB_NAME | gzip > $BACKUP_DIR/$DB_NAME_$DATE.sql.gz
# 删除7天前的备份
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete
加 crontab 定时执行:
# 每天凌晨3点备份
0 3 * * * /bin/bash /root/backup.sh
6. 常见问题
1)备份太大:
- 用 --quick 参数
- 压缩备份
2)备份报错:
- 检查用户名密码
- 检查权限
3)恢复失败:
- 检查 SQL 文件是否完整
- 检查数据库字符集
7. 最佳实践
- 每天定时备份
- 备份文件存到异地
- 定期测试恢复
- 重要数据做双备份
总结
MySQL 备份恢复要点:
- mysqldump 导出 SQL
- gzip 压缩减小体积
- crontab 定时执行
- 定期测试恢复
做运维,数据备份永远是第一位的。
Docker 镜像加速配置
Docker 镜像加速配置
国内拉取 Docker 镜像很慢,配置镜像加速器很有必要。
1. 为什么要加速
Docker Hub 在国外,国内拉镜像经常超时,配置加速器能快很多。
2. 配置方法
Linux(CentOS/Ubuntu):
编辑配置文件:
mkdir -p /etc/docker
vi /etc/docker/daemon.json
写入内容:
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
]
}
重启生效:
systemctl daemon-reload
systemctl restart docker
3. 常用加速器
# 中科大
https://docker.mirrors.ustc.edu.cn
# 网易
https://hub-mirror.c.163.com
# 阿里(需要自己的地址)
https://xxxx.mirror.aliyuncs.com
4. 验证是否生效
docker info
看到 Registry Mirrors 下面有地址就说明成功了。
5. 常见问题
1)拉取还是很慢:
- 换个加速器试试
- 网络问题
2)配置不生效:
- 检查 json 格式对不对
- 重启 docker
3)企业内网:
- 可以自己搭建私有镜像仓库
- 或者用阿里云私有镜像服务
6. 常用镜像源
# 官方
docker pull nginx
docker pull mysql
docker pull redis
# 指定版本
docker pull nginx:1.25
docker pull mysql:8.0
总结
Docker 镜像加速核心:
- 配置 daemon.json
- 用国内加速器地址
- 重启 docker 生效
- docker info 验证
做运维开发,Docker 镜像加速是第一步。
Nginx 反向代理配置
Nginx 反向代理配置
反向代理是 Nginx 最常用的功能,前端请求 Nginx,Nginx 再转发给后端服务。
1. 什么是反向代理
- 正向代理: 代理客户端,比如翻墙
- 反向代理: 代理服务器,客户端不知道真正的后端
2. 基础配置
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
意思是:访问 80 端口的请求,转发到 8080 端口的服务。
3. 常用配置说明
location / {
# 转发地址
proxy_pass http://127.0.0.1:8080;
# 传递真实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;
# 超时设置
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
}
4. 按路径转发不同服务
# 访问 /api/ 转发到后端API服务
location /api/ {
proxy_pass http://127.0.0.1:8080;
}
# 访问 /admin/ 转发到后台管理服务
location /admin/ {
proxy_pass http://127.0.0.1:8081;
}
# 其他访问静态文件
location / {
root /www/wwwroot/html;
}
5. 常见应用场景
1)前后端分离:
- 前端静态文件 Nginx 直接处理
- API 请求转发到后端服务
2)Node.js/Python 后端:
- Nginx 处理 80/443 端口
- 转发到 Node.js 的 3000 端口
3)负载均衡:
- 配合 upstream 转发到多台服务器
6. 常见问题
1)后端获取不到真实 IP:
- 要配置 X-Real-IP 和 X-Forwarded-For
- 后端从请求头里取
2)WebSocket 不支持:
- 需要加特殊头
proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";
3)上传文件大小限制:
client_max_body_size 100m;
总结
Nginx 反向代理核心:
- proxy_pass 转发地址
- proxy_set_header 传递真实信息
- 按 location 转发不同服务
- 注意超时和文件大小限制
做 Web 开发,Nginx 反向代理一定要会配置。
Linux 查看系统资源
Linux 查看系统资源
服务器卡了慢了,得知道哪个进程占资源多。整理常用的查看命令。
1. 查看内存使用
# 查看内存使用情况
free -h
# 持续查看
free -h 1
输出说明:
- total:总内存
- used:已使用
- free:空闲
- buff/cache:缓存
- available:可用
2. 查看 CPU 负载
# 查看 CPU 信息
top
# 按 q 退出
top 常用快捷键:
- P:按 CPU 排序
- M:按内存排序
- 1:查看每个核心
3. 查看磁盘使用
# 查看磁盘空间
df -h
# 查看目录大小
du -sh /www/
# 查看当前目录下每个文件大小
du -sh *
4. 查看进程
# 查看所有进程
ps aux
# 查看 PHP 进程
ps aux | grep php
# 查看端口占用
netstat -tlnp
5. 查看系统信息
# 查看系统版本
cat /etc/os-release
# 查看内核版本
uname -a
# 查看 CPU 核心数
nproc
# 查看内存大小
cat /proc/meminfo | head -5
6. 查找大文件
# 查找大于100M的文件
find / -type f -size +100M
# 查找最近7天修改的文件
find /www -type f -mtime -7
7. 常用场景
网站卡了:
- top 看 CPU 和内存
- df -h 看磁盘满没满
- 看日志
内存不够用:
- free -h 看内存
- top 按 M 排序找占内存大的进程
总结
常用命令记这几个:
- 内存:free -h
- CPU:top
- 磁盘:df -h / du -sh
- 进程:ps aux
- 端口:netstat
做运维这些命令天天用,必须熟。
Nginx日志切割,按天切割nginx日志
前言
Nginx 的访问日志一直写在一个文件里,越写越大,几百GB都有可能。磁盘满了网站就挂了。本文讲一下怎么用 logrotate 自动按天切割 Nginx 日志。
为什么要切割日志
- 日志太大占磁盘
- 分析大文件慢
- 出问题查日志不方便
方法一:用系统自带的 logrotate
CentOS 自带 logrotate,直接配就行。
第一步:写配置文件
vi /etc/logrotate.d/nginx
内容:
/var/log/nginx/*.log {
daily
missingok
rotate 30
compress
notifempty
create 0644 www www
postrotate
/bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
endscript
}
参数说明:
| 参数 | 说明 |
|---|---|
daily |
按天切割 |
rotate 30 |
保留30天的日志 |
compress |
压缩旧日志 |
notifempty |
空文件不切割 |
missingok |
日志文件不存在不报错 |
第二步:测试一下
logrotate -d /etc/logrotate.d/nginx
-d 是调试模式,不会真执行,看一下有没有报错。
第三步:立即执行一次
logrotate -f /etc/logrotate.d/nginx
方法二:自己写脚本
不想用 logrotate,自己写个 shell 脚本:
vi /root/nginx_log.sh
内容:
#!/bin/bash
LOG_PATH=/var/log/nginx
YESTERDAY=$(date -d "yesterday" +%Y%m%d)
cd $LOG_PATH
mv access.log access_$YESTERDAY.log
mv error.log error_$YESTERDAY.log
nginx -s reopen
加执行权限:
chmod +x /root/nginx_log.sh
加到 crontab,每天凌晨执行:
crontab -e
加一行:
0 0 * * * /root/nginx_log.sh
常见坑
坑1:切割后日志不写了
把日志文件改名了,Nginx 还往旧文件写。
原因: Nginx 打开的是文件句柄,改名后它不知道。
解决: 切割后要通知 Nginx 重新打开日志文件:
nginx -s reopen
坑2:旧日志没删,磁盘还是满的
配了切割,但旧日志还留着。
原因: 没配 rotate 参数。
解决: 加上 rotate 30,自动只保留30天。
坑3:压缩太占CPU
所有日志都压缩,CPU 飙高。
解决: 错峰压缩,比如凌晨3点再压缩,或者用 delaycompress 延迟一天再压缩。
坑4:日志权限不对
切割后新日志文件属主不对,Nginx 写不了。
解决: logrotate 配置里加 create 0644 www www。
总结
推荐用 logrotate,系统自带,不用自己维护脚本。
核心配置就这些:
/var/log/nginx/*.log {
daily
rotate 30
compress
postrotate
/bin/kill -USR1 `cat /run/nginx.pid`
endscript
}
记住: 切割完一定要通知 Nginx 重新打开日志文件,不然新日志不写。
遇到问题加QQ23979811 协助处理
Nginx配置访问日志,日志格式详解
前言
网站出问题了,想查一下谁访问过哪个页面,怎么查?Nginx 的访问日志就记录了所有请求。本文讲一下 Nginx 怎么配置访问日志,日志格式都有什么字段。
默认日志位置
Nginx 默认访问日志在:
/var/log/nginx/access.log
错误日志在:
/var/log/nginx/error.log
查看实时日志
tail -f /var/log/nginx/access.log
日志格式定义
Nginx 自带一个默认格式叫 combined:
log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
常用日志变量
| 变量 | 说明 |
|---|---|
$remote_addr |
客户端 IP |
$time_local |
本地时间 |
$request |
请求行(方法+URL+协议) |
$status |
状态码 |
$body_bytes_sent |
发送字节数 |
$http_referer |
来源页面 |
$http_user_agent |
浏览器 UA |
自定义日志格式
想多记点信息,比如响应时间、X-Forwarded-For:
log_format myformat '$remote_addr - [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time"';
access_log /var/log/nginx/access.log myformat;
单独给某个站点配日志
多站点的时候,每个站点单独记日志:
server {
listen 80;
server_name www.aaa.com;
root /var/www/aaa;
index index.html;
access_log /var/log/nginx/aaa_access.log;
error_log /var/log/nginx/aaa_error.log;
}
关闭日志
不想记日志(不推荐):
access_log off;
常见坑
坑1:用了 CDN,日志里都是 CDN IP
访问日志里记的都是 CDN 的 IP,不是真实用户的。
原因: 请求经过 CDN 转发了。
解决: 记 X-Forwarded-For:
log_format combined '$http_x_forwarded_for - $remote_user [$time_local] ...';
坑2:日志太大占磁盘
访问日志一直写,把磁盘占满了。
解决: 配 logrotate 自动切割日志。
坑3:查不到某个 IP 的访问记录
想查某个 IP 今天访问了多少次,日志里没有。
原因: 日志已经被切割轮转了。
解决: 看昨天的日志文件:
grep "IP地址" /var/log/nginx/access.log.1
坑4:日志权限不对
Nginx 写不了日志。
解决: 确保日志目录属主是 www:
chown -R www:www /var/log/nginx/
常用日志分析命令
统计访问量前10的IP:
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -10
统计 404 最多的页面:
awk '($9 == 404)' /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -nr
总结
一行配置开启日志:
access_log /var/log/nginx/access.log;
记住: 日志是排查问题的第一手资料,出事先看日志。
遇到问题加QQ23979811 协助处理
Nginx重启、重载、停止命令,nginx -s reload
前言
改了 Nginx 配置,怎么让它生效?很多新手直接重启服务器,其实没必要。Nginx 有专门的重载命令,不中断服务。
常用命令汇总
| 操作 | 命令 |
|---|---|
| 重载配置(不中断) | nginx -s reload |
| 停止 Nginx | nginx -s stop |
| 优雅停止 | nginx -s quit |
| 检查配置 | nginx -t |
| 重启 Nginx | systemctl restart nginx |
| 查看状态 | systemctl status nginx |
重载配置(最常用)
改完配置,用这个:
nginx -s reload
好处: 不中断现有连接,新连接用新配置。生产环境用这个,不要直接重启。
检查配置
改完配置先检查一下语法:
nginx -t
输出 syntax is ok 才能重载。不然重载失败 Nginx 就挂了。
停止 Nginx
快速停止
nginx -s stop
直接杀进程,不管正在处理的请求。
优雅停止
nginx -s quit
等正在处理的请求完成后再停止。生产环境用这个。
重启 Nginx
用 systemctl 管理的:
systemctl restart nginx
编译安装的:
nginx -s stop
nginx
查看 Nginx 状态
systemctl status nginx
或者看进程:
ps aux | grep nginx
开机自启
systemctl enable nginx
常见坑
坑1:改了配置不生效
执行了 nginx -s reload,但配置还是老的。
原因1: 没检查配置语法,reload 失败了。
解决: 先跑 nginx -t,确认配置没问题再 reload。
原因2: 改的不是实际加载的配置文件。
解决: 用 nginx -t 确认一下加载的是哪个配置文件。
坑2:reload 后 Nginx 挂了
改了配置,reload 后网站打不开。
原因: 配置写错了,新进程起不来,老进程也退了。
解决: 赶紧回滚配置,然后 systemctl restart nginx。
预防: 改配置前先备份,改完先 nginx -t。
坑3:找不到 nginx 命令
执行 nginx: command not found。
原因: 编译安装的 Nginx 不在 PATH 里。
解决: 用完整路径:
/usr/local/nginx/sbin/nginx -s reload
坑4:reload 提示 no PID
执行 nginx -s reload 提示 no PID found。
原因: Nginx 没启动。
解决: 先启动 Nginx。
总结
日常操作流程:
- 改配置
nginx -t检查语法nginx -s reload重载- 测试网站是否正常
记住: 生产环境改配置,永远用 reload,不要用 restart,不中断服务。
遇到问题加QQ23979811 协助处理
Nginx查看配置文件位置,nginx.conf在哪
前言
刚装完 Nginx,想改个配置,发现不知道 nginx.conf 在哪。不同安装方式,配置文件位置不一样。本文讲一下怎么找 Nginx 配置文件。
方法一:nginx -t 命令
最直接的方法:
nginx -t
输出会显示配置文件路径:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
第一行就是配置文件位置。
方法二:ps 命令看进程
看 Nginx 进程启动时用的哪个配置:
ps aux | grep nginx
或者:
ps -ef | grep nginx
输出里 master 进程后面的 -c 参数就是配置文件路径。
方法三:nginx -V 看编译参数
nginx -V
输出里 --conf-path= 后面就是默认配置文件路径。
常见安装方式的默认路径
yum/apt 安装(包管理器)
/etc/nginx/nginx.conf
子配置一般在:
/etc/nginx/conf.d/
/etc/nginx/sites-available/
编译安装
默认在:
/usr/local/nginx/conf/nginx.conf
或者:
/usr/local/nginx/conf/
Docker 安装
容器里的路径是:
/etc/nginx/nginx.conf
如果挂载了宿主机目录,就在宿主机对应目录里。
常用配置文件结构
/etc/nginx/
├── nginx.conf # 主配置文件
├── conf.d/ # 子配置目录
│ ├── aaa.conf
│ └── bbb.conf
├── fastcgi_params # FastCGI 参数
├── mime.types # MIME 类型
└── nginx.conf.default # 默认配置模板
怎么查看完整配置
看主配置
vi /etc/nginx/nginx.conf
看所有加载的配置
nginx -T
-T 会打印所有配置文件的内容,包括 include 进来的子配置。
常见坑
坑1:改了配置不生效
改了 nginx.conf,但重载后还是老配置。
原因: 你改的不是 Nginx 实际加载的配置文件。
解决: 用 nginx -t 确认一下到底加载的是哪个配置文件。
坑2:找不到 nginx.conf
执行 find / -name nginx.conf 找不到。
原因: 可能是编译安装的,不在标准路径。
解决: 用 nginx -V 看 --conf-path= 参数。
坑3:conf.d 里的配置没生效
在 conf.d 里加了 .conf 文件,但没生效。
原因1: 后缀不是 .conf
原因2: 主配置里没 include conf.d/*.conf
解决: 确认 nginx.conf 里有这一行:
include /etc/nginx/conf.d/*.conf;
总结
| 方法 | 命令 |
|---|---|
| 最推荐 | nginx -t |
| 看进程参数 | ps aux \| grep nginx |
| 看编译参数 | nginx -V |
| 看全部配置 | nginx -T |
记住: 找不到配置文件先跑 nginx -t,一目了然。
遇到问题加QQ23979811 协助处理
Nginx 负载均衡配置
Nginx 负载均衡配置
网站流量大了,单台服务器扛不住,就需要负载均衡。这篇讲 Nginx 怎么配负载均衡。
1. 什么是负载均衡
简单说就是把请求分发到多台服务器上,分担压力,提高网站可用性。
2. 基础配置
http {
# 定义后端服务器组
upstream backend {
server 192.168.1.101:80;
server 192.168.1.102:80;
server 192.168.1.103:80;
}
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
3. 负载均衡策略
轮询(默认):
按顺序逐个分发,每台服务器轮流来。
加权轮询:
服务器性能不一样,给好的服务器多分点流量。
upstream backend {
server 192.168.1.101 weight=3;
server 192.168.1.102 weight=2;
server 192.168.1.103 weight=1;
}
ip_hash(会话保持):
同一个 IP 的请求永远分发到同一台服务器,保持登录状态。
upstream backend {
ip_hash;
server 192.168.1.101;
server 192.168.1.102;
}
4. 健康检查
服务器挂了自动跳过,不把请求发过去。
upstream backend {
server 192.168.1.101 max_fails=3 fail_timeout=30s;
server 192.168.1.102 max_fails=3 fail_timeout=30s;
}
- max_fails:失败几次标记为下线
- fail_timeout:多久后重新检查
5. 常用配置说明
location / {
proxy_pass http://backend;
# 传递真实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;
# 超时时间
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
}
6. 常见问题
1)Session 丢失:
- 多台服务器 Session 不共享
- 解决方案:Session 存 Redis,或者用 ip_hash
2)静态文件 404:
- 每台服务器都要有静态文件
- 或者用共享存储
3)服务器性能不均:
- 用 weighted 加权轮询
总结
Nginx 负载均衡要点:
- upstream 定义后端服务器组
- 轮询、加权、ip_hash 三种策略
- 配置健康检查自动下线故障服务器
- Session 要共享或用 ip_hash
做网站架构,负载均衡是必备技能。
Docker 常用命令汇总
Docker 常用命令汇总
Docker 现在是开发必备工具,整理一份常用命令清单,记不住随时翻。
1. 镜像管理
# 查看本地镜像
docker images
# 搜索镜像
docker search nginx
# 拉取镜像
docker pull nginx:latest
# 删除镜像
docker rmi nginx:latest
# 导出镜像
docker save -o nginx.tar nginx:latest
# 导入镜像
docker load -i nginx.tar
2. 容器管理
# 查看运行中的容器
docker ps
# 查看所有容器(包括停止的)
docker ps -a
# 启动容器
docker start nginx
# 停止容器
docker stop nginx
# 重启容器
docker restart nginx
# 删除容器
docker rm nginx
# 强制删除运行中的容器
docker rm -f nginx
3. 运行容器
# 后台运行 nginx
docker run -d --name mynginx -p 80:80 nginx
# 参数说明:
# -d:后台运行
# --name:容器名
# -p:端口映射(主机:容器)
# -v:目录挂载(主机:容器)
# 进入容器内部
docker exec -it mynginx bash
# 查看容器日志
docker logs mynginx
# 实时查看日志
docker logs -f mynginx
4. 查看容器信息
# 查看容器详细信息
docker inspect mynginx
# 查看容器占用资源
docker stats
# 查看容器端口映射
docker port mynginx
5. 数据卷
# 创建数据卷
docker volume create mydata
# 查看所有数据卷
docker volume ls
# 挂载数据卷运行容器
docker run -d -v mydata:/data nginx
# 删除数据卷
docker volume rm mydata
6. Docker Compose
# 启动服务
docker-compose up -d
# 停止服务
docker-compose down
# 查看服务状态
docker-compose ps
# 查看日志
docker-compose logs -f
docker-compose.yml 示例:
version: '3'
services:
nginx:
image: nginx
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
7. 清理命令
# 删除所有停止的容器
docker container prune
# 删除所有无用镜像
docker image prune -a
# 删除所有无用数据卷
docker volume prune
# 一键清理所有无用资源
docker system prune -a
总结
Docker 常用命令就这几类:
- 镜像: pull / images / rmi
- 容器: run / ps / stop / rm / exec
- 日志: logs / stats
- Compose: up / down
日常开发把这些命令用熟就够了,不用背,多敲几次自然记住。
MySQL 备份恢复实战
MySQL 备份与恢复实战
数据库备份是运维的基本功,万一数据丢了,没有备份就完蛋了。这篇整理常用的备份恢复方法。
1. mysqldump 逻辑备份
最常用的备份工具,生成 SQL 文件。
备份整个数据库:
mysqldump -u root -p123456 blog > blog_$(date +%Y%m%d).sql
备份单张表:
mysqldump -u root -p123456 blog users > users.sql
备份所有数据库:
mysqldump -u root -p123456 --all-databases > all.sql
常用参数:
- --databases:指定多个数据库
- --single-transaction:InnoDB 一致性备份,不锁表
- --routines:备份存储过程和函数
- --triggers:备份触发器
2. 恢复数据
# 恢复整个数据库
mysql -u root -p123456 blog < blog_20261007.sql
# 登录后恢复
mysql -u root -p123456
mysql> use blog;
mysql> source /root/blog_20261007.sql;
3. 压缩备份
数据大的时候,直接存 SQL 太占空间,压缩一下:
# 备份并压缩
mysqldump -u root -p123456 blog | gzip > blog_$(date +%Y%m%d).sql.gz
# 恢复压缩备份
gunzip < blog_20261007.sql.gz | mysql -u root -p123456 blog
4. 全量+增量备份
大数据量不能每天全量备份,用 binlog 做增量。
全量备份:
mysqldump -u root -p123456 --single-transaction --master-data=2 blog > full.sql
增量恢复:
# 先恢复全量
mysql -u root -p123456 blog < full.sql
# 再按时间点恢复 binlog
mysqlbinlog --start-datetime="2026-10-07 10:00:00" \
--stop-datetime="2026-10-07 14:00:00" \
/var/lib/mysql/binlog.000001 | mysql -u root -p123456
5. 物理备份(冷备)
直接拷贝数据文件,速度快,但需要停库。
# 停库
systemctl stop mysqld
# 拷贝数据目录
cp -r /var/lib/mysql /backup/mysql_$(date +%Y%m%d)
# 启动
systemctl start mysqld
6. 定时备份脚本
写个脚本每天自动备份:
#!/bin/bash
BACKUP_DIR="/data/backup/mysql"
DATE=$(date +%Y%m%d)
USER="root"
PASS="123456"
# 创建目录
mkdir -p $BACKUP_DIR
# 备份并压缩
mysqldump -u$USER -p$PASS --single-transaction blog | gzip > $BACKUP_DIR/blog_$DATE.sql.gz
# 删除7天前的备份
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete
加到 crontab:
0 3 * * * /bin/bash /root/mysql_backup.sh
7. 恢复注意事项
- 恢复前先测试备份文件能不能用,别等真出事了才发现备份坏了
- 大表恢复慢,别在线上高峰期操作
- 字符集要一致,恢复前先检查
- 恢复前先备份当前数据,防止恢复出错
总结
MySQL 备份三件事:
- 定期备份,别偷懒
- 备份后要测试能不能恢复
- 重要数据异地备份
数据无价,备份一定要做好。
Linux 压缩命令大全
Linux 常用压缩命令大全
Linux 下压缩解压命令很多,记不住每次都要搜,整理一份常用清单。
1. tar 命令(最常用)
tar 是 Linux 最常用的打包工具,可以配合不同压缩格式。
打包不压缩:
# 打包
tar -cvf blog.tar /www/wwwroot/blog/
# 解包
tar -xvf blog.tar
参数说明:
- -c:创建打包文件
- -x:解包
- -v:显示过程
- -f:指定文件名
2. tar.gz 格式(最常用)
# 压缩
tar -zcvf blog.tar.gz /www/wwwroot/blog/
# 解压
tar -zxvf blog.tar.gz
# 解压到指定目录
tar -zxvf blog.tar.gz -C /data/backup/
-z 就是用 gzip 压缩,这是最常用的格式。
3. tar.bz2 格式(压缩率更高)
# 压缩(体积更小,但更慢)
tar -jcvf blog.tar.bz2 /www/wwwroot/blog/
# 解压
tar -jxvf blog.tar.bz2
4. zip 格式
# 压缩
zip -r blog.zip /www/wwwroot/blog/
# 解压
unzip blog.zip
# 解压到指定目录
unzip blog.zip -d /data/backup/
Windows 用户常用 zip 格式,跨平台方便。
5. gz 格式
# 压缩(单个文件)
gzip file.log
# 解压
gunzip file.log.gz
gzip 只能压单个文件,一般和 tar 配合用。
6. 查看压缩包内容
# 查看 tar 包内容
tar -tvf blog.tar
# 查看 tar.gz 包内容
tar -ztvf blog.tar.gz
# 查看 zip 包内容
unzip -l blog.zip
7. 常用场景示例
备份网站文件:
tar -zcvf backup_$(date +%Y%m%d).tar.gz /www/wwwroot/blog/
备份数据库:
mysqldump -u root -p123456 blog | gzip > db_$(date +%Y%m%d).sql.gz
传输文件压缩:
tar -zcvf - /www/wwwroot/blog/ | ssh root@192.168.1.100 "tar -zxvf - -C /backup/"
8. 总结
| 格式 | 压缩率 | 速度 | 命令 |
|---|---|---|---|
| tar.gz | 中 | 快 | tar -z |
| tar.bz2 | 高 | 慢 | tar -j |
| zip | 中 | 中 | zip/unzip |
| tar | 无 | 最快 | tar |
日常用 tar.gz 就够了,跨平台用 zip,追求小体积用 bz2。
CentOS7重启网络报错,network与NetworkManager冲突处理
前言
CentOS7 里有两套网络管理工具:传统的 network 服务和新的 NetworkManager。两个同时用就会打架,重启网络的时候报错,甚至把网络搞断。本文就讲一下这个冲突怎么解决。
报错现象
执行 systemctl restart network 或者 service network restart 的时候,报类似这样的错:
Restarting network (via systemctl): Job for network.service failed. See 'systemctl status network.service' and 'journalctl -xn' for details.
或者:
RTNETLINK answers: File exists
原因
CentOS7 默认装了 NetworkManager(简称 NM),它也在管理网络接口。传统的 network 服务也在管。两个同时管同一个网卡,配置就会冲突。
解决方法
方法一:关掉 NetworkManager(推荐服务器用)
服务器一般不需要图形界面的网络管理工具,直接关掉:
systemctl stop NetworkManager
systemctl disable NetworkManager
然后重启 network:
systemctl restart network
方法二:让 network 忽略 NetworkManager
编辑网络配置文件:
vi /etc/sysconfig/network-scripts/ifcfg-eth0
在文件里加一行:
NM_CONTROLLED=no
意思是这个网卡不归 NetworkManager 管,只由 network 服务管理。
然后重启网络:
systemctl restart network
方法三:全部交给 NetworkManager 管
如果你想用 NM 管理,那就把传统 network 关掉:
systemctl stop network
systemctl disable network
然后用 NM 的命令管理网络:
nmcli connection show
nmcli connection up eth0
验证
重启网络之后,看看网卡 IP 还在不在:
ip addr
如果 IP 还在,能 ping 通外网,就说明搞定了。
常见坑
坑1:改了配置把自己踢下线
远程 SSH 连上去改网络配置,改完重启网络,结果 SSH 断了,再也连不上。
解决:
- 改配置之前先开个备用 SSH 连接,别关
- 或者直接用云服务器控制台的 VNC 操作
- 不要在生产环境随便重启网络
坑2:重启服务器后网络又坏了
临时重启 network 好了,但重启服务器后又不行了。
原因: NetworkManager 又自动启动了,和 network 冲突。
解决: 把 NetworkManager 禁用掉:
systemctl disable NetworkManager
坑3:网卡名不是 eth0
新一点的系统网卡名可能是 ens33、enp0s3 这种。
解决: 先看一下网卡叫什么:
ip addr
然后改对应的配置文件,比如 ifcfg-ens33。
总结
CentOS7 网络冲突的解决思路很简单:
| 方案 | 操作 | 适用场景 |
|---|---|---|
| 关 NM | systemctl disable NetworkManager |
服务器,推荐 |
| NM_CONTROLLED=no | 网卡配置里加这行 | 只用 network 服务 |
| 全用 NM | 关掉 network,用 nmcli | 桌面环境 |
服务器上推荐直接关掉 NetworkManager,用传统的 network 服务管理网络,简单稳定。
遇到问题加QQ23979811 协助处理
MySQL自增主键用完了怎么办,一次讲清楚
前言
做后端开发的同学,建表的时候都喜欢用自增主键,觉得简单方便。但是很多同学不知道,自增主键是有最大值的,用完了就不能再插入数据了。
很多新手同学建表的时候,主键直接用INT,也不知道INT的最大值是多少。结果项目跑了几年,数据量涨上来了,自增主键快用完了,才发现要出问题了。
这篇文章就把MySQL自增主键的知识点讲清楚:不同整型的最大值是多少?用完了会怎么样?怎么避免?已经用完了怎么办?看完之后你就彻底搞懂了。
一、不同整型的最大值
MySQL的整型类型有好几种,每种的最大值都不一样。我们常用的有这几个:
| 类型 | 字节数 | 有符号最大值 | 无符号最大值 |
|---|---|---|---|
| TINYINT | 1 | 127 | 255 |
| SMALLINT | 2 | 32767 | 65535 |
| MEDIUMINT | 3 | 8388607 | 16777215 |
| INT | 4 | 2147483647 | 4294967295 |
| BIGINT | 8 | 9223372036854775807 | 18446744073709551615 |
重点看INT和BIGINT
我们建表的时候,主键一般用INT或者BIGINT。
INT的最大值
INT有符号的最大值是 21亿多(2147483647),无符号的是 42亿多(4294967295)。
很多同学觉得21亿很多,肯定用不完。但是对于一些业务量大的网站,比如电商、社交,用户量很大,一张表可能就有几亿条数据,INT真的不够用。
BIGINT的最大值
BIGINT有符号的最大值是 922亿亿(9223372036854775807),这个数太大了,基本上一辈子都用不完。
所以建议:主键直接用BIGINT,不要用INT,省得以后不够用了还要改。
二、自增主键用完了会怎么样
如果自增主键用到了最大值,再插入数据会怎么样?
MySQL会报错:
ERROR 1467 (HY000): Failed to read auto-increment value from storage engine
或者:
ERROR: Duplicate entry '2147483647' for key 'PRIMARY'
意思就是:自增主键已经到最大值了,不能再插入新数据了。
这时候业务就受影响了,不能插入新数据了,必须赶紧处理。
三、怎么避免自增主键用完
最好的方法就是:建表的时候主键直接用BIGINT,不要用INT。
BIGINT的最大值太大了,基本上不可能用完。这样就不用以后担心主键不够用的问题了。
很多同学觉得BIGINT占空间大,其实BIGINT是8字节,INT是4字节,一张表也就多了几个字节,根本不算什么。但是换来的是永远不用担心主键不够用,这太值了。
怎么查看当前自增值到多少了
想知道当前自增主键已经用到多少了,可以用这个命令:
SHOW TABLE STATUS LIKE '表名';
执行完之后,看Auto_increment那列,就是当前自增值。
然后你对比一下这个类型的最大值,看看还有多少余量。如果已经用了一半了,那就得注意了。
四、已经用完了怎么办
如果你的主键已经快用完了,或者已经用完了,怎么办?
方法1:把INT改成BIGINT
这是最直接的方法。把主键的类型从INT改成BIGINT,这样最大值就变成922亿亿了,够用很久了。
执行的SQL:
ALTER TABLE 表名 MODIFY id BIGINT AUTO_INCREMENT;
注意:这个操作会重建表,如果表很大的话,会花很长时间,而且会锁表。最好在业务低峰期执行,或者用pt-online-schema-change这种工具在线改。
方法2:分库分表
如果你的表已经很大了,改主键类型也不能根本解决问题,那就分库分表,把数据分散到多个表或者多个数据库里。
不过分库分表成本很高,一般都是到万不得已才做。
五、自增主键的其他坑
除了用完的问题,自增主键还有一些其他的坑:
坑1:删除数据之后自增值不回收
很多同学以为删除了数据,自增值会重新开始。其实不是,自增值只会涨,不会降。就算你把表清空了,自增值还是接着之前的继续涨。
比如你插入了100条数据,id到了100,然后你把这100条都删了,再插入一条新数据,id还是101,不是1。
坑2:自增值不连续
因为有事务回滚的情况,自增值可能会不连续。比如一个事务插入了一条数据,id是100,然后事务回滚了,那100这个id就浪费了,下一条数据的id是101。
所以不要指望自增主键是连续的,它只是唯一的,不是连续的。
坑3:用了INT UNSIGNED,觉得够大了
很多同学觉得用了INT UNSIGNED,最大值是42亿,肯定够了。其实对于一些大业务,42亿也不够。还是用BIGINT最保险。
常见坑
坑1:建表的时候用INT主键,后来不够用了
很多同学建表的时候图简单,直接用INT主键,结果项目跑了几年,数据量涨上来了,INT快用完了,才慌慌张张要改成BIGINT。这时候表已经很大了,改起来很麻烦。一开始就用BIGINT,省得以后麻烦。
坑2:不知道自增主键的最大值
很多新手同学根本不知道INT的最大值是多少,觉得反正自增,肯定用不完。其实21亿听起来很多,但是对于大业务来说,真的可能用完。
坑3:改主键类型的时候锁表了
很多同学直接在线上执行ALTER TABLE改主键类型,结果表很大,改了几个小时,一直锁表,业务直接卡住了。改之前一定要评估表大小,最好在业务低峰期执行,或者用在线改表工具。
坑4:以为删除数据之后自增值会降
很多同学删了很多数据,以为自增值会降下来,就不用改类型了。其实不是,自增值只会涨,不会降。删了数据之后,自增值还是原来的,不会变。
坑5:用UUID做主键
很多同学觉得自增主键不够用,就想用UUID做主键。其实UUID做主键问题更多:占空间大、无序导致索引分裂、查询慢。真的不够用的话,改BIGINT就行,没必要用UUID。
总结
MySQL自增主键的核心知识点:
- 不同整型的最大值:INT是21亿(有符号)/42亿(无符号),BIGINT是922亿亿
- 主键直接用BIGINT:不要用INT,省得以后不够用了还要改
- 自增主键用完会报错:不能再插入新数据了
- 怎么查看当前自增值:SHOW TABLE STATUS,看Auto_increment列
- 用完了怎么办:把INT改成BIGINT,或者分库分表
- 自增主键的坑:删除数据自增值不回收、自增值不连续
记住:建表的时候主键直接用BIGINT,这是最稳妥的做法。不要一开始图省事用INT,以后改起来更麻烦。
遇到问题加QQ23979811 协助处理
MySQL死锁排查和解决方法,快速定位死锁
前言
做后端开发的同学肯定都遇到过死锁:两个事务互相等对方释放锁,结果都卡住了,数据库报错"Deadlock found when trying to get lock"。这时候整个业务都卡住了,用户都在骂娘,你急得满头大汗不知道怎么办。
很多新手同学遇到死锁就慌了,不知道怎么排查,也不知道怎么解决。其实死锁不可怕,只要掌握了正确的排查方法,很快就能定位到问题。这篇文章就把MySQL死锁的知识点从头到尾讲清楚:什么是死锁?怎么查看死锁?怎么排查?怎么避免?看完之后你就彻底搞懂了。
一、什么是死锁
死锁就是两个或者多个事务,互相持有对方需要的锁,又互相等对方释放锁,结果谁也动不了,就卡住了。
举个最简单的例子:
- 事务A锁了id=1的行,想要锁id=2的行
- 事务B锁了id=2的行,想要锁id=1的行
- 这时候A等B释放id=2的锁,B等A释放id=1的锁
- 两个事务互相等,就死锁了
MySQL检测到死锁之后,会自动回滚其中一个事务,让另一个事务继续执行。所以死锁不会一直卡住,但是被回滚的那个事务就失败了,业务上会报错。
二、死锁产生的原因
死锁一般都是因为这几种情况:
1. 多个事务以不同的顺序访问同一批资源
这是最常见的死锁原因。比如两个事务,一个先操作表A再操作表B,另一个先操作表B再操作表A,就容易死锁。
2. 事务太长,持锁时间久
如果一个事务里做了很多事情,持锁时间很长,其他事务都在等锁,时间一长就容易死锁。
3. 没有用到索引,锁了太多的行
如果WHERE条件没有用到索引,InnoDB就会扫描全表,把所有行都锁了,这样锁的范围很大,就容易和其他事务的锁冲突,产生死锁。
4. 间隙锁导致的死锁
间隙锁虽然是为了解决幻读,但是也会增加死锁的概率。两个事务都往同一个范围里插数据,间隙锁互相冲突,就容易死锁。
三、怎么查看死锁
MySQL默认会自动检测死锁,并且把死锁的信息记录下来。我们可以用下面的命令查看最近一次死锁的信息:
SHOW ENGINE INNODB STATUS;
执行完之后,会输出一大段信息,找到里面的 LATEST DETECTED DEADLOCK 部分,就是最近一次死锁的详细信息。
这个部分会告诉你:
- 哪个事务持有了什么锁
- 哪个事务在等什么锁
- 最后MySQL回滚了哪个事务
开启死锁日志
如果想要记录所有的死锁信息,不仅是最近一次,可以开启死锁日志:
# my.cnf里配置
innodb_print_all_deadlocks = 1
开启之后,所有的死锁信息都会写到MySQL的错误日志里。
四、怎么排查死锁
拿到死锁日志之后,怎么分析呢?一般按这个步骤来:
步骤1:看两个事务分别执行了什么SQL
死锁日志里会显示两个事务的SQL语句。你要看看这两个SQL分别操作了哪些表、哪些行。
步骤2:看锁的信息
日志里会显示每个事务持有了什么锁,在等什么锁。你要搞清楚:
- 事务A持有了哪些行的锁
- 事务A想要拿哪些行的锁
- 事务B持有了哪些行的锁
- 事务B想要拿哪些行的锁
这样就能搞清楚为什么会死锁了。
步骤3:分析业务逻辑
看完SQL和锁的信息之后,再结合业务逻辑,看看这两个事务为什么会以不同的顺序操作同一批数据。
比如:
- 事务A是扣库存的逻辑
- 事务B是修改订单的逻辑
- 它们都操作了商品表和订单表,但是顺序不一样,就死锁了
五、怎么避免死锁
排查完死锁之后,最重要的是怎么避免下次再出现。常见的避免死锁的方法:
1. 按相同的顺序访问资源
这是最有效的方法。所有的事务都按相同的顺序访问表和行,比如所有事务都先操作商品表,再操作订单表,就不会出现两个事务互相等的情况了。
2. 大事务拆成小事务
事务越小,持锁时间越短,死锁的概率就越低。把大事务拆成多个小事务,每个事务只做一点点事情。
比如不要在一个事务里调用RPC接口、查Redis、做复杂计算,这些耗时操作都放到事务外面。
3. 尽量用索引访问数据
如果WHERE条件没有用到索引,就会扫描全表,把所有行都锁了,这样锁的范围很大,死锁概率就高。一定要确保查询都用到了索引。
4. 降低隔离级别
如果对幻读不是很敏感,可以把隔离级别从可重复读(RR)降到读提交(RC)。这样间隙锁就没了,死锁的概率也会低很多。
很多互联网公司都是用RC隔离级别,就是为了减少锁冲突和死锁。
5. 设置锁等待超时时间
如果真的出现死锁了,不要让事务一直等。可以设置锁等待超时时间:
SET GLOBAL innodb_lock_wait_timeout = 5;
这样如果一个事务等锁超过5秒,就自动报错,不会一直卡住。
六、死锁和锁等待的区别
很多同学分不清死锁和锁等待:
锁等待
一个事务等另一个事务释放锁,但是不存在循环等待,等一会儿就好了。
比如事务A锁了id=1的行,事务B也想锁id=1的行,那B就要等A释放锁,这就是锁等待。
死锁
两个事务互相等对方释放锁,形成了循环等待,谁也动不了。
MySQL会自动检测到死锁,然后回滚其中一个事务。
常见坑
坑1:遇到死锁就重启MySQL
很多同学一遇到死锁就慌了,直接重启MySQL。这是最笨的方法,重启解决不了根本问题,下次还会出现。一定要先排查死锁的原因,从根本上解决。
坑2:以为死锁是MySQL的bug
很多同学觉得死锁是MySQL的问题,其实不是。死锁一般都是业务代码写的有问题,多个事务访问资源的顺序不一致导致的。MySQL只是帮你检测到了死锁,罪魁祸首是你的业务代码。
坑3:死锁日志里的信息看不懂
很多同学拿到死锁日志,里面全是锁的信息,看不懂。其实不用看那么细,重点看两个事务分别执行了什么SQL,操作了哪些表和行,搞清楚它们为什么会互相等就行。
坑4:只靠MySQL自动检测死锁
很多同学觉得MySQL会自动检测死锁、自动回滚,就不用管了。其实不是,死锁虽然不会一直卡住,但是被回滚的那个事务业务上会报错,影响用户体验。最好还是从源头上避免死锁。
坑5:间隙锁导致的死锁
很多同学在可重复读隔离级别下,两个事务往同一个范围插数据,结果死锁了。这就是间隙锁的问题。如果对幻读不是很敏感,降到读提交隔离级别就好了。
总结
MySQL死锁的核心知识点:
- 死锁是什么:两个事务互相持有对方需要的锁,又互相等对方释放,形成循环等待
- 死锁产生的原因:访问资源顺序不一致、事务太长、没用索引锁太多行、间隙锁
- 怎么查看死锁:SHOW ENGINE INNODB STATUS,看LATEST DETECTED DEADLOCK部分
- 怎么排查死锁:看两个事务的SQL、看锁的信息、结合业务逻辑分析
- 怎么避免死锁:
- 按相同的顺序访问资源
- 大事务拆成小事务
- 尽量用索引访问数据
- 降低隔离级别到读提交
- 设置锁等待超时时间
- 死锁和锁等待的区别:锁等待是单向的,等一会儿就好;死锁是循环的,MySQL会自动回滚一个
记住:死锁不可怕,可怕的是不知道为什么会死锁。遇到死锁不要慌,先看死锁日志,找到原因,然后从业务代码上避免。
遇到问题加QQ23979811 协助处理