
“如果第一天是‘把 MySQL 装到机器上’那第二天就是‘和 MySQL 说上话’。”这是我第二天学 MySQL 的真实感受。DAY01 装好了 MySQL 8.0设了 root 密码以为自己已经入门了结果 DAY02 一开始就连不上数据库报了个ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock当场懵了半小时。这一天的目标其实很简单让服务稳定跑起来、建出第一张像样的表、把增删改查和排序这些基础操作练到手熟顺带看一眼索引和存储过程这些“传说中”的东西到底长什么样。这篇文章就是我的 DAY02 学习笔记适合刚装完 MySQL 的新手也适合想快速捡起基础的人。我把当天踩的坑、查的资料、写的每一段 SQL 都整理成下面这份可以直接照着操作的实录。1. 环境准备先让 MySQL 跑起来1.1 装好之后第一件事不是写 SQL很多新手包括我最容易犯的错就是装完 MySQL 立刻打开命令行敲mysql -uroot -p结果各种报错。因为你的 MySQL 服务可能压根没启动就算启动了客户端也可能找不到服务端的 socket 文件。所以第二天开头我先梳理了一下“怎么算装好了”。首先确认安装方式。Linux 上我用的是apt install mysql-serverWindows 用户一般用安装包macOS 用户常走 homebrew还有人用 Docker。不管哪种方式装完第一步都是看服务状态# Linux 下常见命令 systemctl status mysql # 或者 service mysql status # 如果没启动 systemctl start mysql如果你用的是 Dockerdocker run -d --name mysql8 -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ -e MYSQL_DATABASEstudy mysql:8.0注意-p 3306:3306是端口映射少了它容器外根本连不进去。这里我不推荐新手一上来就用 Docker因为多了一层容器网络的干扰出了问题更难排查先把本机原生装好、跑通再说。确认服务启动后还要检查一个关键文件socket。MySQL 在 Unix/Linux 上默认通过一个本地文件进行进程间通信这个文件一般是/var/run/mysqld/mysqld.sock或者/tmp/mysql.sock。客户端连接 localhost 时会去找这个文件找不到就报 socket 错误。可以用命令确认ps -ef | grep mysql # 查看进程是否存在 ls -l /var/run/mysqld/mysqld.sock这一步就是“确认 MySQL 真的活着”而不是“你以为它活着”。我自己的习惯是建一个统一的笔记文档把安装方式、数据目录、配置文件路径统统记下来后面出问题至少知道去哪儿翻。1.2 连接失败八成是 socket 或密码的锅DAY02 我遇到的第一个经典报错就是文章开头那个ERROR 2002。翻译过来就是客户端想通过/tmp/mysql.sock这个文件连接本地服务器但找不到这个文件。原因无非几种MySQL 服务真的没启动。socket 文件路径不对实际不在/tmp下而在/var/run/mysqld/下。MySQL 是 Docker 里跑的本机根本不存在这个 socket 文件要通过 TCP 连接。排查顺序我是这么做的# 1. 看服务状态 systemctl status mysql # 2. 看监听端口 netstat -tlnp | grep 3306 # 有 3306 说明服务起来了TCP 方式至少可以连 # 3. 用 TCP 方式尝试连接 mysql -h 127.0.0.1 -P 3306 -u root -p这里有个知识点mysql -uroot -p默认走 socket 连接而mysql -h 127.0.0.1 -P 3306 -uroot -p走 TCP 连接。如果你能通过 TCP 连上但 socket 连不上不用慌改一下my.cnf里的socket路径就行或者以后统一用127.0.0.1连接。另外一个高频报错是ERROR 1045 (28000): Access denied for user rootlocalhost这就是密码错了。新手阶段我建议直接把 root 密码设置成一个简单好记的比如root123学习而已不用在生产环境那套复杂密码逻辑里纠结。改密码的命令ALTER USER rootlocalhost IDENTIFIED BY root123; FLUSH PRIVILEGES;这一小节如果能顺利走完你和 MySQL 的“连接”就真正建立了。接下来别急着写业务 SQL先把数据类型补一补不然建表的时候会非常痛苦。2. 数据类型选错类型后面全得改2.1 整数、小数、字符串怎么选第二天我建表之前先花了一上午看数据类型后来发现这个时间花得特别值。你建表的时候把类型定错了后面数据一多想改类型就得ALTER TABLE不但耗时还可能锁表线上环境更是灾难。所以建表前把类型搞清楚比什么技巧都实在。MySQL 常用的数据类型就这么几类初期不用记全部记住核心的几个就够了类型用途注意事项INT整数主键、数量、ID默认有符号范围约 -21亿 到 21亿更大用 BIGINTDECIMAL(M,D)金额、精确小数别用 FLOAT/DOUBLE 存钱会有精度丢失VARCHAR(n)可变长字符串n 是字符数不是字节数CHAR(n)定长字符串适合固定长度如手机号、身份证但也不要滥用TEXT长文本最大 64KB超过建议用 MEDIUMTEXT/LONGTEXT 或存文件DATETIME日期时间范围 1000-01-01 到 9999-12-31TIMESTAMP日期时间范围到 2038 年但会自动更新适合记录更新时间这里我特别想提一下热搜词里那个“mysql int5”。刚开始我以为是 INT 类型加 5 的意思后来才知道很多人搜的是 INT 的显示宽度比如INT(5)配合ZEROFILL可以显示成 00005。但说句实话这个功能在日常开发里几乎用不上你写INT就好不要被旧教程里的“int(n)”误导。宽度不影响存储范围只影响显示。另一个容易被坑的是 VARCHAR 长度。UTF-8 字符集下一个汉字最多占 3 个字节utf8mb4 下有的字符占 4 字节VARCHAR(255) 表示可以存 255 个字符不是 255 个字节。你要是拿 VARCHAR(32) 存 30 个汉字完全没问题。比如用户昵称我一般给 VARCHAR(50)文章标题 VARCHAR(200)足够。金额字段必须是 DECIMAL。FLOAT 是浮点数0.1 0.2 算出来可能不是 0.3这在账目上是绝对不能接受的。第一天你可能觉得无所谓等后面做到订单、支付相关项目时会感谢当初的选择price DECIMAL(10,2) NOT NULL DEFAULT 0.002.2 日期时间的坑与默认值日期类型我单独拿出来说因为新手包括我第一天最容易在这里犯迷糊。MySQL 里最常见的两个日期类型是 DATETIME 和 TIMESTAMP。简单区别DATETIME范围大不会随着时区变化存什么显示什么。TIMESTAMP范围到 2038 年会自动根据数据库时区转换并且可以设置自动更新。对于初学阶段记不住那么多就用 DATETIME省心。但有一个场景非常推荐 TIMESTAMP就是记录“更新时间”。看这个建表片段CREATE TABLE article ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );DEFAULT CURRENT_TIMESTAMP的意思是插入时不填写这个字段MySQL 自动填当前时间。ON UPDATE CURRENT_TIMESTAMP更贴心每次这一行数据被 UPDATE它会自动刷新成最新时间。这个功能在业务里几乎必用比如文章修改时间、用户最后登录时间你都希望它自动维护。热搜词里还有“mysql设置默认值为0”这也很常见。很多数字字段要求默认是 0 而不是 NULL。比如订单金额、库存数量你肯定不希望一查出来是 NULL然后在代码里各种判空stock INT NOT NULL DEFAULT 0, score DECIMAL(5,2) NOT NULL DEFAULT 0.00NULL 和 0 是两个概念。NULL 表示“没值”0 表示“数值为零”。数据库设计里能用 NOT NULL DEFAULT 0 的就别留 NULL可以省掉一大堆业务层的空指针判断。DAY02 我把这条当成了建表铁律。3. 建表实操从零设计三张关联表3.1 学生、课程、成绩第一套完整设计数据类型看完就得动手建表。我从热搜词里挑了一个非常经典的场景“学生课程成绩信息实体表设计”这个例子好就好在它有三张表、有主键、有外键、有联合主键能把 DAY02 该学的建表技能全锻炼到。设计思路是这样的学生表和课程表是两个基础实体成绩表记录“哪个学生选了哪门课、考了多少分”。成绩表不能单独有一个主键它应该用(student_id, course_id)作为联合主键因为一个学生对一门课只有一条成绩记录。同时成绩表通过外键关联到学生和课程。建表 SQL 如下CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 学生ID, student_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, gender CHAR(1) DEFAULT M COMMENT 性别, birthday DATE COMMENT 出生日期, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生表; CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 课程ID, course_name VARCHAR(100) NOT NULL COMMENT 课程名称, credit DECIMAL(3,1) NOT NULL DEFAULT 2.0 COMMENT 学分 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT课程表; CREATE TABLE score ( student_id INT NOT NULL COMMENT 学生ID, course_id INT NOT NULL COMMENT 课程ID, score DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT 成绩, exam_time DATETIME NOT NULL COMMENT 考试时间, PRIMARY KEY (student_id, course_id), KEY idx_course_id (course_id), CONSTRAINT fk_score_student FOREIGN KEY (student_id) REFERENCES student(id), CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES course(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT成绩表;几个细节展开说ENGINEInnoDB这是事务型存储引擎支持外键和行级锁。初学者不用纠结 MyISAM 和 InnoDB 的区别无脑 InnoDB 就行这也是 MySQL 8.0 的默认引擎。CHARSETutf8mb4比 utf8 多了 emoji 等四字节字符的支持。强烈建议所有表都用 utf8mb4不要再用 utf8我已经吃过乱码的亏了。COMMENT注释在字段后面写清楚每个字段的意思。单人学习时觉得废话等一个月后回来看表结构你会感谢当时的自己。UNIQUE唯一约束学号不能重复这是业务规则在数据库层面就应该卡住不能只靠代码判断。成绩表为什么要单独KEY idx_course_id (course_id)因为外键一般会自动创建索引但显式声明更清晰而且后面用WHERE course_id ?查询时需要索引加速。写到这里我想多说一句建表不是“写完能用就行”。建表本身就是设计一张管理不好字段类型的表后面所有 SQL 都会很难写。所以 DAY02 一定要多手写建表语句别总是靠 Navicat 的图形界面点按钮。3.2 表建错了ALTER TABLE 逃生指南新手阶段表结构改来改去太正常了。第二天我就把学生表改了三次先加了一个phone字段然后把gender的默认值改了最后又把一个不需要的字段删了。这时候ALTER TABLE是必备技能。常用的几条-- 加字段 ALTER TABLE student ADD COLUMN phone VARCHAR(20) AFTER name; -- 改字段类型和默认值 ALTER TABLE student MODIFY COLUMN gender CHAR(1) NOT NULL DEFAULT F; -- 改字段名 ALTER TABLE student CHANGE COLUMN birthday birth_date DATE; -- 删除字段 ALTER TABLE student DROP COLUMN phone; -- 重命名表 RENAME TABLE student TO stu_info;这里有一个容易混淆的点MODIFY和CHANGE都能改字段但CHANGE能改字段名MODIFY不能。改字段名这种操作在线上一定要谨慎代码里所有用到旧字段名的地方都要同步改否则运行期直接报错。还有表操作里最容易出大事故的TRUNCATE和DELETE的区别。我是建议新手先建立一个概念DELETE FROM student逐行删除可以加WHERE删除的速度一般会记录日志。TRUNCATE TABLE student直接把整个表清空重建速度极快但没法加条件也没法回滚。第二天我做实验时习惯先用TRUNCATE快速清空表再重新插入测试数据因为测试表里没有需要保留的内容。但如果你在业务环境千万不要随便 TRUNCATE。说句难听的我见过有人在生产库上想清一张日志表结果用 TRUNCATE 把整张配置表干掉了的案例。所以 Day02 就要养成一个肌肉记忆执行任何 DELETE/UPDATE/TRUNCATE 之前先看一眼有没有 WHERE再看一眼有没有备份。4. DML 实操围绕成绩表写增删改查4.1 插入和更新别把 WHERE 丢了建完表就是要往里塞数据。第二天我把三张表都插了些测试数据这里记录几个关键操作。多行插入是一次性写入多条数据的高效方式INSERT INTO student (student_no, name, gender, birthday) VALUES (S001, 张三, M, 2000-05-01), (S002, 李四, F, 2001-08-12), (S003, 王五, M, 1999-12-30);插入时如果字段有默认值可以不在 INSERT 里出现。比如created_at有DEFAULT CURRENT_TIMESTAMP那插入时就不用写它MySQL 会自动填。更新操作是重灾区。下面是新手最容易忽略条件导致全表更新的例子-- 危险这样会把所有学生的性别都改成 F UPDATE student SET gender F; -- 正确的写法必须带 WHERE UPDATE student SET gender F WHERE student_no S001;我怕有人不信邪再强调一次在 MySQL 里执行 UPDATE 不带 WHERE 是事故级操作。MySQL 默认是自动提交的一旦执行立刻生效除非你提前设置了SET autocommit 0再配合事务回滚。所以我建议一开始就把下面这句话刻在脑子里动手写 UPDATE 和 DELETE 之前先看一眼有没有 WHERE先确认 WHERE 条件对不对。顺带看一眼事务的雏形考试和面试常提START TRANSACTION; UPDATE score SET score 99 WHERE student_id 1 AND course_id 1; -- 如果发现改错了 ROLLBACK; -- 如果确认无误 COMMIT;事务的意义就是把多个操作打包成一件事要么全成功要么全失败。DAY02 先理解这个思想就行后面深入学习时会展开讲隔离级别、锁等。还有一种更高级的插入写法叫“存在则更新不存在则插入”INSERT INTO score (student_id, course_id, score, exam_time) VALUES (1, 1, 85.5, NOW()) ON DUPLICATE KEY UPDATE score VALUES(score);注意这个语法依赖唯一键或主键。这里联合主键是(student_id, course_id)所以同一个学生对同一门课再次插入时就会自动触发 UPDATE 更新成绩。做成绩录入功能时这个语法非常常用省去了先查再写的两段逻辑。4.2 查询与排序把数据按规则捞出来DML 四兄弟里SELECT 是最值得花时间的。第二天我开始写各种查询条件才发现排序热搜词里的 mysql 排序远没有想象中简单。最基础的排序-- 按成绩从高到低排序 SELECT student_id, course_id, score FROM score ORDER BY score DESC; -- 再按考试时间从早到晚 SELECT student_id, course_id, score, exam_time FROM score ORDER BY score DESC, exam_time ASC;ORDER BY后面可以跟多个字段先按第一个字段排如果相同再按第二个字段排。这个多字段排序的逻辑一定要亲手写一遍才有感觉。DESC 是降序ASC 是升序不写默认 ASC。WHERE 加条件的组合-- 查询分数大于等于 90 分的记录 SELECT * FROM score WHERE score 90; -- 多条件组合 SELECT * FROM score WHERE score 80 AND score 90 ORDER BY score DESC;这里要理解“单引号”和“双引号”的区别MySQL 里字符串一般用单引号双引号在某些模式下也能用但为了规范统一用单引号。另外、、IN、LIKE这些要会用-- 查询学号为 S001 或 S002 的学生 SELECT * FROM student WHERE student_no IN (S001, S002); -- LIKE 模糊查询% 表示任意字符 SELECT * FROM student WHERE name LIKE 张%;我还专门练了 LIMIT 分页-- 每页 5 条取第 2 页 SELECT * FROM score ORDER BY id LIMIT 5 OFFSET 5; -- 等价写法 SELECT * FROM score ORDER BY id LIMIT 5, 5;第一个数字是偏移量第二个数字是条数。分页是几乎所有业务系统都有的需求DAY02 至少要把 LIMIT 语法练会不然以后写列表接口会卡壳。聚合查询我第一次接触时很兴奋原来 SQL 能直接统计-- 每个学生考了几门课 SELECT student_id, COUNT(*) AS course_cnt, AVG(score) AS avg_score, MAX(score) AS max_score FROM score GROUP BY student_id;COUNT计数、AVG平均、MAX/MIN最大最小配合GROUP BY分组统计这是 SQL 最有魅力的地方。DAY02 不要求完全掌握 GROUP BY 和 HAVING但要能看懂上面的语句在干什么。5. 索引初体验与性能认知5.1 用 EXPLAIN 看一次全表扫描DAY02 学习规划里我本来没打算碰索引但学排序时发现数据量一大查询就慢。这时候我查了一下资料才知道 MySQL 查询慢大概率是没用上索引。索引这个词在热搜词里也是高频于是第二天傍晚我提前开始看。先说一个最直观的实验。我在 score 表里插了 1000 条测试数据写个循环插入就行然后执行EXPLAIN SELECT * FROM score WHERE course_id 3;EXPLAIN 是 MySQL 自带的“执行计划查看器”它不会真的执行查询而是告诉你 MySQL 打算怎么查。第一次看到结果里type ALL时我专门查了半天才知道ALL表示全表扫描意思是从头到尾把整张表翻一遍。对于数据量小的表无所谓等表里有上百万条数据这种查询就会变得非常慢。然后我创建一个索引CREATE INDEX idx_score_course ON score(course_id);再执行一次 EXPLAINEXPLAIN SELECT * FROM score WHERE course_id 3;这时候type从ALL变成了refMySQL 会告诉你可以通过索引快速定位。索引的工作原理可以类比书的目录没有目录时你要一页页翻有目录后直接翻到对应页码。CREATE INDEX是创建索引最基本的语句。另外在建表时加上 KEY 也是一种方式比如前面 score 表里KEY idx_course_id (course_id)效果一样。索引字段的选择优先选 WHERE 条件里最常用的字段以及 ORDER BY 排序字段。5.2 索引不是越多越好看到索引提速如此明显我一度想把所有字段都建上索引。还好查资料及时刹住了车索引有代价。代价有两点写入变慢。每次 INSERT、UPDATE、DELETEMySQL 不光要改数据还要维护索引结构索引越多写入越慢。占用空间。索引本身也要存储一张大表建一堆索引磁盘占用很可观。所以索引要建在“区分度高”的字段上。什么叫区分度高就是字段里不同值很多。比如student_no学号、id主键区分度高适合建索引。而gender性别只有 M 和 F 两种值建索引效果极差MySQL 很可能不用它。这个点也是后面面试常问的。联合索引是另一个值得提前知道的概念CREATE INDEX idx_student_course ON score(student_id, course_id);联合索引遵循“最左前缀”原则意思是如果你建的是(student_id, course_id)那么查询条件带student_id时能用这个索引但只带course_id时不一定能用。所以联合索引的字段顺序非常讲究一般把“区分度高”的字段放前面或者按查询频率排。DAY02 能理解这个规则就足够了实际业务中设计联合索引是在后面项目里慢慢练出来的。这里顺便解释几个热搜词里的大词mysql锁原理及面试题、mysql事务处理、mysql存储过程。DAY02 不需要立刻深入理解锁和事务的原理但要知道它们是 InnoDB 引擎的重要能力锁用来保证并发下数据不错乱事务用来保证一组操作要么全成功要么全失败。存储过程是把一段 SQL 逻辑封装到数据库里用CALL调用。这些我在后面的学习日记里会专门展开今天先在索引这个“性能入口”站住脚。6. 常见问题快查与工具选择6.1 第二天高频报错速查表第二天磕磕绊绊我把遇到的报错整理成了表格后面几天一直在用。这几个错误基本覆盖了新手前两周的大部分问题。报错信息原因解决办法ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock服务没启动或 socket 路径不对systemctl status mysql或改用-h 127.0.0.1 -P 3306ERROR 1045 (28000): Access denied用户名或密码错误检查用户名、密码必要时用mysqld_safe --skip-grant-tables重置密码ERROR 1418 (HY000): This function has none of DETERMINISTIC...创建存储过程/函数时权限或声明问题加上DETERMINISTIC或者调整log_bin_trust_function_creators中文写入变乱码字符集不是 utf8mb4建库建表统一指定CHARSETutf8mb4连接时加SET NAMES utf8mb4;数据插入特别慢可能是每条 INSERT 单独提交改为批量 INSERT多条数据一个语句插入服务启动失败Windows 报错 e0434352服务依赖或磁盘权限异常查看错误日志data目录下.err文件修复依赖项ERROR 1418这个错误我是在实验存储过程时碰到的。MySQL 默认日志开启状态不允许创建不明确的存储函数解决办法要么在创建时声明DETERMINISTIC要么设置全局变量。新手其实不用着急写存储过程先会写存储过程的基本框架就行DELIMITER // CREATE PROCEDURE get_score(IN stu_id INT) BEGIN SELECT * FROM score WHERE student_id stu_id; END // DELIMITER ; CALL get_score(1);DELIMITER是告诉 MySQL “等一下别用分号作为语句结束符”因为存储过程内部有分号。我记得第一次写存储过程时一直报错就是因为没加DELIMITER。这个坑迟早都会踩先记下来。6.2 工具链与学习节奏建议DAY02 结束时我对“用什么工具连 MySQL”也有了自己的体会。命令行客户端是必须练熟的我见过很多人只会用 Navicat 点鼠标到服务器上排查问题寸步难行。但日常学习时可视化工具能大大提高效率尤其是看表结构、看查询结果。推荐三个工具按情况选MySQL Workbench官方免费功能全支持 ER 图、执行计划可视化新手首选。DBeaver开源免费跨平台支持多种数据库适合以后接触 PostgreSQL 等。Navicat功能强大、界面好看但是收费软件。网上常有人搜“navicat for mysql 破解安装”我不建议碰破解版一来有安全风险二来作为开发者工具版权意识从第一天就要有。用官方免费工具完全足够起步。另外我注意到热搜词里有“mysql下载官网”“mysql安装教程”这类说明很多人在第一步卡住了。单独提醒一句MySQL 官方下载页面有多个版本新手直接选最新的 LTS 稳定版即可不要选Debug版或者Test版也不要选No thanks, just start my download下面的旧版本选MySQL Community Server就好。关于学习节奏我给自己定了一个 DAY02 的“最低完成清单”能用命令行连接本地 MySQL能解释 socket 和 TCP 连接的区别。能不看笔记手写建表语句包含主键、默认值、注释、utf8mb4。能完成对一个表的增删改查特别是 UPDATE 必须带 WHERE。能写 ORDER BY 排序和 LIMIT 分页。能用 EXPLAIN 判断一个查询是否走了索引。第二天能不能全部掌握不重要重要的是你动手敲了哪些 SQL、踩了哪些坑。我自己最大的体会是MySQL 的基础命令是最值得反复练的建表、插数据、查数据这三件事练到不用想以后学 join、子查询、存储过程这些进阶内容时才不会慌。这一天的内容正好卡在“装好了”和“能用起来”之间跨过这个坎后面的学习就顺畅多了。