ARTICLE DETAIL

资讯详情

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

MySQL多表查询与事务锁:黑马课程复习笔记

MySQL多表查询与事务锁:黑马课程复习笔记 最近抽空把黑马程序员的MySQL课程重新刷了一遍重点复习的是第三章和第四章。这两章在面试里被问到的频率极高实际工作中写复杂查询、处理数据一致性也全靠它们。如果你正在学MySQL或者准备跳槽前系统过一遍数据库基础这篇笔记可以帮你少走很多弯路。我把两章的核心知识点、实操细节和踩过的坑都整理出来了照着过一遍比你闷头看视频有效得多。1. 复习前先搞懂黑马这两章到底在讲什么1.1 章节定位从单表操作走向真实业务黑马程序员的MySQL课程是很多Java开发入门数据库的第一选择。它的前两章主要讲数据库概述、SQL的增删改查、条件查询、排序分页这些单表操作基本上就是让大家先会“和一张表打交道”。但从第三章开始难度明显上来了。第三章叫“多表查询与子查询”第四章叫“事务与锁”。这两个章节解决的问题特别真实第三章解决的是业务数据分散在多张表里你怎么把关联数据一次性查出来第四章解决的是数据写入过程中出现异常、多个用户同时操作同一批数据时怎么保证数据不混乱。我把这两章复习完的最大感受是前面的单表查询是“单词”多表查询是“造句”事务就是“语法规则”。没有这两章你连公司的业务报表都看不懂更别说写接口了。所以如果你只学了前半部分就觉得自己会MySQL了那真的还差得远。1.2 学习资源怎么选才高效黑马程序员的资源主要有三类官网课程视频、配套讲义文档、练习题和项目案例。我的复习方式是视频开1.5倍速过一遍重点是听老师讲“为什么这么写”讲义当作工具书碰到不确定的语法就翻练习题必须自己动手在Navicat或者命令行里敲一遍光看是永远学不会SQL的。有一点特别提醒看视频的时候别光盯着老师的操作一定要自己建两张表比如“部门表”和“员工表”一边看一边跟着写。原因很简单多表查询的SQL就算语法全背下来不亲手跑一遍你很难理解结果集是怎么变出来的。而且自己建表还能顺便练一下建表语法和测试数据插入一举两得。2. 第三章核心拆解多表查询与子查询SQL才是真的入门2.1 连接查询的三种连接方式你真的掌握了吗第三章里篇幅最多的就是连接查询。黑马老师的讲法很清晰把连接查询分成内连接、左外连接、右外连接三种每一种都给了一个简单的部门和员工的例子。先说内连接。内连接就是只返回两个表中匹配成功的记录用INNER JOIN或者JOIN关键字。它的逻辑就像两个集合取交集。SELECT e.name, d.dept_name FROM emp e INNER JOIN dept d ON e.dept_id d.id;这里有个容易忽略的点为什么用ON而不是WHERE其实内连接里你用ON和WHERE最终结果一样但语义不同。ON是连接条件决定哪些行可以匹配WHERE是结果集的过滤条件。如果你把连接条件放到WHERE里遇到左外连接、右外连接时就容易出错所以黑马的讲义里都强调连接条件必须放ON后面。再说左外连接和右外连接。左外连接LEFT JOIN返回左表全部记录右表没有匹配的就显示NULL。右外连接正好相反。为什么要区分左右因为实际业务中你经常需要“主表全部数据”加上“从表的补充信息”。比如查所有部门及其员工数量如果有的部门没有员工内连接就把空部门滤掉了这时就必须用左外连接以部门表为主表。我用生活中的例子理解内连接就是“你对象有你也有”的共同爱好左外连接就是“你所有的爱好以及对象有没有陪你做”右外连接就是“对象所有的爱好以及你有没有参与”。这样一想连接方向就再也不会记反了。2.2 子查询的三种形态标量、行、表学会拆解就不难子查询是第三章的另一个大块头。黑马课程把子查询分成三种标量子查询、行子查询、表子查询。区分标准就是子查询返回的结果是“一个值、一行还是多行多列”。标量子查询最简单返回单个值常用于WHERE里和字段比较。比如查“工资高于平均工资的员工”SELECT name, salary FROM emp WHERE salary (SELECT AVG(salary) FROM emp);子查询先算出平均工资再把每个人的工资拿去做比较。这里的子查询就是“单值”的理解起来很自然。行子查询返回一行多列通常和IN配合。比如查“和某员工同一个部门且同一个职位的其他员工”SELECT * FROM emp WHERE (dept_id, job) (SELECT dept_id, job FROM emp WHERE name 张三);表子查询返回多行多列最常见的是放在FROM后面当作临时表或者放在WHERE后面配合IN、EXISTS。比如查“各部门最高工资的员工信息”可以先在子查询里按部门分组找出最高工资再把原表跟子查询结果连接SELECT e.* FROM emp e INNER JOIN ( SELECT dept_id, MAX(salary) max_salary FROM emp GROUP BY dept_id ) t ON e.dept_id t.dept_id AND e.salary t.max_salary;这个案例非常经典面试也爱问。它的核心思想是先“缩小数据范围”再做连接。我在复习时发现很多同学对子查询发怵是因为总想把整条SQL一句话写出来其实完全可以分步骤先写子查询跑出来看看结果再套进外层查询。MySQL不像编程语言有调试器但这个笨办法最管用。2.3 连接查询实操中那些让你怀疑人生的坑第三章节末尾有一堆课后练习题做完之后我总结出了三个高频坑。第一个坑忘了加连接条件导致笛卡尔积。比如两张表各有10条数据不加ON连接结果就是100条。这种错误往往不会报错只会让数据翻倍如果没注意后面的统计结果全是错的。排查方法也很简单查完先看影响行数是否符合预期再抽查几条关联字段是否匹配。第二个坑多表连接时的字段名冲突。比如员工表有id部门表也有id如果你直接查询SELECT id就会报错“字段不唯一”。解决办法是给表起别名查询时用别名.字段的格式我习惯给每张表一个短别称比如emp e、dept d既能少打字又能避免歧义。第三个坑外连接时把过滤条件写进了ON里导致结果集和预想不一致。我在复习时就犯过这个错用LEFT JOIN查“所有部门以及员工工资大于5000的员工”如果员工不满足条件结果保留部门但员工信息显示NULL但如果我把工资条件写进WHERE那些没有满足条件员工的部门就直接不显示了。要想保留部门过滤条件必须放在ON里和连接条件并列这是一个很细微但影响很大的语法点。3. 第四章核心拆解事务与锁数据安全的第一道防线3.1 事务的ACID到底在说什么别只背四个词第四章一上来就讲事务的四大特性原子性、一致性、隔离性、持久性简称ACID。黑马老师用转账来举例我也用这个例子拆一遍。原子性转账要么成功扣款加款要么都失败不能出现“扣了钱但对方没到账”的中间状态。MySQL通过undo log实现回滚如果执行过程中出错就把已经执行的语句逆操作一遍。一致性不管事务怎么执行事务结束后所有数据的约束、规则都要保持正确。比如转账后总金额不变银行不会因为系统故障多出钱少钱。一致性本质上依赖其他三个特性共同保证。隔离性两个事务同时改同一条记录时彼此不能互相干扰。比如A事务在转账过程中B事务不应该看到“钱已经扣了但还没到账”的中间状态。隔离性靠锁机制和多版本并发控制MVCC实现。持久性事务提交后数据就不能再回滚即使数据库崩溃数据也要保留。MySQL通过redo log保证已经提交的事务不会丢。我自己的理解是原子性是“要么不做要么做全套”隔离性是“你做你的我做我的别偷看半成品”。面试时如果只背概念而说不清底层日志和锁对方会觉得你是死记硬背能把undo log、redo log、锁、MVCC这些词串进来讲才是真的理解。3.2 隔离级别与并发问题一张表看懂脏读、不可重复读、幻读第四章另一个核心内容是隔离级别。MySQL有四种隔离级别读未提交、读已提交、可重复读、串行化。隔离级别越高并发能力越弱但数据越安全。每种级别能解决和遗留的问题我整理成了一张表隔离级别脏读不可重复读幻读常用场景读未提交READ UNCOMMITTED可能可能可能不在乎准确性的统计读已提交READ COMMITTED解决可能可能Oracle默认部分报表场景可重复读REPEATABLE READ解决解决InnoDB下基本解决MySQL默认业务系统常用串行化SERIALIZABLE解决解决解决银行转账高安全场景这里的“脏读”就是读到别的事务未提交的修改比如A改了数据还没提交B就看到了A后面回滚那B就是白读了。“不可重复读”是指同一个事务里两次查询同一行结果不一致因为中间有其他事务提交了修改。“幻读”是指一个事务里两次范围查询第二次多出了一些之前没有的行就像出现幻觉一样。MySQL的默认隔离级别是“可重复读”但InnoDB引擎通过间隙锁等机制在可重复读级别下已经可以阻止幻读所以实际使用中你既保住了数据准确性又不会像串行化那样性能拉胯。面试里特别爱考“MySQL默认隔离级别”和“可重复读为什么能解决幻读”复习时一定要能用自己的话说清楚。3.3 事务实操开启、提交、回滚以及一个隐藏的坑黑马课程在讲完理论后会带你在命令行里实操事务。基本语法很简单START TRANSACTION; UPDATE account SET balance balance - 500 WHERE id 1; UPDATE account SET balance balance 500 WHERE id 2; COMMIT;如果第二步执行出错就执行ROLLBACK;两条UPDATE都会撤销。实操里有一个特别容易踩的坑START TRANSACTION之后如果你执行的是DDL语句比如CREATE TABLE、ALTER TABLE、TRUNCATE这些语句会被隐式提交导致事务提前结束之前的操作无法再回滚。我在复习时专门试过一次发现确实如此。所以事务里只应该放INSERT、UPDATE、DELETE这类DML语句不要混入DDL。另一个坑是表引擎问题。InnoDB支持事务MyISAM不支持。如果你在MyISAM表上执行事务回滚你会发现数据照样变了因为它根本没开启事务。检查表引擎的语句是SHOW TABLE STATUS LIKE account;看Engine字段是不是InnoDB。如果你用的MySQL版本默认是InnoDB一般不会遇到但架不住有人从老项目里接手MyISAM表。这个问题在实际生产环境里遇到过不止一次一定要检查。4. 复习时踩过的坑和排查技巧实录4.1 连接查询结果重复问题多半出在连接条件不完整我在复习第三章练习题时遇到过“查出来的记录比预期多”。比如查“员工的部门名称”员工表10条部门表5条用内连接查出来10条正常。但如果员工表和部门表是多对多关系比如一个人有多个部门你没有把关联表的中间键加上结果就会多出很多行。排查思路很简单先用SELECT *把所有字段打出来看看哪些行的数据是重复的。然后分析重复行的关联字段是否有不同的组合。比如查“员工及其所属部门”时如果通过emp.dept_id dept.id连接但员工表里某些记录的dept_id为NULL内连接会把它们丢掉而如果部门表里存在重复的id理论上不该有也会让数据膨胀。所以连接条件最好建立在唯一索引字段上连接字段尽量是主键或外键。4.2 事务执行失败但回滚没生效先查这三个位置不生效的原因十有八九是下面这三种。第一执行了隐式提交语句。上面说过DDL会隐式提交还有SET、GRANT这些语句也会。如果事务里不小心混进去了前面的操作就永久生效了。解决办法是事务里的语句要控制得尽量少只保留必要的增删改。第二表的引擎不是InnoDB。查一下Engine字段不是InnoDB就改成InnoDB或者设计表时就指定引擎CREATE TABLE t ( id INT PRIMARY KEY ) ENGINEInnoDB;第三没有正确使用ROLLBACK。有的新手会把ROLLBACK;写错位置或者写在了判断条件之外。比如使用存储过程时要先声明DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;这类异常处理语句否则错误发生时程序直接退出事务不会自动回滚。黑马课程里对存储过程的错误处理提得不多这个坑是我在公司项目里踩到的。4.3 事务隔离级别改了不生效区分会话级和全局级你可能在MySQL里执行SET TRANSACTION ISOLATION LEVEL READ COMMITTED;然后发现当前事务还是可重复读。这是因为你没指定SESSION还是GLOBAL。正确写法是SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;这样只影响当前连接如果用GLOBAL会影响之后新连接的会话对当前连接也不生效。查看当前隔离级别用SHOW VARIABLES LIKE transaction_isolation;如果看到还是REPEATABLE-READ就说明你设置错了级别。另外配置文件里也可以改在[mysqld]节下加transaction-isolation READ-COMMITTED改完记得重启MySQL。4.4 复习时如何高效利用黑马练习题黑马的练习题库在官网上可以直接下载但我建议别直接看答案。我做题的方法分三步先自己写SQL跑通然后再故意改错几个条件看看结果变化搞懂背后的逻辑最后把几个经典题型整理成自己的笔记比如“每个部门最高工资的员工”这类题把解法模板记下来。这样一轮下来遇到类似的面试题你就能秒反应。5. 从“能写SQL”到“能讲原理”复习面试与工作衔接建议5.1 面试高频考点这两章最容易被追问的细节面试官问到MySQL几乎绕不开“多表查询的几种方式”和“事务的隔离级别”。但是这里有个陷阱他会先让你答概念然后追问一句“你能说说MySQL可重复读底层是怎么实现的吗”。这个时候如果你只背了“可重复读可以避免不可重复读”多半卡壳。我建议把第三章“子查询的性能问题”和第四章“隔离级别底层实现”串起来理解。子查询里如果用IN后接一个大的子查询性能往往不太好因为子查询可能需要生成临时表优化手段是改成JOIN或者使用EXISTS。而事务隔离级别的实现除了加锁还依赖MVCC每一行有两个隐藏列一个保存版本号一个回滚指针读操作根据版本号去快照读写操作用当前读加锁。这两个点如果能用自己的话讲明白面试官基本会认可你“懂数据库”。5.2 学习资源再扩展黑马之外还能看什么黑马课程最大的优点是案例贴近真实业务视频里带了很多“企业里会这么写”的提示。但是想加深理解我还推荐加上MySQL官方文档作为工具书不用通读核心请看“InnoDB Architecture”和“SQL Optimization”两个部分。刷题方面LeetCode数据库题库有几十道题从易到难都有。虽然有些题在解题风格上偏“脑筋急转弯”但能锻炼你把复杂查询拆成简单步骤的能力。我自己刷了大概三十道再回去看黑马的章节练习题觉得逻辑清楚了很多。5.3 给同学的一点点个人复习建议如果你时间紧张第三、四章至少要保证第三章的连接查询和第四章的ACID、隔离级别完全掌握。这两块是后续学索引优化、锁机制、主从复制的基石。我复习的大体节奏是先花一个晚上把黑马讲义里的目录和案例过一遍把不会的知识点标记出来再花两个晚上动手敲代码把每一段SQL都跑出结果最后一天来做错题总结和知识点串联。这样下来三到四天基本能吃得比较透。另外把所有课堂案例都自己手写一遍比抄十遍笔记有用得多。我习惯把每道练习题写成独立文件SQL文件里加上注释记录自己的解题思路和踩过的坑。下次再遇到同类问题直接翻自己的文件比重新看一遍视频效率高得多。复习完这两章你会发现一个有意思的现象真正难的从来不是语法而是“你知不知道数据是怎么关联起来的”“一个操作会不会破坏数据一致性”。把这两章啃下来MySQL就算迈进门槛了。后面再去啃索引和锁你会觉得顺很多。
返回列表