
说实话MySQL 可能是大多数开发者职业生涯里第一个真正深入用过的数据库。大学课程、课程设计、毕业设计再到工作后的第一个 JavaWeb 项目几乎都绕不开它。但很多人的 MySQL 认知停留在“能跑 CRUD”的层面一旦遇到安装失败、SSL 连接报错、事务没有正确提交导致数据对不上、索引建了查询还是很慢就完全不知道从哪里下手。我这次打算把这些年用 MySQL 积累下来的基础知识点、踩过的坑和排查思路完整串一遍覆盖安装部署、SQL 基本功、索引、事务与锁、存储过程、性能调优和面试高频题。适合刚入门的新手系统学习也适合已经写了几个月 CRUD、想回头查漏补缺的开发者参考。1. 版本选型与安装部署先把环境跑起来很多新手在安装这一步就被劝退了其实大部分安装失败不是操作问题而是版本和安装方式没选对。这一部分我按 Windows、Linux、Docker 三种场景分别说都是我自己实测过的流程。1.1 版本选择MySQL 8.0 还是 5.7先说结论新项目直接选 MySQL 8.0老项目如果用的是 5.7 也没必要强行迁移但要清楚两者的差异。MySQL 8.0 在 2018 年发布到现在已经是绝对的主流版本它的默认认证插件改成了caching_sha2_password5.7 及更早版本用的是mysql_native_password。这两者的差异直接导致了一个经典问题用老版本的客户端或者老版本 JDBC 驱动去连 MySQL 8.0会报Authentication plugin caching_sha2_password cannot be loaded。很多人网上搜到这个报错第一反应是改认证插件其实更省事的办法是升级客户端和驱动全部换成支持 8.0 的版本。另一个值得关注的点是 8.0 的能力提升。它加入了窗口函数、公共表表达式 CTE优化器也比 5.7 强很多还支持函数索引。对于基础学习和日常开发8.0 显然是更合适的选择。至于 5.7.26、8.0.44 这类具体的版本号建议直接去 MySQL 官网 archives 页面下载不要用第三方站点避免下载到被篡改的安装包。还要注意从 5.7.28 开始官方才提供 ARM64 架构的安装包如果你用的是 Apple Silicon 芯片的 Mac 或者 ARM 云服务器下载时一定要认准平台标识。1.2 Windows 安装与初始化常见失败点全梳理Windows 下安装 MySQL 有两条路一个是官方的 MySQL Installer 图形安装器一个是 ZIP 压缩包手动安装。我个人的建议是如果只是想快速跑起来用 ZIP 包反而更可控因为 Installer 对旧版 Windows 的兼容性有时会出问题比如不少人在启动安装器时遇到e0434352这个错误这其实是 .NET Framework 的运行时错误不是 MySQL 本身的问题往往需要先修复或重装 .NET Framework 才能继续。与其被这种环境问题折腾不如走 ZIP 路线。ZIP 安装的完整流程是这样的。先把压缩包解压到比如C:\mysql-8.0.44-winx64在目录下新建my.ini内容里最关键的是basedir和datadir要指向正确路径同时设定端口和字符集[mysqld] basedirC:/mysql-8.0.44-winx64 datadirC:/mysql-8.0.44-winx64/data port3306 character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci [client] default-character-setutf8mb4然后以管理员身份打开命令行先执行初始化命令mysqld --initialize-insecure这一步会生成 data 目录。注意这里有个选择如果加了--initialize-insecure初始 root 账号是没有密码的如果不加系统会生成一个随机密码写在 data 目录下的.err日志文件里。新手建议用--initialize-insecure登录后再自己改密码省得在日志里翻找随机密码。初始化完成后执行mysqld --install MySQL注册 Windows 服务然后net start mysql启动。这里也是很多人卡住的地方最常见的问题是“服务无法启动”排查思路很简单先看 data 目录下的.err日志文件错误原因基本都写在里面。我遇到过的情况有四种一是my.ini路径写错了MySQL 找不到配置二是初始化步骤忘了执行datadir 不存在三是 3306 端口被其他程序占用了可以用netstat -ano | findstr 3306查四是文件目录权限不足尤其要注意 data 目录不能放在需要管理员权限才能写入的位置。改完配置后记得重新初始化或删除旧的 data 目录再试。1.3 Linux 离线安装与 Docker 部署Linux 上装 MySQL 最主流的是 RPM 包和 Docker 容器两种方式。RPM 方式适合内网服务器、尤其是不能随便联网的环境。典型的离线安装就是先在有网的机器上下载mysql-8.0.44-1.el7.x86_64.rpm-bundle.tar解压后会得到 common、libs、client、server 等几个 RPM 包按依赖顺序依次安装rpm -ivh mysql-community-common-*.rpm rpm -ivh mysql-community-libs-*.rpm rpm -ivh mysql-community-client-*.rpm rpm -ivh mysql-community-server-*.rpm安装完成后执行mysqld --initialize初始化然后systemctl start mysqld启动服务用grep temporary password /var/log/mysqld.log找到临时密码。这里提醒一下RPM 安装的 MySQL 默认密码策略比较严格第一次登录后必须修改密码而且新密码要满足长度和复杂度要求可以通过SHOW VARIABLES LIKE validate_password%查看相关配置。如果服务器上装了 Docker用容器部署 MySQL 会快很多。一条命令就能跑起来docker run -d --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDRoot123456 \ -e MYSQL_ROOT_HOST% \ -v /opt/mysql-data:/var/lib/mysql \ --restart always \ mysql:8.0MYSQL_ROOT_HOST%表示允许 root 从任何主机远程登录这在本地开发时方便但生产环境千万别这么干。Docker 部署最常见的失败是容器启动后立刻退出先别急着删容器用docker logs mysql8看日志大部分情况是挂载目录权限不对尤其是 CentOS 等系统开了 SELinux 的情况下需要给挂载目录补上安全上下文或者加:z参数。另外镜像拉取失败也很常见可以配置国内镜像加速源再把mysql:8.0换成mysql:8.0.44这种精确版本减少镜像变化带来的不确定性。2. 库表设计与 SQL 基本功连接、建库、增删改查环境跑起来之后接下来就是最核心的 SQL 基本功。这一部分不只是语法更重要的是理解 MySQL 的账号体系、字符集和数据类型的取舍。2.1 连接方式与账号体系命令行连接数据库的基本写法是mysql -h 127.0.0.1 -P 3306 -u root -p执行后会提示输入密码。这里的-h是主机地址-P是端口-u是用户名-p表示需要密码。新手容易把-P和-p搞混大写 P 是端口小写 p 是密码输错一下就会报奇怪的错误。MySQL 的账号是由“用户名 主机”共同决定的rootlocalhost和root%是两个不同的账号。localhost只允许本机连接%是通配符表示任意主机。很多人远程连接报Access denied或者Host not allowed大概率就是账号的 host 限定太死或者根本没有创建对应的远程账号。正确的做法是专门为应用创建账号而不是开放 root 的远程权限CREATE USER app% IDENTIFIED BY App123456; GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO app%; FLUSH PRIVILEGES;这里顺手提一个热词里高频出现的问题mysql -u -p 执行SQL超时。命令行执行 SQL 超时一般不是 SQL 本身的问题而是连接阶段就卡住了。MySQL 有几个超时参数connect_timeout控制连接握手超时net_read_timeout和net_write_timeout控制读写数据时的超时wait_timeout和interactive_timeout控制空闲连接回收。如果网络环境不好可以先把connect_timeout调大一点同时检查防火墙是否放行了 3306 端口。还有一类连接问题跟 SSL 相关。MySQL 默认会启用 SSL 加密连接但某些旧版客户端或者特殊的中间件在协商 SSL 协议时会报SSL connection error比如SSL_CTX_set_min_proto_version之类的错误。如果是自己写的程序或者配置工具可以在连接串里加sslModeDISABLED跳过 SSL 握手要是用 JDBC参数是jdbc:mysql://127.0.0.1:3306/shop?sslModeDISABLED。注意这只适合内网开发环境公网连接不要轻易关 SSL。2.2 数据库与表的 DDL 操作建库建表是每个项目的第一步但很多人是从复制粘贴开始从不思考为什么这么写。我先展示一个标准的建库建表语句CREATE DATABASE shop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, name VARCHAR(32) NOT NULL COMMENT 用户名, age INT DEFAULT 0 COMMENT 年龄, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0启用 1禁用, created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有两个细节值得展开。第一字符集一定用utf8mb4而不是utf8。MySQL 里的utf8实际上是 utf8mb3最多只能存 3 个字节的字符遇到表情符号会报错或者存成乱码utf8mb4才是完整的 UTF-8 编码。第二DEFAULT 0这个写法很常用很多场景下我们希望age、status这类字段不填时默认是 0而不是 NULL。NULL 和 0 在 SQL 里完全是两回事NULL 参与计算的结果还是 NULLWHERE age 0也查不出 NULL 的记录。所以字段设计时要明确默认值是 0 就用DEFAULT 0允许为空才用NULL。表结构不是一次定死的项目迭代中经常要改结构。常用的 ALTER 语句要熟练ALTER TABLE user ADD COLUMN email VARCHAR(64) NULL AFTER name; ALTER TABLE user MODIFY COLUMN age INT DEFAULT 0; ALTER TABLE user DROP COLUMN email;大表加字段要注意锁表问题。虽然 MySQL 8.0 支持了ALGORITHMINSTANT的瞬时加列操作但修改字段类型、加索引等操作仍然可能锁表。线上大表执行 ALTER 之前最好先评估数据量尽量在业务低峰期操作。2.3 增删改查与排序分页CRUD 是每天写的最多的代码但越是基础越容易出低级错误。我举一个常见的错误写法同时更新多条记录时没有加事务保护一条成功一条失败数据就对不上了。正确写法配合事务后面会专门讲。这里先看最基本的语法INSERT INTO user(name, age, status) VALUES(张三, 20, 0); UPDATE user SET age 21 WHERE id 1; DELETE FROM user WHERE id 1; SELECT id, name, age FROM user WHERE status 0 ORDER BY created_at DESC LIMIT 10;排序ORDER BY是面试和实际使用中出现频率都很高的点。默认是升序ASC降序要写DESC多字段排序时从左到右依次作为排序依据比如ORDER BY status ASC, created_at DESC先按状态排状态相同的再按时间倒序。这里要注意排序字段上有没有索引ORDER BY用不到索引就会产生 filesort数据量一大查询就慢。分页用LIMIT语法是LIMIT 偏移量, 行数比如第 2 页每页 10 条就是LIMIT 10, 10。深分页是个隐藏杀手LIMIT 100000, 10这种写法 MySQL 需要扫描前 10 万多行再丢弃性能很差后面讲优化的时候再细说。3. 索引查询提速的核心机制索引是 MySQL 基础里最值钱的部分没有之一。几乎所有的慢查询优化最后都会落到索引设计上。这一节我把索引的类型、创建方式、失效场景一次讲清楚。3.1 索引类型与创建语法MySQL 的索引从数据结构上讲InnoDB 存储引擎用的是 B 树。为什么不是二叉树、不是红黑树、也不是哈希表我打个比方B 树的每一层节点都像一个文件夹叶子节点才存放真正的数据而且叶子节点之间用指针串联。这样的结构让“按范围查”非常高效比如WHERE age BETWEEN 20 AND 30找到第一个满足条件的记录后顺着叶子节点的链表往后扫就行不需要频繁回溯。哈希索引虽然单点查询更快但不支持范围查询和排序所以 InnoDB 默认不使用哈希索引作为主索引。从功能上分类索引主要有这几种主键索引每张表只能有一个通常就是id数据物理存储顺序跟主键一致所以也叫聚簇索引。唯一索引列的值不能重复比如email、身份证号创建后插入重复值会报Duplicate entry错误。普通索引没有唯一性约束纯粹加速查询。组合索引多个字段联合建立一个索引是实际开发中最常用的索引类型。全文索引用于文本内容的模糊搜索InnoDB 从 5.6 开始支持但中文分词效果一般业务里用得少。创建索引的语法CREATE INDEX idx_user_name ON user(name); CREATE UNIQUE INDEX uk_user_email ON user(email); ALTER TABLE user ADD INDEX idx_name_age (name, age); DROP INDEX idx_user_name ON user;组合索引要特别理解“最左前缀原则”。idx_name_age (name, age)看起来是两个字段实际上它相当于先按 name 排序name 相同的再按 age 排序。所以WHERE name 张三能用到这个索引WHERE name 张三 AND age 20也能用到但单独WHERE age 20就用不上因为索引的左边第一列缺失B 树不知道从哪里开始找。组合索引的字段顺序很有讲究区分度高的字段放左边经常需要等值查询的字段也放左边。比如查询条件大多是status 0 AND created_at xxx索引就应该建(status, created_at)而不是反过来。3.2 索引失效与执行计划排查索引建了不等于查询就能用上。我整理了工作中最常见的索引失效场景每一个都踩过坑对索引列使用了函数比如WHERE YEAR(created_at) 2024这会让索引失效MySQL 8.0 虽然支持函数索引但普通索引不行。隐式类型转换比如phone字段是 VARCHAR查询却写WHERE phone 13812345678MySQL 会先把字段转成数字再比较索引就废了。模糊查询以前缀通配符开头LIKE %李用不了索引LIKE 李%可以。OR连接的条件里有一个非索引列整个查询可能都不走索引。NOT IN、!在某些情况下会让优化器放弃索引要具体看执行计划。索引列本身是允许为 NULL 的且查询条件出现IS NULL有些场景优化器会走全表扫描。排查索引有没有生效最核心的工具是EXPLAINEXPLAIN SELECT id, name FROM user WHERE name 张三;看输出里的几列就够了type从好到差依次是const、eq_ref、ref、range、index、ALL如果能到ref或range基本算正常ALL就是全表扫描要警惕key显示实际用到的索引名rows是预估扫描行数越小越好Extra里如果出现Using filesort表示排序没用上索引出现Using temporary表示用了临时表这两个都是优化重点。另外要理解“回表”。普通索引的叶子节点存的是主键值查询的字段如果不在索引里就要拿着主键再回聚簇索引里查一次完整数据行这个过程叫回表。想避免回表可以把要查的字段都塞进索引里这种叫覆盖索引。比如SELECT name, age FROM user WHERE name 张三如果组合索引是(name, age)那么索引叶子节点上就有 name 和 age 两个字段不需要回表Extra里会显示Using index。覆盖索引是查询优化里性价比很高的一招。4. 事务与锁理解并发和数据一致性的底层逻辑事务这个概念日常写 SQL 不一定天天用到但凡是要保证数据准确性的操作比如转账、下单、库存扣减都离不开它。锁则是事务并发执行时为了保证隔离性而引入的机制两者是强关联的。4.1 事务的 ACID 与隔离级别事务说白了就是一组 SQL 的捆绑执行要么全部成功要么全部失败。它要满足四个特性英文首字母缩写 ACID原子性Atomicity、一致性Consistency、隔离性Isolation、持久性Duration。我在面试常问自己MySQL 是靠什么实现这四个特性的原子性靠 undo log事务回滚时根据 undo log 恢复旧值持久性靠 redo log数据变更先写日志再落盘隔离性靠锁和 MVCC多版本并发控制一致性是前三者共同保证的结果。事务的基本写法START TRANSACTION; UPDATE account SET balance balance - 100 WHERE id 1; UPDATE account SET balance balance 100 WHERE id 2; COMMIT; -- 如果第二条执行失败执行 ROLLBACK;MySQL 里默认每条 SQL 是自动提交的也就是说单条语句本身就带了一个隐式事务。要手动控制多条语句成为一个整体必须显式START TRANSACTION最后用COMMIT提交或者ROLLBACK回滚。我见过太多人在 JavaWeb 项目里忘记加Transactional或者加了但方法内部捕获了异常没有抛出结果事务没生效数据写到一半就停在中间状态了。这是非常经典的线上事故原因。隔离级别是事务并发最大的考点。MySQL 支持四种隔离级别读未提交READ UNCOMMITTED、读已提交READ COMMITTED、可重复读REPEATABLE READ、串行化SERIALIZABLE。从低到高并发能力逐渐变弱但数据一致性越来越强。MySQL 的默认隔离级别是 REPEATABLE READ这一点和 Oracle 的默认 READ COMMITTED 不同是面试最爱挖的细节。各个级别要解决的并发问题分别是读未提交会出现脏读读到其他事务未提交的数据。读已提交解决脏读但会出现不可重复读同一个事务里两次相同的查询结果不一致。可重复读解决不可重复读通过 MVCC 让事务内多次读到的结果一致。串行化把所有事务强制串行执行开销最大实际生产极少使用。MySQL 的可重复读之所以能顺带解决一部分幻读问题靠的是 next-key lock也就是记录锁加间隙锁的组合。关于幻读面试里最常见的追问是“MySQL 的可重复读真的完全解决了幻读吗”我的回答是在大部分通过索引条件查询并配合锁的场景下可以但某些特殊场景下仍然可能发生这也是 Serializable 隔离级别依旧存在的意义。4.2 锁的分类与死锁场景锁的分类可以从多个维度看。按粒度分有表级锁和行级锁按模式分有共享锁和排他锁。InnoDB 支持行级锁MyISAM 只支持表级锁这也是为什么实际生产都用 InnoDB 而不是 MyISAM 的重要原因。共享锁和排他锁是最基础的两个概念。共享锁用 SQL 是SELECT ... LOCK IN SHARE MODE允许其他事务也加共享锁读但不允许别人改排他锁是SELECT ... FOR UPDATE加了之后其他事务既不能读也不能写准确说不能加任何锁。普通SELECT不加锁依赖 MVCC 实现多版本一致性读。UPDATE和DELETE会自动给涉及的行加排他锁。这里提醒一句行锁并不是真的锁“行”InnoDB 是通过索引来定位和锁定记录的如果 SQL 没走到索引行锁会退化成表锁并发性能骤降。所以前面第 3 节讲的索引设计和这里的锁机制是紧紧绑在一起的。死锁是并发写入时最怕遇到的事。举个例子事务 A 先更新 id1 的行事务 B 先更新 id2 的行然后 A 再去更新 id2B 再去更新 id1两边互相等对方的锁就形成死锁。InnoDB 会自动检测死锁并选择回滚一个事务让另一个继续错误信息一般是Deadlock found when trying to get lock; try restarting transaction。在 Java 里遇到这个异常合理的处理是捕获后重试一次。预防死锁的实战经验有三条一是多个事务操作多行数据时保持固定的加锁顺序二是尽量让事务短小精悍减少锁持有时间三是确保更新语句都走索引避免表锁放大冲突范围。查死锁现场可以用SHOW ENGINE INNODB STATUS\G最后一段LATEST DETECTED DEADLOCK会打印出两个事务的 SQL 轨迹定位非常直接。5. 存储过程与错误处理把逻辑放进数据库存储过程在很多互联网公司里并不常见但传统的 ERP、金融系统以及某些无框架的老项目中它依然是核心构件。学习它可以更好地理解 MySQL 的编程能力面试偶尔也会考。5.1 存储过程的入门写法存储过程本质上是保存在数据库端的一组可执行代码。为什么要用它最大的好处是可以把复杂的数据处理逻辑收敛在数据库侧减少应用和数据库之间的往返交互。缺点也很明显不好调试、版本管理困难、迁移麻烦所以它是一把双刃剑。一个最基础的存储过程例子DELIMITER // CREATE PROCEDURE sp_update_user_age(IN uid INT, IN new_age INT) BEGIN UPDATE user SET age new_age WHERE id uid; END // DELIMITER ; CALL sp_update_user_age(1, 30);重点讲一下DELIMITER这个命令。MySQL 默认用分号作为语句分隔符而存储过程内部不可避免要写分号如果不用DELIMITER //先把分隔符改成别的符号MySQL 会在第一个分号处就误认为过程定义结束了。写完过程后记得改回来。存储过程的参数有三种类型IN是输入参数OUT是输出参数INOUT既能传入也能传出。过程内可以定义局部变量用的是DECLARE 变量名 类型 DEFAULT 默认值注意局部变量和标准的SET 变量用户变量作用域完全不同。流程控制方面有IF、CASE、LOOP、WHILE、REPEAT几种写法类似通用编程语言但没有真正的调试断点所以写复杂的逻辑时我通常会把每个步骤的中间结果用SELECT临时查出来验证。5.2 错误处理与调试技巧存储过程里的异常处理是很多人觉得难的部分其实核心就是DECLARE HANDLER。比如我想保证过程内的多个更新操作要么全部成功要么全部回滚可以这样写DELIMITER // CREATE PROCEDURE sp_transfer(IN from_id INT, IN to_id INT, IN amount DECIMAL(10,2)) BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; RESIGNAL; END; START TRANSACTION; UPDATE account SET balance balance - amount WHERE id from_id; UPDATE account SET balance balance amount WHERE id to_id; COMMIT; END // DELIMITER ;DECLARE EXIT HANDLER FOR SQLEXCEPTION意思是过程中的任何 SQL 抛了异常就执行这个处理器ROLLBACK回滚当前事务RESIGNAL把原始错误继续向外抛出这样应用层能拿到准确的错误码。除了SQLEXCEPTION还可以按 SQLSTATE 捕获指定错误或者捕获NOT FOUND来处理游标读取到结尾的情况。有时候需要对数据做校验不满足条件时主动抛出错误用SIGNAL语句SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT 余额不足不能转账;45000是 MySQL 里自定义错误的通用标准状态码。调试存储过程比较有效的办法有两个一个是在过程体里临时加上SELECT打印中间值确认后删掉另一个是查看SHOW WARNINGS很多过程执行时报错但不会直接打印细节这个命令能把最近一次执行产生的警告和错误捞出来。刚学存储过程的时候不要一上来就写几百行的逻辑先写一个小的 UPDATE 过程再逐步加参数、加异常处理、加事务每一步都验证通过再往下加。6. 运维实战常见错误与性能调优基础语法掌握了接下来真正考验人的是运维和排查能力。我把常见错误整理成一个速查表再讲讲连接池和基础调优参数这些都是实用价值最高的部分。6.1 常见连接与启动错误的排查速查错误现象常见原因处理思路net start mysql服务无法启动未初始化 data 目录、my.ini 路径错、端口被占、目录权限不足看 data 目录下 .err 日志确认初始化命令已执行查 3306 端口占用Installer 报 e0434352.NET Framework 运行时损坏修复或重装 .NET Framework或改用 ZIP 包手动部署Access denied for user(1045)密码错误、账号 host 不匹配、认证插件不兼容核对密码检查userhost8.0 需用支持的客户端Cant connect to MySQL server(2003)服务未启动、bind 地址限制、防火墙拦截systemctl status mysqld检查skip-networking放行 3306Host xxx is not allowed(1130)账号只允许 localhost 连接创建user%或指定网段的账号SSL connection error客户端与服务器 SSL 协议版本不匹配升级客户端开发环境可用sslModeDISABLEDDuplicate entry(1062)唯一索引约束被违反检查插入或更新的数据是否重复Sqoop 等工具连接不上 MySQLJDBC 驱动版本过旧、连接串格式错误、网络不通使用 8.0 对应驱动连接串带参数先本地 telnet 测端口这里特别提几个容易忽略的排查习惯。第一MySQL 的错误日志永远是第一现场Windows 下在 data 目录的.err文件里Linux 下在/var/log/mysqld.log别一上来就百度日志里通常已经写明原因了。第二连接问题先用telnet 127.0.0.1 3306或者nc -zv 127.0.0.1 3306测端口是否通再考虑账号权限和应用层配置这样能把问题缩小到网络层还是数据库层。第三修改完my.cnf或my.ini后一定要记得重启服务很多人改了半天参数没生效结果只是没重启。6.2 连接池与基础调优参数为什么应用连接 MySQL 要用连接池因为 MySQL 每次新建连接都要经历 TCP 三次握手、身份认证、权限检查等步骤耗时可能几十毫秒甚至更多高并发场景下频繁建连会直接把服务拖垮。连接池的核心就是复用已经建好的连接用空间换时间。常见的连接池有 HikariCP、Druid、C3P0、DBCPSpring Boot 2.x 默认是 HikariCP它性能很好且配置简单国内项目尤其是配合 MyBatis 的用阿里的 Druid 也很多因为它自带监控页面。Druid 的关键配置参数大概是这样的spring: datasource: druid: initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000 test-while-idle: true validation-query: SELECT 1initial-size是启动时初始化的连接数max-active是最大活跃连接数max-wait是获取连接的最大等待时间超过就报错。这里我特别想说一个坑max-active不是越大越好每个连接背后就是一个 MySQL 连接数据库的max_connections默认只有 151如果应用开了太多实例每个实例又配了很大的连接池数据库连接数很容易被打满导致新的请求全部超时。做容量评估时要把应用实例数和单实例连接池大小相乘远低于数据库上限才是安全的。MySQL 服务端的调优最核心的参数是innodb_buffer_pool_size这是 InnoDB 的缓冲池大小决定了数据页和索引页能有多少缓存在内存里。直觉上的经验值是设置为物理内存的 50% 到 70%一个 16G 内存的机器可以给到 8G 到 11G但要预留系统和其他进程的内存。查看和修改方式SHOW VARIABLES LIKE innodb_buffer_pool_size; SET GLOBAL innodb_buffer_pool_size 8 * 1024 * 1024 * 1024;注意SET GLOBAL是运行时生效永久修改要写到配置文件里。慢查询日志是排查性能问题最直接的工具开启后所有超过阈值的 SQL 都会记下来slow_query_logON slow_query_log_file/var/log/mysql-slow.log long_query_time2long_query_time设置成 2 秒就是超过 2 秒的查询都记录。拿到慢 SQL 后用EXPLAIN分析执行计划大多数情况下要么是没走索引要么是排序和临时表过大要么是深分页针对性地加索引或改写 SQL 就能解决。7. 面试高频题与进阶方向最后的这一部分我给准备面试的朋友整理了一份高频题速查再聊聊从基础走向实战延伸的方向。MySQL 的面试题其实万变不离其宗核心考的就是索引、事务、锁和存储引擎这四块。7.1 高频面试题与回答思路MyISAM 和 InnoDB 的区别。InnoDB 支持事务、行级锁、外键MyISAM 都不支持InnoDB 有聚簇索引数据挂在主键索引的叶子节点上MyISAM 则是索引和数据分开存储。现代项目几乎都用 InnoDB。为什么 InnoDB 用 B 树而不是红黑树或者哈希。哈希只适合等值查询不支持范围查找红黑树虽然平衡但树的高度更高一个 1000 万行的表红黑树要二十几层每层都是一次磁盘 IOB 树三层就能搞定而且叶子节点串联起来非常适合范围扫描。什么是聚簇索引和二级索引。聚簇索引即主键索引叶子节点存整行数据二级索引叶子节点存主键值查询时可能需要回表。DROP、TRUNCATE、DELETE的区别。三者都是删除但DELETE是 DML可以加 WHERE、可以回滚不释放表空间TRUNCATE是 DDL清空全表并重置自增值不可回滚DROP是直接删除表结构。MVCC 的工作原理。简单说就是每一行数据有隐藏的版本号事务的读操作通过 undo log 构造历史版本配合 Read View 实现不加锁的一致性读。这是 MySQL 可重复读隔离级别的底层支撑。内连接、左连接、右连接的区别。内连接只返回两表匹配的行左连接返回左表全部行加右表匹配行右表不匹配的部分补 NULL右连接相反。LEFT JOIN 在数据不一致时最容易出问题多对多关联还会产生重复行写 SQL 时要格外小心。一条更新语句的执行流程。连接器 → 分析器 → 优化器 → 执行器 → 存储引擎执行时先查内存缓冲池命中直接改同时写 undo log 和 redo log事务提交时保证 redo log 落盘。如何优化一条慢 SQL。先EXPLAIN看执行计划没走索引就加索引走了索引还慢就看是否回表过多、是否排序和临时表过大再看 SQL 查询的字段是否能覆盖最后考虑改写 SQL 或者改表结构。面试的时候光背结论是不够的面试官一定会追问“为什么”。把每个结论背后的原理比如 B 树的层数怎么算、事务隔离级别和锁的对应关系完整讲一遍才能让人相信你是真的理解而不是背题。7.2 从基础走向实战扩展方向MySQL 基础学完实际操作中你会接触到很多关联技术这里简单梳理一下方向。一是数据同步和异构存储比如用 Flink CDC 订阅 MySQL binlog 同步到 ClickHouse实现实时分析再比如把 MySQL 的存量表结构自动转换成 TDengine 的超级表和子表用于时序数据场景。二是监控运维常见组合是 Zabbix 7.0 LTS 配合 MySQL 8.0采集 MySQL 的连接数、慢查询、缓冲池命中率指标报警规则加上服务不可用和主从延迟。三是不同语言生态的接入JavaWeb 项目最经典用 JDBC 或 MyBatis 配置数据源C 项目则用mysql-connector-c库注意链接时把库目录和头文件路径配好如果使用 JumpServer 这类堡垒机它内置的数据库可视化工具也能直接连 MySQL 做工单审批和操作审计。四是容器化环境里的备份恢复用 mysqldump 配合 cron 定时备份这在任何规模的项目里都是保命的底线。最后说几句实际的体会写到这里MySQL 基础这块我能想到的坑和知识点差不多都覆盖了。我个人在实际操作中的体会是学 MySQL 最忌讳的是只看不做一定要亲手把安装流程走一遍故意制造几个报错再解决掉这个过程中积累的排查经验比看一百篇教程都管用。遇到问题时先看官方文档和错误日志再去搜索顺序反了容易被各种复制粘贴的答案带偏。另外给生产环境的 MySQL 做任何变更前先备份再在测试环境演练一遍这句话我重复多少次都不嫌多。最后分享一个小技巧建索引和改表结构之前用EXPLAIN看一下当前 SQL 的执行计划改完之后再对比一次用rows的估算行数和Extra的变化来验证效果数据摆在那里性能有没有提升一眼就能看出来。基础的内容虽然不复杂但它是所有高级技巧的地基把地基打牢后面学主从复制、分库分表、Flink CDC 这些进阶方向都会顺畅很多。