
简介这份1000行MySQL学习笔记面向数据库初学者与想系统提升的开发者从Windows服务启动、客户端连接管理到库表创建与修改、存储引擎选择再到视图、触发器、存储过程、事务控制、索引优化等高级特性内容编排由浅入深可作日常查阅的速查手册。资源共1个docx文档压缩包约49KB体积轻量却涵盖常用SQL语法、字段约束、表选项及分区等细节适合离线阅读或打印对照。目前已有480人学习下载笔记中通过大量命令示例清晰展示CREATE TABLE字段约束、ALTER TABLE修改结构、SHOW ENGINES查看引擎等具体操作并对比InnoDB与MyISAM的适用场景帮助读者理解不同存储引擎在事务与读取性能上的取舍。无论是准备数据库面试、快速复习还是需要一份完整命令清单这份笔记都能提供扎实参考价值。1. “史上最全” MySQL 学习笔记到底在记什么从建库到跑批的完整闭环把一份 1000 行的 MySQL 学习笔记无论是 .docx 还是 Markdown拿在手里第一反应通常是“先收藏再说”。但我看到太多人收藏完就再也不打开真到写生产 SQL 时连 utf8mb4 和 utf8 的区别都说不清。这份笔记真正的价值不在于“1000 行”这个数量而在于它能不能覆盖一条完整链路建库建表、写增删改查、跑事务、设计索引、排查线上故障。如果你是要准备面试、接手老项目或者从零搭一套业务库这个方向确实值得投入。本文就按这条链路把它们拆开讲每一章都落到能复现的命令和参数上。2. 建库建表与字符集最容易被忽略的“地基”决定后续 90% 的坑2.1 字符集与排序规则utf8mb4 不是选完就完事在 MySQL 8.0 里默认字符集已经是 utf8mb4但 5.7 及更早版本默认还是 latin1。很多人迁移老库时只改了character_set_server却忘了改已有表的存储字符集结果就是中文写入报错Incorrect string value或者读取时出现一串问号。我一般会在建库前把三层设置一次做完-- 建库时明确指定字符集与排序规则 CREATE DATABASE user_center DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;逻辑说明CREATE DATABASE后面的DEFAULT CHARACTER SET决定了这个库下新建表的默认字符集COLLATE决定排序和比较规则。utf8mb4_0900_ai_ci里的0900对应 Unicode 9.0 标准ai表示口音不敏感ci表示大小写不敏感。如果业务需要大小写敏感的比较比如用户名登录校验就得换成utf8mb4_0900_bin。参数说明utf8mb4_general_ci在 MySQL 5.7 里很常见但性能略差8.0 直接用0900_ai_ci就行。另一个提醒是连接层也要设字符集否则表是 utf8mb4、客户端却是 latin1依然会乱码。连接串里建议显式加characterEncodingutf8JDBC或SET NAMES utf8mb4命令行。2.2 建表约束与自增主键整数类型选错会让你后悔半年很多新手建表时喜欢无脑INT AUTO_INCREMENT但业务量一旦上去INT的上限 21 亿并不算宽裕。日志表、流水表这类高频插入的表我个人会直接上BIGINT避免两年后迁移主键的惨剧。另一个常见问题是不加NOT NULL和DEFAULT导致应用层读到的空值无法被 MyBatis 映射引发空指针。下面这个例子是把约束写全的典型建表语句CREATE TABLE login_log ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, user_id BIGINT UNSIGNED NOT NULL COMMENT 用户ID, login_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 登录时间, ip VARCHAR(64) NOT NULL DEFAULT COMMENT 来源IP, status TINYINT NOT NULL DEFAULT 1 COMMENT 1成功 0失败, PRIMARY KEY (id), KEY idx_user_time (user_id, login_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci COMMENT登录日志表;逻辑说明BIGINT UNSIGNED把负数区间去掉可用上限翻倍到 1844 亿CURRENT_TIMESTAMP作为DEFAULT可以让插入时不传login_time也能自动带时间省掉一层应用代码。参数说明TINYINT用来存状态位足够不要用INT去扛一个只有 0 和 1 的字段VARCHAR(64)存储 IP 够用IPv6 最长 45 字符别留 255。索引idx_user_time是典型联合索引后面第四章会细讲为什么把user_id放前面。2.3 一条 SQL 把表结构看清SHOW CREATE TABLE 的读法接手老项目最怕的是没人告诉你哪张表有坑。与其翻文档不如直接执行SHOW CREATE TABLE它会把真实的建表语句、字符集、约束、索引全部打出来这是排查问题的第一现场。我每次排查慢查询都先跑这条语句确认表结构有没有被改过再看索引。字段注释里往往藏着业务逻辑的线索——比如status字段注释写“1待支付 2已支付 3已退款”你才知道查询条件该怎么写。这条命令不需要任何权限是 DBA 之外的开发最该熟悉的排查工具。3. SQL 高频操作与事务隔离让笔记里的语句真正能跑进生产3.1 DML 的三种写入姿势与“ON DUPLICATE KEY UPDATE”笔记里最容易被抄错的是 INSERT 的可选子句。三种写入姿势分别是普通插入、INSERT IGNORE、INSERT ... ON DUPLICATE KEY UPDATE。普通插入遇到唯一键冲突直接报错INSERT IGNORE会静默跳过冲突行而带ON DUPLICATE KEY UPDATE的写法可以把冲突变成一次更新。示例-- 按 user_id 维度写入评分存在则更新分数 INSERT INTO user_score (user_id, score, update_time) VALUES (10001, 95, NOW()) ON DUPLICATE KEY UPDATE score VALUES(score), update_time VALUES(update_time);逻辑说明ON DUPLICATE KEY UPDATE的触发条件是唯一索引或主键冲突。VALUES(score)在 MySQL 8.0.20 之前表示引用 INSERT 里准备写入的值8.0.20 之后官方推荐用别名写法避免歧义。参数说明这个语句的代价比普通 INSERT 略高因为 InnoDB 要先尝试插入、再走唯一索引检测冲突高并发秒杀场景要慎用。批量写入时一条语句带几百组值即可不建议一次塞几万行max_allowed_packet默认 64MB 很容易被打满。3.2 事务与隔离级别脏读、不可重复读、幻读的复现实验MySQL 的事务核心是 InnoDB 的 MVCC 和锁机制默认隔离级别REPEATABLE READ下你已经不太容易踩到脏读和不可重复读。但“不太容易”不等于“不会”很多人在笔记里记了四个隔离级别却不知道边界在哪。最简单的复现方式是用两个客户端跑一个转账场景A 会话开启事务更新余额但不提交B 会话在READ COMMITTED下读到的还是旧值在READ UNCOMMITTED下会读到未提交的脏数据。-- 客户端1 START TRANSACTION; UPDATE account SET balance balance - 100 WHERE id 1; -- 不写 COMMIT停在原地 -- 客户端2 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT balance FROM account WHERE id 1;逻辑说明START TRANSACTION开启事务后未提交的修改对其他会话是否可见取决于对方会话的隔离级别。READ UNCOMMITTED会读到未提交数据这在财务场景里是灾难。参数说明生产库一般坚持默认的REPEATABLE READ需要更高并发时才考虑改READ COMMITTED。改隔离级别用SET SESSION只影响当前连接SET GLOBAL会影响所有新连接但不会重置已有的。另外注意START TRANSACTION之后如果执行了 DDLMySQL 会隐式提交当前事务笔记里把这个记成“事务中不能跑 DDL”就是从这个机制来的。3.3 排序与去重ORDER BY 的隐式排序陷阱和 DISTINCT 的误区排序的坑往往藏在“结果看起来是对的”里。用ORDER BY对中文按拼音排序时如果字符集排序规则是utf8mb4_0900_ai_ci结果会按拼音排如果是utf8mb4_bin就会按编码排顺序完全不同。另外ORDER BY里混用字段别名时MySQL 8.0 会强制要求别名不能出现在WHERE中否则直接报错。去重方面DISTINCT作用于所有查询列的组合不是只作用于第一列。很多人写SELECT DISTINCT user_id, status以为是在对 user_id 去重实际是对(user_id, status)组合去重结果出现重复 user_id。要真正拿到去重后的 user_id应该用GROUP BY user_id配合聚合函数。4. 索引设计三类索引失效场景与 EXPLAIN 验证4.1 隐式转换、前导模糊查询、函数包裹列索引失效的三个常见场景索引失效的典型场景可以背但要理解为什么。第一类是隐式转换比如字段类型是VARCHAR但查询条件传数字MySQL 会把列转成数字再比较索引就失效了。第二类是前导模糊查询LIKE %abc因为不确定匹配开头是什么字符走不了 B 树的有序查找。第三类是函数包裹列WHERE DATE(create_time) 2024-01-01相当于对索引列做完函数运算才比较索引天然失效。下面这条 SQL 是反面教材-- 错误示范DATE() 包裹 create_time导致 idx_create_time 失效 SELECT * FROM order_info WHERE DATE(create_time) 2024-01-01;逻辑说明对索引列使用函数后MySQL 无法直接利用 B 树的叶子节点顺序做范围匹配只能全表扫描。参数说明正确的写法是把条件改成范围查询WHERE create_time 2024-01-01 AND create_time 2024-01-02这样能走索引范围扫描range。另一个案例是SELECT * FROM user WHERE phone 13800138000如果phone是VARCHAR这个查询会触发隐式转换。解决方式是写字符串字面量WHERE phone 13800138000。4.2 联合索引的最左前缀原则字段顺序决定生死联合索引(user_id, login_time)遵循最左前缀原则查询条件里只有出现user_id时才能用这个索引只给login_time是不行的。很多人建索引时随手把区分度高的字段放后面结果查询根本走不上。生产经验是先放等值查询字段再放范围查询字段。下面演示如何验证-- 建立联合索引 ALTER TABLE login_log ADD INDEX idx_user_time (user_id, login_time); -- 能用上索引的查询user_id 等值 login_time 范围 EXPLAIN SELECT * FROM login_log WHERE user_id 10001 AND login_time 2024-01-01; -- 用不上索引的查询只查 login_time EXPLAIN SELECT * FROM login_log WHERE login_time 2024-01-01;逻辑说明第一条EXPLAIN应该能看到type为range或refkey列出现idx_user_time第二条的type大概率是ALL表示全表扫描。参数说明当user_id的过滤效果好时联合索引把同用户的多行登录记录在叶子节点上连续排列范围查询只需要定位一次再顺序扫。如果业务还有大量只按login_time的查询就得单独补一个login_time的单列索引不要指望联合索引反过来生效。4.3 用 EXPLAIN 读执行计划type、key、rows 是最该看的三个字段EXPLAIN的输出有很多列很多初学者被possible_keys和key搞混。possible_keys列出可能被用到的索引key是实际用到的索引。光看key不够还要看type从好到差依次是system、const、eq_ref、ref、range、index、ALL。ALL就是全表扫描index表示扫描整棵索引树也不一定快。rows是估算要扫的行数数量级在万以上就要警惕。示例EXPLAIN SELECT id, user_id FROM login_log WHERE user_id 10001\G逻辑说明\G让 MySQL 把结果按纵向输出看type: ref、key: idx_user_time、rows: 12就说明命中了索引且扫描行数可控。参数说明如果看到type: ALL且rows上千万立刻考虑加索引。另一个小技巧是在EXPLAIN后面加FORMATJSON能看到cost_info里的代价估算方便做两块执行计划的横向对比。笔记里如果只记了 explain 的列名含义没有记读法顺序等于白记。5. 线上 MySQL 故障排查从启动失败到锁等待的 5 类现场5.1 服务无法启动net start mysql 报错怎么定位Windows 上net start mysql报“服务无法启动”是高频问题真正的原因通常在错误日志里而不是服务窗口。MySQL 5.7 和 8.0 的错误日志默认在数据目录下文件名可能是hostname.err。报错[ERROR] [MY-014060] ... invalid mysql server upgrade通常意味着数据目录里的系统表和二进制版本不匹配常见于跨大版本升级后直接复用旧数据目录。排查步骤是先看日志再确认目录权限。Linux 上用systemctl status mysqld加日志路径更快CentOS 下默认日志在/var/log/mysqld.log。修复方式是把数据目录备份后重新初始化但初始化前一定要确认原来的ibdata1和ib_logfile*没有残留否则又是一个新坑。5.2 SSL 连接报错sql 连接串里 ssl-mode 的取舍MySQL 8.0 默认开启 SSL 要求JDBC 连接串如果不带ssl-mode可能直接报Public Key Retrieval is not allowed。本地开发和内网环境常见做法是显式关闭 SSL?useSSLfalseallowPublicKeyRetrievaltrue。但生产环境不建议因为怕麻烦就全关至少用ssl-modePREFERRED开启协商。这类报错的本质是客户端和服务端在握手阶段协商加密方式失败和字符集问题一样属于连接层问题。参数说明allowPublicKeyRetrievaltrue允许客户端从服务端拉取公钥仅用于caching_sha2_password认证内网开发可以开公网环境开了会有中间人风险。5.3 锁等待与死锁从 information_schema 定位持锁事务Lock wait timeout exceeded是并发写入时的经典报错大部分情况不是真的死锁而是一个事务持锁不释放把别的会话卡住了。排查方式是通过information_schema.innodb_trx找到长时间未提交的事务然后结合sys.innodb_lock_waits看谁阻塞了谁-- 查看当前所有运行中的事务和耗时 SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id FROM information_schema.innodb_trx WHERE trx_state RUNNING; -- 查看锁等待关系 SELECT * FROM sys.innodb_lock_waits\G逻辑说明innodb_trx能看事务启动时间和状态超过几十秒还在RUNNING的事务多半是忘了提交sys.innodb_lock_waits直接列出阻塞者和被阻塞者的线程 ID。查到后可以用KILL trx_mysql_thread_id结束持锁会话但前提是确认它不是核心业务事务。参数说明innodb_lock_wait_timeout默认 50 秒等不及可以改小但治标不治本真正解法是缩短事务体把更新操作批量合并避免事务里穿插网络调用。5.4 误更新数据undo log 与 binlog 的两种后悔药笔记里如果只写了 DELETE 和 UPDATE 的语法没写“如何还原”实用性少一半。MySQL 提供两种常见恢复路径如果事务还没提交直接ROLLBACK用 undo log 回滚如果已经提交则需要靠备份 binlog 做时间点恢复。第二种路径的代价在于你得提前开了binlog并且知道大致误操作时间。日常生产中我的习惯是任何 UPDATE 都先SELECT同样的WHERE条件看影响行数重要表每次改动前用CREATE TABLE tmp AS SELECT * FROM target WHERE ...做临时备份。参数说明binlog_format建议用ROW因为STATEMENT格式在回放时可能因为函数、时间等环境变量产生和原来不一致的结果。误操作后的恢复步骤是把备份恢复到临时实例再用mysqlbinlog从误操作前的时间点增量回放。5.5 Docker 部署 MySQL时区、编码、文件挂载三大参数自建环境里用 Docker 跑 MySQL 已经非常普遍但不少人docker run起来后就发现中文乱码、时间差 8 小时、容器重启数据丢失。乱码是因为容器内默认字符集不是 utf8mb4时区是因为容器默认 UTC。下面是一个我常用的最小化部署命令docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDyour_password \ -e TZAsia/Shanghai \ -v /data/mysql:/var/lib/mysql \ mysql:8.0逻辑说明-e TZAsia/Shanghai设置容器时区-v /data/mysql:/var/lib/mysql把数据目录挂到宿主机避免容器重建后数据丢失。参数说明还可以加--character-set-serverutf8mb4 --collation-serverutf8mb4_0900_ai_ci显式指定字符集。Docker 部署失败大多不是因为镜像本身而是挂载目录权限宿主机目录属主不是 MySQL 的 uid容器启动时会因为目录不可写直接退出。排错时不要只看docker logs先确认/data/mysql的属主是否允许容器内进程写入。另外docker pull mysql报错failed to decode referrers index多半是镜像源解析问题换一个 registry 镜像即可不要把时间花在排查容器本身上。6. 把笔记内化成能力用 mysqldump 做一次完整的恢复演练“看过 1000 行笔记”和“能处理一次真实故障”之间隔着一场演练。我建议你选一个周末的下午在自己的测试库上做一次完整备份与恢复第一步用mysqldump导出全库第二步删掉一张表第三步把备份导回去。命令就三条但做完你会发现很多笔记里没写的细节比如导出时--single-transaction的作用# 导出全部数据带单事务快照不影响线上写入 mysqldump -uroot -p --single-transaction --set-gtid-purgedOFF \ --databases user_center /backup/user_center.sql # 恢复前先看备份文件前几行确认字符集和 CREATE DATABASE 语句 head -50 /backup/user_center.sql # 恢复 mysql -uroot -p /backup/user_center.sql逻辑说明--single-transaction在 InnoDB 下导出时开启一个一致性快照事务不会被 DML 干扰也不会锁表--set-gtid-purgedOFF是为了避免 GTID 信息在普通恢复时干扰全局事务编号。恢复时用重定向相当于把文件当作 SQL 批处理执行。参数说明如果只导出某张表用mysqldump 库名 表名如果只要结构不要数据加--no-data。这个流程跑完之后还有一件事值得做把导出的 SQL 文件在临时实例上执行一遍确认数据行数和原库对得上不要只看到Import finished就以为万事大吉。我自己的习惯是每个月挑一天做恢复演练时间从首次的 40 分钟压到现在的 10 分钟。只有真正把恢复流程跑顺了笔记里的命令才从“读过”变成“会救火”。这个方向是否值得投入我说一个判断标准如果你发现自己面对锁等待、索引失效、字符集乱码时能只看日志就定位到具体章节的知识点那这份笔记就没有白读。希望帮到你。本文还有配套的精品资源点击获取