CentOS7 安装wget、vim、net-tools常用工具包

前言

新装好的 CentOS7 最小化系统,很多常用命令都没有,比如 wget、vim、ifconfig 都用不了。本文就讲一下怎么一次性把常用工具包装上。

为什么要装这些工具

CentOS7 最小化安装(minimal install)为了节省空间,默认不带很多常用工具。刚装完系统你会发现:

  • wget 命令找不到
  • vim 命令找不到
  • ifconfig 命令找不到
  • netstat 命令找不到

所以刚装完系统第一件事,就是把这些常用工具装上。

安装常用工具包

一键安装

最省事的方式,一条命令全装上:

yum install -y wget vim net-tools lrzsz unzip telnet

这一条命令就把最常用的都装了。

各工具说明

工具包 作用
wget 命令行下载文件
vim 文本编辑器
net-tools 包含 netstat、ifconfig 等网络工具
lrzsz 包含 rz、sz 上传下载文件
unzip 解压 zip 文件
telnet 测试端口连通性

分开装

如果你只想装某一个:

# 只装 wget
yum install -y wget

# 只装 vim
yum install -y vim

# 只装 net-tools(ifconfig、netstat)
yum install -y net-tools

常用工具包推荐

基础工具包(必装)

yum install -y wget vim net-tools lrzsz unzip telnet curl

开发工具包

yum groupinstall -y "Development Tools"

这个包包含了 gcc、make 等编译工具,装东西经常需要。

其他常用工具

# 查看进程树
yum install -y psmisc

# 实时监控网络
yum install -y nload

# 查看端口占用
yum install -y lsof

# 查看磁盘性能
yum install -y iotop

# 查看内存
yum install -y htop

常见坑

坑1:ifconfig 命令找不到

执行 ifconfig 提示 command not found。

原因: CentOS7 最小化安装不带 net-tools 包。

解决:

yum install -y net-tools

装完之后 ifconfig 就能用了。

坑2:vim 命令找不到

执行 vi 能用,但 vim 找不到。

原因: 最小化安装只有 vi,没有 vim。

解决:

yum install -y vim

坑3:rz 命令找不到

想从本地上传文件到服务器,执行 rz 提示找不到。

原因: 没装 lrzsz 包。

解决:

yum install -y lrzsz

装完之后:

  • rz:从本地上传文件到服务器
  • sz 文件名:从服务器下载文件到本地

坑4:yum 安装报错

执行 yum install 的时候报错。

原因: yum 源没配好,或者网络不通。

解决: 先换国内源(参考之前的 yum 源更换教程),然后再装。

总结

新系统刚装完,先跑这一条命令:

yum install -y wget vim net-tools lrzsz unzip telnet curl

把常用工具一次性装齐,后面省得每次都报 command not found。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-6 07:47 

Linux查看文件大小,du和df命令区别

前言

服务器磁盘满了,不知道哪个目录占的空间最大?du 和 df 是两个最常用的查看磁盘空间的命令,但很多人搞不清它们的区别。本文一次讲清楚。

du 和 df 的区别

du(Disk Usage)

看的是文件和目录占用的磁盘空间。

比如某个目录下有多少文件,总共占多少空间。

df(Disk Free)

看的是磁盘分区的整体使用情况。

比如整个硬盘总共有多大,用了多少,还剩多少。

一句话总结

  • du:看文件/目录占了多少
  • df:看磁盘分区总共多大、剩多少

df 命令

查看整体磁盘使用情况

df -h

-h 是 human-readable 的意思,输出成 K、M、G 的易读格式。

输出类似:

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        50G   30G   18G  63% /
tmpfs           3.9G     0  3.9G   0% /dev/shm
/dev/sdb1       100G   50G   45G  53% /data

只看某个分区

df -h /

只看根分区的使用情况。

显示文件系统类型

df -Th

多了一列 Type,显示是 ext4、xfs 还是其他类型。

du 命令

查看某个目录占多大空间

du -sh 目录路径

比如看 /var 目录:

du -sh /var

-s 是 summary,只显示总计;-h 是易读格式。

输出类似:

5.2G    /var

查看当前目录下每个子目录的大小

du -sh *

输出当前目录下每个子目录占多大空间。

按大小排序

du -sh * | sort -hr

sort -hr 是按数字从大到小排,这样就能一眼看到哪个子目录最大。

找当前目录下最大的10个文件

du -ah . | sort -hr | head -10

-a 是包括文件,不只是目录。

只看一层深度

du -h --max-depth=1 /var

只看 /var 下第一层的子目录大小,不往下钻。

常见场景

场景1:磁盘满了,找大文件

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

# 然后去那个分区找大目录
cd /
du -sh * | sort -hr | head -10

# 找到大目录后继续往下钻
cd /var
du -sh * | sort -hr | head -10

一步步找到占空间最大的文件,然后删掉。

场景2:看日志占了多少

du -sh /var/log

场景3:看网站占了多少

du -sh /var/www/html

常见坑

坑1:du 和 df 显示的已用空间不一样

明明删了大文件,但 df 显示还是满的。

原因: 文件被进程占用着,删了之后空间没真正释放。

解决: 找到占用这个文件的进程,重启或者杀掉:

lsof | grep deleted

找到 PID 后重启对应的服务。

坑2:du 命令跑了半天还在跑

du 一个大目录,半天没反应。

原因: 目录太大,文件太多。

解决: 加 --max-depth=1 限制深度,或者先确认一下目录有多大再决定要不要跑。

坑3:df 显示已用空间和 du 对不上

df 显示用了 80G,但 du 加起来才 50G。

原因: 可能有隐藏文件、挂载点下面的文件,或者有进程占用着已删除的文件。

解决: 检查:

# 看有没有挂载点
mount

# 看有没有已删除但还被占用的文件
lsof | grep deleted

坑4:du 显示的大小和 ls 不一样

ls 显示文件是 10M,du 显示是 11M。

原因: du 看的是磁盘块占用,文件系统按块分配空间,实际占用会比文件大小略大一点。

解决: 正常现象,不用纠结。

总结

命令 作用
df -h 看磁盘分区整体使用情况
du -sh 目录 看某个目录占多大空间
du -sh * | sort -hr 看当前目录下每个子目录大小并排序
du -h --max-depth=1 目录 只看一层深度
lsof | grep deleted 看已删除但被进程占用的文件

日常运维记住:df 看分区,du 看目录。

遇到问题加QQ23979811 协助处理


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

Linux文件权限755、644含义,chmod修改权限

前言

Linux 文件权限是新手最头疼的东西之一,经常遇到 403 权限不足的报错。755、644 这些数字到底是什么意思?怎么改?本文一次讲清楚。

权限基础

三种身份

Linux 把用户分三类:

  • 文件所有者(u):文件的主人
  • 同组用户(g):和主人在同一个组的人
  • 其他用户(o):所有人

三种权限

每种身份有三种权限:

  • 读(r):4
  • 写(w):2
  • 执行(x):1

加起来就是权限数字。

755、644 是什么意思

755

7 = rwx(读+写+执行)= 4+2+1
5 = r-x(读+执行)= 4+1
5 = r-x(读+执行)= 4+1

合起来就是:所有者能读写执行,同组和其他用户只能读和执行。

常用于: 目录、可执行脚本

644

6 = rw-(读+写)= 4+2
4 = r--(只读)= 4
4 = r--(只读)= 4

合起来就是:所有者能读写,其他人只能读。

常用于: 普通文件、网页

常用权限速查表

数字 含义 用途
755 rwxr-xr-x 目录、脚本
644 rw-r--r-- 普通文件、网页
700 rwx------ 私人目录
600 rw------- 私人文件(比如密钥)
777 rwxrwxrwx 所有人都能读写执行(不推荐)

chmod 修改权限

数字方式(最常用)

chmod 755 文件名

比如把 index.php 改成 644:

chmod 644 index.php

字母方式

chmod u+x 文件名

意思是给所有者(u)加执行权限(x)。

常用的:

  • u+x:所有者加执行权限
  • u-w:所有者去掉写权限
  • g+w:同组加写权限
  • o+r:其他用户加读权限
  • a+x:所有人加执行权限

递归修改目录下所有文件

加 -R 参数:

chmod -R 755 /var/www/html

警告: 递归改权限要小心,别把系统目录改坏了。

chown 修改所有者

有时候不是权限数字的问题,是文件属主不对。

查看文件属主

ls -l 文件名

输出第一列后面就是属主:

-rw-r--r-- 1 www www 1234 Oct  6 10:00 index.php

前面的 www 是用户,后面的 www 是组。

修改文件属主

chown 用户:组 文件名

比如把文件改成 www 用户:

chown www:www index.php

递归修改目录属主

chown -R www:www /var/www/html

常见坑

坑1:网站 403 权限不足

网页打不开,报 403 Forbidden。

原因: 文件权限不对,或者属主不对。

解决:

  1. 文件改成 644:chmod 644 文件
  2. 目录改成 755:chmod 755 目录
  3. 属主改成运行用户:chown www:www 文件

坑2:777 万能解决?

一遇到权限问题就 chmod 777。

原因: 图省事,但不安全。

解决: 777 意味着所有人都能读写执行,恶意文件可以直接往你的目录里写木马。生产环境绝对不要用 777。

坑3:改了权限还是不行

