资讯动态

MySQL基础操作与排坑全攻略:从环境搭建到查询优化与备份恢复

发布时间:2026/10/14 4:36:38 来源:尧图企业网站定制
这份手册其实早该写了。前阵子帮几个刚转行做开发的朋友看数据库问题发现大家卡住的地方都差不多不是不会写某条SQL而是不知道一条SQL跑下去会发生什么出问题时也不知道去哪儿查。MySQL基础操作看起来简单但真正想用得稳、用得好光会敲命令是不够的。于是我把这些年实际用MySQL的经验整理成这份手册不是教科书那样一条条罗列命令而是按一个新手从零开始、真正要把库跑起来的路径来写——从连接上MySQL到建库建表到日常增删改查再到查询优化、备份恢复和问题排查每一步都说清楚为什么这么做、踩过哪些坑。这份内容适合刚入门数据库的开发者、偶尔要写SQL做取数的分析师也适合接手别人项目时快速把基础操作温习一遍。看完你会明白MySQL这东西规则并不复杂真正拉开差距的其实是你有没有养成好习惯。1. 环境准备与连接从零连上你的MySQL1.1 安装与启停别小看这一步装MySQL其实是最没啥技术含量但又最容易出幺蛾子的环节。我见过不少人在Windows上装完MySQL之后服务没启动就开始敲命令然后一脸懵地跑来问“为什么连接不上”。这里强调一下MySQL装好之后它本质上是一个常驻后台的服务进程你得先把服务启动起来客户端才能连进去。Linux环境下我用得比较多Ubuntu和CentOS的命令略有差异但思路一致# Ubuntu/Debian 系 sudo apt update sudo apt install mysql-server sudo systemctl start mysql sudo systemctl enable mysql sudo systemctl status mysql # CentOS/RHEL 系 sudo yum install mysql-server sudo systemctl start mysqld sudo systemctl enable mysqld注意CentOS 7之后默认的软件源里装的是MariaDB。如果你非要装官方MySQL得先配置官方Yum源再去装否则装出来的东西会让你排查到怀疑人生。Windows用户就简单了下载安装包一路Next最后在“服务”里找到MySQL确认它是“正在运行”状态就行。装完之后第一件事不是建库而是用mysqladmin ping或者直接命令行连一下确认服务正常。我习惯先用一个最简单的连接来验证环境再开始干活。1.2 连接MySQL的三种主流方式连接MySQL本质上就是客户端和服务端建立一个通信通道。主流方式就三种命令行、图形化客户端、程序驱动。对于基础操作来说命令行是基本功图形化工具是效率神器两种都得会。命令行连接的标准写法长这样mysql -u root -p -h 127.0.0.1 -P 3306-u指定用户名-p表示需要输入密码-h指定主机地址-P指定端口。默认端口就是3306如果你没改过-P参数可以省略。我习惯在-p后面不带密码直接回车然后交互式输入这样密码不会留在Shell历史记录里。图形化客户端方面DBeaver和Navicat是最常见的两个选择。前者免费开源跨平台适合预算有限的个人开发者后者功能更完整界面更友好适合公司里买了授权的场景。连图形化工具的本质也是填那几个参数主机、端口、用户名、密码理解了命令行图形化工具就是套了个壳。程序驱动的连接方式以Python为例import pymysql conn pymysql.connect( host127.0.0.1, port3306, userroot, passwordyour_password, databasetest_db, charsetutf8mb4 )这里核心就是charsetutf8mb4很多老项目乱码的根源就是这里没设对。连接层面就把字符集定好后面建库建表都省事。1.3 字符集与排序规则一上来就解决乱码根源字符集是新手最容易忽略的配置但几乎所有乱码问题都跟它有关。我打个比方字符集就像一个仓库的“标签系统”你往仓库里放货物时用拼音标签取货时却用英文标签去找当然找不到。MySQL里最常见的字符集是utf8和utf8mb4。这里有个重要区别MySQL的utf8其实不是真正的UTF-8它最多只能存3个字节的字符像emoji表情和一些特殊汉字就存不进去。所以从5.5.3版本开始MySQL提供了utf8mb4这才是完整的UTF-8。我在任何新建的库表里一律用utf8mb4配合utf8mb4_unicode_ci排序规则最大程度避免兼容性问题。建库的时候就把字符集定死后面建表就不用操心CREATE DATABASE IF NOT EXISTS my_app DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;注意千万不要等表建完数据写进去之后再回头改字符集。ALTER TABLE改字符集时MySQL会对已有数据做转换数据量大一点就能把表锁上几个小时生产环境这么干是事故级别的操作。1.4 忘记root密码的急救方案这事我干过不止一次每次都是冷汗直冒。好在你只需要一条特殊的启动参数就能进到“免密模式”然后重置密码。安全地重置密码标准流程如下停掉MySQL服务。以跳过授权表的方式启动MySQLmysqld --skip-grant-tablesWindows下是在配置文件里加skip-grant-tables。此时不需要密码就能登录mysql -u root。先刷新权限FLUSH PRIVILEGES;再改密码ALTER USER rootlocalhost IDENTIFIED BY 新密码;退出重启MySQL服务。很多人会在第4步直接改密码结果发现报错ERROR 1290。这是因为跳过授权表模式下很多需要写权限的操作被限制了。先FLUSH PRIVILEGES;让MySQL恢复正常的内存权限结构再执行ALTER USER就顺手了。重要安全提醒--skip-grant-tables模式下任何本机进程都能无密码连上你的数据库这等于把大门敞开。操作完一定要记得恢复服务重启不要留着这个模式过夜。2. 库与表一切操作的根基2.1 数据库的创建、选择与删除数据库本质上就是一组对象的容器里面装着表、视图、存储过程这些。基础操作其实少得可怜建库、看库、切库、删库。-- 创建数据库IF NOT EXISTS可以避免重复创建报错 CREATE DATABASE IF NOT EXISTS my_app DEFAULT CHARACTER SET utf8mb4; -- 查看所有数据库 SHOW DATABASES; -- 切换到指定数据库 USE my_app; -- 查看当前所在库 SELECT DATABASE(); -- 删除数据库千万千万确认清楚再执行 DROP DATABASE my_app;这里有句我必须反复强调的话DROP DATABASE和DROP TABLE是没有回收站的。我见过有人手滑在生产环境执行了DROP操作那一刻整个部门都安静了。如果你在操作生产库删库之前建议先SHOW DATABASES确认你人在正确的实例上再确认一遍库名。有条件的话生产环境把这个权限收掉只保留在测试环境。2.2 建表的核心数据类型怎么选建表是数据库设计里最重要的一环。表结构一旦定下来后续改动的成本是所有操作里最高的。我的原则是宁可在前期多花十分钟想清楚字段类型也不要后面靠ALTER TABLE来修修补补。一个典型的用户表长这样CREATE TABLE user ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL COMMENT 用户名, email VARCHAR(100) DEFAULT NULL COMMENT 邮箱, age TINYINT UNSIGNED DEFAULT NULL COMMENT 年龄, balance DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 余额, status TINYINT NOT NULL DEFAULT 1 COMMENT 状态1启用 0禁用, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;每个字段我都说说为什么这么选id用BIGINT UNSIGNED而不是INT。很多人觉得INT够用了但INT最大到21亿多对很多业务来说听着够用可一旦上了规模你会发现这个数字非常容易达到。BIGINT UNSIGNED上限大得离谱代价只是每行多2个字节这笔账怎么算都划算。username用VARCHAR(50)。字符串类型里VARCHAR是按内容长度存储的可变长类型50个字符足够覆盖绝大多数场景。这里补充一点VARCHAR括号里的数字是字符数而不是字节数所以VARCHAR(50)理论上最多能存50个汉字。age用TINYINT UNSIGNED因为人年龄最多到百来岁TINYINT范围是0~255刚好覆盖小而省空间。balance必须用DECIMAL(10,2)这是金钱字段的铁律。千万不能用FLOAT或DOUBLE存钱二进制浮点数在精度上天然有误差。你存100.00读出来可能变成99.9999999。做账出错了这个责任背不起。create_time和update_time直接用DEFAULT CURRENT_TIMESTAMP数据库自动填时间不用程序代码里再画蛇添足设置一遍。2.3 修改表结构的常见操作与避坑结构设计得再好业务一变还是免不了加字段。ALTER TABLE是基础操作里最需要谨慎的一组命令。-- 增加字段 ALTER TABLE user ADD COLUMN nickname VARCHAR(50) DEFAULT COMMENT 昵称 AFTER username; -- 删除字段 ALTER TABLE user DROP COLUMN nickname; -- 修改字段类型 ALTER TABLE user MODIFY COLUMN nickname VARCHAR(100) DEFAULT COMMENT 昵称; -- 修改字段名 ALTER TABLE user CHANGE COLUMN nickname nick_name VARCHAR(50) DEFAULT COMMENT 昵称;几个重要经验第一生产环境加字段时ALTER TABLE会触发表级锁。虽然是MySQL 5.6之后的Online DDL可以在线加字段但数据量大的表依然会带来明显的IO开销和延迟。大表上执行DDL一定要挑业务低谷期并且先看一眼磁盘空间够不够因为部分版本的Online DDL需要额外空间。第二CHANGE COLUMN和MODIFY COLUMN的区别是CHANGE可以顺便改字段名MODIFY只能改类型或属性。实际使用中能MODIFY就别CHANGE减少误改字段名的风险。第三删除字段之前先看一眼有没有相关的索引或视图引用它。删字段时如果别的表还在用程序一跑起来立刻会报“Unknown column”的错误。3. 数据操作增删改查的日常3.1 INSERT插入数据的三种姿势插入数据看起来最简单其实有不少讲究。我见过太多人一条一条往表里插数据插一千条用了十分钟这在性能上是灾难级别的。第一种单条插入。INSERT INTO user (username, email, age) VALUES (zhangsan, zhangsanexample.com, 25);第二种批量插入。一次INSERT语句里带多组VALUES。INSERT INTO user (username, email, age) VALUES (lisi, lisiexample.com, 26), (wangwu, wangwuexample.com, 27), (zhaoliu, zhaoliuexample.com, 28);批量插入的好处在于一次网络往返可以写入多行数据IO次数大幅减少。我实测过同样插入一万条数据一条一条插和每次插100条这种批量方式时间能差出五到十倍。第三种带冲突处理的插入。-- 如果主键或唯一键冲突则忽略当前插入 INSERT IGNORE INTO user (id, username, email) VALUES (1, zhangsan, zsexample.com); -- 如果冲突则更新指定字段 INSERT INTO user (id, username, email) VALUES (1, zhangsan, zsexample.com) ON DUPLICATE KEY UPDATE email VALUES(email);这里说明一下两者适用场景INSERT IGNORE适合“有就跳过”的幂等写入ON DUPLICATE KEY UPDATE适合“有就更新”的同步逻辑。注意MySQL 8.0.20之后官方推荐用别名语法AS new配合VALUES引用老写法虽然还兼容但已标记为过时新项目建议直接用别名写法。3.2 SELECT基础不要无脑SELECT *查询是MySQL里最常用也最值得深入的操作。基础查询首先要改掉一个坏习惯SELECT *。很多新手图省事一上来就SELECT * FROM user这在表只有三五个字段时没问题但一旦表字段膨胀到几十个SELECT *会带来三个问题一是网络传输量变大二是无法利用覆盖索引后面会讲三是查出来的列多了代码里很可能会隐式地用到一些根本不需要的字段导致程序耦合变高。正确的做法是只查你需要的列SELECT id, username, email FROM user WHERE status 1;WHERE后面跟的是过滤条件。基础的运算符就那么几个、!/、、、、、BETWEEN AND、IN、LIKE。刚入门的时候容易在两个地方卡住一个是用去判断NULL。NULL不是一个值它代表“不确定”所以WHERE email NULL永远查不出任何数据得写WHERE email IS NULL。另一个是LIKE的模糊匹配位置。LIKE %abc表示以abc结尾LIKE abc%表示以abc开头。写到%的位置不一样走不走索引的差别也很大abc%可以用到索引%abc基本不能用到这个后面索引章节再展开。3.3 UPDATE与DELETE改数据前先做好这三件事UPDATE和DELETE是DML里最危险的两个操作。每次执行之前我给自己定了三条铁律第一条先SELECT出来看看影响范围。你要UPDATE或DELETE的数据先用一样的WHERE条件SELECT一次确认影响到的人和数据是你预期中的。SELECT的结果行数就是你说的“影响行数”对不上就先别执行。-- 先确认 SELECT * FROM user WHERE age 60; -- 再更新 UPDATE user SET status 0 WHERE age 60;第二条务必用事务包住。MySQL里只要表引擎是InnoDBUPDATE和DELETE都可以用事务回滚。START TRANSACTION; UPDATE user SET balance balance - 100 WHERE id 1; -- 先不提交查一下结果对不对 SELECT * FROM user WHERE id 1; -- 确认没问题 COMMIT; -- 或者有问题 ROLLBACK;这个习惯能救命。我亲眼见过有人在生产库上执行UPDATE忘了加WHERE结果整个表的某个字段被改成一个值当场脑子一片空白的过程。如果包在事务里一条ROLLBACK就解决问题裸执行的话就只能翻备份去了。第三条DELETE可以不删用UPDATE标记。很多业务场景下的“删除”其实是逻辑删除也就是加个deleted字段值为1表示已删除。这样做的好处是数据可恢复、保留历史痕迹。真正的物理删除只用于临时表或者确定不再需要的数据。-- 逻辑删除 UPDATE user SET status 0, deleted 1 WHERE id 123; -- 物理删除谨慎谨慎再谨慎 DELETE FROM user WHERE id 123;4. 查询进阶排序、分组与多表连接4.1 条件查询的优先级别被AND和OR带偏写查询的时候如果WHERE条件里同时有AND和OR很多人会被搞晕。其实道理很简单AND的优先级高于OR。也就是说WHERE a 1 OR b 2 AND c 3实际上是a 1 OR (b 2 AND c 3)。我在实际项目里建议能用括号就用括号括号不仅让逻辑清晰还避免别人接手的时候产生歧义。-- 明确表达意图 SELECT * FROM user WHERE (status 1 AND age 18) OR (vip_level 3);还有个经验IN比多个OR更高效也更简洁。WHERE age IN (18, 20, 25)比WHERE age 18 OR age 20 OR age 25在语义上更清晰。但要注意IN列表里的元素别太多几百上千个的时候MySQL处理起来会有性能问题这种情况要考虑其他方式。4.2 ORDER BY与LIMIT分页的正确写法排序是查询里最常用的操作之一。ORDER BY默认是升序ASC要降序就写上DESC。多字段排序时写在前面的字段优先级更高。SELECT id, username, age, create_time FROM user ORDER BY age DESC, id ASC LIMIT 0, 20;这个写法就是“先按年龄从大到小排年龄一样的再按ID从小到大排”。需要注意ORDER BY的字段顺序不同结果可能完全不同特别是两个字段值相等的时候。分页是后端开发每天要面对的需求。LIMIT 0, 20表示从偏移量0开始取20条LIMIT 20, 20表示从第20条开始取20条也就是第二页。这里藏着一个经典性能坑深分页。当你想拿第1000页时LIMIT 19980, 20MySQL会把前19980条都扫描一遍再丢弃然后返回最后的20条数据量一大这个查询会慢到让你怀疑服务器是不是挂了。应对深分页有个常用技巧先取主键再回表取数据。SELECT id, username, age FROM user WHERE id ( SELECT id FROM user ORDER BY id LIMIT 19980, 1 ) ORDER BY id LIMIT 20;原理是子查询只索引扫描并通过主键定位到目标起点再取出20条。这样跳过了前面的无效扫描性能会好很多。实际项目中如果分页深度超过几百我都会认真考虑改成这种写法。4.3 聚合函数与GROUP BY统计报表的基础日常需求里免不了“算总数、算平均值、分组统计”。聚合函数就是干这个的。SELECT COUNT(*) AS total_cnt, COUNT(DISTINCT status) AS status_cnt, AVG(age) AS avg_age, SUM(balance) AS total_balance FROM user;COUNT(*)和COUNT(1)在MySQL 8.0下的性能差异基本可以忽略但很多老教程还在争论哪个更快其实没必要。真正要注意的是COUNT(字段名)——它会忽略值为NULL的行。如果你想知道“有多少人填了邮箱”要写COUNT(email)而不是COUNT(*)。GROUP BY和聚合函数配合时有个新手重灾区分组之后想过滤条件。-- 错误写法 SELECT age, COUNT(*) FROM user WHERE COUNT(*) 10 GROUP BY age; -- 正确写法 SELECT age, COUNT(*) FROM user GROUP BY age HAVING COUNT(*) 10;WHERE是对原始行做过滤HAVING是对分组后的聚合结果做过滤。什么时候用WHERE、什么时候用HAVING记住这一句话就行先筛选行、后分组过滤能用WHERE先过滤掉的数据别留到HAVING里再处理因为处理的行数越少越快。4.4 JOIN连接INNER、LEFT、RIGHT一次讲清楚多表连接是MySQL迈向中级水平的必经之路。我用一个例子说明假设有两张表一张user用户表一张order订单表一个用户可以有多个订单。-- 内连接只返回两边能匹配上的数据 SELECT u.username, o.order_sn, o.amount FROM user u INNER JOIN order o ON u.id o.user_id; -- 左连接返回user表所有数据order匹配不上就补NULL SELECT u.username, o.order_sn, o.amount FROM user u LEFT JOIN order o ON u.id o.user_id;内连接INNER JOIN就像两个圆圈的重叠部分只有两边都有的才会出来。左连接LEFT JOIN是以左表为准左表的每一行都会出现右表匹配不上就填空值NULL。右连接RIGHT JOIN方向相反实际开发中很少有人用RIGHT JOIN因为把表顺序换一下LEFT JOIN就能实现同样的效果我建议你也不用。LEFT JOIN最容易踩的坑就是“记录数变多”。我在项目里就遇到过一个用户有3个订单用LEFT JOIN一查这个用户就变成3行了。如果你本意是想查用户维度的数据这个结果就会造成统计翻倍。解决办法通常是用DISTINCT去重或者改成子查询/EXISTS的方式。-- 只查有过订单的用户 SELECT u.id, u.username FROM user u WHERE EXISTS ( SELECT 1 FROM order o WHERE o.user_id u.id );EXISTS的性能在小数据量时和JOIN差别不大但语义上更清晰。判断“是否存在”的场景我推荐优先用EXISTS关联字段没匹配上时它不会产生多余数据。5. 索引与事务性能与安全的两根支柱5.1 索引创建、使用与失效场景索引是MySQL性能的根基没有索引的表数据一多就是全表扫描。我打个比方一本没有目录的书你想找一个关键词只能一页页翻有了索引就像按拼音查字典翻两下就定位到了。通常在WHERE条件、ORDER BY字段、JOIN关联字段上考虑建索引。-- 普通索引 CREATE INDEX idx_email ON user(email); -- 唯一索引 CREATE UNIQUE INDEX uk_email ON user(email); -- 联合索引 CREATE INDEX idx_age_status ON user(age, status); -- 删除索引 DROP INDEX idx_age_status ON user;联合索引有个“最左前缀原则”。idx_age_status(age, status)这种复合索引实际生效规则是查询条件里必须包含age这个索引才可能被用到只写status条件却没用age索引就失效了。这就好比你查字典先查部首再查笔画跳过分部首那一步后面的笔画索引就帮不上忙了。我整理过几个必背的索引失效场景对索引字段做了函数或计算WHERE DATE(create_time) 2024-01-01这样用不上create_time上的索引。隐式类型转换字段是字符串类型查询条件却传了数字MySQL做了类型转换索引失效。LIKE以%开头LIKE %abc用不上索引LIKE abc%可以。OR条件中有一个字段没有索引WHERE email ab.com OR age 20如果age没索引整个条件可能不走索引。5.2 EXPLAIN一条命令看穿SQL性能这是排查SQL性能问题最实用、最应该养成习惯的命令没有之一。在任何SELECT语句前面加上EXPLAINMySQL不会真的执行这条SQL而是返回执行计划。EXPLAIN SELECT u.id, u.username, o.order_sn FROM user u LEFT JOIN order o ON u.id o.user_id WHERE u.age 25;结果里重点看两个字段type和rows。type表示访问类型从好到差大概是systemconsteq_refrefrangeindexALL。如果看到ALL说明是全表扫描基本可以判断这条SQL还有优化空间。rows是预估扫描的行数这个数字越大SQL通常越慢。我给自己定了一个简单标准线上查询的type不能出现ALLrows不能超过表数据量的一个较小比例。真出现了就把WHERE条件里的字段加索引再EXPLAIN一次看rows是不是降下来了。5.3 事务ACID与并发控制事务是一组SQL操作的执行单元要么全部成功要么全部回滚。START TRANSACTION; UPDATE account SET balance balance - 5000 WHERE id 1; UPDATE account SET balance balance 5000 WHERE id 2; -- 如果执行过程中没有报错 COMMIT; -- 如果任何一步有问题 ROLLBACK;这是转账的经典场景。第一笔扣款成功后第二笔入款如果因为任何原因失败了ROLLBACK会把第一笔操作也撤销保证账户余额不会凭空少钱。这就是事务的原子性。InnoDB引擎默认支持事务MyISAM引擎不支持。所以建表时ENGINEInnoDB这句话在需要事务保护的业务表上绝对不能省。事务的隔离级别值得知道默认是REPEATABLE READ可重复读。在可重复读下你自己开的事务里读到的数据是稳定的不会因为别人提交了新数据而读到不同的值。这个特性对业务一致性体验很重要所以MySQL默认选它。有个容易遇到的问题事务开着不提交会一直握着行锁。这时别的会话想更新同一行数据就会卡在那里表现为“看起来像服务器卡死了”实际上是锁等待。遇到这种问题先查一下有没有长时间未提交的事务SELECT * FROM information_schema.innodb_trx;把查到的trx_id对应会话KILL掉或者等它自己提交锁就会释放。养成好习惯程序里的事务一定要在finally块里确保关闭连接或回滚避免事务长时间悬挂。6. 日常维护备份恢复与问题排查6.1 备份与恢复平时多演练出事不慌张备份这事很多新手觉得没必要直到亲手弄丢过一次数据才追悔莫及。MySQL里最靠谱、最常用的备份工具是mysqldump。# 备份单个数据库 mysqldump -u root -p --single-transaction my_app /backup/my_app_20250101.sql # 备份多个数据库 mysqldump -u root -p --databases my_app test_db /backup/multi_20250101.sql # 备份所有数据库 mysqldump -u root -p --all-databases /backup/all_20250101.sql几个参数说明一下--single-transaction是InnoDB表的必带参数。它会在备份开始时开启一个可重复读事务这样备份过程中数据依然能被读写不会锁表。不加这个参数的话备份期间表被锁住线上业务直接卡住。恢复就是把备份文件重新塞回MySQLmysql -u root -p my_app /backup/my_app_20250101.sql这里有个实操细节mysqldump备份出来的SQL文件如果包含CREATE DATABASE语句恢复时可以直接mysql -u root -p xxx.sql不需要先建库如果备份时没加--databases参数文件里通常没有建库语句就需要你先手动建一个空的库再导入。再强调一件事备份恢复必须提前演练。真到事故发生时才发现备份文件是坏的、或者恢复流程走到一半卡住了那才是最崩溃的。我个人的习惯是每个月找一台测试实例把最近一次备份完整恢复一遍确认可用。6.2 常见问题排查速查表我整理了一份自己反复用到的MySQL问题排查表按“现象-可能原因-解决思路”三列呈现希望对你有直接帮助报错/现象可能原因解决思路ERROR 1045 (28000): Access denied用户名或密码错误确认账号和密码注意host匹配必要时用root重置ERROR 2003 (HY000): Cant connect to MySQL server服务没启动、端口被防火墙拦截检查服务状态telnet测试端口查看防火墙规则ERROR 1146 (42S02): Table doesnt exist表名拼写错误、没选对数据库用SHOW TABLES核对确认USE了正确的库ERROR 1064 (42000): You have an error in your SQL syntaxSQL语法错误检查引号、逗号、关键词拼写重点看字符串是否漏了引号ERROR 1366: Incorrect string value字符集不匹配导致乱码/写入失败连接设置charsetutf8mb4检查库表字符集查询很慢缺索引、深分页、扫描行数大用EXPLAIN看执行计划针对WHERE字段建索引更新卡住不动行锁等待、事务未提交查innodb_trx看锁占用KILL相关会话连接数超限Too many connections连接没有释放、连接池配置过大排查代码连接释放调整max_connections重启应用让连接池回收这份表我打印出来贴过工位后来发现不用贴了因为实操多了自然会记住。不过对刚入门的人来说报错看得懂就已经解决了一半剩下的就是按图索骥的问题。6.3 慢查询日志找到拖慢系统的真凶线上系统变慢十有八九是慢查询在作祟。MySQL提供了慢查询日志可以把执行时间超过阈值的SQL记录下来。这个功能默认是关的可以在MySQL配置文件里开启slow_query_log 1 slow_query_log_file /var/log/mysql/mysql-slow.log long_query_time 1long_query_time 1表示执行时间超过1秒的SQL都会被记录。开启一段时间后直接看日志文件就能知道哪些SQL需要优化。更高效的做法是用mysqldumpslow工具对慢查询日志做聚合统计mysqldumpslow -t 10 /var/log/mysql/mysql-slow.log这个命令会把日志中相似的SQL语句归类然后按执行次数、耗时排序直接暴露出最需要优化的Top 10。我优化线上系统时第一步几乎都是先开慢查询日志收集一天数据再针对排名靠前的SQL做EXPLAIN分析。路径清晰效率也高。最后说一点我自己的体会MySQL基础操作这东西看着零散其实是一棵完整的技能树——连接是根库表是树干增删改查是枝叶索引和事务是支撑整棵树的养分。把这几个环节的规范习惯都养成了后面学高级玩法就是水到渠成的事。别忘了以后每次写UPDATE、DELETE之前多想一下我刚才说的那三件事。踩过坑的人都会懂这些习惯不是别的是拿真金白银和头发换来的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