ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MySQL从入门到进阶:索引、事务、备份与排错实战全解析

MySQL从入门到进阶:索引、事务、备份与排错实战全解析 我接触 MySQL 的时间说长不长说短不短十来年总有了。这几年带新人、做方案评审、帮项目组处理线上故障发现一个问题反复出现很多人的 MySQL 知识是“碎片化”的这里看一个安装教程那里抄一段索引写法出了问题再搜个报错。遇到简单场景能应付一旦涉及性能、数据一致性、自动化运维就很容易翻车。所以我想花点时间把 MySQL 从入门到进阶这条路上的核心内容按我自己的学习路径和理解方式整理成一篇长文。这篇内容不追求把官方文档搬一遍而是把“一个后端开发/运维实际会用到的东西”串起来从怎么装、怎么建表、怎么写 SQL到索引原理、事务隔离、存储过程、备份脚本、常见报错排查完整过一遍。标题写着“图文并茂”虽然这里嵌入不了真正的截图但我尽量用表格、命令示例和结构拆解的方式把每个环节讲透。这篇总结适合三类人刚入行想系统学 MySQL 的后端开发或运维写过一些 SQL 但没搞懂底层原理、想补短板的同学准备面试需要快速回顾 MySQL 高频考点的求职者。如果你是数据库老手那有些基础部分可以跳着看后面几章关于避坑和经验的内容相信也能有收获。1. 学习 MySQL 的正确打开方式1.1 先搞懂 MySQL 到底是什么MySQL 是一个关系型数据库管理系统这个说法有点绕。说人话它就是负责按“表”的结构帮你存数据、改数据、查数据的一个后台服务程序。你用 SQL 语句告诉它“我要查什么”它把结果返回给你。相比直接读写文件数据库帮你解决了并发控制、崩溃恢复、数据一致性、高效检索这些麻烦事。很多新人喜欢直接背 SQL 命令我觉得这个顺序反了。你不需要先把 SQL 背得滚瓜烂熟再学 MySQL而是应该先明白“SQL 是接口MySQL 是引擎”。你写SELECT * FROM user WHERE id 1这条 SQL 进了 MySQL 之后要做语法解析、权限校验、优化器生成执行计划、存储引擎读取数据最后再回给你结果。理解了这条链路后面看索引、看慢查询、看调优才会有“原来如此”的感觉而不是死记硬背。1.2 MySQL 的整体架构用一张图说清楚虽然没有真实截图但架构这块你用笔记软件画一画就清楚了。我按自己的理解把它拆成四层连接层负责接收客户端的连接请求验证用户名密码管理连接线程处理连接数限制。像 Workbench、Navicat、你的后端程序连接 MySQL第一步走的就是这一层。Server 层包括查询缓存8.0 已经移除了、解析器、优化器、执行器。一条 SQL 是死是活、执行计划怎么走基本由这一层决定。存储引擎层真正落数据的地方。InnoDB 是默认引擎支持事务和行锁MyISAM 是老牌引擎不支持事务表锁为主。8.0 之前还能随意换引擎8.0 之后系统表也必须用 InnoDB。文件系统层数据最终落在磁盘上的.ibd、.frm、日志文件里。binlog、redo log、undo log 这些日志的写入和恢复机制都在这层。我拿公司打个比方连接层是前台负责接待Server 层是业务经理分析你提的需求、规划怎么做存储引擎层是仓库管理员具体取货放货文件系统是仓库本身。你问前台“给我把编号 id1 的东西拿出来”前台把话传给业务经理经理规划好路线仓库管理员进去把东西取出来。任何一个环节卡壳你都拿不到货。1.3 版本怎么选别在第一步就踩坑现在生产环境的主流版本一个是 5.7一个是 8.0。我的建议很简单新项目优先选 8.0老项目维持原版本不要在生产上做无谓的大版本升级。8.0 相对 5.7 有很多实在的提升默认字符集改为 utf8mb4中文和 emoji 都不再有编码烦恼新增了窗口函数和公共表表达式 CTE写复杂统计 SQL 方便很多支持不可见索引、降序索引、原子 DDL在线改表更稳caching_sha2_password默认认证插件安全性更高但也导致老版本客户端比如 5.x 的驱动、比较旧的 Navicat连接报错。如果你装了 8.0 之后用老工具连不上第一反应先查认证插件问题。有些同学喜欢追新看到“mysql 9.7”之类的版本号就去体验。我的看法是学习环境随便折腾生产环境求稳。MySQL 的版本节奏比较快但企业里真正大面积使用的还是 5.7 和 8.0。把这两个版本吃透足够你应付 99% 的场景。另外如果你看到“mysql 4.1.22”这类很老的下载地址不建议再用了老版本在字符集、性能、安全方面都有明显问题学了容易走偏。2. 环境搭建Windows、Linux、Docker 我全走了一遍2.1 Windows 安装压缩版比安装包更好用很多新手装 Windows 版 MySQL 喜欢用图形安装包mysql-installer-community一路 Next。但实际工作中我反而更推荐压缩版zip archive尤其是用 8.0.x 的压缩版。原因有三个解压即用卸载干净方便同时维护多版本。以mysql 8.0.46 压缩版为例步骤大概是去官网下载mysql-8.0.46-winx64.zip不用注册账号也能下选“No thanks, just start my download”。解压到一个路径比如D:\mysql-8.0.46-winx64。路径里不要带着空格或者中文别问我是怎么知道的。在解压目录下新建my.ini文件内容参考[mysqld] basedirD:/mysql-8.0.46-winx64 datadirD:/mysql-8.0.46-winx64/data port3306 character-set-serverutf8mb4 default-storage-engineINNODB max_connections200 [client] default-character-setutf8mb4用管理员身份打开 CMD进入 bin 目录依次执行mysqld --initialize-insecure mysqld -install net start mysql这里有个细节--initialize-insecure会生成一个 root 空密码账号方便你第一次登录如果你用--initialize它会生成随机密码放在 data 目录的.err日志里很多人第一次装找不到密码就卡住了。我建议新手先用--initialize-insecure登录后再自己ALTER USER改密码。如果你在“安装 mysql start the server”那一步报“服务无法启动”或者“1067 错误”90% 是 my.ini 里的路径写错了或者datadir指定的目录不存在。把 data 目录删掉重新初始化一次大概率能解决。2.2 Linux 安装用离线包解决没网环境生产环境大部分跑在 Linux 上常见发行版是 CentOS 或者 Ubuntu。CentOS 7/8 下最简单的安装方式是用官方 yum 源rpm -ivh https://dev.mysql.com/get/mysql80-community-release-el8-1.noarch.rpm yum install mysql-community-server systemctl start mysqld但很多公司内网服务器不能直接访问外网这时候你需要在一台有网的机器上把 rpm 包和依赖包下载好再拷贝进去离线安装。遇到“centos8 安装离线 mysql 依赖”这种需求我一般这样处理先在有网的机器上用yumdownloader --resolve mysql-community-server拉全所有依赖 rpm再打包传到内网用rpm -ivh *.rpm安装。如果提示依赖缺失比如缺libaio、numactl-libs、openssl-devel按提示逐个下载对应的 rpm 包补进去就行。这里想多说一句离线安装出现依赖问题时不要看到缺什么就去搞什么先看清楚当前系统的版本和架构再到packages.ubuntu.com或 rpmfind 上选匹配版本。强行--nodeps安装一时爽后面启动直接火葬场。2.3 Docker 安装本地练习最省心如果你想快速起一个 MySQL 环境练手Docker 是最省心的方式三四行命令就搞定docker run -d \ --name mysql-demo \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ -e TZAsia/Shanghai \ -v /data/mysql:/var/lib/mysql \ mysql:8.0注意几个参数MYSQL_ROOT_PASSWORD是初始化 root 密码TZ设置时区否则NOW()可能差 8 个小时数据目录挂载到宿主机容器删了数据还在。如果你要连本地已有的 MySQL把端口-p 3306:3306改成-p 3307:3306这类不冲突的映射即可。Docker 方式不适合做性能测试毕竟容器本身有网络和 IO 开销但用来学 SQL、练索引、测试存储过程体验非常顺滑。想重置环境就docker rm -f mysql-demo再来一遍完全无损。2.4 客户端工具Workbench 与 Navicat 的取舍有了服务端还得有趁手的图形工具。官方自带的 MySQL Workbench 完全免费功能涵盖连接管理、SQL 编辑、ER 图、导入导出、性能监控。它的使用教程其实没什么好背的核心就两个新建连接时填 Host、Port、User、Password写完 SQL 后点闪电按钮执行点“不是闪电那个”则是格式化 SQL。Workbench 的查询结果区还可以直接编辑数据很适合新手快速验证。Navicat 我一直觉得是 Windows 生态里最顺手的 MySQL 工具之一界面响应快表结构可视化管理很成熟。但官方版本是收费软件网上搜“navicat 17 for mysql 注册码”“navigator for mysql 免费版”这类东西很容易踩到盗版和激活工具不仅有法律风险还容易中病毒。我的建议是预算允许就买正版或者干脆用免费的老版本、开源替代品 DBeaver。记住一句工具的洁癖直接影响写代码的质感别在这上面贪便宜。另外如果你做数据迁移可能用到 sqoop 从 MySQL 导数据到大数据平台。sqoop 连接不上 mysql 时第一反应不应该是调驱动而是先确认 MySQL 的bind-address、端口是否对外开放、账号是否授权了对应 host。九成连接问题都出在这三个地方。3. SQL 基本功从建表到增删改查一次讲透3.1 数据类型选不好后面全是泪MySQL 的数据类型按大类分就是数值型、字符串型、日期时间型三大类。很多初学者会问“mysql 可以存储整数数值的是哪几个”——这个问题很基础但选错类型在生产上真的很要命。数值类型里整数分为TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT区别是存储长度不同。比如TINYINT占 1 字节范围 -128 到 127INT占 4 字节范围约 -21 亿到 21 亿。用身份证号这种超过INT范围的数字要么用BIGINT要么用VARCHAR。业务主键上我一般直接用BIGINT UNSIGNED AUTO_INCREMENT给自己留够余地别等表几千万数据了再改主键类型。字符串类型里CHAR是定长VARCHAR是变长。存 md5、手机号这种固定长度的用CHAR有微弱的检索优势存标题、正文用VARCHAR更省空间。至于TEXT我会尽量避免作为索引字段而且它在 8.0 之前的某些版本里有默认值限制设计表时能拆就拆。日期时间类型DATETIME存日期加时间和时区无关TIMESTAMP存的是 UTC 时间受时区影响。日志表我习惯用DATETIME避免服务器时区设置错误导致数据差 8 小时。注意不要把时间存成字符串查询效率低日期函数也用不上。3.2 建库建表与修改结构的基本姿势建库的语法很简单CREATE DATABASE IF NOT EXISTS demo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;utf8mb4_general_ci里的ci是 case insensitive就是字符串比较默认不区分大小写。后面你会遇到“mysql 自动忽略大小写”的困惑其实根源就在字符集排序规则上。建表时字段名和类型要想清楚但更关键的是几个常见约束CREATE TABLE user ( id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 主键, name varchar(50) NOT NULL COMMENT 姓名, age int NOT NULL DEFAULT 0 COMMENT 年龄, email varchar(100) DEFAULT NULL COMMENT 邮箱, 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_email (email) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;注意几点AUTO_INCREMENT字段必须是索引ON UPDATE CURRENT_TIMESTAMP会在更新行时自动维护更新时间这个非常好用unique key用来保证字段唯一性但如果你要给一张已经有很多重复数据的表加唯一索引MySQL 会直接报错。处理办法是先查重复数据保留一条删掉其他再添加索引。改表结构用的是ALTER TABLEALTER TABLE user ADD COLUMN phone varchar(20) DEFAULT NULL COMMENT 手机号; ALTER TABLE user MODIFY COLUMN age smallint NOT NULL DEFAULT 0 COMMENT 年龄; ALTER TABLE user DROP COLUMN phone;这里要提醒一句生产环境千万慎用ALTER TABLE。大表加字段或改类型时MySQL 会在底层重建表可能锁住读写。8.0 支持ALGORITHMINSTANT的瞬时加列但也不适合所有操作。规范做法是在低峰期执行或者用pt-online-schema-change这类工具。3.3 除了 selectUPDATE 也要讲明白查询大家都很熟SELECT、JOIN、GROUP BY、ORDER BY这些基础不过多啰嗦。我重点说排序ORDER BY后面可以跟多个字段比如ORDER BY age DESC, id ASC意思是先按 age 倒序age 相同的再按 id 正序。这个在写排行需求时很常用。MySQL 里UPDATE的完整语法一般是UPDATE table_name SET column1 value1, column2 value2 WHERE condition写更新语句最容易出事的地方有两个一是忘写WHERE结果变成全表更新建议在开发环境顺手把事务开着再执行二是SET子句里出现了子查询而且这个子查询的源表跟要更新的表是同一张。比如你想把用户表的积分设为所有用户积分的平均值直接写UPDATE user SET score (SELECT AVG(score) FROM user);在 MySQL 里会报错You cant specify target table user for update in FROM clause。原因很简单你不能在更新一张表的同时去子查询里读同一张表MySQL 会认为读写冲突。解决办法是包一层临时表UPDATE user SET score (SELECT avg_score FROM (SELECT AVG(score) AS avg_score FROM user) AS tmp);很多人面试或笔试时卡在这个“更新子查询”上其实就记一句话子查询里不能直接出现正在更新的表想绕过去就多包一层。3.4 常用 SQL 语句清单顺手收藏我把日常用得最多的 SQL 场景整理成下表每一条都是可以直接抄作业的场景示例分页查询SELECT * FROM user ORDER BY id LIMIT 10 OFFSET 20;去重统计SELECT COUNT(DISTINCT user_id) FROM order;分组统计SELECT dept_id, COUNT(*) FROM employee GROUP BY dept_id;条件分支SELECT name, IF(age 18, 成年, 未成年) AS tag FROM user;模糊搜索SELECT * FROM user WHERE name LIKE 张%;多表连接SELECT u.name, o.order_no FROM user u INNER JOIN orders o ON u.id o.user_id;行转列SELECT user_id, MAX(CASE WHEN attrage THEN val END) AS age FROM ext GROUP BY user_id;拼接字符串SELECT CONCAT(first_name, last_name) AS full_name FROM user;取当前时间SELECT NOW(), CURDATE();字段加固定值SELECT id, score 5 AS bonus_score FROM exam;关于SELECT score 5这类表达式我想多说一句它不会改变表里存的数据只是查询结果里显示“加 5 之后的值”。如果你想要真正把值改掉那才是UPDATE exam SET score score 5。这个区别新手特别容易搞混。4. 进阶核心索引、事务、存储过程与连接池4.1 索引是怎么加速查询的为什么不能乱建索引的本质是“目录”。没有任何索引的时候MySQL 要一行一行扫全表刷过千万行的表必然慢有了索引它通过 B 树结构快速定位数据查询复杂度从 O(n) 降到 O(log n)。B 树你可以粗浅理解成一棵所有数据都落在叶子节点的多路搜索树叶子节点之间用指针串联所以排序和范围查询非常舒服。InnoDB 里有两种重要索引聚簇索引和二级索引。聚簇索引主键索引叶子节点直接存储整行数据二级索引普通索引、联合索引叶子节点存的是索引列和主键值。你通过二级索引查数据时先查到主键再用主键回聚簇索引里去取整行这个过程叫“回表”。这也是为什么很多优化会提到“覆盖索引”——索引里已经包含了你要的字段就不用回表了。创建索引的语法CREATE INDEX idx_name_age ON user(name, age); ALTER TABLE user ADD UNIQUE INDEX uk_email(email);设计联合索引时最核心的原则是“最左前缀原则”。(name, age)这个索引能加速WHERE name 张三也能加速WHERE name张三 AND age20但无法加速WHERE age20。因为 B 树先按 name 排序name 相同才按 age 排序你直接跳过了 name 去查 age树就没法往下走了。我给新人的建索引口诀是查询频繁、区分度高、更新少的列才值得建索引不要每个字段都建索引太多会导致写入变慢、占用磁盘LIKE %xx%这种前置模糊查询索引基本失效联合索引里把等值查询的字段放左边范围查询的字段放右边。4.2 事务与隔离级别面试必问的硬骨头事务就是一组要么全部成功、要么全部失败的数据库操作。经典的转账场景A 扣钱、B 加钱两个操作必须一起成功或一起失败。MySQL 的 InnoDB 天然支持事务靠 undo log 实现回滚靠 redo log 实现崩溃恢复靠锁和 MVCC 实现并发控制。事务的四个特性英文缩写 ACID也就是原子性、一致性、隔离性、持久性。面试官接下来几乎必问事务隔离级别有几种默认是什么InnoDB 默认的隔离级别是REPEATABLE READ可重复读。四个隔离级别分别是READ UNCOMMITTED读未提交能读到别的事务还没提交的数据脏读。READ COMMITTED读已提交只能读已提交数据解决了脏读但可能不可重复读。REPEATABLE READ可重复读同一个事务里多次查询结果一致解决了不可重复读但有幻读风险。InnoDB 通过 next-key lock 基本解决了幻读。SERIALIZABLE串行化事务完全串行性能最差一般不用。MVCC多版本并发控制是 InnoDB 实现高并发的关键。它通过隐藏字段维护版本链让普通的SELECT不阻塞写、普通写不阻塞读。这个知识点得很细面试时要能讲清楚 undo log 版本链和 ReadView 的生成时机。4.3 存储过程能用但别滥用存储过程就是把一段 SQL 逻辑预先编译保存在数据库里客户端直接调用。它的好处是减少网络传输、封装复杂逻辑、复用性高缺点是调试困难、版本管理困难、数据库压力大。我个人的态度简单封装可以复杂业务逻辑不要全塞进去因为后期维护真的会骂人。一个带错误处理的存储过程示例DELIMITER $$ CREATE PROCEDURE transfer( IN from_account INT, IN to_account INT, IN amount DECIMAL(10,2), OUT result_code INT, OUT result_msg VARCHAR(200) ) BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; SET result_code -1; SET result_msg transaction failed; END; START TRANSACTION; UPDATE account SET balance balance - amount WHERE id from_account; IF ROW_COUNT() 0 THEN SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT from account not exists; END IF; UPDATE account SET balance balance amount WHERE id to_account; IF ROW_COUNT() 0 THEN SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT to account not exists; END IF; COMMIT; SET result_code 0; SET result_msg success; END$$ DELIMITER ;这里DECLARE EXIT HANDLER FOR SQLEXCEPTION就是捕获异常用的一旦 SQL 执行异常自动回滚事务。如果你用 mysql 存储过程的时候遇到报错排查顺序一般是先确认 DELIMITER 是否设置了再确认变量声明是否放在最前面接着用SHOW PROCEDURE STATUS看过程是否存在最后在调用时用CALL transfer(...)并把OUT参数带上才能看到具体的错误信息。4.4 数据库连接池性能瓶颈的隐形杀手很多初学者学 MySQL 时只关注语句忽略了后端程序与数据库之间的连接管理。每次请求都新建连接是非常昂贵的操作因为 MySQL 需要分配线程、验证权限、建立上下文。连接池的作用就是提前建好一批连接用的时候取、用完归还避免频繁建立和销毁连接。主流的 Java 连接池 HikariCP 有核心参数maximumPoolSize最大连接数、minimumIdle最小空闲连接数、connectionTimeout获取连接的超时时间、maxLifetime连接最大存活时间。配置连接池别盲目调大连接数不是越多越好。数据库实例能同时处理的连接是有限的连接过多反而导致上下文切换频繁、性能下降。通常一个后端实例的池子配 10~30 个就够用了而不是一上来就 100 个。判断连接池是不是瓶颈看两个指标活跃连接数是否接近上限、获取连接等待时间是否变长。生产上还常遇到“Too many connections”的报错这时候不是扩大连接池就能解决的要排查是不是有慢 SQL 占着连接、或者某个服务没有正确归还连接。4.5 高频面试题快速串讲这部分把我最常被问到的 MySQL 面试题按主题列一遍纯当提纲供你查漏补缺主题核心问题回答要点基础MySQL 和 Redis 的区别MySQL 持久化、强一致、关系模型Redis 缓存、内存操作、多种数据结构索引为什么用 B 树而不用 B 树/哈希B 树非叶子节点不存数据单节点能存更多索引树矮范围查询通过链表相邻叶子遍历索引什么情况索引会失效前置模糊、函数包裹、隐式类型转换、违反最左前缀、优化器判断全表更快事务什么是幻读怎么解决事务内两次查询同一条件第二次查到其他事务新插入的数据InnoDB 的 next-key lock日志redo log、binlog、undo log 分别干嘛redo 崩溃恢复、binlog 主从复制和时间点恢复、undo 回滚和 MVCC优化一个慢 SQL 怎么排查EXPLAIN 看 type/key/rows开慢查询日志看索引是否命中再考虑改写 SQL架构主从复制原理主库写 binlog从库 IO 线程拉取写 relay logSQL 线程重放特殊mysql 中 int5 会怎样是表达式计算查询结果加 5不会改变原表数据深入浅出地讲MySQL 面试准备其实就是“原理 场景 排查思路”三者结合。只背概念没有案例面试官一问“线上遇到死锁怎么办”你就接不住。5. 日常运维与自动化备份、大小写、端口这些事5.1 用 bat 脚本实现自动备份Windows 环境下给 MySQL 做定时自动备份很多人以为要装复杂软件其实一个批处理脚本就够了。我这边经常写的是这个echo off set userroot set password123456 set host127.0.0.1 set port3306 set dbnamedemo set backup_dirD:\mysql_backup set date_str%date:~0,4%%date:~5,2%%date:~8,2% set backup_file%backup_dir%\backup_%dbname%_%date_str%.sql if not exist %backup_dir% mkdir %backup_dir% D:\mysql-8.0.46-winx64\bin\mysqldump.exe -h%host% -P%port% -u%user% -p%password% --single-transaction --routines --triggers %dbname% %backup_file% echo backup completed: %backup_file%这段脚本有两个坑要注意。第一个是中文乱码如果你的库里有中文建议 mysqldump 时加--default-character-setutf8mb4并且输出的 SQL 文件保存为 UTF-8 编码。第二个是--single-transaction对 InnoDB 表可以保证备份期间不锁库这是 InnoDB 引擎特有的。如果需要清理太久远的备份可以在脚本后面加一句forfiles /p %backup_dir% /m *.sql /d -7 /c cmd /c del path保留最近 7 天就够。5.2 大小写敏感问题别再被坑了很多人在 MySQL 里验证字符串相等时发现“大小写似乎不区分”这主要和字符集的排序规则有关。utf8mb4_general_ci中的ci意味着不区分大小写所以SELECT abc ABC返回 1。如果你需要区分大小写有两种方式建库/建表时用utf8mb4_bin排序规则或者在查询时加BINARYSELECT * FROM user WHERE BINARY name Tom;有人问“kingbase mysql 模式字符串不区分大小写咋回事”这里也顺带说一句第三方数据库在做 MySQL 兼容模式时默认排序规则往往对齐 MySQL 的ci规则所以表现也是不区分大小写这不算 bug而是设计选择。如果业务上严格要求区分要把排序规则显式设置成bin或者用BINARY操作。5.3 端口号、远程连接和 sqoop 连不上的问题MySQL 默认端口是 3306。这个值在启动配置里改改完重启服务[mysqld] port3307很多人本地 MySQL 起不来通常是端口被占用可以用netstat -ano | findstr 3306看一下是被谁占了。远程连接不上时除了看端口还要看bind-address是不是绑定了127.0.0.1如果只想内网访问推荐保持默认绑定。改了绑定地址后记得授权账号的 host像这样CREATE USER app% IDENTIFIED BY password; GRANT SELECT, INSERT, UPDATE, DELETE ON demo.* TO app%; FLUSH PRIVILEGES;用 sqoop 从 MySQL 导数据时连接不上排查顺序也是如此先用telnet 主机 3306看端口通不通再确认账号 host 权限最后才是检查 JDBC 驱动版本。5.4 忘记 root 密码后的本地重置思路先把前提说清楚这种方法只适合自己管理的数据库忘记密码后恢复访问而且要求你能登录到 MySQL 所在的操作系统。对别人的库做任何“数据库解密”操作都有法律风险千万别碰。Linux 下的重置步骤一般是停掉 MySQL 服务systemctl stop mysqld。在/etc/my.cnf的[mysqld]段加一行skip-grant-tables重启服务。用mysql -uroot免密登录执行FLUSH PRIVILEGES; ALTER USER rootlocalhost IDENTIFIED BY NewPassword123!;把skip-grant-tables那行注释掉重启服务。整个过程最需要注意的是skip-grant-tables模式下 MySQL 没有任何鉴权任何人连上就是管理员所以这个配置只能临时用改完密码必须立刻去掉并重启。5.5 数据库压测与日常巡检建议最后提一下日常巡检。我会在业务低峰期跑几条命令检查服务器层面show global status里的线程数、连接数、慢查询数再配合EXPLAIN抽查线上慢 SQL。慢查询日志开启方式SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 1;生产环境把long_query_time设为 1 秒比较合理如果某条 SQL 经常出现在慢日志里先用EXPLAIN看执行计划里的typeALL就是全表扫描ref、range都算有索引参与。这个动作养成习惯后很多性能问题都能提前发现而不是等用户卡到投诉了才去救火。6. 常见问题与避坑实录6.1 这些报错我当年都被坑过我把自己和其他同事在 MySQL 使用过程中遇到最多的几个报错整理出来做成速查表遇到类似问题可以直接对号入座。报错或现象可能原因解决办法Access denied for user rootlocalhost密码错误或账号 host 限制确认密码必要时走 skip-grant-tables 重置Cant connect to MySQL server on x.x.x.x (10060)网络不通、防火墙拦截、MySQL 未监听到对应网卡telnet 测试端口检查 bind-address检查防火墙Authentication plugin caching_sha2_password cannot be loaded客户端版本太老不认识 8.0 新认证插件升级客户端驱动或把账号改成 mysql_native_password不推荐长期用Table xxx doesnt exist表名大小写或字符集排序规则问题检索show tables确认表名Windows 下注意大小写配置Data too long for column字符串超出字段长度修改字段类型或截断数据Deadlock found when trying to get lock多个事务加锁顺序不一致统一加锁顺序缩短事务时间必要时重试机制Expression #1 of SELECT list is not in GROUP BYSQL 模式包含 only_full_group_by 且查询不规范改写查询把非聚合字段放进 GROUP BY 或使用 ANY_VALUE 包裹6.2 一句 SQL 从慢到快的排查思路遇到慢查询不要急着加索引先用EXPLAIN看一下 MySQL 打算怎么执行。重点看几列type从好到差依次是system、const、eq_ref、ref、range、index、ALL看到ALL基本意味着全表扫描key看实际用到的索引名rows是预估扫描行数数量巨大就要警惕。我曾经处理过一个分页查询慢的问题SQL 长这样SELECT * FROM orders ORDER BY create_time DESC LIMIT 10000, 20;索引是加在create_time上的但深分页还是慢。原因很好理解MySQL 要先把前面 10000 行都扫描出来然后扔掉只留最后 20 行。优化思路是“延迟关联”或者叫“先查主键再回表”SELECT o.* FROM orders o INNER JOIN ( SELECT id FROM orders ORDER BY create_time DESC LIMIT 10000, 20 ) tmp ON o.id tmp.id;这样子查询里只扫描主键和排序列大大减少回表的代价。这种写法在报表、分页接口里非常实用属于那种知道和不知道差距很大的技巧。6.3 真遇到大表改动怎么不坑业务生产环境大表加索引、加字段最怕锁表。8.0 的很多 DDL 已经支持在线执行但也不是绝对的。我的建议顺序是先看表数据量再看是否有长事务占用然后在低峰期操作最后准备一个回滚方案。如果表超过千万行加索引时建议用ALGORITHMINPLACE, LOCKNONE的语法尝试ALTER TABLE big_table ADD INDEX idx_user_id (user_id), ALGORITHMINPLACE, LOCKNONE;如果发现锁等待严重立刻终止操作。实在没把握就先在从库测试或者用成熟的在线 DDL 工具。改表这种操作稳比快重要。6.4 关于“mysql 9.7”和旧版本的看法热搜词里出现了“mysql 9.7”这种关键词我大概知道你想找什么。实话实说MySQL 的社区版和商业版版本号一直是分开的企业里主流稳定版本已经验证过的是 5.7 和 8.0。对普通开发者和运维来说与其追一个刚发布的新版本不如把 8.0 今天能用的窗口函数、CTE、不可见索引这些特性用好。如果是学习练手装最新版体验新功能当然可以如果是生产环境还是踏实一点。版本升级也是一门工程SQL 行为变化、认证插件变化、第三方工具兼容性每一项都在劝你“不要为了新而新”。写在最后学 MySQL 这件事我的体会是开头最难的其实不是记命令而是建立整体认知框架。你得先知道它由哪几部分组成、一条 SQL 是怎么从客户端走到磁盘的再往里面填细节才有意义。很多人收藏了一堆 MySQL 教程但真正动手建库、建表、造数据、写慢 SQL 来验证索引的执行计划却很少做。所以这篇总结的最后我给一个具体建议拉一个 Docker MySQL选一个平时业务里让你头疼过的报表场景把表结构和数据量人为造大一点然后一步步优化到响应能用为止。这个过程走一遍比在评论区里看任何“面试宝典”都有用。如果你在实操中遇到这篇文章里没有覆盖到的报错先别急着发帖问自己按“连接层、Server 层、存储引擎层、配置项”这个顺序往下拆十有八九能定位到问题。踩过几次坑之后你会发现MySQL 其实挺“讲道理”的——它只是严格按照你给的配置和 SQL 在执行出问题时多问问自己我到底想让它做什么我告诉它的够不够准确。把这条思维线理顺了你就已经走完了“从入门到入魔”最难的一步。
返回列表