执行了 chmod 还是报权限不足。

原因: SELinux 拦截了。

解决: 检查 SELinux:

getenforce

如果是 Enforcing,临时关掉试试:

setenforce 0

坑4:上传文件没有执行权限

写了个脚本想运行,报 Permission denied。

原因: 文件没有执行权限。

解决:

chmod +x 脚本.sh

总结

命令 作用
chmod 755 文件名 改成 755 权限
chmod 644 文件名 改成 644 权限
chmod -R 755 目录 递归改目录权限
chown www:www 文件名 改文件属主
chown -R www:www 目录 递归改目录属主

记住两个常用的:目录 755,文件 644,基本就够用了。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-6 07:45 

Linux创建用户、删除用户、修改密码完整命令

前言

服务器上要加新用户、删旧用户、改密码,这些都是日常操作。本文把常用的用户管理命令都整理出来,新手也能照着做。

创建用户

useradd 命令

最简单的创建用户:

useradd 用户名

比如创建一个叫 zhangsan 的用户:

useradd zhangsan

创建完之后,这个用户还不能登录,因为没设密码。

创建用户并设密码

一行搞定:

useradd zhangsan && passwd zhangsan

执行 passwd zhangsan 之后会提示输入两次密码。

创建用户并指定家目录

默认家目录是 /home/用户名,想改的话用 -d:

useradd -d /data/zhangsan zhangsan

创建用户并指定用户组

useradd -g 用户组 用户名

比如把 zhangsan 加到 www 组:

useradd -g www zhangsan

创建用户并指定 UID

useradd -u 1001 zhangsan

修改密码

passwd 命令

修改某个用户的密码:

passwd 用户名

比如修改 zhangsan 的密码:

passwd zhangsan

执行后会提示输入两次新密码。

自己改密码

passwd

直接执行,会提示输入旧密码,然后输入新密码两次。

免交互改密码(脚本用)

写脚本的时候不能交互式输入,用这个:

echo "新密码" | passwd --stdin 用户名

比如:

echo "123456" | passwd --stdin zhangsan

删除用户

userdel 命令

只删用户,不删家目录:

userdel 用户名

比如:

userdel zhangsan

删除用户并删家目录

加 -r 参数,把家目录也删掉:

userdel -r zhangsan

警告: -r 会把 /home/zhangsan 整个删掉,里面的文件都没了,确认清楚再执行。

查看用户信息

查看用户 UID、GID、组

id 用户名

比如:

id zhangsan

输出类似:

uid=1001(zhangsan) gid=1001(zhangsan) 组=1001(zhangsan)

查看用户信息

grep 用户名 /etc/passwd

查看所有用户

cat /etc/passwd

每行一个用户,格式是:

用户名:x:UID:GID:注释:家目录:登录Shell

常见坑

坑1:创建用户后不能登录

创建了用户但 SSH 登录不了。

原因: 没设密码,或者 Shell 是 /sbin/nologin。

解决:

  1. 先设密码:passwd 用户名
  2. 检查 Shell:grep 用户名 /etc/passwd,最后一列应该是 /bin/bash

如果是 /sbin/nologin,改回来:

usermod -s /bin/bash 用户名

坑2:删了用户但家目录还在

执行了 userdel zhangsan,但 /home/zhangsan 还在。

原因: 没加 -r 参数。

解决: 手动删:

rm -rf /home/zhangsan

坑3:普通用户没权限 sudo

新用户不能用 sudo。

原因: 没加到 sudoers 里。

解决: 加到 wheel 组(CentOS)或者 sudo 组(Ubuntu):

usermod -aG wheel 用户名

坑4:密码过期了

用户突然不能登录了,提示密码过期。

解决: root 改密码:

passwd 用户名

或者设置密码永不过期:

chage -M 99999 用户名

总结

操作 命令
创建用户 useradd 用户名
设密码 passwd 用户名
删除用户 userdel 用户名
删除用户+家目录 userdel -r 用户名
查看用户信息 id 用户名
加 sudo 权限 usermod -aG wheel 用户名

日常运维就这几个命令,够用了。

遇到问题加QQ23979811 协助处理


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

CentOS7 yum安装软件报错,yum源更换教程

前言

CentOS7 默认的 yum 源是国外的,装软件特别慢,有时候还报错 "Could not resolve host" 或者 "Failed to connect"。换个国内源就好了,速度飞快。本文就讲一下怎么换 yum 源。

报错现象

执行 yum install xxx 的时候,报类似这样的错:

Could not retrieve mirrorlist http://mirrorlist.centos.org/...

或者:

Could not resolve host: mirrorlist.centos.org

或者下载速度特别慢,几 KB/s。

原因

默认 yum 源是 CentOS 官方的,服务器在国外,国内访问慢。有时候官方源还会挂。

更换国内源

第一步:备份原来的源

mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

第二步:下载阿里源

curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

或者用 wget:

wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

第三步:清理缓存并重建

yum clean all
yum makecache

第四步:测试

yum install -y wget

应该秒装完成。

其他国内源

清华源

curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.tuna.tsinghua.edu.cn/repo/Centos-7.repo

网易源

curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.163.com/.help/CentOS7-Base-163.repo

中科大源

curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.ustc.edu.cn/repo/Centos-7.repo

安装 EPEL 源

EPEL 源是 Extra Packages for Enterprise Linux,很多常用软件(比如 nginx)都在 EPEL 里。

yum install epel-release -y

装完之后也可以换成阿里的 EPEL 源:

mv /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.backup
curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
yum clean all
yum makecache

常见坑

坑1:换完源还是慢

换了阿里源还是慢。

原因: DNS 解析慢,或者服务器本身带宽小。

解决: 换个源试试(清华、网易),或者检查 DNS:

cat /etc/resolv.conf

坑2:yum makecache 报错

执行 yum makecache 的时候报错。

原因: 源文件没下好,或者网络不通。

解决: 检查源文件内容对不对:

cat /etc/yum.repos.d/CentOS-Base.repo

或者重新下一遍。

坑3:CentOS7 已经 EOL 了

CentOS7 官方已经停服了(2024年6月30日),官方源已经不能用了。

解决: 用 vault 源(存档源):

阿里 vault 源:

curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-7.repo

坑4:404 Not Found

装软件的时候报 404。

原因: 源文件里的路径不对,或者软件包被删了。

解决: 换个源,或者清理缓存:

yum clean all
yum makecache

总结

换 yum 源就三步:

  1. 备份:mv /etc/yum.repos.d/CentOS-Base.repo 备份
  2. 下载新源:curl 下载阿里/清华源
  3. 清缓存:yum clean all && yum makecache

国内服务器一定要换国内源,不然装软件能急死。

遇到问题加QQ23979811 协助处理


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

PHP 会话管理详解

PHP 会话管理详解

Session 和 Cookie 是 PHP 做用户登录、状态保持的基础,搞懂原理才不会出问题。

1. Cookie 和 Session 的区别

Cookie:存在客户端浏览器里,每次请求自动带在请求头里。

  • 大小限制 4KB
  • 不安全,用户可以改
  • 适合存不敏感的数据

Session:存在服务器端,客户端只存一个 session_id。

  • 大小没有限制
  • 更安全
  • 适合存用户登录信息

2. Cookie 基本用法

// 设置 Cookie(7天过期)
setcookie('user_id', '123', time() + 3600 * 24 * 7);

// 读取 Cookie
echo $_COOKIE['user_id'];

// 删除 Cookie(过期时间设为过去)
setcookie('user_id', '', time() - 3600);

3. Session 基本用法

// 开启 Session(必须在最前面)
session_start();

// 设置 Session
$_SESSION['user_id'] = 123;
$_SESSION['user_name'] = '张三';

// 读取 Session
echo $_SESSION['user_name'];

// 删除单个 Session
unset($_SESSION['user_id']);

// 销毁所有 Session
session_destroy();

4. Session 工作原理

  1. 用户第一次访问,PHP 生成唯一 session_id
  2. 把 session_id 通过 Cookie 发给浏览器
  3. 浏览器下次访问自动带上这个 Cookie
  4. 服务器根据 session_id 找到对应 Session 数据

5. Session 配置

php.ini 里常用配置:

; Session 保存路径
session.save_path = "/tmp"

; Session 过期时间(分钟)
session.gc_maxlifetime = 1440

; Cookie 有效期(0 表示浏览器关闭就过期)
session.cookie_lifetime = 0

6. 常见问题

1)Session 丢失:

  • 没有 session_start()
  • Cookie 被禁用
  • session 文件目录权限不对

2)多台服务器 Session 共享:

  • 默认 Session 存在本地文件,多台服务器不共享
  • 解决方案:存在 Redis 里
// Session 存 Redis
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', 'tcp://127.0.0.1:6379');

7. 安全建议

  • 登录成功后重新生成 session_id,防止会话固定攻击
  • 敏感数据不要存在 Cookie 里
  • Session 过期时间不要太长
  • HTTPS 下设置 Cookie 的 secure 属性
// 登录成功后重新生成 ID
session_regenerate_id(true);

总结

PHP 会话管理核心:

  • Cookie 存客户端,Session 存服务器
  • Session 靠 Cookie 里的 session_id 识别用户
  • 多服务器部署 Session 存 Redis
  • 登录后重新生成 session_id 防攻击

