做MySQL开发或者运维的朋友,应该都收藏过Mysql命令大全这类资料。但说实话,我见过太多人存了一堆手册,真到了线上出问题的时候,翻半天也找不到对症的那条命令。这篇不是把官方文档复述一遍,而是把我这几年在生产环境里真正高频使用、踩过坑的MySQL命令整理成一套能直接照着手册操作的内容。不管你是刚学数据库的学生、日常写业务的研发,还是要独立维护库的运维,都能在里面找到用得上的命令,以及命令背后为什么要这么写的门道。
1. 连接与库表管理:一切操作的起点
1.1 连上MySQL的几种实用方式
日常连库,最常用的是mysql命令行客户端,基础语法是:
mysql -h 192.168.1.100 -P 3306 -u myuser -p注意端口参数是大写-P,如果你用小写-p,MySQL会把它当成密码参数,后面再跟东西就乱了。我之前就见过有人把-p和密码写在一起,像-p123456这样,虽然能用,但密码会出现在操作系统的进程列表和shell历史记录里,安全上很被动。正确做法是只写-p,回车后再交互式输入密码。
连接本机库还有两种常见姿势。一种是直接省略-h,默认连接localhost;另一种是走socket连接:
mysql -u root -p -S /tmp/mysql.sock-S指定socket文件路径,这种方式比TCP连接更快,适合在本机做批量操作或者紧急排查时用,绕开了网络层的开销。
连接成功后,我习惯先确认版本和环境:
SELECT VERSION(); SELECT CURRENT_USER(); SHOW VARIABLES LIKE 'character_set_server';这三条命令能帮你快速定位是不是版本特性导致的SQL写法差异,也能第一时间发现字符集配置是否正常。很多所谓“中文乱码”问题,其实在连上库这一刻就已经注定了,后面我会专门讲。
1.2 库级别操作与字符集选择
库层面的命令没多少,但每一步都有讲究:
SHOW DATABASES; -- 查看所有库 CREATE DATABASE mydb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 建库,指定字符集 USE mydb; -- 切换当前库 ALTER DATABASE mydb CHARACTER SET utf8mb4; -- 修改库字符集 DROP DATABASE mydb; -- 删库,务必确认DROP DATABASE这种命令,我建议你在生产环境永远不要手敲。真要清理库,先把库名写进一个文件,让另外一位同事复核后再执行,多一道确认环节,能省掉后面补数据的巨大麻烦。
字符集这块必须单独说。我见过太多老库还在用utf8,而MySQL里的utf8最多只能存3字节,像生僻字、特殊符号这类四字节字符根本存不进去,插入时直接报错。从MySQL 5.5.3开始,utf8mb4才是真正完整的UTF-8实现。所以新库一律用utf8mb4,排序规则utf8mb4_general_ci或者utf8mb4_unicode_ci都行,看你对排序精度的要求。我自己默认用utf8mb4_general_ci,效率更高,业务上没遇到过需要unicode排序的场景。
1.3 建表命令里的字段设计智慧
建表是我认为MySQL命令中最需要提前想清楚的一类,因为表结构一旦上线,后面改起来成本极高。给你一份我常用的建表模板:
CREATE TABLE users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键', username VARCHAR(64) NOT NULL DEFAULT '' COMMENT '用户名', email VARCHAR(128) NOT NULL DEFAULT '' COMMENT '邮箱', status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1正常 0禁用', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (id), UNIQUE KEY uk_username (username), KEY idx_status (status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';这套模板有几个细节值得展开说。
INT UNSIGNED意味着主键不会出现负数,容量翻倍到42亿左右,对绝大多数业务足够。如果业务量更大,可以考虑BIGINT UNSIGNED,反正现在磁盘和内存都不贵,别在设计上抠搜。
created_at和updated_at这两个字段,所有业务表都应该有。ON UPDATE CURRENT_TIMESTAMP这个特性特别省心,只要这一行数据被更新,时间戳自动刷新,不用在业务代码里手动维护。有一次我排查一个数据同步问题,就是靠updated_at判断哪条记录是近期改过的,几分钟就锁定了脏数据来源。
DATETIME和TIMESTAMP二选一的时候,我偏好DATETIME,原因很简单:TIMESTAMP的范围只到2038年,而且会受时区影响,不同时区的服务器读出来的值不一样。DATETIME没有这些限制,存的是什么就显示什么,逻辑更直观。
表结构字段的基本操作命令一并列在这里,方便查阅:
DESC users; -- 查看表结构 SHOW CREATE TABLE users\G; -- 看建表语句,包含所有索引定义 ALTER TABLE users ADD COLUMN age TINYINT NOT NULL DEFAULT 0 COMMENT '年龄'; ALTER TABLE users DROP COLUMN age; ALTER TABLE users MODIFY COLUMN email VARCHAR(191) NOT NULL DEFAULT ''; ALTER TABLE users RENAME TO members;需要提醒的是,ALTER TABLE在大表上执行时要格外小心。比如给一个千万级记录的表加字段,MySQL 5.6之前的版本会锁全表,业务直接停摆;5.6以后的版本虽然用了在线DDL,但依然会消耗大量IO和CPU。我的经验是这类操作放到业务低峰期执行,并且先在测试库用同样量级的数据预跑一遍,估算真实耗时。
2. 数据读写命令:增删改查的进阶玩法
2.1 SELECT查询不该踩的坑与常用套路
SELECT是开发时写得最多的命令,但很多坑恰恰藏在这里。
生产环境里,我几乎不写SELECT *。这不是洁癖,而是实打实的性能问题:第一,*会给优化器增加负担,无法利用覆盖索引;第二,查到不需要的大字段,比如TEXT类型的详情内容,白白浪费IO和内存;第三,一旦表结构加了字段,业务代码里按位置取值的逻辑直接崩。所以规范做法是明确列出需要的字段:
SELECT id, username, email FROM users WHERE status = 1 ORDER BY id DESC LIMIT 20;接着说说LIKE模糊查询。LIKE '%abc%'这种写法,因为前导通配符的存在,无法走索引,会全表扫描。如果业务确实需要这种模糊匹配,要么用LIKE 'abc%'这种前缀匹配,要么引入全文索引或者外部搜索引擎。我参与过的一个搜索功能,最初用LIKE '%关键字%'在一张500万行的表上查询,接口响应要3秒多,后来改成前缀匹配加缓存,压到了200毫秒以内。
GROUP BY有一个很经典的坑,就是MySQL 5.7之后的ONLY_FULL_GROUP_BY模式。在这个模式下,SELECT的字段必须全部出现在GROUP BY子句里,或者被聚合函数包住,否则直接报错:
-- 错误示例:select的字段不在group by里 SELECT status, username, COUNT(*) FROM users GROUP BY status; -- 正确写法 SELECT status, COUNT(*) AS cnt, MAX(created_at) AS latest FROM users GROUP BY status;JOIN联表查询也是高频场景。内连接INNER JOIN取两表交集,左连接LEFT JOIN以左表为准。这里我有个底层的优化心得:关联查询时,小表驱动大表,并且确保ON条件里的字段两边都有索引。
SELECT u.username, o.order_no, o.amount FROM users u INNER JOIN orders o ON u.id = o.user_id WHERE o.status = 'paid' ORDER BY o.created_at DESC LIMIT 50;2.2 INSERT、UPDATE、DELETE里的保命细节
数据变更类命令,风险最大,也最能体现一个开发者的成熟度。
先看插入。单条插入很简单,批量插入要注意SQL长度限制:
INSERT INTO users (username, email) VALUES ('alice', 'alice@example.com'), ('bob', 'bob@example.com');一次性插入几千条没问题,但上百万条数据千万别一条条插入,也别一条SQL拼太多条。我实测过,把10万条数据按每批500条批量插入,比逐条插入快差不多两个数量级。
INSERT ... ON DUPLICATE KEY UPDATE是去重写入的利器。它的逻辑是:如果插入的记录触发了唯一键冲突,就执行后面的更新操作;如果没冲突,就正常插入。这个命令天然适合做幂等写入。
INSERT INTO users (username, email, status) VALUES ('alice', 'newmail@example.com', 1) ON DUPLICATE KEY UPDATE email = VALUES(email), status = VALUES(status);注意,VALUES()函数在新版本里标记为废弃,建议改用别名写法:
INSERT INTO users (username, email, status) VALUES ('alice', 'newmail@example.com', 1) AS new ON DUPLICATE KEY UPDATE email = new.email;UPDATE和DELETE最大的坑是忘了写WHERE条件。这个错误我自己犯过,也帮别人善后过。一条UPDATE users SET status=0;直接清零全表状态,一条DELETE FROM orders;直接清空订单表。所以现在我对团队的要求是:任何变更类SQL,先写WHERE,再回头补SET,永远不要在没有任何条件的情况下执行UPDATE和DELETE。
更稳妥的做法是,先SELECT查出要变更的数据量,确认影响范围,再执行变更:
-- 先确认影响行数 SELECT COUNT(*) FROM users WHERE status = 0 AND last_login_at < '2024-01-01'; -- 确认无误后再更新 UPDATE users SET status = 2 WHERE status = 0 AND last_login_at < '2024-01-01';DELETE和TRUNCATE的区别也说一下。DELETE是一行行删,走事务,可以回滚,但不会重置自增ID;TRUNCATE是直接重建表,速度极快,但不可回滚,而且会把自增ID归零。生产环境清大表历史数据,我会用DELETE加LIMIT分批删,避免一次性产生大量binlog和锁:
DELETE FROM logs WHERE created_at < '2024-01-01' LIMIT 10000;循环执行上面这条,每次删一万条,直到影响行数为0。这种方式对主从复制友好,不会造成从库长时间延迟。
3. 索引与性能优化命令:让慢查询无处遁形
3.1 索引管理命令与最左前缀原则
索引是MySQL性能的核心,没有之一。管理索引的命令本身很简单:
CREATE INDEX idx_username ON users(username); -- 创建普通索引 CREATE UNIQUE INDEX uk_email ON users(email); -- 创建唯一索引 ALTER TABLE users ADD INDEX idx_status_created (status, created_at); -- 联合索引 DROP INDEX idx_username ON users; -- 删除索引 SHOW INDEX FROM users; -- 查看表上所有索引命令好记,真正的智慧在于知道在哪些字段上建索引。
我的基本判断标准是:WHERE条件里的字段、ORDER BY排序的字段、JOIN的关联字段,这三类字段值得加索引。但索引不是越多越好,因为每次写入都要同步维护索引,索引太多会拖慢写入速度,还占磁盘空间。
联合索引有一个极其重要的原则叫最左前缀原则。比如我建了(status, created_at)这个联合索引,那么查询条件里只要包含status,这个索引就能用上;但如果只查created_at,这个联合索引就帮不上忙。
-- 能用到联合索引 SELECT * FROM users WHERE status = 1 AND created_at > '2024-01-01'; -- 只能用到部分索引 SELECT * FROM users WHERE status = 1; -- 用不到这个联合索引 SELECT * FROM users WHERE created_at > '2024-01-01';所以我设计联合索引时,永远把区分度最高、最常出现在等值条件的字段放在最左侧。这个顺序错了,索引效果会打不小的折扣。
3.2 EXPLAIN解读:慢查询排查的照妖镜
任何一条SQL变慢了,我的第一反应都是在这条SQL前面加EXPLAIN,看执行计划:
EXPLAIN SELECT u.username, o.order_no FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.status = 1;输出结果里,我最关注这几个字段:
type字段是访问类型,从好到差大致是const、eq_ref、ref、range、index、ALL。看到ALL就意味着全表扫描,如果表数据量大,这就是性能瓶颈的信号。key字段显示实际用到的索引,rows是预估扫描行数,这个数字越小越好。
Extra字段里最值得警惕的两种情况:Using filesort表示排序没走索引,需要额外的排序操作;Using temporary表示用了临时表,常见于GROUP BY和DISTINCT的组合场景。出现这两种情况,优化方向一般是调整索引让排序字段包含在索引里,或者改写SQL。
慢查询日志是另一个重要工具。开启方式:
SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';long_query_time设为1,代表超过1秒的查询都会被记录下来。第二天起来直接看慢日志,找出执行次数多、耗时长的SQL,一条条EXPLAIN分析,这就是DBA日常优化的工作闭环。
4. 用户权限与安全管理命令:别再用root跑业务
4.1 创建用户与最小权限授权
很多开发同学图省事,业务代码里直接用root连接数据库。这是我在生产环境绝对禁止的操作,因为一旦业务代码被注入,攻击者拿到root就等于拿到了整台数据库服务器。正确的做法是为每个业务创建独立账号,按最小权限原则授权。
用户创建和授权的基础命令:
CREATE USER 'appuser'@'192.168.1.%' IDENTIFIED BY 'welcome123'; GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO 'appuser'@'192.168.1.%'; FLUSH PRIVILEGES;这里有两个细节要展开。
第一,'appuser'@'192.168.1.%'这部分,前面是用户名,后面是允许登录的来源主机。%是通配符,表示允许从任意主机登录。安全起见,我建议把来源IP限制到应用服务器的网段,这样即使密码泄露,攻击者也只能从特定网段连接,攻击面小很多。
第二,GRANT后面不是授权越多越好。对普通业务账号,只给SELECT、INSERT、UPDATE、DELETE这四个常用权限就够了。DDL权限比如CREATE、ALTER、DROP,应该由专门的运维账号掌握。如果业务确实需要批量导入数据,单独授一个FILE权限,用完立刻回收。
查看和回收权限的命令:
SHOW GRANTS FOR 'appuser'@'192.168.1.%'; REVOKE DELETE ON mydb.* FROM 'appuser'@'192.168.1.%';修改密码的命令也要掌握:
ALTER USER 'appuser'@'192.168.1.%' IDENTIFIED BY 'newpass123';关于FLUSH PRIVILEGES,这里有一个常见的误解。用GRANT、REVOKE、ALTER USER这类命令修改权限后,MySQL会自动刷新权限表,不需要额外执行FLUSH PRIVILEGES。只有直接操作mysql.user表(比如用INSERT往表里加用户),才需要手动执行FLUSH PRIVILEGES让它生效。我自己平时规范操作,基本用不到这个命令,但总有人问,就一并说清楚。
4.2 连接状态与运行状况实时监控
数据库出问题,往往先从连接数异常开始。我最常执行的监控命令是这三条:
SHOW VARIABLES LIKE 'max_connections'; -- 最大连接数配置 SHOW STATUS LIKE 'Threads_connected'; -- 当前已用连接数 SHOW PROCESSLIST; -- 查看所有正在执行的连接当Threads_connected接近max_connections时,新的连接会被拒绝,应用侧表现就是疯狂的“Too many connections”报错。这时候用SHOW PROCESSLIST看当前有哪些连接在跑什么SQL,经常能抓到罪魁祸首——某个没加索引的全表扫描查询,或者一个长时间持有锁的事务。
遇到确实需要立即处理的连接,用KILL命令结束它:
KILL 12345; -- 12345是processlist里的IdKILL要慎用,尤其不要随意kill正在执行的事务,可能造成数据不一致。只有在确认是问题SQL、且业务可以接受该连接中断的情况下才动手。
5. 备份恢复与日常运维命令:关键时刻能救命
5.1 mysqldump参数的选择与恢复操作
备份这件事,平时不起眼,真到误删数据或者服务器宕机的时候,它就是救命稻草。MySQL最常用的逻辑备份工具是mysqldump,我给出一个经过生产验证的参数组合:
mysqldump -u backup_user -p \ --single-transaction \ --routines \ --triggers \ --events \ --default-character-set=utf8mb4 \ mydb > mydb_$(date +%Y%m%d_%H%M%S).sql每个参数都有它存在的理由。
--single-transaction是InnoDB表备份的核心参数,它利用事务的一致性快照,在不锁表的情况下完成备份,在线备份就靠它。如果没有这个参数,备份大表时会锁住全表,线上写入直接卡住。
--routines、--triggers、--events分别表示导出存储过程、触发器、事件调度器。默认情况下mysqldump不会导出这些,如果你只备份了表结构加数据,恢复后会发现存储过程全丢了,业务直接报错。这两个参数我吃过亏才记住。
--default-character-set=utf8mb4保证导出文件的字符集正确,避免备份文件本身就是乱码。
恢复命令更简单:
mysql -u root -p mydb < mydb_20250101_000001.sql恢复前要确保目标库已存在,否则会报错。如果恢复的库和原来的库名不同,可以在mysqldump导出时加--databases mydb参数,这样备份文件里会带上CREATE DATABASE和USE语句,恢复时不用手动建库。
我还习惯在备份后做一次完整性抽查,不恢复全量,只把备份文件头尾看一看:
head -50 mydb_20250101_000001.sql grep -c "INSERT INTO" mydb_20250101_000001.sqlgrep统计出的INSERT INTO数量,可以和上一次备份的数量做个对比,数量级不对就要警惕备份是否成功。定期的备份恢复演练也很重要,我所在的团队每个季度都会在测试环境做一次全量恢复演练,真到需要恢复数据那天,流程才不会手忙脚乱。
5.2 日志体系与InnoDB状态查看
MySQL的日志体系是排查问题的重要入口。查看二进制日志的常用命令:
SHOW VARIABLES LIKE 'log_bin'; SHOW BINARY LOGS; -- 列出所有binlog文件 SHOW MASTER STATUS; -- 查看当前写入的binlog文件和位置binlog在误删数据场景里的价值很大。如果开启了binlog,即使没有备份文件,也能通过binlog把数据恢复到误操作之前的某个时间点。这也提醒我们:生产环境一定要开启binlog,并且设置expire_logs_days合适的保留天数,我一般建议保留7到15天。
InnoDB存储引擎自带一个状态报告命令,排查锁等待问题几乎是必用的:
SHOW ENGINE INNODB STATUS\G输出内容里重点看LATEST DETECTED DEADLOCK部分,这里会记录最近一次死锁涉及的事务、SQL语句和锁资源。还有TRANSACTIONS部分,能看到当前未提交的事务和它们持有的锁。出现死锁或者锁等待超时的时候,这条命令能帮你快速定位是哪两个事务互相等待,从而设计出规避方案。
6. 高频问题排查速查表:遇到问题先照这个查
把日常运维中最常见的问题和排查命令整理成一张速查表,方便你遇到状况时第一时间对照操作:
| 问题场景 | 典型现象 | 排查命令 | 常用解决方案 |
|---|---|---|---|
| 连接数打满 | 应用报Too many connections | SHOW PROCESSLIST、SHOW STATUS LIKE 'Threads_connected' | 杀异常连接、优化慢SQL、调大max_connections |
| 查询特别慢 | 接口响应时间飙升 | EXPLAIN慢SQL、查慢查询日志 | 补索引、改写SQL、拆分大查询 |
| 更新卡住 | UPDATE一直不返回 | SHOW ENGINE INNODB STATUS\G、查information_schema.innodb_trx | 定位长事务并回滚或提交,规范事务粒度 |
| 主从延迟 | 从库数据落后主库 | SHOW SLAVE STATUS\G看Seconds_Behind_Master | 排查大事务和慢SQL,必要时重新同步从库 |
| 中文乱码 | 插入中文变问号 | SHOW VARIABLES LIKE 'character%' | 统一客户端、连接、库表字符集为utf8mb4 |
| 误删数据 | DELETE或DROP误操作 | 查binlog时间点 | 用binlog做时间点恢复,平时做备份 |
这里挑两个展开讲讲。
主从延迟问题,SHOW SLAVE STATUS\G输出里最关键的是Seconds_Behind_Master字段,它表示从库落后主库的秒数。延迟常见原因是主库执行了大事务,binlog在从库回放时需要较长时间。我遇到过最典型的情况是运维在主库一次性DELETE了几百万行历史数据,从库回放这个事务花了十几分钟,期间从库上的报表查询全是旧数据。解决思路是把大事务拆成小批次执行。
再看中文乱码问题。乱码不是玄学,本质就是客户端、连接层、库表、字段这几个环节的字符集不一致。排查命令:
SHOW VARIABLES LIKE 'character_set_client'; SHOW VARIABLES LIKE 'character_set_connection'; SHOW VARIABLES LIKE 'character_set_database'; SHOW VARIABLES LIKE 'character_set_server';这四个值如果不一致,很容易在数据写入或者读取时出现乱码。最省心的做法是配置文件里的[client]和[mysqld]都设置default-character-set=utf8mb4,让所有环节统一。新库建表时明确指定字符集,连接串里也加上characterEncoding=utf8mb4参数,从前到后全链路统一,乱码问题基本就绝迹了。
写在最后的一点体会
从最初对着文档抄命令,到现在能根据业务场景灵活组合命令解决问题,我最大的体会是:MySQL命令不是背出来的,是用出来的。每一条命令背后都对应一个真实场景,与其死记参数,不如把你手头业务里最频繁的几个操作吃透。我建议你从今天开始,把自己常用到的命令整理成一份自己的命令笔记,标注哪些参数是你实测过有效的,哪些坑是你踩过的,这样积累下来的东西,比任何网上的大全都有价值。平时多在生产环境之外的测试库练习这些命令,尤其是权限管理、备份恢复、锁排查这三类,真到了线上需要你临场发挥的那一天,你会感谢自己提前做过这些功课。