MySQL开启binlog日志,恢复数据教程
前言
误删了数据,有备份但太老了,怎么办?开启binlog可以恢复到任意时间点的数据。
一、什么是binlog
binlog是MySQL的二进制日志,记录所有写操作(增删改)。
作用:
- 数据恢复
- 主从复制
二、开启binlog
查看是否开启
SHOW VARIABLES LIKE 'log_bin';
| Variable_name | Value |
|---|---|
| log_bin | OFF |
OFF就是没开。
开启方法
改my.cnf:
[mysqld]
# 开启binlog
log_bin = /var/log/mysql/mysql-bin
# 服务ID
server_id = 1
# binlog格式(推荐row)
binlog_format = ROW
# 保留7天
expire_logs_days = 7
# 单个文件最大1G
max_binlog_size = 1G
重启MySQL:
systemctl restart mysqld
验证开启成功
SHOW VARIABLES LIKE 'log_bin';
显示ON就是开了。
三、查看binlog文件
SHOW BINARY LOGS;
输出:
| Log_name | File_size |
|---|---|
| mysql-bin.000001 | 123456 |
| mysql-bin.000002 | 234567 |
四、恢复数据
场景:误删了表,要恢复
第一步:找到要恢复的位置
先看binlog内容:
mysqlbinlog /var/log/mysql/mysql-bin.000002
找到误操作的位置点(position)。
第二步:恢复到备份点
先恢复最近的全量备份:
mysql -u root -p < backup.sql
第三步:恢复binlog
从备份点开始,恢复binlog到误操作前:
mysqlbinlog --start-position=备份点position --stop-position=误操作前position /var/log/mysql/mysql-bin.000002 | mysql -u root -p
按时间恢复
mysqlbinlog --start-datetime="2026-10-08 07:00:00" --stop-datetime="2026-10-08 07:30:00" /var/log/mysql/mysql-bin.000002 | mysql -u root -p
五、常用操作
刷新binlog(新开一个文件)
FLUSH LOGS;
删除旧binlog
-- 删除指定文件之前的
PURGE BINARY LOGS TO 'mysql-bin.000010';
-- 删除7天前的
PURGE BINARY LOGS BEFORE DATE(NOW() - INTERVAL 7 DAY);
查看当前binlog位置
SHOW MASTER STATUS;
六、binlog格式
| 格式 | 说明 | 优缺点 |
|---|---|---|
| STATEMENT | 记录SQL语句 | 日志小,但可能不一致 |
| ROW | 记录每行变化 | 日志大,但精确 |
| MIXED | 混合 | 折中 |
推荐用ROW,最安全。
常见坑
坑1:binlog没开就想恢复
原因: 事前没开启binlog。
解决: 现在就去开,不然出事就晚了。
坑2:binlog格式不对
用STATEMENT格式,恢复后数据不一致。
解决: 用ROW格式。
坑3:binlog占满磁盘
binlog一直写,磁盘满了。
解决: 设 expire_logs_days = 7,自动删旧的。
坑4:恢复顺序错了
先恢复binlog再恢复备份,数据更旧了。
正确顺序: 先恢复全量备份 → 再恢复binlog增量。
坑5:mysqlbinlog命令找不到
原因: MySQL环境变量没配。
解决: 用全路径:
/usr/local/mysql/bin/mysqlbinlog
总结
开启binlog三步:
- 改my.cnf加
log_bin - 重启MySQL
- 验证
SHOW VARIABLES LIKE 'log_bin'
记住: 生产环境一定要开binlog,不然出事就完蛋。
遇到问题加QQ23979811 协助处理
MySQL查看数据库大小,表占用空间
前言
磁盘快满了,不知道哪个库哪个表占空间大。本文教你查MySQL数据库和表的大小。
一、查看所有数据库大小
SELECT
table_schema AS '数据库名',
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS '大小(MB)'
FROM information_schema.tables
GROUP BY table_schema
ORDER BY SUM(data_length + index_length) DESC;
输出:
| 数据库名 | 大小(MB) |
|---|---|
| mydb | 512.34 |
| test | 128.56 |
| mysql | 10.24 |
二、查看某个数据库的所有表大小
SELECT
table_name AS '表名',
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS '大小(MB)',
table_rows AS '行数'
FROM information_schema.tables
WHERE table_schema = 'mydb'
ORDER BY (data_length + index_length) DESC;
把 mydb 换成你的数据库名。
三、查看单个表大小
SELECT
table_name AS '表名',
ROUND((data_length) / 1024 / 1024, 2) AS '数据大小(MB)',
ROUND((index_length) / 1024 / 1024, 2) AS '索引大小(MB)',
table_rows AS '行数'
FROM information_schema.tables
WHERE table_schema = 'mydb' AND table_name = 'users';
四、查看所有表大小(按大小排序)
SELECT
table_schema AS '数据库',
table_name AS '表名',
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS '大小(MB)',
table_rows AS '行数'
FROM information_schema.tables
ORDER BY (data_length + index_length) DESC
LIMIT 20;
只看前20大的表。
五、用命令行查看
查看数据目录
du -sh /var/lib/mysql/
查看某个库的大小
du -sh /var/lib/mysql/mydb/
查看所有库的大小
du -sh /var/lib/mysql/*/
六、常用场景
场景1:磁盘满了,找大表
SELECT
table_schema AS '数据库',
table_name AS '表名',
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS '大小(MB)'
FROM information_schema.tables
ORDER BY (data_length + index_length) DESC
LIMIT 10;
找出最大的10个表。
场景2:表很大,想知道数据和索引各占多少
SELECT
table_name AS '表名',
ROUND(data_length / 1024 / 1024, 2) AS '数据(MB)',
ROUND(index_length / 1024 / 1024, 2) AS '索引(MB)'
FROM information_schema.tables
WHERE table_schema = 'mydb';
场景3:清理无用数据,释放空间
删了很多数据,但磁盘空间没释放。
原因: InnoDB删数据后不会自动释放磁盘空间。
解决: 优化表:
OPTIMIZE TABLE users;
常见坑
坑1:table_rows不准
InnoDB的table_rows是估算值,不是精确值。
原因: InnoDB是多版本的,统计信息不精确。
解决: 要精确行数用 SELECT COUNT(*) FROM 表名;
坑2:删了数据空间没释放
DELETE删了很多行,但文件大小没变。
原因: InnoDB的表空间不会自动收缩。
解决: OPTIMIZE TABLE 表名; 或者重建表。
坑3:临时表占空间
performance_schema等系统库也占空间。
解决: 正常现象,不用管。
坑4:ibdata1文件很大
共享表空间文件很大,但不知道里面是什么。
原因: 所有InnoDB表都存在一个文件里。
解决: 开 innodb_file_per_table,每个表独立文件。
总结
常用查询:
- 查所有库大小:
GROUP BY table_schema - 查某库所有表:
WHERE table_schema='库名' - 查某表大小:
WHERE table_schema='库名' AND table_name='表名'
记住: 磁盘满了先查information_schema.tables找大表。
遇到问题加QQ23979811 协助处理
MySQL删除表、清空表,truncate和delete区别
前言
清空表数据,用DELETE还是TRUNCATE?删表用DROP?很多人搞混,本文讲清楚区别。
三种操作
1. DELETE:删行
-- 删指定行
DELETE FROM users WHERE id = 1;
-- 删全表
DELETE FROM users;
特点:
- 一行一行删,慢
- 可以加WHERE条件
- 可以回滚(在事务里)
- 自增ID不重置,继续从之前的数开始
- 会写binlog
2. TRUNCATE:清空表
TRUNCATE TABLE users;
特点:
- 直接删表重建,快
- 不能加WHERE条件
- 不能回滚
- 自增ID重置,从1开始
- 比DELETE快很多
3. DROP:删表
DROP TABLE users;
特点:
- 表结构+数据全删
- 表都没了
- 最快
对比表
| 对比项 | DELETE | TRUNCATE | DROP |
|---|---|---|---|
| 删什么 | 删行 | 删表重建 | 删表 |
| 速度 | 慢 | 快 | 最快 |
| WHERE | 支持 | 不支持 | 不支持 |
| 回滚 | 可以 | 不可以 | 不可以 |
| 自增ID | 不重置 | 重置 | 表都没了 |
| 表结构 | 保留 | 保留 | 删除 |
| 写binlog | 是 | 是(默认) | 是 |
什么时候用哪个
用DELETE
- 只删部分数据(带WHERE)
- 需要回滚
- 数据量小
用TRUNCATE
- 清空全表数据
- 数据量大,要快
- 不需要回滚
用DROP
- 表不要了
- 表结构也删
常见坑
坑1:DELETE删全表很慢
DELETE FROM users;
100万行删半天。
解决: 清空全表用 TRUNCATE TABLE users;
坑2:TRUNCATE不能回滚
执行TRUNCATE后想后悔,回不去了。
解决: 执行前先备份,或者用DELETE(慢但能回滚)。
坑3:TRUNCATE自增ID重置
清空表后,自增ID从1开始了。
原因: TRUNCATE会重建表。
解决: 如果不想重置自增ID,用DELETE。
坑4:外键约束导致TRUNCATE失败
ERROR 1701 (42000): Cannot truncate a table referenced in a foreign key constraint
原因: 有其他表的外键关联到这个表。
解决: 先关外键检查:
SET FOREIGN_KEY_CHECKS = 0;
TRUNCATE TABLE users;
SET FOREIGN_KEY_CHECKS = 1;
坑5:DELETE不写WHERE删全表
手滑了,没写WHERE,把全表删了。
解决: 执行DELETE前,先SELECT一下看条件对不对。
性能对比
| 操作 | 10万行 | 100万行 |
|---|---|---|
| DELETE全表 | 几十秒 | 几分钟 |
| TRUNCATE | 0.01秒 | 0.01秒 |
| DROP | 0.01秒 | 0.01秒 |
TRUNCATE和DROP都是秒级,DELETE要逐行删。
总结
一句话总结:
- 删部分数据 → DELETE
- 清空全表 → TRUNCATE
- 表不要了 → DROP
记住: 清空表别用DELETE,太慢了。
遇到问题加QQ23979811 协助处理
MySQL优化查询速度,explain分析执行计划
前言
SQL查得慢,不知道为什么慢。用EXPLAIN看MySQL怎么执行这条SQL,就能找到问题在哪。
一、EXPLAIN怎么用
EXPLAIN SELECT * FROM users WHERE name = '张三';
输出很多列,重点看这几个:id、select_type、table、type、key、rows、Extra。
二、重点列详解
1. type(访问类型)
最重要的一列,看SQL查得快不快。
从好到差:
| type | 说明 | 速度 |
|---|---|---|
| system | 表只有一行 | 最快 |
| const | 主键或唯一索引查一次 | 很快 |
| eq_ref | 联表时主键关联 | 很快 |
| ref | 非唯一索引查 | 快 |
| range | 索引范围扫描 | 中 |
| index | 扫整个索引树 | 慢 |
| ALL | 全表扫描 | 最慢 |
目标: 至少达到range,最好是ref以上。
如果type是ALL,就是全表扫描,必须优化。
2. key(实际用到的索引)
key: NULL
说明没用索引。
key: idx_name
说明用到了idx_name索引。
3. rows(扫描行数)
MySQL预估要扫描多少行。
rows越大,越慢。
4. Extra(额外信息)
常见的:
- Using where:WHERE过滤
- Using index:覆盖索引,不用回表,好
- Using filesort:需要额外排序,不好
- Using temporary:用了临时表,不好
三、常见问题
问题1:type=ALL,全表扫描
原因: 没加索引,或者索引失效。
解决: 给WHERE条件的字段加索引。
问题2:key=NULL,没用索引
原因:
- 没建索引
- 索引失效了(函数、类型转换、前导模糊查询等)
解决: 建正确的索引,避免索引失效写法。
问题3:Using filesort
原因: ORDER BY的字段没走索引。
解决: 给ORDER BY的字段加索引。
问题4:Using temporary
原因: GROUP BY或DISTINCT用了临时表。
解决: 给GROUP BY的字段加索引。
四、实战案例
慢SQL
SELECT * FROM orders WHERE user_id = 100 ORDER BY create_time DESC LIMIT 10;
EXPLAIN结果:
| id | select_type | table | type | key | rows | Extra |
|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | ALL | NULL | 100000 | Using where; Using filesort |
问题:
- type=ALL,全表扫描
- key=NULL,没用索引
- rows=100000,扫了10万行
- Using filesort,还要额外排序
优化
加联合索引:
ALTER TABLE orders ADD INDEX idx_userid_createtime (user_id, create_time);
再EXPLAIN:
| id | select_type | table | type | key | rows | Extra |
|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | ref | idx_userid_createtime | 10 | Using where |
优化效果:
- type变成ref
- 用到了索引
- rows从10万变成10
- 没有filesort了
五、常用优化技巧
1. 避免SELECT *
只查需要的列,能用上覆盖索引。
2. WHERE条件加索引
经常查询的字段加索引。
3. 联合索引最左前缀
联合索引 (a,b,c),查询条件要从最左列开始。
4. 避免索引失效
- 不要对索引字段用函数
- 不要隐式类型转换
- 不要前导模糊查询
like '%xxx'
总结
EXPLAIN看这三点:
- type:是不是全表扫描
- key:有没有用到索引
- rows:扫描了多少行
记住: 慢SQL先EXPLAIN,找到问题再优化。
遇到问题加QQ23979811 协助处理
MySQL远程连接失败,权限和防火墙问题
前言
本地能连MySQL,远程连接就报错。常见原因:用户没授权、防火墙没开、MySQL只监听本地。
报错长什么样
ERROR 2003 (HY000): Can't connect to MySQL server on 'xxx.xxx.xxx.xxx'
或者:
ERROR 1130 (HY000): Host 'xxx.xxx.xxx.xxx' is not allowed to connect to this MySQL server
原因一:用户没授权远程登录
排查
SELECT user, host FROM mysql.user;
看root用户的host是 localhost 还是 %。
如果是 localhost,就只能本地登录。
解决
授权远程登录:
-- 方法1:授权root所有IP
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '你的密码';
-- 方法2:只允许某个IP
GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.168.1.100' IDENTIFIED BY '你的密码';
-- 刷新
FLUSH PRIVILEGES;
原因二:防火墙没开3306端口
排查
在本地服务器上测试:
netstat -tlnp | grep 3306
看MySQL监听的是 127.0.0.1:3306 还是 0.0.0.0:3306。
如果是 127.0.0.1:3306,说明只监听本地。
解决防火墙
CentOS7 firewalld
# 开3306端口
firewall-cmd --permanent --add-port=3306/tcp
# 重载
firewall-cmd --reload
Ubuntu ufw
ufw allow 3306/tcp
原因三:MySQL只监听本地
排查
netstat -tlnp | grep 3306
如果显示 127.0.0.1:3306,就是只监听本地。
解决
改my.cnf:
[mysqld]
bind-address = 0.0.0.0
注释掉 skip-networking(如果有的话)。
重启MySQL:
systemctl restart mysqld
原因四:云服务器安全组
阿里云、腾讯云等云服务器,除了系统防火墙,还有安全组。
解决
在云服务器控制台,找到安全组,放行3306端口。
原因五:MySQL版本问题
MySQL 8.0默认用 caching_sha2_password,老客户端不支持。
解决
创建用户时指定老的加密方式:
CREATE USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '你的密码';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%';
FLUSH PRIVILEGES;
排查步骤
第一步:本地能连吗
先在服务器上本地连一下:
mysql -u root -p
本地能连,说明MySQL本身没问题。
第二步:看MySQL监听地址
netstat -tlnp | grep 3306
第三步:防火墙开了吗
firewall-cmd --list-ports
看3306在不在。
第四步:远程telnet测试
在本地电脑上测试:
telnet 服务器IP 3306
如果连不上,就是网络/防火墙问题。
常见坑
坑1:改了授权忘了刷新
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%';
改完就退出,结果还是连不上。
解决: 执行 FLUSH PRIVILEGES;
坑2:bind-address没改
授权了,防火墙也开了,还是连不上。
原因: MySQL只监听本地。
解决: 改 bind-address = 0.0.0.0
坑3:安全组没开
系统防火墙开了,云服务器安全组没开。
解决: 去云控制台放行3306端口。
坑4:密码不对
报错不是"连接不上",而是"Access denied"。
原因: 密码错了。
解决: 确认密码,注意大小写。
总结
远程连接失败排查顺序:
- 先授权远程用户
- 再开防火墙3306
- 然后改bind-address
- 最后查云安全组
记住: 90%的情况都是没授权或者防火墙没开。
遇到问题加QQ23979811 协助处理
MySQL修改root密码,多种方法汇总
前言
知道旧密码,想改个新密码,有好几种方法。本文汇总常用的改密码方法。
方法一:ALTER USER(推荐)
登录MySQL后:
ALTER USER 'root'@'localhost' IDENTIFIED BY '你的新密码';
然后刷新:
FLUSH PRIVILEGES;
优点: 官方推荐,语法标准,MySQL 5.7/8.0都支持。
方法二:SET PASSWORD
SET PASSWORD FOR 'root'@'localhost' = PASSWORD('你的新密码');
刷新:
FLUSH PRIVILEGES;
注意: MySQL 8.0以后 PASSWORD() 函数被删了,不能用了。
方法三:UPDATE直接改
UPDATE mysql.user SET authentication_string=PASSWORD('你的新密码')
WHERE User='root' AND Host='localhost';
刷新:
FLUSH PRIVILEGES;
注意: 同样,MySQL 8.0不能用PASSWORD()函数。
方法四:命令行修改
不用登录MySQL,直接命令行:
mysqladmin -u root -p password "你的新密码"
输入旧密码后,就改成新密码了。
MySQL 8.0改密码
MySQL 8.0改密码语法有变化:
方法一:ALTER USER(推荐)
ALTER USER 'root'@'localhost' IDENTIFIED BY '你的新密码';
方法二:SET PASSWORD
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的新密码';
不同Host的root用户
root可能有多个:
SELECT user, host FROM mysql.user WHERE user='root';
输出可能是:
| user | host |
|---|---|
| root | localhost |
| root | 127.0.0.1 |
| root | % |
每个host的root密码是独立的,要分别改。
ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码';
ALTER USER 'root'@'127.0.0.1' IDENTIFIED BY '新密码';
ALTER USER 'root'@'%' IDENTIFIED BY '新密码';
常见坑
坑1:改了密码还是旧密码能登
原因: 没执行 FLUSH PRIVILEGES;
解决: 改完密码一定要刷新权限。
坑2:MySQL 8.0报语法错误
ERROR 1064 (42000): You have an error in your SQL syntax
原因: 用了 PASSWORD() 函数,MySQL 8.0删了。
解决: 用 ALTER USER 语句。
坑3:改了密码远程登不上
本地能登,远程连不上。
原因: 只改了localhost的密码,远程用户的密码没改。
解决: 把所有host的root密码都改了。
坑4:密码太简单报错
ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
原因: MySQL密码强度校验太严。
解决: 用复杂点的密码(大小写+数字+特殊字符),或者关掉密码校验:
SET GLOBAL validate_password_policy=LOW;
坑5:改完密码phpStudy/phpMyAdmin连不上
原因: 站点配置文件里的密码还是旧的。
解决: 改项目配置文件里的数据库密码。
总结
知道旧密码改密码:
| 方法 | 适用版本 | 推荐度 |
|---|---|---|
ALTER USER |
5.7/8.0 | 推荐 |
SET PASSWORD |
5.7及以前 | 一般 |
UPDATE mysql.user |
5.7及以前 | 不推荐 |
mysqladmin |
所有版本 | 一般 |
记住: 改完密码一定要 FLUSH PRIVILEGES;
遇到问题加QQ23979811 协助处理
MySQL查看当前连接数,show processlist
前言
MySQL连接数太多,报"Too many connections",怎么看当前有多少连接?哪些连接在跑什么SQL?用show processlist。
一、查看当前连接
基本命令
SHOW PROCESSLIST;
输出:
| Id | User | Host | db | Command | Time | State | Info |
|---|---|---|---|---|---|---|---|
| 123 | root | localhost:54321 | mydb | Query | 0 | starting | SHOW PROCESSLIST |
| 124 | www | 192.168.1.100:1234 | mydb | Sleep | 30 | NULL |
每列含义:
- Id:连接ID,kill的时候用
- User:用户名
- Host:客户端IP和端口
- db:当前数据库
- Command:当前命令(Query/Sleep/Connect)
- Time:当前状态持续时间(秒)
- State:状态
- Info:正在执行的SQL
显示完整SQL
SHOW FULL PROCESSLIST;
默认Info列只显示前100个字符,加FULL显示完整SQL。
二、查看连接数
当前连接数
SHOW STATUS LIKE 'Threads_connected';
最大连接数
SHOW VARIABLES LIKE 'max_connections';
历史最大连接数
SHOW STATUS LIKE 'Max_used_connections';
三、杀掉连接
杀掉某个连接
KILL 连接ID;
比如杀掉ID为123的连接:
KILL 123;
杀掉所有Sleep连接
-- 先查出来
SELECT CONCAT('KILL ', id, ';') FROM information_schema.processlist
WHERE user = 'www' AND command = 'Sleep';
把结果复制出来执行,就能批量杀掉。
四、常用查询
按用户统计连接数
SELECT user, COUNT(*) FROM information_schema.processlist
GROUP BY user;
按IP统计连接数
SELECT SUBSTRING_INDEX(host, ':', 1) AS ip, COUNT(*)
FROM information_schema.processlist
GROUP BY ip;
查看执行超过10秒的SQL
SELECT * FROM information_schema.processlist
WHERE time > 10;
五、调整最大连接数
临时调整
SET GLOBAL max_connections = 1000;
永久调整
改my.cnf:
[mysqld]
max_connections = 1000
重启MySQL生效。
常见坑
坑1:Sleep连接太多
大量Sleep连接占着连接数,导致新连接进不来。
原因: 程序没关连接,连接池没配好。
解决:
- 调小
wait_timeout,让空闲连接自动断开 - 检查程序连接池配置
SET GLOBAL wait_timeout = 60;
坑2:看不到其他用户的连接
普通用户只能看到自己的连接。
解决: 用root用户登录,或者PROCESS权限。
坑3:kill了连接还在
kill了连接,但连接还在列表里。
原因: 连接正在回滚,需要等一会。
解决: 等几秒再看,或者重启MySQL。
坑4:连接数突然暴涨
突然一堆连接进来,数据库卡死。
原因: 可能是程序bug,或者被攻击了。
排查: 按IP统计,看哪个IP连接最多。
总结
常用命令:
| 命令 | 作用 |
|---|---|
SHOW PROCESSLIST |
看当前连接 |
SHOW FULL PROCESSLIST |
看完整SQL |
SHOW STATUS LIKE 'Threads_connected' |
看当前连接数 |
KILL 连接ID |
杀掉连接 |
记住: 连接数满了先看processlist,找出是谁占着连接。
遇到问题加QQ23979811 协助处理
MySQL报错"Table 'xxx' doesn't exist"解决
前言
查询数据库时报错:Table 'xxx' doesn't exist,明明表就在那,为什么说不存在?本文排查常见原因。
报错长什么样
SELECT * FROM users;
报错:
ERROR 1146 (42S02): Table 'mydb.users' doesn't exist
常见原因
原因1:数据库选错了
当前在别的库里,所以找不到表。
排查: 先看当前在哪个库:
SELECT DATABASE();
如果显示的不是你要的库,就切换:
USE mydb;
再查一次。
原因2:表名写错了
拼错了,或者大小写不对。
排查: 看看库里到底有哪些表:
SHOW TABLES;
确认表名是不是和你写的一模一样。
注意: Linux下MySQL表名区分大小写,Users 和 users 是两个表。
原因3:表真的被删了
表确实不存在了,可能被误删了。
排查: 用SHOW TABLES确认。如果真没有,从备份恢复。
原因4:字符集导致的问题
表名里有特殊字符,编码不对导致识别不出来。
排查: 用 SHOW TABLES LIKE 'xxx%'; 模糊匹配一下。
原因5:MySQL升级后表损坏
升级MySQL后,某些表报错不存在,但文件还在。
原因: 表引擎损坏,或者frm文件丢了。
排查: 看数据目录下有没有对应文件:
ls /var/lib/mysql/mydb/
原因6:用户没有权限
虽然报错是"不存在",但有时候权限不够也会这么报。
排查: 用root用户登录试试,如果root能看到,就是权限问题。
排查步骤
第一步:确认当前数据库
SELECT DATABASE();
第二步:查看所有表
SHOW TABLES;
第三步:模糊搜索表名
SHOW TABLES LIKE '%user%';
第四步:确认表文件是否存在
ls /var/lib/mysql/你的库名/ | grep 表名
常见坑
坑1:Linux下表名区分大小写
Windows开发没问题,上传到Linux服务器就报错。
解决: 表名统一用小写,或者在my.cnf加:
lower_case_table_names = 1
注意: 这个参数只能在初始化MySQL前设置,已经用了再改会出问题。
坑2:分表了忘了加表名
用了分表,代码里查的是 users,实际表名是 users_0、users_1。
解决: 确认分表规则,代码里拼对表名。
坑3:视图报错
查视图时报"表不存在",可能是视图依赖的表被删了。
排查: 看视图定义:
SHOW CREATE VIEW 视图名;
坑4:临时表
会话结束临时表就没了,新开一个连接查就报错。
解决: 临时表只在当前会话有效,别跨连接用。
坑5:误删了库
整个库都没了,当然找不到表。
排查:
SHOW DATABASES;
看你的库还在不在。
总结
排查顺序:
- 先看当前在哪个库
- 再SHOW TABLES看表在不在
- 确认表名大小写对不对
- 最后看数据目录下文件在不在
记住: 90%的情况都是库选错了或者表名写错了。
遇到问题加QQ23979811 协助处理
MySQL查看表结构,desc和show create table
前言
接手别人的项目,不知道表有哪些字段、什么类型、有没有索引,全靠DESC和SHOW CREATE TABLE。
一、查看表有哪些字段
方法1:DESC(最常用)
DESC users;
或者:
DESCRIBE users;
输出:
| Field | Type | Null | Key | Default | Extra |
|---|---|---|---|---|---|
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(50) | YES | NULL | ||
| age | int(11) | YES | NULL |
每列含义:
- Field:字段名
- Type:字段类型
- Null:能不能为NULL
- Key:有没有索引
- Default:默认值
- Extra:额外属性(自增等)
方法2:SHOW COLUMNS
SHOW COLUMNS FROM users;
和DESC效果一样,写法不同而已。
二、查看建表语句
SHOW CREATE TABLE
SHOW CREATE TABLE users;
输出:
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
这个命令能看到:
- 完整的建表SQL
- 所有字段和类型
- 主键、索引
- 表引擎、字符集
- 默认值、自增等
三、查看表状态
SHOW TABLE STATUS LIKE 'users'\G
能看到:
- 表行数
- 数据大小
- 索引大小
- 更新时间
- 引擎类型
- 字符集
四、查看表的所有索引
SHOW INDEX FROM users;
或者:
SHOW KEYS FROM users;
五、查看数据库里所有表
SHOW TABLES;
六、实用组合
查看所有表的表名
SHOW TABLES;
查看某库的表
USE 数据库名;
SHOW TABLES;
或者:
SHOW TABLES FROM 数据库名;
七、从information_schema查
更灵活的方式,从系统表查:
查表的字段信息
SELECT * FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'mydb'
AND TABLE_NAME = 'users';
查表的索引信息
SELECT * FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = 'mydb'
AND TABLE_NAME = 'users';
常见坑
坑1:DESC看不到索引详细信息
DESC只告诉你Key列是啥,但不知道索引名。
解决: 用 SHOW INDEX FROM 表名;
坑2:SHOW CREATE TABLE输出太长
直接看会换行很乱。
解决: 加 \G 按行显示:
SHOW CREATE TABLE users\G
坑3:不知道当前在哪个库
查的时候报错说表不存在。
解决: 先 SELECT DATABASE(); 看当前库。
坑4:表名大小写问题
Linux下MySQL表名区分大小写,Windows不区分。
解决: 表名小写,开发时注意。
总结
常用命令总结:
| 命令 | 作用 |
|---|---|
DESC 表名 |
看字段列表 |
SHOW CREATE TABLE 表名 |
看完整建表语句 |
SHOW INDEX FROM 表名 |
看索引 |
SHOW TABLES |
看所有表 |
SHOW TABLE STATUS |
看表状态 |
记住: 看表结构首选DESC,要看完整建表语句用SHOW CREATE TABLE。
遇到问题加QQ23979811 协助处理
MySQL备份数据库,mysqldump定时备份
前言
数据库不备份,出事就完蛋。本文讲用mysqldump备份MySQL,再配crontab定时自动备份。
一、mysqldump基本用法
备份单个数据库
mysqldump -u root -p mydb > /backup/mydb.sql
输入密码后,就把mydb库备份到mydb.sql了。
备份多个数据库
mysqldump -u root -p --databases db1 db2 > /backup/mydbs.sql
备份所有数据库
mysqldump -u root -p --all-databases > /backup/all.sql
备份单个表
mysqldump -u root -p mydb users > /backup/users.sql
二、常用参数
| 参数 | 说明 |
|---|---|
--single-transaction |
InnoDB一致性备份,不锁表 |
--routines |
备份存储过程和函数 |
--triggers |
备份触发器 |
--events |
备份事件调度器 |
--master-data=2 |
记录binlog位置,用于主从 |
--default-character-set=utf8mb4 |
指定字符集 |
推荐完整命令
mysqldump -u root -p \
--single-transaction \
--routines \
--triggers \
--events \
--default-character-set=utf8mb4 \
mydb > /backup/mydb_$(date +%Y%m%d).sql
三、压缩备份
备份完太大,直接压缩:
mysqldump -u root -p mydb | gzip > /backup/mydb.sql.gz
恢复的时候:
gunzip < /backup/mydb.sql.gz | mysql -u root -p mydb
四、定时备份脚本
创建备份脚本:
vi /usr/local/bin/mysql_backup.sh
内容:
#!/bin/bash
# 备份目录
BACKUP_DIR=/backup/mysql
# 数据库配置
DB_USER=root
DB_PASS=你的密码
DB_NAME=mydb
# 创建备份目录
mkdir -p $BACKUP_DIR
# 备份文件名(带日期)
DATE=$(date +%Y%m%d_%H%M%S)
FILE=$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz
# 执行备份
mysqldump -u$DB_USER -p$DB_PASS \
--single-transaction \
--routines \
--triggers \
--default-character-set=utf8mb4 \
$DB_NAME | gzip > $FILE
# 删除7天前的备份
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete
echo "备份完成:$FILE"
加执行权限:
chmod +x /usr/local/bin/mysql_backup.sh
五、配置crontab定时任务
编辑crontab:
crontab -e
加上:
# 每天凌晨2点备份
0 2 * * * /usr/local/bin/mysql_backup.sh >> /var/log/mysql_backup.log 2>&1
六、恢复备份
恢复普通备份
mysql -u root -p mydb < /backup/mydb.sql
恢复压缩备份
gunzip < /backup/mydb.sql.gz | mysql -u root -p mydb
常见坑
坑1:密码写在命令行里不安全
mysqldump -u root -p123456 mydb > xxx.sql
这样密码会出现在进程列表里。
安全做法: 用配置文件存密码。
# /root/.my.cnf
[mysqldump]
user=root
password=你的密码
权限设600:
chmod 600 /root/.my.cnf
以后备份就不用输密码了:
mysqldump mydb > xxx.sql
坑2:备份时锁表
大库备份的时候,业务卡住了。
原因: 没加 --single-transaction。
解决: InnoDB库一定要加这个参数。
坑3:备份文件是空的
备份完一看,文件0字节。
原因: 密码错了,或者数据库名错了。
解决: 先手动执行一次,看报错。
坑4:备份没包含存储过程
备份后恢复,发现存储过程没了。
原因: mysqldump默认不备份存储过程。
解决: 加 --routines 参数。
总结
三步搞定自动备份:
- 写备份脚本
- 配crontab定时执行
- 定期清理旧备份
记住: 备份了不算数,能恢复才算数。
遇到问题加QQ23979811 协助处理
MySQL创建数据库、创建用户、授权完整命令
前言
新装MySQL,要创建数据库、创建用户、授权,一套操作下来经常懵。本文整理完整命令,照着抄就行。
第一步:登录MySQL
mysql -u root -p
输入root密码登录。
第二步:创建数据库
CREATE DATABASE mydb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
说明:
mydb是数据库名,自己改utf8mb4支持emoji,推荐用utf8mb4_general_ci是排序规则
第三步:创建用户
MySQL 5.7及以前
CREATE USER 'myuser'@'localhost' IDENTIFIED BY '你的密码';
MySQL 8.0
CREATE USER 'myuser'@'localhost' IDENTIFIED BY '你的密码';
允许远程登录
CREATE USER 'myuser'@'%' IDENTIFIED BY '你的密码';
% 表示所有IP都能连,生产环境建议指定具体IP。
第四步:授权
授权所有权限(常用)
GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'localhost';
说明:
mydb.*表示mydb库下所有表- 要授权所有库就写
*.*
只授权查询权限
GRANT SELECT ON mydb.* TO 'myuser'@'localhost';
授权指定权限
GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO 'myuser'@'localhost';
授权远程用户
GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'%';
第五步:刷新权限
FLUSH PRIVILEGES;
这步必须做! 不然授权不生效。
查看和撤销权限
查看用户权限
SHOW GRANTS FOR 'myuser'@'localhost';
撤销权限
REVOKE ALL PRIVILEGES ON mydb.* FROM 'myuser'@'localhost';
删除用户
DROP USER 'myuser'@'localhost';
常见坑
坑1:创建了用户但没授权
用户登录上了,但什么都做不了。
原因: 只创建了用户,没授权。
解决: 创建用户后一定要 GRANT。
坑2:改了密码不生效
改完密码还是旧密码能登。
原因: 没 FLUSH PRIVILEGES。
解决: 执行 FLUSH PRIVILEGES;
坑3:远程连接不上
本地能登,远程连不上。
原因: 用户只允许localhost登录。
解决: 创建 '用户'@'%' 的远程用户,或者授权。
坑4:密码太简单报错
创建用户时报密码不符合策略。
原因: MySQL 5.6以后有密码强度校验。
解决: 用复杂点的密码,或者关掉密码校验。
坑5:MySQL 8.0密码加密方式变了
老客户端连不上MySQL 8.0。
原因: MySQL 8.0默认用caching_sha2_password,老客户端不支持。
解决: 创建用户时指定老的加密方式:
CREATE USER 'myuser'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';
完整一键脚本
-- 登录MySQL后执行
CREATE DATABASE mydb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'myuser'@'localhost' IDENTIFIED BY '你的密码';
GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'localhost';
FLUSH PRIVILEGES;
总结
三步走:
- 建库
CREATE DATABASE - 建用户
CREATE USER - 授权
GRANT+FLUSH PRIVILEGES
记住: 授权完一定要刷新!
遇到问题加QQ23979811 协助处理
MySQL大表分页越查越慢,优化方案
前言
大表分页查询,第一页很快,第十页就慢,第一百页直接卡死。这是MySQL分页的经典问题,本文讲怎么优化。
为什么越往后越慢
普通分页:
SELECT * FROM orders ORDER BY id LIMIT 100000, 10;
MySQL 会先扫描前100010条,然后丢掉前100000条,只返回最后10条。
越往后翻,扫描的行越多,当然越慢。
优化方案一:子查询优化
先查出起始位置的id,再用id做条件:
SELECT * FROM orders
WHERE id >= (SELECT id FROM orders ORDER BY id LIMIT 100000, 1)
LIMIT 10;
原理: 子查询走索引,只查id列,很快。然后外层用id直接定位,不用扫那么多行。
优化方案二:游标分页(推荐)
记住上一页最后一条的id,下次从这个id之后查:
-- 第一页
SELECT * FROM orders ORDER BY id LIMIT 10;
-- 第二页(假设上一页最后id是100)
SELECT * FROM orders WHERE id > 100 ORDER BY id LIMIT 10;
-- 第三页(假设上一页最后id是110)
SELECT * FROM orders WHERE id > 110 ORDER BY id LIMIT 10;
优点: 不管翻到第几页,都是直接定位,速度一样快。
缺点: 不能跳页,只能上一页下一页。
优化方案三:覆盖索引
只查需要的列,不要 SELECT *:
SELECT id, title, create_time FROM orders ORDER BY id LIMIT 100000, 10;
如果查询的列都在索引里,MySQL直接从索引返回数据,不用回表,快很多。
优化方案四:业务上限制深分页
根本解决: 不让用户翻到那么深。
- 搜索结果只显示前100页
- 用条件过滤,不要全表翻
- 用搜索引擎(ES)代替深分页
常见坑
坑1:用了offset就一定慢
其实offset小的时候(比如前10页),差别不大。
结论: 数据量不大的时候,普通分页就行,不用过度优化。
坑2:游标分页不能用排序
游标分页只能按主键或唯一索引排序。
解决: 如果按其他字段排序,先查出对应的id列表,再用id定位。
坑3:子查询效率也不高
子查询在MySQL 5.6以前有优化器问题。
解决: MySQL 5.6以后子查询优化得很好了,放心用。
坑4:没加where条件
深分页没加where,全表扫描。
解决: 尽量加上时间范围、状态等过滤条件。
性能对比
| 方案 | 第1页 | 第100页 | 第1000页 |
|---|---|---|---|
| 普通分页 | 快 | 慢 | 很慢 |
| 子查询优化 | 快 | 中 | 慢 |
| 游标分页 | 快 | 快 | 快 |
| 搜索引擎 | 快 | 快 | 快 |
总结
大表分页优化三板斧:
- 小数据量:普通分页就行
- 中等数据量:子查询优化
- 大数据量:游标分页 + 搜索引擎
记住: 深分页不是MySQL擅长的事,数据量大了就上ES。
遇到问题加QQ23979811 协助处理
MySQL数据库导入sql文件报错解决
前言
导入SQL文件到MySQL,经常报各种错。本文总结最常见的导入报错和解决方法。
常用导入命令
mysql -u root -p 数据库名 < xxx.sql
或者登录MySQL后:
source /路径/xxx.sql;
常见报错及解决
报错1:Unknown database
ERROR 1049 (42000): Unknown database 'xxx'
原因: 数据库不存在。
解决: 先创建数据库:
CREATE DATABASE xxx DEFAULT CHARSET utf8mb4;
然后再导入。
报错2:Access denied
ERROR 1044 (42000): Access denied for user
原因: 用户没权限。
解决: 用root用户导入,或者给用户授权。
报错3:File not found
ERROR 1045 (28000): Access denied
或者文件找不到。
原因: 文件路径不对,或者文件权限不对。
解决:
- 确认文件存在:
ls -l /路径/xxx.sql - 确认文件可读:
chmod 644 /路径/xxx.sql - 用绝对路径,别用相对路径
报错4:Table already exists
ERROR 1050 (42S01): Table 'xxx' already exists
原因: 表已经存在了。
解决: 两种方法:
- 先删表再导入:
DROP TABLE xxx; - SQL文件里加
DROP TABLE IF EXISTS xxx;
报错5:Duplicate entry
ERROR 1062 (23000): Duplicate entry 'xxx' for key
原因: 主键重复。
解决:
- 先清空表再导入
- 或者用
INSERT IGNORE跳过重复
报错6:字符集乱码
导入后中文变成乱码。
原因: 字符集不对。
解决: 导入前设置字符集:
SET NAMES utf8mb4;
source /路径/xxx.sql;
或者命令行指定:
mysql -u root -p --default-character-set=utf8mb4 数据库名 < xxx.sql
报错7:max_allowed_packet太小
ERROR 2006 (HY000): MySQL server has gone away
原因: SQL文件太大,MySQL接收不了。
解决: 修改my.cnf,加大max_allowed_packet:
[mysqld]
max_allowed_packet = 64M
重启MySQL生效。
导入大文件技巧
方法1:分卷压缩
把大SQL文件拆成小的:
split -b 10M xxx.sql small_
然后逐个导入。
方法2:跳过外键检查
导入时临时关闭外键检查,更快:
SET FOREIGN_KEY_CHECKS = 0;
source /路径/xxx.sql;
SET FOREIGN_KEY_CHECKS = 1;
常见坑
坑1:用错用户名密码
导入时一直提示密码错误。
解决: 确认用户名密码,或者用 mysql -u root -p 交互式输入密码。
坑2:SQL文件不是UTF-8编码
导入后中文乱码。
解决: 用 file 命令查看文件编码:
file -i xxx.sql
坑3:没选对数据库
导入后数据不知道导到哪去了。
解决: 命令行一定要指定数据库名,或者先 USE 数据库名;
总结
导入SQL文件三步走:
- 先确认数据库存在
- 用正确的字符集导入
- 大文件就调大max_allowed_packet
记住: 报错信息一定要看清楚,别瞎试。
遇到问题加QQ23979811 协助处理
MySQL忘记root密码重置(Linux环境)
前言
忘记MySQL root密码是常见问题,不用慌,本文教你一步步重置。
重置步骤
第一步:停止MySQL服务
systemctl stop mysqld
第二步:以跳过权限模式启动
mysqld_safe --skip-grant-tables &
或者用这个方式:
systemctl set-environment MYSQLD_OPTS="--skip-grant-tables"
systemctl start mysqld
第三步:无密码登录MySQL
mysql -u root
现在不需要密码就能直接进了。
第四步:修改密码
-- 刷新权限
FLUSH PRIVILEGES;
-- 修改密码(MySQL 5.7)
ALTER USER 'root'@'localhost' IDENTIFIED BY '你的新密码';
-- 如果上面不行,用这个
UPDATE mysql.user SET authentication_string=PASSWORD('你的新密码') WHERE User='root' AND Host='localhost';
-- 刷新权限
FLUSH PRIVILEGES;
第五步:退出,正常重启MySQL
exit
systemctl stop mysqld
systemctl start mysqld
第六步:用新密码登录测试
mysql -u root -p
输入新密码,能进去就成功了。
常见坑
坑1:MySQL 8.0和5.7语法不一样
MySQL 8.0 不能用 PASSWORD() 函数了。
解决: 用 ALTER USER 语句。
坑2:skip-grant-tables没生效
启动后还是要密码。
原因: 可能MySQL已经在跑了,新启动的没生效。
解决: 先彻底停掉MySQL:
systemctl stop mysqld
pkill -9 mysqld
再重新启动。
坑3:改完密码忘记FLUSH PRIVILEGES
改完密码直接退出,结果还是不能用新密码登录。
解决: 改完密码一定要执行 FLUSH PRIVILEGES;
坑4:远程登录还是不行
本地改完密码,远程连接还是连不上。
原因: root用户只允许本地登录。
解决: 需要单独授权远程登录:
CREATE USER 'root'@'%' IDENTIFIED BY '密码';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
总结
六步重置root密码:
- 停MySQL
- 跳过权限启动
- 无密码登录
- 改密码
- 正常重启
- 测试登录
记住: 改完密码一定要 FLUSH PRIVILEGES;
遇到问题加QQ23979811 协助处理
MySQL 事务隔离级别
MySQL 事务隔离级别
事务隔离是数据库的重要概念,搞懂4种级别很有必要。
1. 事务的ACID
- 原子性(Atomicity): 要么全成功,要么全失败
- 一致性(Consistency): 事务前后数据一致
- 隔离性(Isolation): 多个事务互不影响
- 持久性(Durability): 提交后永久保存
2. 4种隔离级别
1)读未提交(Read Uncommitted)
- 能读到未提交的数据
- 有脏读、不可重复读、幻读问题
- 性能最好
2)读提交(Read Committed)
- 只能读到已提交的数据
- 解决脏读
- 有不可重复读、幻读问题
- Oracle默认级别
3)可重复读(Repeatable Read)
- 同一个事务里读多次结果一样
- 解决脏读、不可重复读
- 有幻读问题
- MySQL默认级别
4)串行化(Serializable)
- 事务排队执行
- 解决所有问题
- 性能最差
3. 三种问题
脏读:
- 读到另一个事务未提交的数据
- 对方回滚了,你读的就是脏数据
不可重复读:
- 同一个事务里,两次读同一行结果不一样
- 中间被其他事务修改了
幻读:
- 同一个事务里,两次查询行数不一样
- 中间被其他事务插入了
4. 查看和设置
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
5. 实际使用
- 一般用默认的可重复读就够了
- 对一致性要求高的用串行化
- 不要随便改隔离级别
总结
MySQL事务隔离级别要点:
- 读未提交:性能最好,问题最多
- 读提交:解决脏读
- 可重复读:MySQL默认,解决不可重复读
- 串行化:最安全,性能最差
- 理解脏读、不可重复读、幻读
做数据库开发,事务隔离一定要懂。
Redis 常用数据类型
Redis 常用数据类型
Redis 不只是简单的 key-value,有5种常用数据类型,每种适合不同场景。
1. String(字符串)
最基础的类型,存字符串、数字都可以。
# 设置
set name "张三"
# 获取
get name
# 自增
incr age
# 设置过期时间
set token "abc" ex 3600
适用场景: 缓存、计数器、Session。
2. Hash(哈希)
存对象,比如用户信息。
# 设置单个字段
hset user:1 name "张三"
# 获取单个字段
hget user:1 name
# 设置多个字段
hmset user:1 name "张三" age 20 city "北京"
# 获取所有字段
hgetall user:1
适用场景: 存储对象(用户信息、商品信息)。
3. List(列表)
有序列表,可以做消息队列。
# 左侧添加
lpush list "a" "b" "c"
# 右侧添加
rpush list "d"
# 左侧弹出
lpop list
# 查看范围
lrange list 0 -1
适用场景: 消息队列、最新列表。
4. Set(集合)
无序,不重复,做去重。
# 添加
sadd set "a" "b" "c"
# 查看所有
smembers set
# 判断是否存在
sismember set "a"
# 交集
sinter set1 set2
适用场景: 去重、共同好友、标签。
5. ZSet(有序集合)
有序,带分数排序。
# 添加
zadd rank 100 "张三" 90 "李四" 80 "王五"
# 查看排名(从高到低)
zrevrank rank "张三"
# 查看分数
zscore rank "张三"
# 查看排行榜
zrevrange rank 0 9
适用场景: 排行榜、分数排序。
总结
Redis 5种数据类型:
- String:缓存、计数器
- Hash:存对象
- List:消息队列
- Set:去重、标签
- ZSet:排行榜
根据场景选对数据类型,用起来才高效。
PHP 字符串处理函数
PHP 字符串处理函数
字符串处理是 PHP 最常用的操作,这些函数一定要会。
1. 截取字符串
// 截取前n个字符
substr("hello world", 0, 5); // hello
// 中文截取(用mb_substr)
mb_substr("你好世界", 0, 2, "utf-8"); // 你好
2. 替换字符串
// str_replace 替换
str_replace("world", "PHP", "hello world"); // hello PHP
// 只替换第一次出现
str_replace("a", "b", "aaa", $count);
3. 查找字符串
// 查找位置(找不到返回false)
strpos("hello world", "world"); // 6
// 注意:用 === 0 判断开头
if (strpos($str, "hello") === 0) {
echo "以hello开头";
}
4. 大小写转换
// 转小写
strtolower("Hello"); // hello
// 转大写
strtoupper("hello"); // HELLO
// 首字母大写
ucfirst("hello"); // Hello
// 每个单词首字母大写
ucwords("hello world"); // Hello World
5. 去除空白
// 去除两边空格
trim(" hello "); // hello
// 去左边
ltrim(" hello "); // hello
// 去右边
rtrim(" hello "); // hello
6. 字符串拼接
// 用点拼接
$str = "hello" . " " . "world";
// 多变量拼接
$a = "hello";
$b = "world";
$c = "$a $b"; // hello world
7. 分割和合并
// 分割成数组
$arr = explode(",", "a,b,c"); // ["a", "b", "c"]
// 数组合并成字符串
$str = implode(",", $arr); // a,b,c
8. 常用技巧
1)判断是否包含字符串:
if (strpos($str, "hello") !== false) {
echo "包含hello";
}
2)反转字符串:
strrev("hello"); // olleh
3)字符串长度:
strlen("hello"); // 5
mb_strlen("你好", "utf-8"); // 2
总结
PHP 字符串处理要点:
- substr 截取,注意中文用 mb_substr
- str_replace 替换
- strpos 查找,用 !== false 判断
- trim 去空格
- explode/implode 分割合并
- 注意中文编码问题,用 mb 开头的函数
写 PHP,字符串函数天天用。
MySQL 索引优化技巧
MySQL 索引优化技巧
索引是数据库性能的关键,用对了查询快很多。
1. 索引类型
- 主键索引: 主键自带,唯一且非空
- 普通索引: 最基本的索引
- 唯一索引: 字段值不能重复
- 联合索引: 多个字段组成的索引
- 全文索引: 全文搜索用
2. 创建索引
-- 普通索引
CREATE INDEX idx_name ON users(name);
-- 唯一索引
CREATE UNIQUE INDEX idx_phone ON users(phone);
-- 联合索引
CREATE INDEX idx_name_age ON users(name, age);
3. 索引失效情况
1)不要在索引字段上做运算:
-- 失效
SELECT * FROM users WHERE age + 1 = 20;
-- 生效
SELECT * FROM users WHERE age = 19;
2)like 左模糊:
-- 失效
SELECT * FROM users WHERE name LIKE '%张';
-- 生效
SELECT * FROM users WHERE name LIKE '张%';
3)联合索引不满足最左前缀:
-- 索引:(name, age)
-- 生效(从name开始)
SELECT * FROM users WHERE name = '张三' AND age = 20;
-- 失效(跳过name直接查age)
SELECT * FROM users WHERE age = 20;
4)用 or 连接:
- or 两边有一个没索引就全表扫描
4. 优化原则
- 频繁查询的字段加索引
- 区分度高的字段适合做索引
- 不要加太多索引(影响写入性能)
- 联合索引注意最左前缀
- 尽量扩展索引,不要新建索引
5. 查看索引
-- 查看表的索引
SHOW INDEX FROM users;
6. 常见问题
- 索引加了还是慢?检查是不是失效了
- 查询还是慢?用 EXPLAIN 分析
- 写入慢?索引太多了
总结
MySQL 索引优化要点:
- 常用查询字段加索引
- 注意索引失效情况
- 联合索引最左前缀
- 不要加太多索引
- EXPLAIN 分析执行计划
数据库性能优化,索引是第一步。
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 共享
做大并发网站,负载均衡是必备的。