搞懂这些,用户登录功能就不会出问题了。


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

Linux查看进程占用内存最高,kill进程方法

前言

服务器内存用满了,不知道哪个进程占的?找到之后怎么杀掉?本文就讲一下怎么查看占用内存最高的进程,以及怎么 kill 掉。

查看内存占用

top 命令(最常用)

top

打开后默认按 CPU 排序,按 M 键(大写)就会按内存排序。

输出里重点看:

  • PID:进程号
  • USER:运行用户
  • %MEM:内存占用百分比
  • RES:实际用的物理内存(KB)

按 q 退出。

ps 命令

一行命令直接看内存占用前 10:

ps aux --sort=-%mem | head -10

输出按内存从大到小排,前 10 个就是最占内存的。

只看进程名和 PID

ps aux --sort=-%mem | head -10 | awk '{print $2, $11}'

只显示 PID 和进程名,更简洁。

kill 杀掉进程

kill 命令

先从 top 或 ps 里找到进程 PID,然后:

kill 进程号

比如杀掉 PID 为 1234 的进程:

kill 1234

kill -9 强制杀

有时候 kill 没用,进程就是不死,用 -9 强制杀:

kill -9 1234

注意: -9 是强杀,进程没机会清理资源,可能导致数据丢失。尽量先试普通 kill,不行再 -9。

pkill 按名字杀

不用找 PID,直接按进程名杀:

pkill nginx

杀掉所有叫 nginx 的进程。

killall 杀同名进程

killall nginx

和 pkill 类似,也是按名字杀。

常用操作示例

找出最占内存的进程并杀掉

# 找前5个最占内存的
ps aux --sort=-%mem | head -5

# 假设 PID 是 1234,杀掉
kill 1234

杀掉所有 php-fpm 进程

pkill php-fpm

杀掉某个用户的所有进程

pkill -u www

常见坑

坑1:杀错进程了

把系统关键进程杀了,服务器直接挂。

解决: 杀之前先确认一下这个进程是干嘛的:

ps -p 进程号 -o comm=

或者:

ls -l /proc/进程号/exe

坑2:kill 了进程又自动起来了

杀掉之后过几秒又自动启动了。

原因: 有服务管理工具(systemd、supervisor)在监控,挂了就自动拉起。

解决: 先停服务再杀:

systemctl stop nginx

或者:

supervisorctl stop nginx

坑3:kill 没权限

普通用户杀不了 root 启动的进程。

解决: 加 sudo 或切 root:

sudo kill 1234

总结

命令 作用
top 实时看进程,按 M 按内存排序
ps aux --sort=-%mem 按内存排序看进程
kill 进程号 正常杀进程
kill -9 进程号 强制杀进程
pkill 进程名 按名字杀进程
killall 进程名 杀同名所有进程

服务器内存爆了就用 top 按 M 排序,找到最占内存的杀掉就行。

遇到问题加QQ23979811 协助处理


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

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。


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

Linux怎么查看当前在线用户,踢出用户命令

前言

服务器是多人用的,有时候需要看看谁在线上,或者把某个挂机的用户踢下线。Linux 自带了几个命令可以查看当前在线用户,本文就讲一下怎么用。

查看在线用户

who 命令

最简单的命令,直接看谁在线:

who

输出类似:

root     pts/0        2026-10-06 09:00 (192.168.1.100)
zhangsan pts/1        2026-10-06 09:15 (192.168.1.101)
lisi     pts/2        2026-10-06 09:20 (192.168.1.102)

每一列分别是:用户名、终端、登录时间、来源 IP。

w 命令

比 who 更详细,还能看到用户在干嘛:

w

输出类似:

 09:30:00 up  2:15,  3 users,  load average: 0.00, 0.01, 0.05
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/0    192.168.1.100    09:00    5.00s  0.03s  0.01s w
zhangsan pts/1    192.168.1.101    09:15    15.00s 0.02s  0.00s bash
lisi     pts/2    192.168.1.102    09:20    1:30   0.01s  0.01s top

多了几列:

  • LOGIN@:登录时间
  • IDLE:空闲时间
  • JCPU:该终端所有进程用的 CPU 时间
  • PCPU:当前进程用的 CPU 时间
  • WHAT:当前在跑什么命令

users 命令

只看用户名,重复的就重复显示:

users

输出:

root zhangsan lisi

last 命令

看历史登录记录:

last

会列出最近登录过的用户,包括用户名、终端、来源 IP、登录时间、退出时间、总共在线多久。

踢出用户

pkill 踢出某个用户

比如把 zhangsan 踢下线:

pkill -u zhangsan

这会杀掉 zhangsan 的所有进程,直接把他踢下线。

杀掉某个终端

先看 who,找到要踢的人的终端,比如 pts/1:

who

然后杀掉这个终端:

pkill -t pts/1

或者:

kill -9 $(ps -t pts/1 -o pid=)

踢所有非 root 用户

警告!这会把其他所有登录的人都踢掉:

pkill -u -root

常见坑

坑1:把自己踢下线了

执行 pkill 的时候把自己的终端也杀掉了,SSH 直接断开。

解决: 踢人的时候确认一下自己的终端,别把自己踢了:

who am i

先看自己是哪个终端,然后踢别人的时候避开自己的终端。

坑2:踢了用户但他又登回来了

踢了之后用户马上又登上来了,没用。

解决: 如果是恶意用户,直接改密码或者封 IP:

# 改密码
passwd 用户名

# 封 IP
firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='192.168.1.101' reject"
firewall-cmd --reload

坑3:看不到图形界面登录的用户

who 只看终端登录的,图形界面登录的可能看不到。

解决: 用 last 看历史记录,或者看 /var/log/auth.log(CentOS 是 /var/log/secure)。

总结

查看在线用户的常用命令:

命令 作用
who 看谁在线
w 看谁在线 + 在干嘛
users 只看用户名
last 看历史登录记录
pkill -u 用户名 踢出某个用户
pkill -t pts/1 杀掉某个终端

日常运维用 w 就够了,信息最全。

遇到问题加QQ23979811 协助处理


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

Redis 缓存雪崩解决

Redis 缓存雪崩解决方案

缓存雪崩是 Redis 最常见的线上事故之一,搞懂怎么预防,关键时刻能救命。

1. 什么是缓存雪崩

大量缓存同时过期,或者 Redis 整个挂了,所有请求都打到数据库,数据库瞬间压力爆炸,直接崩溃。

就像雪崩一样,一层压一层,全完了。

2. 和缓存穿透、击穿的区别

穿透:查不存在的数据,缓存挡不住,一直打数据库。

击穿:一个热点 key 突然过期,瞬间大量请求打数据库。

雪崩:大量 key 同时过期,或者 Redis 挂了,整个数据库被打垮。

3. 解决方案一:过期时间加随机值

最常用的方法。不要所有 key 都设一样的过期时间,在基础时间上加随机数:

// 不好:所有 key 都 1 小时过期
$redis->setex('product:1', 3600, $data);

// 好:基础 1 小时,加 0-600 秒随机
$ttl = 3600 + rand(0, 600);
$redis->setex('product:1', $ttl, $data);

这样过期时间分散开,不会一起失效。

4. 解决方案二:热点数据永不过期

热点数据不要主动设过期,后台异步更新:

// 商品详情这种热点数据,不设过期
$redis->set('product:1', $data);

// 后台定时任务更新
// 不依赖过期时间,由程序控制刷新

5. 解决方案三:Redis 高可用

Redis 自己要高可用,不能单点:

  • 主从架构 + 哨兵,主挂了自动切从
  • Redis Cluster 集群,分片存储,一个节点挂了不影响全局
# Redis Sentinel 哨兵配置
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000

6. 解决方案四:熔断降级

数据库撑不住的时候,要能自动降级:

  • 限流:用令牌桶、漏桶算法,限制请求量
  • 降级:返回默认数据或者"系统繁忙"
  • 熔断:数据库连续报错多了,直接不访问数据库

常用工具:Sentinel、Hystrix。

7. 解决方案五:多级缓存

不要只靠 Redis 一层缓存:

用户请求 → 浏览器缓存 → CDN → Nginx 本地缓存 → Redis → 数据库

多级缓存,就算 Redis 挂了,前面还有 Nginx 缓存挡着。

8. 应急处理

真出问题了怎么办:

  1. 先限流,别让数据库被打死
  2. 恢复 Redis 服务
  3. 重新预热热点数据
  4. 慢慢放开流量

总结

缓存雪崩解决方案:

  • 过期时间加随机值,别一起失效
  • 热点数据永不过期,后台更新
  • Redis 做高可用集群
  • 加熔断限流兜底
  • 多级缓存层层防护

记住这几点,缓存雪崩就不怕了。


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

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 断了,再也连不上。

解决:

  1. 改配置之前先开个备用 SSH 连接,别关
  2. 或者直接用云服务器控制台的 VNC 操作
  3. 不要在生产环境随便重启网络

坑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 协助处理


emer 发布于  2026-10-6 07:38 

PHP 错误处理最佳实践

PHP 错误处理最佳实践

错误处理是 PHP 开发最容易忽略但最重要的环节,写好了排查问题事半功倍,写不好线上出问题抓瞎。

1. 错误级别

