旅游网站的规划与建设开题报告电商中seo是什么意思
旅游网站的规划与建设开题报告,电商中seo是什么意思,软件设计师难考吗,微信小程序怎么一键删除目录
一、 三大并发读问题
二、 SQL 标准定义的四个隔离级别
1. 读未提交 (Read Uncommitted)
2. 读已提交 (Read Committed, RC)
3. 可重复读 (Repeatable Read, RR)
4. 串行化 (Serializable)
三、 总结对比表
四、 核心知识点补充
了解 MySQL 是如何通过 MVCC 和锁…目录一、 三大并发读问题二、 SQL 标准定义的四个隔离级别1. 读未提交 (Read Uncommitted)2. 读已提交 (Read Committed, RC)3. 可重复读 (Repeatable Read, RR)4. 串行化 (Serializable)三、 总结对比表四、 核心知识点补充了解 MySQL 是如何通过 MVCC 和锁机制来实现“可重复读”的吗这是一个非常高频的面试进阶题。第一部分MVCC解决普通查询的并发问题1. 隐式字段与 Undo Log2. Read View一致性视图—— 关键所在3. 判定规则第二部分Next-Key Lock解决“当前读”的幻读问题1. 锁的分类2. 如何运作第三部分面试官可能会问的“特殊情况”终极总结一句话回答面试官详细介绍一下这个read view 实现的底层原理一、 Read View 的核心数据结构二、 可见性判断算法 (Visibility Algorithm)1. 检查是不是我自己改的2. 检查是不是“很久以前”就提交的3. 检查是不是“未来”才发生的4. 最复杂的中间情况落在 min 和 max 之间三、 图解案例演示四、 为什么 RC 和 RR 表现不同总结RR这个不是不能解决幻读的问题吗一、 理论 vs. 现实1. SQL 标准定义你的理解是对的2. MySQL InnoDB 的“超纲”实现二、 为什么会有争议那个 1% 的例外能够复现幻读的特殊场景三、 面试满分回答模板总结1. 场景设定2. 对应 Read View 的参数3. 解答你的疑惑4. 一张图总结事务隔离级别Transaction Isolation Levels是数据库设计中非常核心的概念它属于ACID特性中的I (Isolation, 隔离性)。简单来说隔离级别定义了当多个用户事务同时访问数据库时他们之间的干扰程度。隔离级别越高数据越安全一致性越好但并发性能通常越低。为了理解隔离级别我们首先需要知道如果没有隔离会发生什么并发问题。一、 三大并发读问题当多个事务并发执行时如果不进行隔离会出现以下三种主要问题脏读 (Dirty Read):现象事务 A 修改了数据但尚未提交事务 B 就读取到了这个数据。如果事务 A 后来回滚撤销了那么事务 B 读到的就是“脏”数据根本不存在的数据。例子老板发工资把你工资改成 2 万未提交你查到了 2 万很高兴。结果老板发现改错了回滚了事务。你读到的 2 万就是脏读。不可重复读 (Non-repeatable Read):现象在同一个事务内多次读取同一行数据结果不一样。这是因为在两次读取之间另一个事务**修改Update**或删除并提交了该数据。重点侧重于数据内容被修改。幻读 (Phantom Read):现象在同一个事务内多次执行同一个查询比如查询“所有工资大于 1 万的人”发现记录的数量变了多了或少了一行。这是因为另一个事务在两次查询之间**插入Insert**或删除了符合条件的行。重点侧重于数据条数的变化像产生了幻觉。二、 SQL 标准定义的四个隔离级别数据库定义了四个级别来解决上述问题。级别从低到高隔离性越来越强但性能越来越差。1. 读未提交 (Read Uncommitted)含义一个事务可以读取另一个未提交事务修改的数据。解决的问题无。存在的问题脏读、不可重复读、幻读。特点极其不安全极少在实际项目中使用。2. 读已提交 (Read Committed, RC)含义一个事务只能读取已经提交的数据。解决的问题脏读。存在的问题不可重复读、幻读。特点这是大多数数据库如Oracle,SQL Server,PostgreSQL的默认隔离级别。在这个级别下虽然解决了脏读但在同一个事务里由于别人提交了修改你前后读取的数据可能不一致。3. 可重复读 (Repeatable Read, RR)含义确保在同一个事务中多次读取同一条数据的结果是一样的。即便别人修改并提交了数据我也看不到直到我自己的事务结束。解决的问题脏读、不可重复读。存在的问题理论上标准 SQL 中存在幻读。特点这是MySQL (InnoDB 引擎)的默认隔离级别。注意虽然标准 SQL 定义 RR 级别无法解决幻读但 MySQL 的 InnoDB 引擎通过MVCC多版本并发控制和Next-Key Lock间隙锁技术在 RR 级别下已经很大程度上避免了幻读。4. 串行化 (Serializable)含义强制事务串行执行。通过强制加锁让事务排队一个接一个地做。解决的问题所有并发问题脏读、不可重复读、幻读。特点安全性最高但并发性能最低极易造成死锁和超时。除非对数据一致性要求极高且并发量很小否则很少使用。三、 总结对比表隔离级别脏读不可重复读幻读性能常用场景Read Uncommitted✅ 可能✅ 可能✅ 可能最高极少使用Read Committed (RC)❌ 解决✅ 可能✅ 可能高Oracle/PG 默认Repeatable Read (RR)❌ 解决❌ 解决✅ 可能*中MySQL 默认Serializable❌ 解决❌ 解决❌ 解决最低极严格的财务场景(注✅ 表示会发生该问题❌ 表示该级别解决了该问题。)( 关于 MySQL RR 级别的幻读MySQL 通过间隙锁机制解决了大部分幻读但在特殊语法下仍可能出现)*四、 核心知识点补充MVCC (多版本并发控制):现代数据库如 MySQL、PostgreSQL大多不是单纯靠“锁”来实现 RC 和 RR 级别的而是通过 MVCC。简单说就是给数据拍“快照”读数据时读的是快照版本写数据时生成新版本这样读写不冲突性能更高。如何选择如果你使用的是 MySQL通常保持默认的Repeatable Read即可。如果业务允许不可重复读例如主要是报表统计或者是对实时性要求极高但一致性要求稍低的场景有些互联网公司会将 MySQL 调整为Read Committed以获得略高的并发度且减少间隙锁导致的死锁。您想了解 MySQL 是如何通过 MVCC 和锁机制来实现“可重复读”的吗这是一个非常高频的面试进阶题。了解 MySQL 是如何通过 MVCC 和锁机制来实现“可重复读”的吗这是一个非常高频的面试进阶题。这是一个非常棒的切入点MySQL 的 InnoDB 引擎在“可重复读”Repeatable Read, RR级别下的实现是面试中的“重难点”因为它打破了标准 SQL 的定义标准 SQL 认为 RR 级别只能解决“不可重复读”无法解决“幻读”。但在 MySQL InnoDB 中RR 级别实际上同时解决了“脏读”、“不可重复读”并很大程度上解决了“幻读”。它是通过“两条腿走路”的策略来实现的对于普通查询Select使用MVCC多版本并发控制。对于当前读Select...for update / Update / Delete使用Next-Key Lock临键锁。下面为你深度拆解这两个机制。第一部分MVCC解决普通查询的并发问题MVCC (Multi-Version Concurrency Control)的核心思想是“你读你的快照我改我的新值互不干扰。”在 RR 级别下MVCC 是如何保证“可重复读”的呢1. 隐式字段与 Undo Log每一行数据在背后都有两个隐藏字段DB_TRX_ID: 最后一次修改该行数据的事务 ID。DB_ROLL_PTR: 指向Undo Log回滚日志的指针。当我们修改数据时旧数据会被写入 Undo Log形成一条版本链。2. Read View一致性视图—— 关键所在Read View 相当于一个“快照窗口”它记录了生成快照那一刻系统中有哪些事务是活跃的未提交。RC读已提交与 RR可重复读的区别就在于生成 Read View 的时机RC 级别每次执行SELECT语句时都会重新生成一个新的 Read View。所以你可以看到别人刚提交的数据。RR 级别在同一个事务中只在第一次执行SELECT时生成 Read View后续所有的查询都复用这同一个 Read View。3. 判定规则当你去读数据时InnoDB 会拿着数据的版本号trx_id跟你的 Read View 比对如果这个版本是在你开启事务之前提交的 -可见。如果这个版本是在你开启事务之后才生成的 -不可见。如果不可见顺着 Undo Log 找上一个版本直到找到可见的版本为止。总结因为 RR 级别复用了第一次查询时的 Read View不管期间别的事务怎么修改、插入数据你的 Read View 就像一张旧照片永远定格在事务开始的那一刻。这就解决了不可重复读和普通查询下的幻读。第二部分Next-Key Lock解决“当前读”的幻读问题MVCC 只能解决普通的SELECT快照读。但如果你要执行更新操作或者加锁查询如SELECT ... FOR UPDATE这被称为当前读。当前读必须读取最新的数据MVCC 在这里就失效了。如果此时没有锁机制就会出现幻读比如我想把所有人的工资更新为 0结果别人刚插入一条新纪录导致我更新完后发现还有一条没变成 0。MySQL 使用Next-Key Lock来解决这个问题。1. 锁的分类Record Lock (记录锁)锁住某一行记录例如锁住 id5。Gap Lock (间隙锁)锁住两条记录之间的空隙不包含记录本身例如锁住 5 到 10 之间的缝隙防止别人插入 id7。Next-Key Lock (临键锁)Record Lock Gap Lock。既锁住记录又锁住前面的间隙。2. 如何运作假设表中有一个索引列值有 1, 5, 10。事务 A 执行 SELECT * FROM table WHERE id 5 FOR UPDATE;InnoDB 会加上Next-Key Lock锁住记录10。锁住(5, 10]的区间防止插入 6, 7, 8, 9。锁住(10, ∞)的区间防止插入 11, 12...。结果其他事务如果想在这个范围内执行INSERT操作会被直接阻塞Block直到事务 A 提交。总结通过锁住“间隙”彻底杜绝了别人偷偷插入新数据的可能性从而在“当前读”下解决了幻读。第三部分面试官可能会问的“特殊情况”虽然 MySQL 在 RR 级别下做得很好但在极端情况下幻读仍可能发生虽然很少见。场景事务 A 开启。事务 A 执行快照读SELECT * FROM user WHERE id 10结果为空。事务 B 插入一条id 10的数据并提交。事务 A 再次执行快照读SELECT由于复用 Read View结果还是空看起来正常。但是如果事务 A 执行UPDATE user SET namea WHERE id 10。UPDATE是当前读它能读到事务 B 提交的最新数据。更新成功后这条数据的trx_id变成了事务 A 自己的 ID。事务 A 再次SELECT。根据 MVCC 规则看到是自己修改的数据可见结果事务 A 突然读到了id 10的记录。幻读出现了。终极总结一句话回答面试官MySQL InnoDB 在可重复读RR级别下通过MVCC多版本并发控制配合Read View 的复用解决了普通查询的不可重复读和幻读同时利用Next-Key Lock临键锁锁住记录和间隙解决了当前读Locking Read下的幻读问题。详细介绍一下这个read view 实现的底层原理这是一个非常硬核且底层的问题。真正理解了Read View一致性视图的结构和算法你才算真正看透了 MVCC 的本质。Read View 本质上是一个数据结构它在事务执行查询的那一刻被创建相当于给当前数据库系统的所有事务状态拍了一张“快照”。InnoDB 内部通过这个 Read View 结构配合每一行数据的隐藏字段DB_TRX_ID事务ID来判断数据“可不可见”。下面我从数据结构、可见性算法和案例演示三个层面为你拆解底层原理。一、 Read View 的核心数据结构在 InnoDB 的源码中Read View 主要包含 4 个核心字段。理解这 4 个字段的含义是解题的关键m_ids(活跃事务列表)这是一个列表List记录了生成 Read View 这一刻系统里所有**“活跃中”未提交**的事务 ID。含义这些事务还没提交它们改的数据我不应该看到。min_trx_id(最小活跃事务ID)也就是m_ids里的最小值。注意在 InnoDB 源码中它叫up_limit_id这名字容易搞混记含义就好。含义凡是事务 ID 小于这个值的说明在快照生成前就已经提交了肯定是可见的。max_trx_id(预分配事务ID)这是系统当前**“下一个要分配的事务 ID”**即当前最大事务 ID 1。注意它不是m_ids里的最大值而是系统全局的最大 ID。在 InnoDB 源码中它叫low_limit_id。含义凡是事务 ID 大于等于这个值的说明是在快照生成之后才开启的事务肯定是不可见的属于未来的事务。creator_trx_id(创建者事务ID)即当前这个事务自己的 ID。含义我自己改的数据我当然能看到。二、 可见性判断算法 (Visibility Algorithm)当事务要去读某一行数据时会拿到这行数据的trx_id即最后修改这行数据的事务 ID然后拿着这个 ID 去跟 Read View 进行比对。比对逻辑是一个严密的流程式判断假设当前行数据的事务 ID 为trx_id1. 检查是不是我自己改的判断trx_id creator_trx_id?结果如果是可见。2. 检查是不是“很久以前”就提交的判断trx_id min_trx_id?结果如果小于最小值说明在生成 Read View 之前修改这行数据的事务就已经提交了。可见。3. 检查是不是“未来”才发生的判断trx_id max_trx_id?结果如果大于等于最大值预分配ID说明这个数据是由一个在 Read View 生成之后才启动的事务修改的。不可见。4. 最复杂的中间情况落在min和max之间判断如果min_trx_id trx_id max_trx_id。逻辑此时需要去m_ids(活跃列表)里查一下情况 Atrx_id在m_ids列表中。说明生成快照时这个事务还没提交活跃中。不可见。情况 Btrx_id不在m_ids列表中。说明虽然这个 ID 比较大但它在生成快照的那一刻已经提交了不是活跃状态。可见。三、 图解案例演示为了让你彻底明白我们来模拟一个场景。场景假设系统当前有两个活跃事务ID 分别为100和200。你是事务300creator_trx_id 300。系统下一个还没分配的 ID 是301。此时事务 300 执行查询生成的 Read View 如下m_ids:[100, 200]min_trx_id:100max_trx_id:301creator_trx_id:300现在数据库里有 4 行数据它们的trx_id分别如下请判断可见性数据行该行的 trx_id你的 Read View 比对过程结果原因Row A5050 100(min_trx_id)可见早已提交Row B300300 300(creator_trx_id)可见我自己改的Row C400400 301(max_trx_id)不可见未来的事务改的Row D100在[100, 200]列表里不可见它是活跃事务还没提交Row E150100 150 301且不在列表里可见虽然比 100 大但它不在活跃列表说明生成视图前已提交如果数据不可见怎么办InnoDB 会通过数据行的 DB_ROLL_PTR 指针沿着 Undo Log 版本链往回找找到上一个版本再次用上面的规则判断直到找到一个“可见”的版本为止。四、 为什么 RC 和 RR 表现不同这个机制完美解释了为什么 RC读已提交和 RR可重复读表现不同。它们的算法完全一样区别仅仅在于Read View 生成的时机。Read Committed (RC):每次执行SELECT语句都会重新生成一个新的 Read View。后果如果事务 100 在你两次查询之间提交了第二次生成的 Read View 里的m_ids就没有 100 了所以你就能看到它提交的数据。Repeatable Read (RR):事务启动后第一次执行SELECT语句时生成 Read View。之后在这个事务内无论查询多少次都复用这同一个 Read View。后果即使事务 100 后来提交了但你的 Read View 是旧的100 依然在你的“活跃列表”里所以系统判定它“不可见”。这就实现了“可重复读”。总结Read View 的底层原理就是划定一条时间线快照。通过 min_trx_id 和 max_trx_id 确定由于时间太早或太晚而明确可见/不可见的范围通过 m_ids 列表排除掉那些“当时正忙着”的事务。RR这个不是不能解决幻读的问题吗这是一个非常精准且关键的疑问你的理解完全符合SQL 标准SQL Standard的定义但在MySQL InnoDB的具体实现中情况发生了“越级”。为了彻底解开这个结我们需要区分“教科书上的标准”和“MySQL 的工程实现”。这就像考试大纲和学霸的实际成绩大纲要求SQL 标准考 80 分就算优秀RR 级别允许幻读。学霸表现MySQL InnoDB虽然只要求 80 分但它实际考了 99 分RR 级别解决了大部分幻读。一、 理论 vs. 现实1. SQL 标准定义你的理解是对的在 SQL-92 标准中隔离级别的定义如下Repeatable Read (RR):要求解决“脏读”和“不可重复读”。允许“幻读”发生。Serializable (串行化):只有在这个级别才必须解决“幻读”。所以从纯理论或数据库通用的角度来说RR 确实不能解决幻读。2. MySQL InnoDB 的“超纲”实现MySQL 的 InnoDB 引擎为了提升性能不想让大家在生产环境中都去用慢吞吞的Serializable级别所以它在 RR 级别下多做了很多工作快照读普通 Select用 MVCC 解决幻读。当前读Select...for update用 Next-Key Lock 解决幻读。结论MySQL 在 RR 级别下打破了标准 SQL 的限制实质上解决了幻读问题。二、 为什么会有争议那个 1% 的例外既然 MySQL 这么厉害为什么有些严谨的文章还是说“MySQL 的 RR 并没有彻底解决幻读”是因为有一个极其特殊的“边缘案例”Edge Case在这个案例下幻读还是会发生。能够复现幻读的特殊场景这个场景利用了 MVCC 的更新机制。准备表里没有id30的记录。事务 A (RR级别)SELECT * FROM table WHERE id30;结果空正常。事务 BINSERT INTO table (id, name) VALUES (30, ghost);并提交。此时数据库里真实存在了id30。事务 ASELECT * FROM table WHERE id30;结果空正常因为 Read View 复用了看不到事务 B 的新提交。到这里为止幻读都被完美防住了。关键操作 - 事务 A 执行UPDATE table SET namecaught WHERE id30;注意UPDATE是当前读它不看 Read View而是看数据库最新状态。它发现有id30于是更新成功更新后这行数据的trx_id变成了事务 A 自己的 ID。事务 A 再次SELECT * FROM table WHERE id30;结果读到了原因MVCC 规则说“自己修的数据自己可见”。这就产生了幻读事务 A 一开始以为没数据中间也没人告诉它有数据结果它随手 update 一下突然就“变”出了一条数据。三、 面试满分回答模板如果面试官问“RR 级别能解决幻读吗” 建议按照以下逻辑回答层次感极强先说标准表态你懂基础“按照 SQL-92 标准的定义Repeatable Read (RR) 级别是不要求解决幻读的只有 Serializable 级别才能解决。”再说 MySQL表态你懂原理“但是在 MySQL 的InnoDB引擎中它对 RR 级别进行了优化。通过MVCC机制解决了快照读下的幻读通过Next-Key Lock间隙锁解决了当前读下的幻读。”最后补刀表态你懂深度“所以MySQL 的 RR 级别很大程度上避免了幻读。不过在极个别的特殊场景下比如事务 A 先快照读事务 B 插入事务 A 再 Update 该记录幻读依然可能发生。因此严谨地说MySQL 在 RR 级别下解决了绝大部分幻读问题。”总结你说“RR 不能解决幻读” -符合 SQL 标准理论。说“MySQL RR 能解决幻读” -符合 MySQL 工程实际。理解了这个差异你就掌握了数据库面试中最容易混淆的一个知识点。这句话确实非常绕尤其是源码里的名字起得还跟直觉相反。我用**“银行排队取号”**的例子给你翻译一下保证秒懂。1. 场景设定假设银行一直在发号号码是连续增加的1, 2, 3...。现在的状态是100号还在柜台办事没走。101号动作很快办完走了提交了。102号还在柜台办事没走。103号动作很快办完走了提交了。这个时候你作为104号进来了你看了一眼大厅的情况生成了一个 Read View快照。2. 对应 Read View 的参数这时候你的 Read View 数据如下m_ids(活跃列表)[100, 102]解释只有这两个人还在办事其他的要么办完走了要么还没来。min_trx_id(最小活跃ID)100解释活着的人里号码最小的是 100。max_trx_id(也就是系统全局最大ID / low_limit_id)105注意这里是关键为什么是 105因为 100, 101, 102, 103 都已经出现过了。你是 104。系统下一个要发的号码是105。3. 解答你的疑惑疑惑点 A为什么max_trx_id不是m_ids里的最大值看例子m_ids里的最大值是102。看全局但实际上103已经来过并且办完走了。如果你认为最大值是 102那你就会觉得 103 是“未来的人”这就不对了。103 是过去的人你应该能看到他留下的数据。结论m_ids只是一个**“滞留人员名单”**它不能代表系统发号发到哪里了。只有max_trx_id才知道系统已经发到 104 了下一个是 105。疑惑点 B源码里为什么叫low_limit_id这名字特别坑它是从“不可见范围”的角度命名的。它的含义是“不可见范围”的下限 (Low Limit)。人话凡是 ID 大于等于这个数的 105都是将来才出生的人绝对不可见。它是区分“现在”和“未来”的分界线。4. 一张图总结Plaintext时间轴 -------------------------------------------- 事务ID 99(已走) 100(在办) 101(已走) 102(在办) 103(已走) | 105(还没来) ^ ^ | ^ | | | | m_ids(活跃列表): [ 100, 102 ] | | | | min_trx_id: 100 (活跃列表里的最小值) | | | | Read View 生成时刻 (你站在这) ------------------------------------------- | | max_trx_id: 105 (下一个要发的号) ---------------------------------------m_ids里的最大值是 102。max_trx_id是 105。中间那个103虽然比 102 大但它不在m_ids里说明它已经提交了是可以被看见的。这就是为什么不能用m_ids的最大值作为分界线的原因。