ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

数据库核心理论与实战:事务、MVCC、SQL优化与并发控制

数据库核心理论与实战:事务、MVCC、SQL优化与并发控制 1. 从ACID到MVCC数据库最核心的理论盘一遍先说个现实问题不管是做课程设计、应付面试还是实际开发你迟早都得回答“事务四个特性是什么”这种问题。但很多人背完ACID就结束了真被问到“为什么需要undo log和redo log两种日志”就卡壳。这一节我把事务这块彻底拆开讲。1.1 事务的四要素到底在解决什么问题ACID这四个字母很多人能脱口而出原子性、一致性、隔离性、持久性但问到底层是怎么实现的就答不上来了。我就用转账这个经典场景来说。你要给朋友转100块钱系统要做两件事你的账户扣100朋友的账户加100。原子性保证的就是这两件事要么全成要么全不成。如果扣款成功但加款失败这笔钱就凭空消失了。MySQL InnoDB实现原子性靠的是undo log操作之前先把原始数据记下来出错了就顺着undo log回滚。持久性靠的是redo log这个很多新手理解偏了。它不是说把数据写到磁盘就完了而是说只要事务提交了即使数据库下一秒宕机重启后数据还是在的。InnoDB的做法是先写redo log再写数据页这叫WALWrite-Ahead Logging。如果数据页还没刷盘就断电了重启后就拿redo log重放一遍把丢失的更新补回来。一致性和隔离性就更有意思了。一致性是最终目标原子性、隔离性都是为它服务的。你转账前后两个人的总金额是不变的这就是钱层面的一致性。数据库层面还约束了各种规则比如外键、唯一约束、check约束违反了就回滚。隔离性则是处理并发场景的两个事务同时操作同一条数据如果互不设防就会出现脏读、不可重复读、幻读这些问题。所以ACID不是四个独立的东西它们是配合工作的。原子性保证操作不半途而废持久性保证提交了就不丢隔离性保证并发不出乱子最后一起促成一致性。1.2 隔离级别和并发异常的对应关系隔离性不是想设多高就设多高太高了并发性能差太低了数据又容易出错。SQL标准定了四个隔离级别不同的数据库实现还不太一样。先看并发场景下会出什么问题脏读事务A改了数据还没提交事务B读到了这个修改过的值然后A回滚了B读到的就是个不存在的脏数据。不可重复读事务A先读了一条记录事务B把这条记录改了并提交事务A再读一次发现值变了同一个事务里两次读结果不一样。幻读事务A按条件查出一批记录事务B往这个范围内插入了一条新记录并提交事务A再按同样条件查发现多了一行像是产生了幻觉。四个隔离级别对付这些问题的能力如下隔离级别脏读不可重复读幻读读未提交Read Uncommitted可能可能可能读已提交Read Committed不会可能可能可重复读Repeatable Read不会不会可能串行化Serializable不会不会不会这里有个特别多人搞混的点MySQL默认隔离级别是可重复读它通过间隙锁Gap Lock把幻读也基本解决了。但Oracle默认是读已提交所以在Oracle里一个事务内两次相同查询是可能读到不同数据的。很多人从MySQL转到Oracle在这个上面栽过跟头。1.3 MVCC是怎么让读写不打架的高并发下如果读要等写、写要等读系统就用不了了。MVCC多版本并发控制的思路就是给数据行的每次修改都留个版本读操作读的是快照不用等写操作释放锁。具体实现靠三样东西隐藏字段、undo log版本链、ReadView。每行数据除了业务字段还有两个隐藏列一个是事务IDtrx_id一个是回滚指针roll_pointer。每次更新不会直接覆盖旧值而是生成一个新版本旧版本通过回滚指针串成链表。当事务发起查询时会生成一个ReadView里面记录了当前活跃事务的ID列表。通过比较版本链上每个版本的trx_id和ReadView里的信息就能判断这个版本对当前事务是否可见。这样读已提交级别每次查询都生成新的ReadView可重复读级别只生成一次ReadView后续查询都复用所以同一事务里看到的快照是一致的。搞懂MVCC之后你对“数据库为什么会死锁”“为什么同样的SQL在不同隔离级别下结果不一样”这些问题都会理解得更深。很多面试官就喜欢从MVCC一步步追问到锁再追问到死锁排查这条线是完整的。2. SQL实操细节复盘分组、连接和最容易翻车的点热搜词里出现了“mysql数据库 - 分组选择数据”“数据库增删改查”“查询数据库”这些说明大家最常用的还是SQL本身。但SQL看着简单真正写起来坑不少尤其是分组和连接这两块很多课程设计里都能看到明显写错的例子。2.1 增删改查的高风险动作先说查询。几乎所有业务系统80%的操作都是SELECT但很多人查数据就只会SELECT *。这在数据量小的时候无所谓数据量大了就是灾难。我见过一台开发库执行一个SELECT * FROM user结果查出来几百万行直接拖垮了数据库连接池。正确的习惯是先想清楚要哪些字段再写SELECT。再说明显的坑。UPDATE和DELETE不带WHERE条件这是所有数据库事故里最常见的一种。我朋友在测试环境执行UPDATE user SET status 1清掉一条测试数据忘了加WHERE结果全表状态都被改了。好在是测试环境但同一个错误放到生产环境就是重大事故。这里有个保险习惯先SELECT确认再UPDATE或DELETE。比如要删除id为1024的记录先执行SELECT * FROM user WHERE id 1024确认查出来的就是要删的那条再执行DELETE。有时候还应该先用COUNT(*)看看影响行数心里有数再动手。INSERT也有细节。批量插入一定要关注一次插入的数据量几千条分批次插入比一次性塞几万条要稳妥。还有个常见问题是违反唯一约束比如并发场景下两个请求同时插入相同唯一键的数据。解决办法可以靠数据库的唯一索引兜底也可以在业务层用insert ... on duplicate key update这种语法处理MySQL和达梦都支持类似的UPSERT语义。2.2 分组和聚合的过滤顺序“分组选择数据”这个需求太常见了比如查每个部门的平均工资、每个商品的销售总量。核心就是GROUP BY 聚合函数COUNT、SUM、AVG、MAX、MIN。这里最常见的错误是在GROUP BY之后用WHERE过滤聚合结果。记住一条铁律WHERE是分组前过滤行HAVING是分组后过滤组。比如想查平均工资大于5000的部门正确写法是SELECT dept_id, AVG(salary) AS avg_salary FROM employee WHERE status 1 GROUP BY dept_id HAVING AVG(salary) 5000;注意几个细节WHERE先过滤掉status不等于1的员工再进行分组这样CPU不用处理不需要的数据HAVING里写的是聚合后的条件过滤的是组SELECT里出现的非聚合字段必须出现在GROUP BY里否则在ONLY_FULL_GROUP_BY模式下直接报错。还有个容易被忽视的点是NULL值。聚合函数里COUNT()会统计NULL行COUNT(column)不会统计NULL行。如果你统计的是“部门里有考勤记录的人数”用COUNT(attendance_time)如果统计部门总人数用COUNT()。两者混用数据就是错的。2.3 多表连接的执行逻辑面试经常问INNER JOIN、LEFT JOIN、RIGHT JOIN的区别这个大多数人都能说出来。但问到“LEFT JOIN为什么有时候比INNER JOIN慢怎么优化”能答明白的人就少了。我先说下SQL的执行顺序很多人写SQL不按顺序想但数据库执行是有固定流程的FROM - ON - JOIN - WHERE - GROUP BY - HAVING - SELECT - DISTINCT - ORDER BY - LIMIT这个顺序特别重要。比如LEFT JOIN WHERE很多人误以为WHERE是在连接之后才过滤的其实对LEFT JOIN来说ON决定左表保留与否WHERE则会把不符合条件的右表列变成NULL。如果你想保留左表所有行只是过滤右表的某些值条件应该写在ON里而不是WHERE里。再说到连接的性能。MySQL 8.0之前只有Nested Loop Join就是一张表循环嵌套另一张表。表一大性能就很差。MySQL 8.0开始引入了Hash Join用在等值连接且无法走索引的场景一下子快了很多。Oracle早就支持了。这提示我们如果做等值连接尽量让关联字段有索引数据量大的连接查询优先考虑用EXPLAIN看执行计划。3. 数据库设计ER图、范式与主键选择的实战经验热搜词里有个典型的毕业设计场景“我成考毕业设计论文数据库er图用哪种方法”后面还跟着“数据库设计 - 博客系统”。这说明很多人不仅要写代码还得把数据库设计文档、ER图画出来交给老师。这一节就讲讲数据库设计从需求到落地要怎么做。3.1 ER图到底该用什么方法画ER图实体-联系图是数据库设计的核心产物也是论文里必须有的图。很多人在这一步就很纠结到底用哪个工具、用哪套符号体系。先说符号体系。目前最常见的两套一套是Chen方法用矩形表示实体、菱形表示联系、椭圆表示属性另一套是Crow‘s Foot乌鸦脚方法用不同的连线端点表示一对多、多对多关系。现在教材上常见的还有基于UML类图的表示方式。毕业设计论文里用Chen方法的最多因为老师熟悉、结构清晰一眼就能看出实体和联系。但说实话我自己设计时基本是先画Crow’s Foot因为它在表达基数关系时更直观。比如“一个用户只能发一篇博客一篇博客只能属于一个用户”这种一对一关系Crow‘s Foot用两根竖直短线表示一对多用三叉的“乌鸦脚”表示。画完关系直接转成表结构几乎不会漏外键。至于工具我常用的组合是这样的快速原型用draw.io或者ProcessOn免费、上手快、导出图片方便正式论文图用Visio或者PlantUML。PlantUML是写代码生成ER图的好处是改起来方便不会因为调图形位置浪费一下午。比如简单的语法就能画出一对多关系startuml entity “User” as user { *id : bigint PK * name : varchar } entity “Blog” as blog { *id : bigint PK * user_id : bigint FK title : varchar } user ||--o{ blog enduml不管你用哪种方法最关键的不是图多好看而是图和表结构对得上。我评审论文时经常遇到画的ER图和后面建的SQL表完全对不上的情况这种图在答辩时会被问到怀疑人生。3.2 三大范式背后的取舍范式是数据库设计的理论基础但很多人学完之后有个误解范式越高越好。真实生产环境根本不是这样。第一范式1NF每列都是不可再分的原子值。举例一个人有多个手机号不能存成“13800138000,13900139000”这种字符串要么拆行要么拆列。这个基本是常识。第二范式2NF在1NF基础上非主键列必须完全依赖于主键不能只依赖主键的一部分。这个主要针对联合主键。比如订单明细表用“订单号商品ID”做主键如果“商品名称”只依赖商品ID而不依赖订单号就违反2NF了。解决办法是拆表。第三范式3NF非主键列不能依赖于另一个非主键列。比如用户表里有“部门ID”和“部门名称”部门名称依赖部门ID不直接依赖用户ID这就冗余了。正规做法是把部门单独建表。但反范式在企业项目里太常见了。比如报表系统里要查一个订单关联的用户名、部门名、商品名如果全部严格按3NF设计做一次查询要join七八张表报表直接卡死。这种场景下很多人会把用户名、部门名冗余到订单表里用空间换时间配合定时任务或CDC同步维护冗余字段。结论很清晰核心业务表订单、账户、交易尽量满足3NF保证一致性查询密集的表可以适当冗余但冗余字段必须有明确的同步和更新方案否则就等着数据对不上吧。3.3 主键方案怎么选主键设计直接影响数据库性能和后续扩展。我来说说几种常见方案的优劣。自增主键最简单插入性能好索引紧凑。但缺点是数据迁移容易冲突比如两个库合并时两边id都是从1开始的就得重新生成。而且自增id很容易被爬虫猜出业务量别人通过id100和id101的差值就知道你新增了多少数据。UUID主键没有迁移冲突问题但有个非常大的坑UUID是随机字符串在InnoDB里主键是聚簇索引随机插入会导致页分裂索引碎片化严重写入性能下降明显。如果非要用UUID建议用有序UUID或者雪花ID尽量保证插入的递增性。雪花IDSnowflake是目前分布式系统里最主流的主键方案。64位的ID由时间戳、机器ID、序列号组成全局唯一且大致有序。很多大厂的分布式ID服务都是这个思路的变种。自研雪花ID不难但要注意时钟回拨问题机器的时间往回跳了就可能导致ID重复需要有兜底策略。还有外键的问题。教科书上说外键保证引用完整性要建。但互联网公司普遍不建物理外键用代码层面去控制引用关系。原因很简单外键约束会让每次插入、更新、删除都去检查关联表影响性能而且分库分表之后外键根本没没法跨库生效。所以如果你做课程设计或者企业内部小系统建外键没问题老师喜欢做高并发系统慎用物理外键。4. 并发控制锁、死锁与连接池的生产级视角热搜词里“数据库并发锁”“数据库死锁”“mysql的数据库连接池”这几个是高频中的高频。这一节我从生产环境的视角来讲这些知识面试爱问线上出了问题也非常实用。4.1 InnoDB锁的分类与加锁时机锁这个话题很多初学者只听说过“锁”这个词不知道锁到底加在哪、什么时候加。InnoDB的锁我习惯这样分类来记。从粒度分有表级锁和行级锁。InnoDB的行锁不是锁整个行而是通过索引项来加锁。如果SQL没有走索引行锁会退化成表锁这也就意味着索引对并发控制也很关键。从模式分有共享锁S锁读锁和排他锁X锁写锁。S锁和S锁兼容S锁和X锁不兼容X锁和X锁也不兼容。加锁就是按照这个兼容性矩阵排队。从算法分还有记录锁Record Lock、间隙锁Gap Lock、临键锁Next-Key Lock。记录锁锁住单条记录间隙锁锁住两个索引值之间的区间防止别的事务往这个区间插入数据主要用来解决幻读临键锁是记录锁和间隙锁的组合锁住记录加前面的间隙。很多人在可重复读隔离级别下会困惑为什么明明没查到记录却插入不进去多半就是间隙锁在起作用。比如表里只有id10和id20两条记录你执行了SELECT * FROM t WHERE id BETWEEN 11 AND 19 FOR UPDATEInnoDB会把(10, 20)这个区间锁住别的事务想插入id15的记录就会被阻塞。4.2 死锁排查的完整思路死锁的本质是多个事务各自持有锁又在等对方持有的锁形成一个环。数据库不会让这个环永远等下去InnoDB会检测到死锁并回滚其中一个事务然后报出Deadlock found错误。排查死锁我会按这个思路来第一步查看最近一次死锁日志。执行SHOW ENGINE INNODB STATUS里面有LATEST DETECTED DEADLOCK那段会告诉你两个事务各持有哪个锁、在等哪个锁。日志里会显示事务的SQL语句和持锁的索引记录。第二步根据日志还原事务执行顺序。真实案例里最常见的死锁场景是两条SQL以相反的顺序更新两行记录。比如事务A先更新id1再更新id2事务B先更新id2再更新id1两者就可能相互等待。解决办法是让所有事务都按同样的顺序访问资源。第三步从业务层面想办法。比如插入时使用INSERT ... ON DUPLICATE KEY UPDATE代替先查后插缩小事务范围减少持有锁的时间必要时在SQL的where条件里加hint强制走索引避免锁范围扩大。死锁这个问题不可能完全避免目标是大幅降低发生概率。线上环境我建议开启死锁监控把日志收到日志平台里定期分析有没有频繁死锁的表和SQL。4.3 数据库连接池参数与选型每个需要访问数据库的应用程序基本都会用连接池。连接池的价值在于数据库连接的创建和关闭都很重频繁创建销毁连接会拖慢接口响应。连接池说白了就是预先创建一批连接让应用随时取用用完归还。Java生态里HikariCP现在是Spring Boot默认的连接池性能好、代码少。Druid是阿里开源的自带监控页面中小团队用得多。连接池参数里最核心的几个我来解释一下。maximumPoolSize最大连接数。不是越大越好每个连接背后都是数据库的一个线程或进程连接太多反而增加上下文切换成本。经验值是((core_count * 2) effective_spindle_count)这个公式附近简单说CPU核数的2到4倍比较常见。minimumIdle最小空闲连接数如果业务波动大可以设置成和maximum一致避免频繁创建连接。connectionTimeout获取连接的超时时间。线上经常遇到连接池被打满、请求排队的情况如果超时时间设太长用户会一直卡着建议根据接口耗时来设一般5秒以内。maxLifetime连接最大存活时间。这个必须小于数据库侧的wait_timeout否则连接会被数据库提前关闭连接池不知道取出来才发现连接已经断了。经典异常就是“Communications link failure”。还有一个和连接池相关的经典问题数据库重启后应用连接池里的连接全部失效但应用不自知导致一段时间的报错。解决办法除了调短maxLifetime还可以在连接池里配置连接测试SQL比如Druid的validationQuery设成SELECT 1取连接前先验证。5. 工具链与生态盘点从达梦到向量数据库这一节比较杂但热搜词里出现了很多工具和数据库名比如“达梦数据库”“人大金仓数据库”“dbx数据库工具”“navicat连接达梦数据库”“oracle数据库安装教程”“timed序列数据库”“向量数据库”“flutter内嵌数据库”。我按实际使用场景把这些串起来说。5.1 国产数据库选型与Navicat连接实操这几年国产数据库用得越来越多其中达梦和人大金仓是两大主力。达梦数据库从Oracle语法兼容性上做得比较到位存储过程、包、序列这些概念基本都是从Oracle沿袭下来的所以有Oracle背景的DBA迁移成本很低。人大金仓更像PostgreSQL路线的对象模型上也有自己的特点。选择哪家除了看项目需求和预算还要看团队的技能储备。团队熟Oracle就选达梦熟PostgreSQL就选人大金仓迁移会更顺畅。如果你的环境允许用Docker跑一个达梦实例也不难docker run -d \ -p 5236:5236 \ -e SYSDBA_PWDsysdba123 \ --name dm8 \ dameng/dm8:latestNavicat连接达梦的关键在于驱动。Navicat本身支持达梦连接时需要选择达梦的驱动类型填好主机IP、端口默认5236、用户名SYSDBA和密码。我踩过一个坑新版Navicat可能需要下载达梦的JDBC驱动并放到Navicat的驱动目录里否则会报“驱动类不存在”。5.2 常用数据库工具的定位与坑Oracle官方客户端/SQL*Plus最基础适合服务器上直接操作。PL/SQL Developer老牌Oracle开发工具连接局域网其他机器的Oracle时需要本机装Oracle Instant Client并在工具里配置好Oracle Home和OCI库路径。这个配置经常出问题报错一般集中在“ORA-12154 TNS无法解析”和“OCI.dll加载失败”前者一般是tnsnames.ora没配对后者是Instant Client位数与工具不匹配。Navicat目前我用得最多的图形化工具支持MySQL、Oracle、达梦、人大金仓等多种数据库一个客户端搞定。IDEA Database工具Java开发最方便可以直接在IDE里执行SQL、看表结构、导出脚本。dbx数据库工具轻量级的数据库客户端适合快速连接多种数据库官网上可以下载到对应版本。它的特点是小而快适合临时查数据但功能丰富度不如Navicat。再说一个工具类的高频痛点导入导出。Excel导入数据库很多人直接在Navicat里右键导入但Excel里的日期格式、空值、超长文本经常导致导入失败。我的习惯是先把Excel转成CSV用UTF-8编码再对齐目标表字段最后分批导入。字段类型能设VARCHAR就先设VARCHAR导完再转换能省掉一堆类型不匹配的报错。5.3 新方向向量数据库、时序数据库与内嵌数据库除了传统关系型数据库现在还有几个方向值得了解。向量数据库这两年因为AI应用火得不行。它存的不是结构化字段而是把文本、图片、音视频转成高维向量然后通过近似最近邻算法ANN做相似度检索。典型的产品有Milvus、Chroma、Qdrant。如果你做的项目需要语义搜索或RAG检索增强生成就会用到这类数据库。就目前生态而言Milvus用在生产环境相对成熟有完整的分布式方案轻量级原型可以先用Chroma或者SQLite的向量扩展。时序数据库专门处理时间戳数值这类数据比如监控指标、物联网传感器数据。代表作是Prometheus、InfluxDB、ClickHouse也能承担时序场景。如果你做监控大屏、设备数据采集选这几个比用MySQL靠谱得多因为它们的写入吞吐量、时间范围聚合查询都针对时序场景做了优化。内嵌数据库的代表是SQLite。它不需要独立服务整个数据库就是一个文件放在应用进程里跑。Flutter开发移动端就经常用SQLite做本地存储配合sqflite插件插进去查出来都很快。还有热搜词里的“linux下的单文件数据库”基本说的也是SQLite或用它封装的产品。简单归纳一下关系型数据库管业务核心数据时序数据库管监控物联网数据向量数据库管AI检索嵌入式数据库管本地离线存储。选型的关键是场景不是一个工具包打天下。6. 高频问题与面试题速查复习到最后要能“说”出来这一节主要解决两个问题一个是热搜词里那些报错和操作问题另一个是面试常考的高频题。复习的时候脑子里有知识是一回事能清晰地讲出来、能快速定位问题是另一回事。6.1 常见报错背后的排查思路热搜词里有几个报错很典型我来逐个拆解排查思路。“超出最大数据库坐标值。此错误的一个可能原因是dxf导入时使用的单位与其导出时的单位不一致。”这个问题根本不在数据库本身而是CAD文件处理流程里的单位问题。dxf文件里通常带单位信息但很多设计软件导出时并不检查导致导入方按错了单位解释坐标数值就溢出了。排查思路很直接检查源文件导出的单位设置再检查导入时选择的单位是否一致。这类问题提醒我数据库报错不一定是数据库的锅要先看上游数据本身有没有问题。“multisim访问数据库发生错误怎么解决”。Multisim是电子设计自动化工具它报访问数据库错误多半是软件自带的元件数据库路径配置不对或者BDEBorland Database Engine没有正确初始化。我看到热搜里还有“borland database engine数据库无法初始化”这两个问题常常连在一起。BDE是很多老款EDA软件赖以访问Paradox/Access数据库的中间层初始化失败一般是缺少BCB运行库或系统路径有中文。排查顺序先重装或修复BDE再检查软件安装路径是否含中文最后看系统是否缺VC运行库。“docker 内部iserver如何连接达梦数据库”。这个报错场景是容器化部署中的应用要去连外部的达梦数据库。在Docker容器里连接宿主机或外部数据库最常见的坑是连接地址写成了localhost或127.0.0.1但在容器内这是容器自身而不是宿主机。如果达梦跑在宿主机上容器内应用应该用host.docker.internal或宿主机的实际局域网IP。另外还要检查达梦端口5236是否在防火墙里放行以及容器网络是bridge还是host模式不同模式下网络通路的地址不同。6.2 数据库面试题快速过一遍我把面试里出现频率极高的题目整理成一张速查表回答思路也一起写了面试题核心回答思路MySQL和Oracle的区别语法分页、自增、字符串拼接、事务隔离级别默认值、索引实现InnoDB B树 vs Oracle B树、存储过程兼容性什么是索引为什么用B树减少磁盘IO次数B树矮胖、非叶子节点不存数据、叶子节点有链表便于范围查询聚簇索引和非聚簇索引聚簇索引的叶子节点就是数据行二级索引叶子节点存主键值。回表就是通过二级索引查到主键后再去聚簇索引查数据最左前缀原则联合索引(a,b,c)查询条件只有b或c时用不到索引必须包含a才能走。因为索引先按a排序再按b排序再按c排序一条SQL执行流程连接器验证身份→查询缓存MySQL8删除→分析器词法语法分析→优化器选执行计划→执行器调存储引擎接口分库分表怎么分垂直拆分按业务模块分库水平拆分按数据量分表常见算法有哈希取模、按时间分片、Range分片。分完之后还要考虑分布式ID和跨库查询的代价数据库的高可用方案主从复制、半同步复制、MGR或PXC、读写分离、哨兵/仲裁节点。面试重点在于说清楚主从复制的日志点机制和故障切换流程如何定位慢SQL先开慢查询日志找到sql然后用EXPLAIN看type、key、rows、Extra字段重点关注type是否为ALL全表扫描、Extra是否Using filesort再做索引和SQL层面的优化回答这些题有个技巧不要干背概念把“为什么”讲出来。比如问到B树你补一句“是因为InnoDB的数据是存在磁盘上的树的高度越小查询一个叶子节点需要的IO次数就越少三层B树能存上千万行数据”面试官就会觉得你是真懂不是背答案。6.3 复习路线与自查清单最后把我的复习路线整理一下方便你按图索骥。理论层事务ACID、隔离级别、MVCC、锁机制。这四条是整个数据库知识的主干理解它们的关联是复习的核心。一定要能手画出MVCC的版本链结构能口述ReadView的可见性判断逻辑。SQL层增删改查、聚合、子查询、多表连接、窗口函数MySQL 8.0支持、分页写法。重点练习GROUP BY HAVING的过滤逻辑以及LEFT JOIN中ON和WHERE位置的区别。手写一遍所有常见SQL比看十遍教程有用。设计层ER图、范式、主键策略、索引设计。可以从“博客系统”这种经典题目入手画ER图转成建表语句再根据查询需求创建索引走一遍完整的数据库设计流程。工具层Navicat建库、建表、导入导出、执行计划分析IDEA连接数据库Docker容器中应用访问数据库的网络配置。每个操作都实际操作一遍报错别慌按“先看网络通不通再看驱动装没装再看权限够不够”的思路查。做一遍自查如果你能不看资料、用自己的话把事务隔离级别、死锁产生的四个条件、索引失效的场景、一条SQL走上还是没走索引的判断方法全部讲清楚这轮复习就算过关了。
返回列表