PHP 有不同的错误级别:

  • E_ERROR:致命错误,脚本停止
  • E_WARNING:警告,脚本继续
  • E_NOTICE:注意,一般是不规范写法
  • E_PARSE:语法解析错误
// 开发环境显示所有错误
error_reporting(E_ALL);
ini_set('display_errors', 1);

// 生产环境不显示错误,只记日志
error_reporting(E_ALL);
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/var/log/php/error.log');

2. 错误 vs 异常

PHP 里错误和异常是两回事:

  • 错误:语法错误、内存不足这种,用 try/catch 抓不住
  • 异常:业务层面的错误,比如数据库连接失败

PHP 7+ 大部分错误会抛 Error 异常,也可以抓了。

3. try/catch 用法

try {
    // 可能出错的代码
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
} catch (PDOException $e) {
    // 捕获异常
    echo "数据库连接失败:" . $e->getMessage();
    // 记日志
    error_log("DB Error: " . $e->getMessage());
}

4. 自定义错误处理

// 设置自定义错误处理器
set_error_handler(function($errno, $errstr, $errfile, $errline) {
    // 把错误转成异常抛出
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});

// 设置异常处理器
set_exception_handler(function($e) {
    // 所有未捕获的异常都到这里
    error_log("Uncaught: " . $e->getMessage());
    echo "系统繁忙,请稍后再试";
});

// 注册关闭函数,抓致命错误
register_shutdown_function(function() {
    $error = error_get_last();
    if ($error && in_array($error['type'], [E_ERROR, E_PARSE])) {
        error_log("Fatal: " . $error['message']);
    }
});

5. 开发 vs 生产环境

开发环境:

  • 打开 display_errors,错误直接显示在页面
  • 打开所有错误级别
  • 详细的报错信息方便调试

生产环境:

  • 关闭 display_errors,不要把错误暴露给用户
  • 错误全部写到日志文件
  • 页面显示友好的"系统错误"提示

6. 常用错误处理技巧

// 1. 用 @ 抑制错误不好,应该判断
// 不好:
$file = @file_get_contents('config.php');

// 好:
$file = file_exists('config.php') ? file_get_contents('config.php') : null;

// 2. 不要把数据库错误信息直接返回给前端
// 不好:
echo $e->getMessage();

// 好:
error_log($e->getMessage());
echo json(['code' => 500, 'msg' => '系统错误']);

// 3. 日志要分级
error_log('信息:用户登录', 3, '/var/log/info.log');
error_log('警告:登录失败次数过多', 3, '/var/log/warn.log');
error_log('错误:数据库连接失败', 3, '/var/log/error.log');

7. 总结

PHP 错误处理核心:

  • 开发环境显示错误,生产环境只记日志
  • try/catch 捕获业务异常
  • set_exception_handler 兜底
  • 不要把错误细节暴露给用户
  • 日志分级,方便排查

搞懂这些,线上出问题排查效率翻倍。


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

MySQL覆盖索引是什么,为什么能提升查询性能

前言

做SQL优化的同学肯定听说过覆盖索引,很多文章里都说用覆盖索引能提升查询性能。但是很多新手同学搞不懂覆盖索引到底是什么,为什么能提升性能。

这篇文章就把覆盖索引的知识点从头到尾讲清楚:什么是覆盖索引?为什么能提升性能?怎么判断是不是用了覆盖索引?怎么利用覆盖索引优化查询?看完之后你就彻底搞懂了。

一、什么是覆盖索引

先说说回表是什么。

MySQL的InnoDB引擎,主键索引是聚簇索引,叶子节点直接存整行数据。普通索引的叶子节点存的是主键值。

如果你用普通索引查询,查到主键值之后,还要再去主键索引里查一遍,拿到完整的行数据。这个过程就叫回表。

那什么是覆盖索引呢?就是:你要查询的字段,在索引里就都有了,不用再回表去主键索引里查了。这就叫覆盖索引,或者叫索引覆盖。

简单来说:查询需要的所有数据,索引里都有,不用回表,就是覆盖索引。

二、为什么覆盖索引能提升性能

为什么覆盖索引能提升性能?因为少了回表这一步。

我们对比一下:

没有覆盖索引的情况

  1. 先在普通索引里查,找到主键值
  2. 再拿着主键值去主键索引里查,拿到完整的行数据

这样要查两棵B+树,查了两次,当然慢。

有覆盖索引的情况

  1. 直接在索引里查到所有需要的数据,就完事了

这样只查了一棵B+树,少了回表这一步,当然快。

尤其是表很大的时候,回表要做随机IO,性能很差。如果不用回表,直接在索引里就能拿到数据,那性能提升就非常明显了。

三、怎么判断是不是覆盖索引

用explain看执行计划的时候,如果Extra列里显示 Using index,就说明用了覆盖索引,不用回表。

比如:

+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | users | ref   | name          | name | 767     | const|  10  | Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+

看到Extra里的Using index了吗?这就说明用了覆盖索引。

四、怎么利用覆盖索引优化查询

知道了覆盖索引的好处,那怎么利用它来优化查询呢?

1. 查询的时候只查需要的字段,不要SELECT *

很多同学写SQL的时候喜欢写SELECT *,把所有字段都查出来。这样你要查的字段太多了,索引里不可能全都有,就必须回表。

如果你只查需要的几个字段,那就有可能把这几个字段都加到索引里,这样就能用到覆盖索引了。

比如:

-- 不好的写法
SELECT * FROM users WHERE name = '张三';

-- 好的写法
SELECT id, name, age FROM users WHERE name = '张三';

2. 建联合索引,把经常查询的字段都加进去

如果你经常根据name查询,并且经常查name和age,那就建一个(name, age)的联合索引。

这样当你查SELECT name, age FROM users WHERE name = '张三'的时候,索引里就有name和age了,不用回表,直接就能拿到数据。

3. 把常用的查询字段加到索引里

除了WHERE条件的字段,SELECT里的字段也可以加到索引里,这样就能实现覆盖索引。

比如你经常查SELECT id, name, age FROM users WHERE name = ?,那就建一个(name, age)的联合索引,因为id是主键,InnoDB的普通索引叶子节点本来就存主键值,所以索引里就有id、name、age三个字段了,正好覆盖你的查询。

五、覆盖索引的注意点

1. 不是所有查询都能用覆盖索引

只有你要查询的字段都在索引里的时候,才能用覆盖索引。如果有字段不在索引里,那就必须回表。

2. 覆盖索引也不是万能的

不要为了覆盖索引,把所有字段都加到索引里。索引太多了,会影响插入和更新的性能。要权衡一下,哪些查询是高频的,值得为它建覆盖索引。

3. 主键索引天然就是覆盖索引

如果你是用主键查询,那本来就是在主键索引里查,直接就能拿到所有数据,天然就是覆盖索引。

常见坑

坑1:以为只要用了索引就是覆盖索引

很多同学以为只要查询用到了索引,就是覆盖索引。其实不是,用到索引只是说用索引找到了主键值,如果你还要查其他字段,还是要回表。只有Extra里显示Using index,才是真的覆盖索引。

坑2:写SELECT *,用不了覆盖索引

很多同学写SQL的时候喜欢SELECT *,结果要查的字段太多了,索引里根本放不下,就用不了覆盖索引。记住:只查需要的字段,别查所有字段。

坑3:为了覆盖索引,索引建得太多

很多同学为了让某个查询用上覆盖索引,把那个查询的所有字段都加到索引里。结果一个表建了好几十个索引,插入更新的时候要维护这么多索引,性能很差。覆盖索引是好,但是不要滥用,要权衡。

坑4:不知道Using index就是覆盖索引

很多同学看explain的时候,看到Extra里的Using index不知道是什么意思。记住:Using index就是覆盖索引,不用回表,性能很好。

坑5:联合索引的顺序不对

很多同学建联合索引的时候,顺序建反了,结果用不了覆盖索引。记住:联合索引要满足最左前缀原则,WHERE条件里的字段要放在前面,SELECT的字段放在后面。

总结

MySQL覆盖索引的核心知识点:

  1. 什么是覆盖索引:查询需要的所有数据,索引里都有,不用回表
  2. 为什么能提升性能:少了回表这一步,不用查两棵B+树,减少IO
  3. 怎么判断:explain的Extra列里显示Using index,就是覆盖索引
  4. 怎么利用:
    • 不要写SELECT *,只查需要的字段
    • 建联合索引,把WHERE和SELECT的字段都加进去
  5. 注意点:不要为了覆盖索引建太多索引,要权衡性能

记住:覆盖索引是SQL优化里很重要的一个手段,很多时候只要把SELECT *改成只查需要的字段,再建个合适的联合索引,性能就能提升好几倍。

遇到问题加QQ23979811 协助处理


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

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自增主键的核心知识点:

  1. 不同整型的最大值:INT是21亿(有符号)/42亿(无符号),BIGINT是922亿亿
  2. 主键直接用BIGINT:不要用INT,省得以后不够用了还要改
  3. 自增主键用完会报错:不能再插入新数据了
  4. 怎么查看当前自增值:SHOW TABLE STATUS,看Auto_increment列
  5. 用完了怎么办:把INT改成BIGINT,或者分库分表
  6. 自增主键的坑:删除数据自增值不回收、自增值不连续

