
今天2026年3月2日晚上整理第一章学习笔记时我决定把前两节的内容按自己的理解重新过一遍。MySQL 是绝大多数中小型业务系统和 Web 项目绕不开的关系型数据库安装配置、建表查询、事务与索引这些概念几乎是日常开发的上游基础。这篇笔记围绕“第一章前两节”来写覆盖了基础概念、环境搭建、SQL 实操入门、核心机制扫盲以及新手高频报错的排查思路适合刚接触数据库的初学者跟着过一遍也适合已经用过 MySQL 但没系统梳理过底层机制的人拿来查漏补缺。1. 第一章前两节到底覆盖了什么先给这门课划个重点1.1 先说清楚三个很容易混淆的词数据库、DBMS、SQL很多新手第一天就被这三个词绕晕。我第一次学的时候也分不清“数据库”和“数据库管理系统”到底有什么区别后来用了一个很朴素的类比才彻底记住数据库像是一个存放文件的仓库仓库本身有货架、有编号、有出入库规则而 DBMS数据库管理系统是管理这个仓库的那套流程和工具你向仓库管理员下达指令管理员按规则存取货物。SQL 则是你和管理员沟通时约定好的“指令语言”。在 MySQL 语境下平时说的“MySQL”其实是指 MySQL 这个 DBMS 软件。一个 MySQL 实例里可以创建多个数据库对应多个仓库每个数据库里又能建多张表表是真正存数据的地方。表由行和列组成列定义了字段和类型行是每一条具体记录。第一章前两节的重点之一就是把“DBMS → 数据库 → 表 → 行/列”这几层关系彻底搞清楚。如果你直接跳过这些概念去背 SQL 语句后面碰到字符集、存储引擎、事务隔离级别时会很吃亏。1.2 第一章前两节的隐藏主线连接 → 建表 → 查询抛开教材的目录结构我自己整理出前两节的隐藏主线这条线贯穿了后面所有章节第一步是让客户端能连上 MySQL 服务端第二步是创建一个属于自己的数据库和表第三步是往表里写入数据并查询出来。听起来很简单但前两节课反复强调的恰恰是这些基础动作背后的“为什么”。比如建表时为什么要指定字段类型因为 MySQL 是强类型存储你声明INT的列不会接受abc这种字符串。比如查询时为什么要学WHERE的执行顺序因为先过滤再排序和先排序再过滤性能差异可能相差几个数量级。这些都是摘要里常见的“MySQL学习”会涉及到的点但很多新手教程只会让你机械地敲命令。我在笔记里习惯把所有命令拆成“目的 → 语法 → 例子 → 常见坑”四段这种记录习惯对后面的存储过程、主从复制学习特别有用。2. 从零装好 MySQL 并跑通第一条查询环境准备里的那些坑2.1 不同系统下的安装方式选择安装 MySQL 是让初学者心态崩溃的高发区。前两节课程里通常只给一个标准安装流程但实际生活中你可能会遇到 Windows 笔记本、Linux 服务器、Docker 容器三种完全不同环境。我三种都装过先说结论本地学习直接装原生安装包服务器部署能用 Docker 用 Docker内网离线环境才考虑 rpm 或离线包。在 Windows 上最省事的方式是下载 MySQL Installer选择Server only组件一路 Next 基本能完成安装。真正容易出问题的是安装接近尾声时设置 root 密码和选择 Windows Service 名称那一步服务名很多人图省事直接默认成MySQL80结果后面在命令行里执行mysql -u root -p时系统提示无法连接一查才发现是服务没启动。所以装完第一件事不是急着用而是先确认服务状态Windows 下用services.msc打开服务管理器找到对应服务确保它是“正在运行”。Linux 上分 yumCentOS/RHEL和 aptUbuntu/Debian两派。以我常用的 CentOS 为例要装 MySQL 8.0 通常是先装官方仓库再执行yum install mysql-server装完用systemctl start mysqld启动。首次启动后 MySQL 会生成一个临时 root 密码查看日志才能拿到grep temporary password /var/log/mysqld.log。这个细节在面试题里也是高频考点很多人记不清初始密码去哪找。如果完全无外网就只能提前下载好 rpm 包传到内网再逐个安装依赖顺序是mysql-community-common、mysql-community-libs、mysql-community-client、mysql-community-server依赖顺序错了会报一堆令人头大的错误。Docker 方式我最推荐给只是想快速验证语法的人。一条命令就能跑起来docker run -d \ --name mysql-demo \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ mysql:8.0注意3306端口不能被占用如果本机已经装了 MySQL建议把端口映射改成3307:3306避免冲突。2.2 第一次登录和字符集问题为什么中文总是乱码安装完成后第一次登录常遇到两类问题。一类是“密码错误”另一类是“字符集乱码”。密码错误往往不是因为记错而是因为安装时设置了密码校验策略导致你设的简单密码没过关。MySQL 8.0 默认要求密码至少包含大小写字母、数字和特殊符号长度不能少于 8 位。想降低校验强度可以在登录后执行SET GLOBAL validate_password.policy LOW; SET GLOBAL validate_password.length 6;乱码问题则在建库环节就要预防。MySQL 默认字符集在不同版本里不一样老版本默认是latin1存入中文会显示成一排问号。现在用 8.0建议在my.cnf或my.ini里显式设置[mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_0900_ai_ciutf8mb4是完整的 UTF-8 实现能存下四字节的 emoji 和一些生僻汉字。光在服务端设置还不够建库时最好再指定一次CREATE DATABASE school DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;这样三层保险服务端、建库、建表都统一后续基本不会再遇到中文乱码。2.3 命令行还是图形工具初学者的第一周应该怎么选我不建议初学者一上来就用 Navicat 或 MySQL Workbench 这种图形工具虽然拖拽建表确实方便但会掩盖掉很多 SQL 语感训练的机会。前两节课的核心是“用命令行把每条语句敲出来”哪怕敲得慢也能直观感受每条 SQL 返回的结果集。等你对SHOW DATABASES、USE school、SHOW TABLES这套命令足够熟悉了再切换到图形工具效率就会高很多。MySQL Workbench 是官方工具免费且够用Navicat 交互更顺手但要注意合规使用正版或试用版。图形工具真正有用的场景是看表结构设计、导出导入数据、调试存储过程这些是命令行不太直观的地方。作为学习阶段的工具我建议命令行占七成、图形工具占三成。3. 动手过一遍基础 SQL建库、建表与增删改查的实操细节3.1 建库建表字段类型不是随便选的前两节之后应该具备独立设计一张简单表的能力。我用一个学生课程成绩表来演示这个场景在热搜词里也出现过很适合作为练习模板。建表前先想清楚字段类型这一步很能体现基本功。拿成绩表来说学号固定长度的数字或字符串用VARCHAR(20)更合理因为学号可能含有字母和零填充不建议用INT保存。姓名用VARCHAR(50)按中文姓名长度留足空间。成绩小数用DECIMAL(5,2)不要用FLOAT。DECIMAL是精确类型存储金额、成绩这类要求精确计算的值不会产生“0.30000000000000004”这种浮点误差。性别只有两个常用值用TINYINT(1)存 0/1比存字符串省空间。考试时间用DATETIME不要用VARCHAR存日期。字符串存日期会导致无法使用日期函数排序和计算热搜里“mysql将字符串转为日期”这个需求绝大多数就是因为建表时偷懒用错类型。一个最小可用的建表语句如下CREATE TABLE student_score ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键自增, student_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, student_name VARCHAR(50) NOT NULL COMMENT 姓名, gender TINYINT DEFAULT 0 COMMENT 性别 0未知 1男 2女, course_name VARCHAR(100) NOT NULL COMMENT 课程名, score DECIMAL(5,2) NOT NULL COMMENT 成绩, exam_time DATETIME NOT NULL COMMENT 考试时间, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 记录创建时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生成绩表;这里的主键用了BIGINT而不是INT是刻意为之。INT最大约 21 亿对一张会长期增长的业务表来说并不安全用BIGINT更保险。热搜里有个“mysql中int5”大概是想问INT字段做加法运算的问题其实INT类型本身有范围限制一旦累加超过 2147483647 就会报Out of range错误所以生产环境自增主键我基本只用BIGINT。3.2 增删改查插入、更新、删除的细节建完表后先插入几条数据。插入语法很简单但有几个细节容易被忽略INSERT INTO student_score (student_no, student_name, gender, course_name, score, exam_time) VALUES (20260001, 张三, 1, MySQL课程, 95.50, 2026-03-02 10:00:00), (20260002, 李四, 2, MySQL课程, 88.00, 2026-03-02 10:00:00);插入时可以不写id字段让自增列自动生成但如果你显式插入了某条id后续自增的起始值会被改写这在数据迁移时经常导致主键冲突。更新数据时最容易犯的错是忘记带WHEREUPDATE student_score SET score 100;这条语句会把整张表的成绩全部改成 100。MySQL 默认在非安全模式下不会拦截这种无WHERE更新我就在测试环境里干过这事整个表的数据都被改掉了只能靠备份恢复。给个建议正式环境务必给 MySQL 开启sql_safe_updates这样执行不带WHERE的UPDATE或DELETE时MySQL 会直接拒绝执行SET sql_safe_updates 1;删除操作同理DELETE FROM student_score WHERE student_no 20260001才是精准删除。想清空整张表优先考虑TRUNCATE TABLE它的速度比DELETE FROM快得多但无法回滚。用哪个取决于你的意图删少量记录用DELETE清空表结构保留表定义用TRUNCATE。3.3 查询的骨架WHERE、ORDER BY、GROUP BY 和 LIMIT 的执行顺序查询是 SQL 里最灵活也最考验逻辑的部分。前两节不会讲太深但几个子句的执行顺序一定要趁早记住否则写出来的 SQL 经常“结果不对但不知道错在哪”。标准执行顺序是FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY→LIMIT这意味着你写的WHERE是在分组之前过滤而HAVING是在分组之后过滤。想查“平均成绩大于 90 分的课程”不能写成SELECT course_name, AVG(score) FROM student_score WHERE AVG(score) 90 GROUP BY course_name;因为WHERE执行时分组还没发生AVG(score)根本算不出来。正确写法是用HAVINGSELECT course_name, AVG(score) AS avg_score FROM student_score GROUP BY course_name HAVING AVG(score) 90;排序则用ORDER BY默认升序从高到低要加DESC。分页查询在业务系统里几乎是标配用LIMIT控制返回行数SELECT student_name, score FROM student_score ORDER BY score DESC LIMIT 10 OFFSET 0;OFFSET 0表示从第 0 行开始取 10 条也就是第一页。OFFSET越大扫描越慢这是后面学习“深分页优化”的伏笔。4. 事务、锁和索引第一章提前建立这三块底层认知后面会轻松很多4.1 事务ACID 并不是四个高深莫测的单词前两节可能只是简单提了事务但我想说这一部分是整本教材最重要的知识地基之一。事务把一组 SQL 操作打包成一个不可分割的执行单元要么全部成功要么全部失败。它为什么重要我用银行转账举例从 A 账户扣 1000 元给 B 账户加 1000 元。如果扣款成功但加款过程崩溃系统就出现了钱凭空消失的故障。为了杜绝这种情况事务必须满足 ACID 四个特性原子性Atomicity保证操作不可分割一致性Consistency保证数据始终符合业务规则隔离性Isolation保证并发事务互不干扰持久性Durability保证提交后数据不会丢失。这四个词分开看都不难难在理解它们之间的相互作用。在 MySQL 里默认的 InnoDB 引擎是支持事务的MyISAM 不支持。这也是为什么我建表时显式加了ENGINEInnoDB。手动控制事务的语法不算复杂START TRANSACTION; UPDATE account SET balance balance - 1000 WHERE account_no A; UPDATE account SET balance balance 1000 WHERE account_no B; COMMIT;如果在第二条 UPDATE 时发现账户余额不足就执行ROLLBACK让两条语句一起回滚。需要注意 MySQL 默认开启了自动提交autocommit1单条语句默认就是一次事务所以想多条语句作为一个整体必须显式开启START TRANSACTION或用BEGIN。隔离性在并发场景下有个经典问题脏读、不可重复读、幻读。第一章如果没细讲也没关系但要先知道 InnoDB 提供了四种隔离级别读未提交、读已提交、可重复读、串行化。MySQL 默认是“可重复读”这个选择本身很有讲究早年间 Binlog 格式配合行为差异导致把默认级别改成“读已提交”容易出现主从复制不一致的问题。这些我在笔记后面会单独写第一章先建立概念就够。4.2 锁行锁为什么会变成死锁事务并发执行时数据库用“锁”来保证数据安全。InnoDB 支持行级锁也就是只锁住正在操作的那几行其他事务依然能操作别行。这比表级锁并发能力强也更难排查问题。拿最经典的“余额扣减”来说两个事务同时更新同一行时后到的那个事务必须等待前一个事务提交或回滚。这种等待如果设计不合理就可能形成死锁事务 A 锁住了第一行、想要第二行事务 B 锁住了第二行、想要第一行两边互不相让。MySQL 检测到死锁后会主动选择牺牲其中一个事务让它回滚并抛出Deadlock found错误。初学者听到死锁会觉得离自己很远其实一个简单的批量更新就有机会触发。比如事务 A 更新学号为 1、2 的记录事务 B 更新学号为 2、1 的记录两者加锁顺序不一致在特殊调度下就会死锁。规范的开发习惯是多个事务访问多张表或多行数据时尽量保持相同的加锁顺序。排查死锁可以使用SHOW ENGINE INNODB STATUS;输出结果里LATEST DETECTED DEADLOCK部分会显示具体持锁和等待的 SQL。这个命令是 DBA 排查死锁的第一工具关键词“mysql锁原理及面试题”里的很多问题基本都能从这个命令的实际输出来理解。4.3 索引为什么主键默认就有索引建表时我特意写了PRIMARY KEY除了标识唯一性它还有另一个隐藏属性主键字段默认会创建一个聚簇索引。换句话说InnoDB 的表是按照主键索引的 B 树结构来组织数据文件本身的查主键时不用回表效率最高。这也是为什么 InnoDB 表强烈建议有主键没有主键时 InnoDB 还需要额外生成隐藏主键既浪费空间又影响写入性能。非主键字段想加速查询就要手动建二级索引。比如经常按学号查成绩给student_no加个索引很合理CREATE INDEX idx_student_no ON student_score(student_no);索引不是越多越好每个二级索引都会在写入时增加维护成本。前两节理解到这里就够了索引是在“读得快”和“写得慢”之间做权衡。等到学习EXPLAIN时你会看到possible_keys和key两个字段前者表示可能用到的索引后者表示实际用到的索引如果key是NULL说明这条查询走了全表扫描数据量大时通常会慢得让人着急。4.4 主从复制和存储过程前两节埋下的伏笔你可能在热搜里看到“怎么使用 mysql 主从复制”“mysql存储过程”这些词它们通常不是第一章前两节的内容但非常适合在第一遍学基础时稍微瞄一眼。主从复制的核心原理是主库把变更写入二进制日志Binlog从库把日志拉到本地再重放这些日志。这个过程依赖 Binlog而 Binlog 的行格式又和事务隔离级别有千丝万缕的联系所以我说事务、锁、索引是提前要建立的三块底层认知。存储过程则是一组 SQL 语句的集合可以像调用函数一样被调用。MySQL 里写存储过程要特别注意分隔符问题——默认的分号是语句结束符定义存储过程时为了不让 MySQL 在内部语句处就截断要先修改分隔符DELIMITER // CREATE PROCEDURE get_avg_score(IN c_name VARCHAR(100)) BEGIN SELECT course_name, AVG(score) FROM student_score WHERE course_name c_name; END // DELIMITER ;初次看这段代码最容易被DELIMITER绕晕。它本身不是 SQL 语句而是 MySQL 客户端命令作用是告诉客户端“现在用//作为整段语句的结束标记”。这个概念属于基础中的基础也是热搜里“mysql中触发器中分隔符”反复出现的原因。5. 新手最容易撞上的几个报错与排查套路5.1 ERROR 2002Cant connect to local MySQL server through socket /tmp/mysql.sock这个错误在热搜里原样出现了当年第一次遇到了也把我卡了半天。它的字面意思是客户端连接本机 MySQL 时找不到/tmp/mysql.sock这个套接字文件。套接字是 Linux 上本地进程间通信的一种方式MySQL 客户端和服务器在同一台机器时默认不走 TCP/IP 网络而是通过套接字文件通信。如果服务没启动或者启动后生成的套接字路径和你客户端默认路径不一致就会报错。排查步骤按下面的顺序来确认服务是否在运行systemctl status mysqld或service mysql status。如果服务没在运行启动它再试。如果服务确实在运行检查套接字文件实际位置find / -name *.sock。确认客户端使用的默认套接字路径和服务端配置一致。不一致时连接时显式指定mysql -u root -p -S /var/lib/mysql/mysql.sock这个错误最迷惑人的地方在于明明“服务好像在运行”但客户端就是连不上。原因大概率是套接字路径不同或者服务启动后崩溃又立刻被守护进程拉起导致你看到的状态是“正在运行”但实际连接一打就断。遇到这种不死不活的状态最直接的办法是systemctl restart mysqld进行一次完整干净的启动。5.2 密码重置和 SSL 连接错误安全相关但新手逃不掉另一个高频问题是安装完 MySQL 8.0 后用临时密码登录系统强制要求先改密码ALTER USER rootlocalhost IDENTIFIED BY NewStrongPassword123!;如果你之前乱设密码导致后来忘了需要在跳过权限表的状态下重启服务来重置。操作步骤是修改配置加入skip-grant-tables重启 MySQL此时不需要密码就能登录然后直接改密码。这个操作存在安全风险因为任何本机用户都能无密码访问数据库所以重置完必须把skip-grant-tables从配置里删掉再次重启恢复正常模式。我在测试环境里曾忘了删这个参数结果服务裸奔了两天教训很深。SSL 连接错误在 MySQL 8.0 里也很常见。默认情况下客户端和服务器会尝试建立加密连接但如果服务器 SSL 配置有问题或者客户端驱动版本太老就会报SSL connection error。如果你是在内网学习环境且数据本身不敏感可以临时禁用 SSLmysql -u root -p --ssl-modeDISABLED不过在真实生产环境里不建议长期关闭 SSL因为数据库连接很可能会经过网络传输账号密码和数据都有被截获的风险。更合理的做法是去检查服务器端证书配置或者更新客户端的 CA 证书。5.3 字体乱码和时区问题看起来小影响却很大前面说过建库时要用utf8mb4但有时候你已经建完库了才发现历史数据乱码。这种情况要先检查三处客户端连接字符集、表字符集、连接参数。在命令行连接时可以显式指定mysql -u root -p --default-character-setutf8mb4时区问题则是 MySQL 8.0 另一个新新手容易忽略的点。默认时区是服务器的系统时区如果服务器时区设置不对CURRENT_TIMESTAMP存进去的时间会和业务系统差好几个小时。常见做法是在 JDBC 连接串里加serverTimezoneAsia/Shanghai或者启动 MySQL 时加上--default-time-zone08:00。这类问题不会让数据库崩掉但会让日志分析和报表统计出现诡异偏差排查时特别耗时间。5.4 用好三个查看命令排查效率翻倍工具类问题排查到最后往往会发现“原来 MySQL 自带的信息这么全”。我碰到拿不准的问题会优先查看SHOW VARIABLES LIKE %character%; SHOW VARIABLES LIKE %time_zone%; SHOW ENGINE INNODB STATUS;第一个看字符集第二个看时区第三个看 InnoDB 状态包括事务、锁、死锁记录。这三个命令配合SHOW PROCESSLIST查看当前正在执行的线程就能解决掉大部分环境类问题。SHOW PROCESSLIST最大的价值是能看到某个 SQL 是不是卡住了如果State列长时间显示Waiting for table metadata lock通常是有另一个会话对表加了元数据锁排查思路就该转向“谁锁住了表”而不是盲目重启。6. 第一章学完后的自查清单我习惯在每章结尾给自己列一个“不做题、只看能不能说清”的自查清单这个方法帮我发现了很多“以为自己会了其实并没有”的知识盲区。第一章前两节结束后我会要求自己不看任何资料直接回答这几个问题数据库、DBMS、SQL 三者的区别是什么能不能用大白话讲给别人听能不能不假思索地写出建库、建表、插入、查询四类语句知不知道WHERE、GROUP BY、HAVING、ORDER BY、LIMIT的执行顺序能不能解释为什么 InnoDB 表要有主键、主键和普通索引有什么区别能不能说清楚ERROR 2002的常见原因和排查顺序这些问题如果能流畅回答说明第一章前两节的基础真的扎稳了。如果答不上来我建议别急着往下学存储过程、主从复制或者性能调优先把这一章再翻一遍。MySQL 是典型的“地基不牢、地动山摇”的领域前期欠下的概念债到了排查慢查询、分析死锁的时刻会加倍奉还。最后分享一个我个人的经验学 MySQL 前两节时我刚把注意力放在“能跑通”而不是“能讲清”结果后面一遇到事务和锁就一头雾水。后来调整了学习方式每学一个新语法都强迫自己回答“这条语句在底层做了什么”比如建索引时想一下 B 树怎么变化开启事务时想一下 undo log 怎么记录旧值。这个习惯让后面的存储过程、主从复制、性能调优学起来顺畅了很多。环境问题可以靠搜索引擎解决但底层认知只能靠自己一砖一瓦搭起来。