记住:建表的时候主键直接用BIGINT,这是最稳妥的做法。不要一开始图省事用INT,以后改起来更麻烦。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 09:05 

MySQL InnoDB和MyISAM引擎区别,怎么选

前言

很多新手同学刚学MySQL的时候,根本不知道存储引擎是什么,建表的时候直接用默认的。其实存储引擎很重要,不同的存储引擎有不同的特点,适合不同的场景。

MySQL最常用的两个存储引擎就是InnoDB和MyISAM。很多同学搞不清楚它们有什么区别,也不知道应该选哪个。这篇文章就把这两个引擎的区别讲清楚,看完之后你就知道怎么选了。

一、什么是存储引擎

存储引擎就是MySQL里,真正存储数据、管理数据的东西。MySQL是把数据和引擎分开的,不同的表可以用不同的存储引擎。

就像你有一个仓库,你可以选择用货架放东西,也可以用箱子放东西,不同的存放方式有不同的特点。存储引擎就是这个"存放方式"。

二、InnoDB和MyISAM的核心区别

我们先看一个对比表,一目了然:

特点 InnoDB MyISAM
事务支持 ✅ 支持 ❌ 不支持
外键支持 ✅ 支持 ❌ 不支持
锁粒度 行锁 表锁
崩溃恢复 ✅ 支持 ❌ 不支持
全文索引 ✅ 5.6以后支持 ✅ 支持
主键索引 聚簇索引 非聚簇索引
读写性能 写性能好,读也不错 读性能好,写性能差

三、InnoDB的特点

InnoDB是MySQL默认的存储引擎,从MySQL 5.5开始,默认就是InnoDB了。

1. 支持事务

这是InnoDB最大的特点。支持ACID事务,也就是原子性、一致性、隔离性、持久性。如果你需要事务,那就必须用InnoDB。

2. 支持行锁

InnoDB的锁粒度是行锁。也就是说,一个事务锁了某一行,其他事务还可以操作表里的其他行。这样并发性能就很好。

3. 支持外键

InnoDB支持外键约束,可以保证数据的一致性。

4. 支持崩溃恢复

InnoDB有redo log,MySQL崩溃之后重启,能够自动恢复数据,不容易丢数据。

5. 聚簇索引

InnoDB的主键索引是聚簇索引,叶子节点直接存数据。这样按主键查询速度很快。

InnoDB适合什么场景

  • 需要事务的场景
  • 读写都比较频繁的场景
  • 对数据一致性要求高的场景
  • 大部分互联网业务场景

四、MyISAM的特点

MyISAM是MySQL早期默认的存储引擎,现在用的越来越少了。

1. 不支持事务

MyISAM不支持事务,执行SQL就是直接执行,没有事务的概念。

2. 表锁

MyISAM的锁粒度是表锁。也就是说,一个操作锁了整张表,其他操作都要等。这样并发性能就很差。

3. 读性能好

MyISAM的读性能很好,因为它的结构简单,查询的时候直接读文件就行。

4. 不支持崩溃恢复

MyISAM没有redo log,MySQL崩溃之后,很可能会损坏数据,需要手动修复。

5. 非聚簇索引

MyISAM的索引是非聚簇索引,叶子节点存的是数据的地址,按主键查询要回表。

MyISAM适合什么场景

  • 读多写少的场景
  • 不需要事务的场景
  • 一些日志表、统计报表表
  • 对数据一致性要求不高的场景

五、怎么选InnoDB和MyISAM

很多同学问:到底应该选哪个?

其实现在大部分场景,直接选InnoDB就对了。因为InnoDB是默认的,功能也全,支持事务、行锁、崩溃恢复,大部分业务都适用。

MyISAM现在基本上只有一些特殊场景才会用,比如:

  • 纯读的表,比如一些静态配置表
  • 一些历史遗留的老项目,原来是用MyISAM的

记住:新项目直接用InnoDB,不用考虑MyISAM。

六、怎么查看表用的是什么引擎

查看某张表的引擎

SHOW TABLE STATUS LIKE '表名';

执行完之后,看Engine那列,就是这张表用的存储引擎。

查看数据库里所有表用的引擎

SELECT TABLE_NAME, ENGINE 
FROM information_schema.TABLES 
WHERE TABLE_SCHEMA = '数据库名';

七、怎么修改表的引擎

如果一张表原来是用MyISAM,想改成InnoDB,可以执行:

ALTER TABLE 表名 ENGINE = InnoDB;

注意:这个操作会把整张表重建一遍,如果表很大的话,会花很长时间,而且会锁表。最好在业务低峰期执行。

常见坑

坑1:以为MyISAM性能更好

很多同学觉得MyISAM读性能好,就想用MyISAM。其实现在InnoDB的性能已经很好了,尤其是读多写少的场景,InnoDB也不差。而且MyISAM不支持事务、表锁,并发高了性能很差。现在新项目直接用InnoDB就对了。

坑2:不知道MySQL默认引擎变了

很多老教程说MySQL默认是MyISAM,其实从MySQL 5.5开始,默认就是InnoDB了。不要被老教程误导。

坑3:用MyISAM做业务表

很多同学不知道MyISAM不支持事务,结果把业务表用MyISAM,出了问题才发现。只要是业务表,需要事务的,一定要用InnoDB。

坑4:修改引擎的时候没注意锁表

很多同学直接在线上执行ALTER TABLE改引擎,结果锁表了,业务直接卡住了。改引擎之前一定要评估表大小,最好在业务低峰期执行,或者用pt-online-schema-change这种工具在线改。

坑5:以为InnoDB一定比MyISAM慢

很多同学觉得InnoDB因为支持事务、行锁,所以性能比MyISAM差。其实不是,现在InnoDB的性能已经非常好了,尤其是读写都有并发的场景,InnoDB的行锁比MyISAM的表锁性能好很多。

总结

InnoDB和MyISAM的核心区别:

  1. InnoDB支持事务,MyISAM不支持
  2. InnoDB是行锁,MyISAM是表锁,InnoDB并发性能好
  3. InnoDB支持崩溃恢复,MyISAM不支持
  4. InnoDB支持外键,MyISAM不支持
  5. InnoDB是聚簇索引,按主键查询快
  6. 现在MySQL默认就是InnoDB,新项目直接用InnoDB
  7. MyISAM只有一些特殊场景才用,比如纯读的表

记住:现在不用纠结选哪个,新项目直接用InnoDB就对了,不用考虑MyISAM。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 09:03 

MySQL乐观锁和悲观锁,业务中怎么选

前言

做后端开发的同学肯定都遇到过并发修改数据的问题:两个人同时改同一条数据,结果后提交的把先提交的覆盖了,数据就不对了。这时候怎么办?就需要用到并发控制。

常见的并发控制方案有两种:乐观锁和悲观锁。很多同学搞不懂这两个有什么区别,也不知道业务里应该选哪个。这篇文章就把乐观锁和悲观锁的概念、实现方法、怎么选讲清楚,看完之后你就知道什么时候用哪个了。

一、什么是悲观锁

悲观锁,顾名思义,就是很悲观。它认为并发冲突的概率很高,所以在操作数据之前,一定要先把数据锁住,然后再操作。

就像你要拿一个贵重物品,你怕别人也拿,所以先把它锁起来,你操作完了再放开。

悲观锁的特点

  • 先锁再操作:操作之前先加锁
  • 并发度低:同一时间只能有一个事务操作
  • 适合冲突多的场景:如果并发冲突很频繁,悲观锁比较合适

二、什么是乐观锁

乐观锁,顾名思义,就是很乐观。它认为并发冲突的概率很低,所以操作数据的时候不加锁,等更新的时候再检查一下,看看有没有人在这期间改过数据。

就像你要拿一个东西,你觉得没人会跟你抢,所以直接去拿,拿的时候再看一眼,如果发现已经被别人拿了,那就重新来。

乐观锁的特点

  • 操作的时候不加锁:先查出来,更新的时候再检查
  • 并发度高:不用提前加锁,多个事务可以同时操作
  • 适合冲突少的场景:如果并发冲突不频繁,乐观锁性能更好

三、悲观锁怎么实现

悲观锁一般用数据库的行锁来实现。MySQL里有两种方式:

1. SELECT ... FOR UPDATE

这个语句会给查询到的行加排他锁,其他事务就不能再改这些行了。

举个例子:

-- 开启事务
BEGIN;

-- 查询商品库存,并且加锁
SELECT stock FROM goods WHERE id = 1 FOR UPDATE;

-- 如果库存够的话,扣减库存
UPDATE goods SET stock = stock - 1 WHERE id = 1;

-- 提交事务
COMMIT;

这个流程就是:先把这行锁住,然后查库存,然后扣减库存,提交事务的时候释放锁。在这个过程中,其他事务想要修改这行数据,就必须等你释放锁。

2. 用UPDATE语句自带的锁

其实UPDATE语句本身就会加排他锁。所以你也可以不用SELECT FOR UPDATE,直接用UPDATE:

UPDATE goods SET stock = stock - 1 WHERE id = 1 AND stock > 0;

这样的话,如果库存够,就更新成功;如果库存不够,就更新0行。这其实也是一种悲观锁的实现。

四、乐观锁怎么实现

乐观锁一般用版本号或者时间戳来实现。

1. 版本号方式

在表里加一个version字段,每次更新的时候,把version加1。更新的时候检查一下version是不是跟之前查出来的一样,如果一样就更新,不一样就说明已经被别人改过了。

举个例子:

-- 先查出来数据,得到version
SELECT stock, version FROM goods WHERE id = 1;

-- 假设查出来version是1,stock是10

-- 更新的时候,带上version条件
UPDATE goods 
SET stock = stock - 1, version = version + 1 
WHERE id = 1 AND version = 1;

如果这个UPDATE语句执行之后,影响行数是1,说明更新成功了;如果影响行数是0,说明这期间已经有人改过数据了,更新失败,这时候就需要重试。

2. 时间戳方式

跟版本号方式差不多,只不过用时间戳代替版本号。每次更新的时候,把更新时间改成当前时间,更新的时候检查一下时间是不是跟之前查出来的一样。

五、乐观锁和悲观锁怎么选

很多同学问:业务里到底应该选乐观锁还是悲观锁?

其实没有绝对的答案,要看具体场景:

选悲观锁的场景

  1. 并发冲突很频繁:如果很多人同时操作同一条数据,冲突很频繁,用悲观锁比较好。不然乐观锁每次都更新失败,要不断重试,反而更麻烦
  2. 事务很长:如果事务里要做很多事情,持锁时间长,用悲观锁可以避免别人在你操作的期间改数据
  3. 不能重试的场景:如果业务上不能重试,那就用悲观锁,提前锁住,保证能成功

选乐观锁的场景

  1. 并发冲突很少:如果并发冲突的概率很低,用乐观锁性能更好,不用提前加锁,并发度高
  2. 事务很短:如果事务里只做一点点事情,那用乐观锁就够了
  3. 读多写少:如果大部分是读,只有很少的写,用乐观锁比较合适

总结一下: 冲突多、事务长 → 悲观锁;冲突少、读多写少 → 乐观锁。

六、乐观锁的ABA问题

乐观锁有一个经典的问题,就是ABA问题。

什么是ABA问题?比如:

  • 事务A查出来数据是version=1,值是100
  • 这时候事务B把数据改成了200,又改回了100,version变成了3
  • 然后事务A更新的时候,它以为值还是100,所以更新成功了

但是中间数据其实已经被改过了,只是最后又改回来了。这就是ABA问题。

怎么解决ABA问题

一般用版本号就能解决ABA问题,因为每次修改版本号都会加1,就算最后改回来了,版本号也不一样了。

如果用时间戳的话,也能解决ABA问题。

常见坑

坑1:以为乐观锁是不加锁

很多同学以为乐观锁就是不加锁,其实不是。乐观锁是更新的时候再加锁,只是操作之前不加锁。它本质上还是要靠数据库的行锁来保证更新的原子性。

坑2:乐观锁更新失败了不知道重试

很多同学用乐观锁,更新失败了就直接报错给用户了。其实更新失败是正常的,这时候应该自动重试几次,重试成功了就没事了。如果重试几次都失败,再报错。

坑3:悲观锁加的锁太大

很多同学用SELECT FOR UPDATE,结果WHERE条件没有用到索引,导致锁了整张表,而不是某一行。这样并发度就太低了,性能很差。一定要确保WHERE条件用到了索引,这样才会加行锁。

坑4:事务太长,持锁时间久

用悲观锁的时候,如果事务很长,持锁时间久,其他事务都在等锁,并发性能就很差。所以一定要把事务尽量缩短,不要在事务里做耗时操作。

坑5:版本号用的不对

很多同学用版本号,但是更新的时候忘了把version加1,或者更新的时候没有带上version条件,这样乐观锁就失效了。一定要确保更新的时候,version加了1,并且WHERE条件里带上了原来的version。

总结

乐观锁和悲观锁的核心知识点:

  1. 悲观锁:认为冲突多,先加锁再操作,并发度低,适合冲突多的场景
  2. 乐观锁:认为冲突少,操作的时候不加锁,更新的时候再检查,并发度高,适合冲突少的场景
  3. 悲观锁实现:SELECT ... FOR UPDATE,或者直接用UPDATE语句自带的锁
  4. 乐观锁实现:版本号方式,或者时间戳方式
  5. 怎么选:冲突多、事务长用悲观锁;冲突少、读多写少用乐观锁
  6. ABA问题:乐观锁的经典问题,用版本号可以解决
  7. 乐观锁更新失败要自动重试:不要直接报错给用户,重试几次就好了

记住:没有绝对的好和坏,看具体场景选。大部分互联网场景,读多写少,用乐观锁比较多,性能更好。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 09:01 

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死锁的核心知识点:

  1. 死锁是什么:两个事务互相持有对方需要的锁,又互相等对方释放,形成循环等待
  2. 死锁产生的原因:访问资源顺序不一致、事务太长、没用索引锁太多行、间隙锁
  3. 怎么查看死锁:SHOW ENGINE INNODB STATUS,看LATEST DETECTED DEADLOCK部分
  4. 怎么排查死锁:看两个事务的SQL、看锁的信息、结合业务逻辑分析
  5. 怎么避免死锁:
    • 按相同的顺序访问资源
    • 大事务拆成小事务
    • 尽量用索引访问数据
    • 降低隔离级别到读提交
    • 设置锁等待超时时间
  6. 死锁和锁等待的区别:锁等待是单向的,等一会儿就好;死锁是循环的,MySQL会自动回滚一个

记住:死锁不可怕,可怕的是不知道为什么会死锁。遇到死锁不要慌,先看死锁日志,找到原因,然后从业务代码上避免。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 08:59 

MySQL性能优化实战,从这几个方面下手

前言

做网站开发或者运维的同学肯定都遇到过这种情况:网站越来越慢,数据库CPU占用越来越高,用户体验越来越差。这时候怎么办?很多同学一上来就说:加机器!加内存!其实这是最笨的方法。

MySQL性能优化是一个系统工程,要从多个方面下手:SQL、索引、表结构、配置参数、架构。很多时候,不用加机器,只要优化一下SQL和索引,性能就能提升好几倍。

这篇文章就把MySQL性能优化的完整思路整理出来,从定位问题到SQL优化、表结构优化、参数优化、架构优化,一步一步讲清楚。看完之后你就知道遇到性能问题应该从哪里下手了。

一、优化之前先定位问题

很多同学一上来就瞎优化,优化了半天也不知道有没有效果。正确的做法是:先定位问题,再针对性优化。

怎么定位慢SQL

1. 开启慢查询日志

MySQL的慢查询日志可以记录所有执行慢的SQL。先把慢查询日志开了,看看哪些SQL慢。

# my.cnf里配置
slow_query_log = 1
long_query_time = 1   # 超过1秒的SQL记录下来
slow_query_log_file = /var/log/mysql/slow.log

2. 用explain分析慢SQL

找到慢SQL之后,用explain看看它的执行计划,看看有没有走索引,是不是全表扫描。

3. 看数据库的状态

用下面的命令看看数据库当前的状态:

SHOW GLOBAL STATUS;

重点关注这几个指标:

  • Slow_queries:慢查询的数量
  • Threads_connected:当前连接数
  • Innodb_row_reads:读了多少行数据
  • Innodb_data_reads:读了多少数据

二、SQL和索引优化

这是最常用、也是性价比最高的优化方式。很多时候,加个索引,SQL性能就能提升几十倍。

1. 加合适的索引

哪些字段要加索引

  • WHERE条件里经常用到的字段
  • 表连接的字段
  • ORDER BY、GROUP BY的字段
  • 区分度高的字段

索引的注意点

  • 不要加太多索引,索引也占空间,而且插入更新的时候要维护索引
  • 联合索引要注意最左前缀原则
  • 尽量不要在索引字段上做函数操作,不然索引会失效

2. 避免索引失效的情况

常见的索引失效场景:

  • WHERE条件里对索引字段做函数操作
  • WHERE条件里用了!=、<>、NOT IN
  • LIKE以%开头
  • 联合索引不满足最左前缀
  • 字段类型不匹配(比如字符串字段用数字去查)

3. 优化SQL写法

不要用SELECT *

只查需要的字段,不要查所有字段。这样不仅减少数据传输,还能用到覆盖索引。

避免大分页

比如LIMIT 100000, 10,这种越往后越慢。优化方法:用上次的最大id来查。

避免在WHERE里做计算

比如WHERE age + 1 = 18,这样索引会失效。应该写成WHERE age = 17。

小表驱动大表

联表查询的时候,小表在前,大表在后,性能更好。

三、表结构优化

1. 选择合适的数据类型

  • 能用数字类型就不要用字符串
  • 能用短的就不要用长的,比如用TINYINT不要用INT
  • 时间类型用DATETIME或者TIMESTAMP,不要用字符串存时间

2. 避免太多字段

一张表不要有太多字段,字段太多的话,数据页能放的行就少,IO次数就多。不常用的字段可以拆到另一张表里。

3. 适当加冗余字段

有些字段虽然可以联表查出来,但是如果经常用到,可以考虑加冗余字段,减少联表查询。

4. 大表做归档

如果表里有很多历史数据,但是查询的时候很少用到,可以把历史数据归档到另一张表里,主表只保留最近的数据。这样主表数据量小了,查询就快了。

四、配置参数优化

MySQL的默认配置很多都不是最优的,需要根据自己的服务器配置调一下。

1. innodb_buffer_pool_size

这个是InnoDB的缓冲池大小,是最重要的参数。一般设置成服务器内存的50%~70%。如果你的服务器内存是16G,那这个参数就设成8G~10G。

这个参数设大了,很多数据和索引都能在内存里,就不用读磁盘了,性能会好很多。

2. innodb_log_file_size

这个是redo log的大小,一般设成256M~1G。这个参数影响写性能,太小的话会频繁刷盘。

3. max_connections

最大连接数,根据你的业务量调整,一般设成500~1000就够了。不要设太大,不然每个连接都占内存,反而会拖慢性能。

4. query_cache

MySQL 8.0已经把查询缓存去掉了,因为查询缓存的命中率不高,而且维护成本很高。如果是老版本的MySQL,建议把查询缓存关了。

五、架构层面优化

如果SQL和索引都优化过了,还是不行,那就要从架构层面下手了。

1. 读写分离

主库写,从库读,把读的压力分散到多个从库上。大部分网站都是读多写少,读写分离效果很明显。

2. 加缓存

把热点数据放到Redis里,直接从Redis读,不用查数据库。这是提升性能最明显的方式。

3. 分库分表

单表数据量太大了,到了千万级甚至亿级,那就分库分表,把数据分散到多个表或者多个数据库里。

4. 用更好的硬件

如果前面的优化都做了,还是不够,那只能加硬件了。比如用SSD代替机械硬盘,数据库的性能瓶颈很多时候都是磁盘IO。SSD的IOPS比机械硬盘高几个数量级,提升非常明显。

常见坑

坑1:上来就加机器,不优化SQL

很多同学一遇到性能问题就说:加机器!加内存!其实很多时候都是SQL写的烂,没加索引,优化一下SQL性能就能提升好几倍。加机器是最后手段,不是第一选择。

坑2:索引加了很多,但是都没用到

很多同学觉得索引加的越多越好,结果加了一堆索引,真正查询的时候一个都没用到。加索引之前要先explain一下,看看是不是真的能用到。

坑3:以为加了索引就一定快

很多同学以为只要加了索引,查询就一定快。其实不是,如果索引字段的区分度很低,比如性别只有男和女,那加了索引也没用,优化器可能还是会选择全表扫描。

坑4:调参数瞎调

很多同学看了网上的优化文章,把一堆参数往自己服务器上套,结果把数据库搞出问题了。调参数一定要一点点调,调完观察效果,不要一下子改一堆参数。

坑5:不做监控

很多同学数据库出问题了才知道性能差,平时根本没监控。一定要做监控,比如慢查询数量、CPU使用率、连接数这些指标,提前发现问题。

总结

MySQL性能优化的思路和步骤:

  1. 先定位问题:开慢查询日志,找到慢SQL
  2. SQL和索引优化:加合适的索引,优化SQL写法,这是性价比最高的
  3. 表结构优化:选合适的数据类型,大表归档,适当加冗余字段
  4. 配置参数优化:重点调innodb_buffer_pool_size
  5. 架构层面优化:读写分离、加缓存、分库分表
  6. 最后才是加硬件:比如用SSD、加内存

记住:性能优化是一个系统工程,要从多个方面下手。不要指望一招就能解决所有问题,先从最简单、最容易见效的地方下手,比如加索引、优化SQL,然后再一步步深入。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 08:57 

MySQL分库分表方案,什么时候需要分表

前言

做网站的同学肯定都遇到过这种情况:网站用户越来越多,数据库里的表数据量越来越大,查询越来越慢,插入也越来越慢。这时候怎么办?很多同学第一反应就是:分库分表!

但是分库分表不是万能药,也不是说只要数据量大了就一定要分。很多同学上来就分库分表,结果把自己搞的更累了,问题反而更多。

这篇文章就把分库分表的知识点从头到尾讲清楚:什么时候需要分库分表?怎么分?分完之后有什么问题?看完之后你就知道什么时候该分,什么时候不该分了。

一、什么时候需要分库分表

很多同学一上来就问:分库分表好不好?其实这是个伪命题。分库分表是有代价的,不是什么场景都适合。

一般来说,出现下面这些情况的时候,才考虑分库分表:

1. 单表数据量太大

一般来说,MySQL单表数据量到了千万级之后,性能就开始下降了。如果到了亿级,那基本上就必须分了。

但是也不是说千万级就一定要分,要看你的表结构和查询。如果表结构简单,查询也都走索引,几千万条数据性能还是能接受的。

2. 单库压力太大

一个数据库实例能承受的连接数、IOPS、CPU都是有限的。如果单库的QPS太高,扛不住了,这时候就需要分库,把压力分散到多个数据库实例上。

3. 业务发展到一定阶段

很多互联网公司的业务发展到一定阶段,单库单表确实扛不住了,这时候才需要分。创业初期的小项目,根本没必要分库分表,纯属给自己找麻烦。

总结一下: 分库分表是最后的手段,不是第一选择。在分之前,先看看能不能通过优化SQL、加索引、读写分离、加缓存这些手段解决问题。如果这些手段都用了还是不行,再考虑分库分表。

二、分库分表的两种方式

分库分表有两种方式:垂直拆分和水平拆分。

1. 垂直拆分

垂直拆分又分两种:

垂直分库

按照业务把不同的表分到不同的数据库里。比如把用户相关的表放到user库,订单相关的表放到order库,商品相关的表放到product库。

优点:

  • 业务解耦,不同业务之间互不影响
  • 可以针对不同的业务做不同的优化

缺点:

  • 跨库联表查询比较麻烦
  • 分布式事务问题

垂直分表

把一张大表的字段拆到两张表里。比如把不常用的字段拆到另一张表里,常用的字段留在原表里。

比如一张用户表,有基本信息和详细信息。把基本信息(id、昵称、头像)放到user表,详细信息(简介、地址、备注)放到user_detail表。

优点:

  • 减少单表的数据量,查询更快
  • 把热点字段和冷门字段分开

缺点:

  • 查询的时候可能要联表
  • 代码复杂度变高

2. 水平拆分

水平拆分就是把同一张表的数据,按照某个规则分到多张表里。每张表的结构是一样的,只是数据不一样。

比如一张user表有1000万条数据,我们把它分成4张表:user_0、user_1、user_2、user_3。按照用户id取模,0的放user_0,1的放user_1,以此类推。

优点:

  • 单表数据量小了,性能上去了
  • 可以分散到多个数据库实例上

缺点:

  • 跨片查询很麻烦
  • 分布式事务问题
  • 扩容麻烦

三、怎么选择分片键

水平拆分的时候,最关键的就是选分片键。分片键选不好,后面会很痛苦。

好的分片键有什么特点

  1. 数据分布均匀:不要有的分片数据特别多,有的特别少
  2. 查询都能带上分片键:大部分查询都能根据分片键定位到具体的分片,不用扫全部分片
  3. 尽量不要跨片:尽量避免需要跨片查询的场景

常见的分片键选择

1. 用user_id做分片键

如果你的业务大部分都是跟用户相关的,比如用户的订单、用户的评论,那用user_id做分片键就很合适。同一个用户的数据都在同一个分片上,查询的时候不用跨片。

2. 用order_id做分片键

如果是订单系统,大部分查询都是按订单id查的,那就用order_id做分片键。

3. 按时间做分片

如果是日志类的数据,按时间查询比较多,那就按时间做分片。比如一个月一张表,或者一个季度一张表。

分片算法选什么

常见的分片算法有:

1. 取模

比如:user_id % 4,这样数据会比较均匀。

优点: 数据分布均匀
缺点: 扩容的时候很麻烦,要重新对所有数据做取模,大部分数据都要迁移

2. 范围分片

比如:id在1-1000万的放第一个分片,1000万-2000万的放第二个分片。

优点: 扩容简单,直接加新的分片就行
缺点: 数据分布不均匀,新的数据都写到新的分片上,容易出现热点

3. 一致性哈希

一致性哈希可以解决扩容的时候数据迁移太多的问题。

四、分库分表带来的问题

分库分表不是银弹,分完之后会带来很多新的问题。

1. 分布式事务

原来一个事务里操作同一张表,现在跨了多个分片,原来的本地事务就不管用了,需要用分布式事务。分布式事务的复杂度和性能损耗都是很大的。

2. 跨片查询

原来一条SQL就能查出来的数据,现在要从多个分片查出来再合并。比如要统计所有用户的总数,就得查所有分片然后加起来。如果要分页就更麻烦了。

3. 跨片联表

原来两张表联查很简单,现在两张表在不同的分片上,联查就很麻烦。

4. 全局唯一ID

原来用自增主键就行了,现在分了多个表,自增主键就重复了。需要用全局唯一ID,比如雪花算法。

5. 扩容麻烦

一开始分了4个表,后来数据量太大了,要扩成8个表。这时候数据要重新分布,迁移量很大。

五、常用的分库分表中间件

自己写分库分表的逻辑太麻烦了,一般都是用现成的中间件:

1. ShardingSphere

Apache的开源项目,国内用的比较多。支持分库分表、读写分离、分布式事务这些功能。

2. MyCat

也是一个开源的分库分表中间件,用的也比较多。

3. 官方方案

MySQL本身也有一些分区的功能,比如Range分区、List分区。但是这个是在单库内的分区,不是真正的分库分表。

常见坑

坑1:一上来就分库分表

很多同学刚做项目,就想着分库分表,搞的很复杂。其实小项目根本没必要,单库单表就能扛住。分库分表是等到数据量真的大了再做的事情,不要提前过度设计。

坑2:分片键选的不好

很多同学分片键随便选一个,结果大部分查询都带不上分片键,每次都要扫全部分片,性能反而更差了。分片键一定要选那些查询最常用的字段。

坑3:用取模算法,扩容的时候崩溃了

很多同学一开始用取模算法分了4个表,后来要扩到8个表,结果发现数据全都要重新分布,迁移量巨大,欲哭无泪。所以一开始就要考虑好扩容的问题。

坑4:分完之后跨片查询太多

很多同学分完库分完表之后,发现原来的很多查询都要跨片,性能反而比原来更差了。这就是分片键没选好,或者业务设计的问题。

坑5:以为分库分表是万能的

很多同学觉得分库分表能解决所有性能问题,其实不是。分库分表只是把单表的数据量变小了,但是复杂SQL、慢SQL这些问题还是存在的。优化SQL、加索引这些基础工作还是要做。

总结

MySQL分库分表的核心知识点:

  1. 分库分表是最后的手段,不是第一选择,先试试优化SQL、加索引、读写分离、加缓存
  2. 什么时候需要分:单表千万级以上、单库压力太大、业务真的到那个规模了
  3. 垂直拆分:按业务拆库、按字段拆表
  4. 水平拆分:同一张表按某个规则拆成多张表
  5. 分片键很重要:要数据分布均匀、查询都能带上、尽量不要跨片
  6. 分片算法:取模(均匀但扩容麻烦)、范围(扩容简单但容易有热点)、一致性哈希
  7. 分完之后的问题:分布式事务、跨片查询、跨片联表、全局唯一ID、扩容麻烦
  8. 常用中间件:ShardingSphere、MyCat

记住:分库分表是有代价的,能不分就不分。真的要分的时候,一定要想清楚分片键和分片算法,不然后面会很痛苦。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 08:56 

MySQL explain执行计划详解,分析SQL性能必备

前言

做后端开发的同学肯定都遇到过这种情况:写了一条SQL,查询特别慢,但是不知道为什么慢。是没走索引?还是全表扫描?还是数据量太大?

这时候就需要用MySQL的explain命令了。explain可以告诉你MySQL是怎么执行这条SQL的,走了哪个索引,扫了多少行,是不是全表扫描。搞懂了explain,你就能快速定位SQL慢的原因,然后针对性优化。

很多新手同学不知道explain怎么用,也不知道每一列是什么意思。这篇文章就把explain的知识点从头到尾讲清楚,看完之后你就能看懂执行计划了。

一、explain是什么

explain就是MySQL提供的一个分析工具。你在SELECT语句前面加上EXPLAIN,MySQL就不会真的执行这条SQL,而是告诉你它准备怎么执行这条SQL。

explain能告诉你什么

  • 这条SQL有没有用到索引
  • 扫了多少行数据
  • 有没有全表扫描
  • 用的是哪个索引
  • 表连接的顺序是什么

二、怎么用explain

用法很简单,就在你的SELECT语句前面加上EXPLAIN就行:

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

执行完之后,MySQL会返回一行结果,每一列都代表不同的信息。

三、explain的各列详解

explain返回的结果有很多列,我们重点讲几个常用的。

1. id

这一列表示SELECT的序号。如果有多个SELECT(比如子查询),id越大的越先执行。

2. select_type

这一列表示查询的类型,常见的有:

  • SIMPLE:简单查询,没有子查询或者UNION
  • PRIMARY:最外层的查询
  • SUBQUERY:子查询里的第一个SELECT
  • DERIVED:派生表(FROM子句里的子查询)
  • UNION:UNION里的第二个及以后的SELECT

3. table

这一列表示当前这一行是在查哪张表。

4. type

这一列非常重要,表示MySQL是怎么找到数据的,也就是访问类型。从好到差依次是:

  • system:表里只有一行数据,最好的情况
  • const:通过索引一次就找到了,比如WHERE id=1,id是主键
  • eq_ref:联表查询的时候,用主键或者唯一索引关联,最多匹配一行
  • ref:用普通索引关联,可能匹配多行
  • range:索引范围扫描,比如WHERE id BETWEEN 1 AND 10
  • index:扫描整个索引树,比全表扫描好一点
  • ALL:全表扫描,最差的情况,一定要优化

重点关注: 如果type是ALL或者index,说明这条SQL性能很差,要赶紧优化。

5. possible_keys

这一列表示理论上可能用到的索引。如果是NULL,说明没有用到索引。

6. key

这一列表示实际用到的索引。如果是NULL,说明真的没用到索引。

重点关注: 如果possible_keys有值,但是key是NULL,说明MySQL没有选择用索引,可能是数据量太小,或者索引失效了。

7. key_len

这一列表示用了索引的长度。可以用来判断用了联合索引的前几个字段。

8. ref

这一列表示用了哪个常量或者列来和索引做比较。

9. rows

这一列表示MySQL估计要扫多少行才能找到需要的数据。这个值越大,说明要扫的数据越多,性能越差。

10. Extra

这一列非常重要,包含了很多额外的信息,常见的有:

  • Using where:服务器在存储引擎返回数据之后,又做了一层WHERE过滤
  • Using index:用了覆盖索引,不需要回表,性能很好
  • Using temporary:用了临时表,一般是GROUP BY或者ORDER BY的时候没有用到索引
  • Using filesort:用了文件排序,ORDER BY没有用到索引,性能差
  • Using join buffer:联表的时候用了缓冲,说明联表的字段没有索引
  • Impossible WHERE:WHERE条件永远是false,根本查不到数据

重点关注: 如果Extra里出现了Using temporary或者Using filesort,说明SQL性能有问题,要优化。

四、重点看哪几列

很多同学看explain结果的时候,这么多列不知道重点看什么。其实重点看这几个:

  1. type:有没有全表扫描(ALL)
  2. key:有没有用到索引
  3. rows:扫了多少行
  4. Extra:有没有Using filesort或者Using temporary

只要这几个都没问题,这条SQL的性能一般就不会太差。

五、举个例子

比如我们执行这条SQL:

EXPLAIN SELECT * FROM orders WHERE user_id = 100 AND status = 1;

执行完之后看到:

+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table  | type | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
|  1 | SIMPLE      | orders | ref  | user_id       | user_id | 4       | const |  100 | Using where |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+

我们来分析一下:

  • type是ref:说明用了普通索引,还不错
  • possible_keys是user_id:理论上可以用user_id索引
  • key是user_id:实际用了user_id索引
  • rows是100:估计要扫100行
  • Extra是Using where:说明返回之后又做了一层过滤

从这个结果来看,这条SQL用了索引,性能还可以。但是status字段没有用到索引,如果status过滤性好的话,可以考虑建一个(user_id, status)的联合索引。

六、explain的扩展用法

除了基本的explain,还有几个扩展的用法:

EXPLAIN PARTITIONS

看这条SQL会不会命中分区表:

EXPLAIN PARTITIONS SELECT * FROM orders WHERE ...;

EXPLAIN FORMAT=JSON

输出JSON格式的执行计划,信息更详细:

EXPLAIN FORMAT=JSON SELECT * FROM users WHERE ...;

常见坑

坑1:看到type是const就以为没问题

很多同学看到type是const就以为这条SQL性能很好。但是如果rows很大,或者Extra里有Using filesort,性能还是可能有问题。不能只看type,要综合看。

坑2:以为possible_keys有值就一定会用索引

很多同学看到possible_keys有值,就以为这条SQL一定会用到索引。其实不是,possible_keys只是说理论上可能用到,实际用不用要看key列。如果key是NULL,说明MySQL最后没有用这个索引。

坑3:rows是精确值

很多同学以为rows列是精确的行数,其实不是。rows是MySQL估计出来的行数,不是精确值。但是大致能反映出扫描的数据量大小。

坑4:以为Using filesort就是用了文件排序

很多同学看到Extra里有Using filesort就以为是真的用磁盘文件排序。其实不是,Using filesort只是说明MySQL做了额外的排序操作,不一定是用磁盘,数据量小的时候也可能在内存里排。

坑5:只看单表的explain,不看联表的

很多同学优化SQL的时候,只看单表的执行计划,不看联表的。其实联表的时候,表的连接顺序对性能影响很大。explain会按表的连接顺序一行一行列出来,要按顺序看。

总结

MySQL explain执行计划的核心知识点:

  1. explain的作用:看MySQL是怎么执行SQL的,不用真的执行
  2. type列最重要:从好到差是system > const > eq_ref > ref > range > index > ALL,出现ALL就是全表扫描
  3. key列:实际用到的索引,NULL就是没用到索引
  4. rows列:估计要扫多少行,越大越慢
  5. Extra列:重点关注Using filesort和Using temporary,出现这两个就要优化
  6. 重点看type、key、rows、Extra这四列,其他的了解就行
  7. rows是估计值,不是精确的,但是能反映大致的数据量

explain是分析SQL性能的必备工具,不管是面试还是实际开发,都是必须掌握的。以后遇到SQL慢,先explain一下,看看是哪里出了问题。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 08:54