CMU_15445_P4_Part3

news/2025/3/14 9:37:03/文章来源:https://www.cnblogs.com/wevolf/p/18771486

Primary Key Index

BUSTUB 支持使用下面的方式创建主键索引

CREATE TABLE t1(v1 int PRIMARY KEY);
CREATE TABLE t1(v1 int, v2 int, PRIMARY KEY(v1, v2));

当创建一个表的时候如果确定了主键, 那么这张表的 is_primary_key 会被设置为 true. 由于在 P4 中添加了主键相关的信息, 并且 Projet4 中仅涉及主键的修改与更新, 因此前面的 Project3 中 Insert 与 Update 中非主键的修改会出现不一致的出入, 导致 P3 有些测试案例没有通过, 需要做一些调整.

Index Scan

多 MVCC 下的 Index Scan 与 SeqScan 是类似的, 因为都仅涉及到版本链的读, 而不涉及到版本链的修改, 不会涉及到多并发情况下修改版本链的问题, 按照 SeqScan 中事务的时间戳信息, 在正确的时间戳下读取版本链的信息即可.

Inserts

我们需要修改 Insert Executor 以支持插入主键索引, 同时需要考虑存在多个事务在不同的线程内同时插入相同主键索引的情况, 插入带有主键索引的 tuple 的步骤如下:

  1. 首先检查 Index 中是否已经存在这个 tuple, 也就是插入的这个 tuple 对应的检索是不是已经指向其他的 tuple 了, 如果已经存在, abort 当前事务. 但是这部分仅在 #Task4.1 生效, 因为在 #Task4.2 中检索 index 可能指向一个被删除的 tuple, 这种情况下, 事务可以继续 Insert, 不必 Abort. 在 #Task4 中, 对于冲突的情况, 我们仅需要设置事务的状态为 TAINTED. TAINTED 状态表示这个事务将要 Abort, 但是还没有, 因此有可能 TableHeap 中已经插入数据了, 但是不必要清除它. 如果此时还有其他事务插入数据, 看到了 TableHeap 中还未清除的数据, 那么还是会被看作写写冲突.
  2. 在 TableHeap 中创建 tuple.
  3. 创建 tuple 之后, 将这个 tuple 加入到检索中, 因为 BUSTUB 仅考虑主键索引, 主键索引具有唯一性, 如果此时已经有 tuple 加入到相同的检索中, 会发出冲突. 在顺序执行的情况下不会出现这种现象, 但是在并发场景下, 事务 Txn_A 执行完 2 之后, 事务 Txn_B 执行了 2,3 并且插入的主键相同, 此时会出现这种冲突.

img

我们用上面的例子说明冲突的情况, Txn9 依次插入 (A,4), (B,4), (C,4), 我们将第一列作为主键.

  1. 当插入 (A,4) 的时候, 由于 (A,4) 已经存在了, 所以存在写写冲突, Txn9 事务 Abort
  2. 插入 (B,4), 首先在 TableHeap 中创建一个 tuple, 然后将 RID 与新建的 tuple 加入到检索 Index 中.
  3. 当插入 (C,4) 的时候, Txn9 首先检测到没有写写冲突, 因此在 TableHeap 中创建了 tuple (C,4), 此时另一个事务 Txn10, 发现也没有检索存在冲突, 因此执行了上面的步骤 2,3, 创建了 tuple (C,5), 并且加入了检索. 执行完后, 检索 Index[C] 已经存储了 tuple, 因此事务 Txn9 在将 (C) 加入到检索时, 会发生冲突, 事务 Txn9 Abort.

Index Scan, Deletes and Updates

这个 Task 我们需要完成对 delete, update executor 的主键索引的支持. 这部分开始将会启用 multi-version index scan executor, 也就是多版本并发控制下的 index scan executor, 还要更新 insert, update, delete executor.

检索的功能是指向指定的 RID, 读取这个 RID 对应的 tuple, 一旦一个 tuple 在索引表中创建了一个检索, 即使这个 tuple 被删除了, 在 TableHeap 中, 这个 tuple 被标记为删除, 但是索引表中仍然存储这个索引关系, 也就是这个检索仍然指向这个 RID, 因此不同于 Seqscan Executor 会跳过这个 tuple, IndexScan Executor 仍然会返回这个 RID.

这里会导致之前的 insert Executor 改变的情况是, 当 insert 到 TableHeap 中一个标记为 deleted 的 tuple 的时候, insert Executor 应该会更新这个 tuple, 而不是新创建一个. 因此索引项一旦创建, 总是指向相同的 RID, 此时会对我们前面的 Insert Executor 的插入过程产生下列的变化.

  1. 孩子节点 IndexScan Executor 返回需要插入的 rid, 如果这个 rid 对应的位置已经存储了一个 tuple, 有两种情况
    1. 这个 tuple 正在被其他事务修改, 或者被其他事务提交, 那么当前事务冲突, 应该 Abort
    2. 这个 tuple 的 deleted 标记位为 true, 并且已经被其他事务 committed, 那么可以在这个 tuple 上更新新插入的 tuple, 而不必 Aborted. 更新的过程是需要更新这个 tuple 的版本链的, 在 MVCC 中, 加入索引的时候, 更新版本链的时候, 在 update executor 和 delete executor 中是相同的, 我们后续合并在一起讨论.
    3. 其他情况和原来一致, 在 TableHeap 中插入这个 tuple
  2. 在 TableHeap 中插入这个 tuple
  3. 步骤 (3) 是向索引表中插入检索, 在插入索引之前需要检查该索引对应的 tuple 是否已经存在, 如果已经存在, 检查是否标记为删除, 如果标记为删除, 不冲突, 使用

Index Scan

在加入主键索引后, 同时加入多线程与多事务并发引来的新的冲突的问题:

在 catalog_的一张表中, 索引和数据库的物理数据是分开存储的, 通过表号来建立关系. 并且在 MVCC 或者其他的并发控制中, 不会对一张表的索引或者表的数据加锁, 也就是不会加表级锁和索引级别的锁. 因为不会对索引表和数据表加锁, 所以同一时刻可能存在多个事务同时访问同一张表的物理数据, 以及访问索引中同一个表项的情况, 这时就会发生所谓的冲突.

为了合理的处理这些冲突, BUSTUB 数据库引擎需要检测并处理这些冲突, 检测与处理这些冲突的基本方式就是检测 TableHeap 中的 tuple 的 tuple_meta 信息, 即时间片与 is_deleted 删除信号信息.

可能出现的冲突汇总

  1. MVCC 通过多版本的控制避免了读写冲突, 因此在 SeqScan Executor 与 IndexScan Executor 中不会存在读写冲突导致事务 Aborted, 而是通过时间戳的方式使得事务访问到正确的版本. 需要特别注意的情况是当一个 tuple 正在被其他事务修改的时候, 需要特别注意版本链的状态, 有可能存在TableHeap 中的 tuple 与版本链中的第一个 undolog 的时间戳相同的情况, 需要特别注意.
  2. IndexScan Executor 的特殊性: 由于一张表的索引表是单独存储的, 并且仅存储检索与 RID 的键值对, 即使 RID 对应位置的 tuple 被删除, 索引表的表项也不会删除, 而是会返回对应位置的 RID, 并且 tuple 的标识 is_deleted_ 为 true.
  3. 多并发下多个事务同时修改同一个 tuple 的写写冲突: 不同于 Task3 中检测写写冲突, 当一个事务尝试修改一个 tuple 的时候检测到另一个事务正在修改这个 tuple 的, 会出现写写冲突. 在多事务并发的情况下会出现的问题是, 有可能两个事务同时检测一个 tuple, 都发现这个 tuple 是可以访问的, 因此两个事务同时处理一个 tuple. 或者一个 tuple 修改了数据, 还没来得及修改 tuple_meta 的 timestamp, 此时也会出现冲突.
  4. 在 update 与 delete executor 中不存在两个事务同时检测到同一个 tuple 分别被这两个事务正在修改, 也就是说 tuple_meta.ts_ 只能等于两个事务其中的一个.
  5. update 和 delete Executor 读取 TableHeap 中的 tuple 一定会尝试修改这个 tuple, 而 IndexScan 和 SeqScan Executor 只会读这个 tuple.
  6. 由于插入到索引表中的键值对不会被删除, 因此只有 Insert Executor 会插入键值对, 也就是索引项. update 和 delete Executor 会修改 TableHeap 中的 tuple, 但是不会修改索引项中的内容.

冲突解决的方式

  1. IndexScan Executor 与 SeqScan Executor 的实现方式中不会存在这个问题, IndexScan Executor 中读取 tuple 版本的判断使用与 SeqScan Executor 一样的方式即可.
  2. 在 Insert Executor 中, 我们会检查某一个索引项是不是已经存在对应的 RID, 如果已经存在, 还需要检查此时 TableHeap 中这个 tuple 的状态是否为已经删除, 如果为已经删除, 那么不会产生冲突, 应该更新 TableHeap 中这个 tuple 的内容为最新的状态.
  3. 当多个事务同时检查到可以更新某个 tuple 的时候, 在创建一个 undolog 之后, 尝试修改这个 tuple 的版本链的时候, 应该首先检查这个版本链是否已经处于被修改的状态, 即 in_progress_ 是否为 true.

UPDATE Executor 中并发问题的解决

  1. Update Executor 每次会遍历所有 TableHeap 中的所有的 tuple 对应的 rids, 然后更新这些 rids 在 TableHeap 中对应的 tuple 中的数据, Update Executor 是 Pipeline Breaker, 因此会先读取整个 tuples 中的列表.
  2. 会有多个事务同时进入 Update Executor, 因此第一步是检测当前 tuple 是否正在被其他 uncommitted 的事务修改, 如果存在, 那么是写写冲突, 应该 Aborted 当前事务
  3. 如果这个 tuple 正在被当前事务修改过, 那么不会有冲突, 因为当前事务正在修改 tuple, 不可能有另一个事务也同时在修改 tuple
  4. 如果多个事务同时进入 Update Executor, 并且检测到这个 tuple 的状态是已经被 committed, 可以被修改, 那么会有多个事务去尝试修改这个 tuple.
  5. 尝试修改一个 tuple 首先要获取这个 tuple 的版本链, 当一个事务 Txn_A 获取版本链之后, 在第 4 步, 一同进入的后续的事务应该无法再获取版本链, 这里应该设计成一个自旋锁, 为什么后续事务不会直接 Aborted, 而是选择自旋锁不断地获取版本链呢, 这是因为当前事务有可能 Aborted, 导致回退, 这种情况后续的事务就应该获取到版本链.

涉及到主键索引的更新

在 Update 语句中可能涉及到更新主键, 在上面我们说过, 通常情况下, 只有 Insert Executor 会修改索引表, 实际如果 Update Executor 更新的是主键, 也会修改索引表, 涉及到主键的修改. 涉及到主键的 Update 的时候, 通常步骤如下:

  1. 在 Update Executor 的 Init() 阶段, 判断此次更新是否会修改主键, 也就是 Update Executor 会不会修改主键那一列, 判断的方式是通过 UpdatePlanNode 中的 target_expressions_ 来判断的, 因为其中不修改的 column 的类型一定是 ColumnValueExpression. 否则就是修改的 column.
  2. 如果是修改主键的 Update Executor, 由于之前已经确定 Update Executor 是 Pipeline Breaker, Update Executor 在修改主键的时候需要先将 TableHeap 中所有主键对应的 tuple 删除, 设置为删除标记. 但是仅设置 TableHeap 中的删除标志, 而不会修改索引表以及表项.
  3. 删除了所有的 tuple 之后, 再进行插入操作, 在 TableHeap 中新插入 tuple, 插入操作需要更新索引表项, 就是会插入索引项.
  4. 步骤 2,3 与之前的 Update 是完全独立与不同的, 因此是分开的步骤.

BUG 记录

  1. 在 Insert Executor 中, 当已经存在检索的时候, 更新 TableHeap 中的 tuple 信息应该使用检索位置对应的 rid. 同时还需要修改 TableHeap 中 tuple 的状态从被删除到存在, 也就是 is_deleted 设置为 false.
  2. 需要注意新生成一个 undolog 的时候的 is_deleted_ 标志, 在 Update Executor 和 Delete Executor 中, 需要设置为原来的 TableHeap 中的状态, 最好不要直接设置为默认的 false, 最重要的是需要初始化, 否则可能随机玄学
  3. 在 MVCC 的 Update Executor 中, 当多个事务并发处理的时候, 例如语句 UPDATE Accounts SET Balance = Balance - 30 WHERE ID = 1; 假设 ID=1 的有多行, Update Executor 现在是 Pipeline Break, 会先将需要修改的 RID 读取到一个数组中, 但是如果 Update Executor 的 Child Executor 发现, 这些所有需要 Update 的行都正在被其他事务修改, 因此 Update Executor 实际上不会更新任何一行.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/898514.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Hyper V文件复制优化:减少复制冲突与错误

在Hyper-V环境中进行文件复制时,为了减少复制冲突与错误,可以采取以下优化措施:一、权限设置与访问控制 确保足够的访问权限: 无论是使用共享文件夹还是其他复制方法,都要确保主机和虚拟机之间有足够的访问权限。权限设置不当可能会导致无法访问或复制文件失败。 在Window…

苍穹外卖开发记录 -day2

好久没写博了,这几天一直在写简历,投简历,然后吃闭门羹。加了个技术学习群,能和别人交流一下感觉还挺有用的。昨天和前天其实也在写苍穹外卖,都是写了一些增删改查的无聊接口。今天更新一下记录一下知识点。学了一个很重要的知识点是面向切面编程(AOP)。 AOP的概念 AOP的作…

Z.Fantasy.GenerateCode:代码生成器

Z.Fantasy.GenerateCode:代码生成器 在当今快速迭代的软件开发环境中,如何提高开发效率一直是开发者们关注的焦点。今天为大家推荐一款简单的代码生成工具 —— Z.Fantasy.GenerateCode,它不仅能帮助你快速生成代码,更能让代码生成过程变得优雅而灵活。 🌟 为什么选择 Z.…

[Java] Java 17 FAQ

概述: Java 17 FAQ for Java 17 Q: 利用反射机制给 private 属性的 Field 设置为 true(field.setAccessible(true))时报: "java.lang.reflect.InaccessibleObjectException: Unable to make field private int java.io.StringReader.next accessible: module java.base d…

Hyper-V单硬盘空间管理

在Hyper-V环境中,单硬盘空间管理是一项至关重要的任务,它直接关系到虚拟化环境的稳定性和性能。以下是一些关于Hyper-V单硬盘空间管理的关键策略和建议:一、磁盘空间紧张的原因分析 在Hyper-V环境中,磁盘空间紧张的原因多种多样,主要包括: 虚拟机快照积累:虚拟机快照是虚…

如何通过文件安全传输网关,解决堡垒机远程运维的安全问题?

堡垒机又称运维审计系统或跳板机,是指在特定网络环境下,为保障网络和数据不受内外部用户的入侵和破坏,运用技术手段监控和记录运维人员对网络内服务器、网络设备、安全设备、数据库等的操作行为,以实现集中报警、及时处理及审计定责的设备。当用户采用堡垒机开展远程运维和…

可以使用MathNet.Numerics生成一条X方向的N阶曲线

本文介绍不依赖贝塞尔曲线,如何绘制一条平滑曲线,用于解决无贝塞尔控制点的情况下绘制曲线、但数据点不在贝塞尔曲线的场景。 在上一家公司我做过一个平滑曲线编辑工具,用于轮椅调整加减速曲线。基于几个用户可控制的点,生成一条平滑的曲线,控制点需要保持在曲线上。 今天…

20241905 2024-2025-2 《网络攻防实践》 第3次作业

20241905 2024-2025-2 《网络攻防实践》 第3次作业 1. 实验内容实践tcpdump:使用tcpdump开源软件对在本机上访问网站过程进行嗅探,并回答问题:你在访问网站首页时,浏览器将访问多少个Web服务器?他们的IP地址都是什么?实践wireshark:使用Wireshark开源软件对在本机上以TE…

项目到底该怎么管?掌握“六抓六放”原则就够了!

很多人一听“项目管理”,就觉得复杂、高大上,好像必须懂各种工具、框架,还得写一堆流程文档,才能把项目搞好。 其实,项目管理说到底,就是抓住关键、放掉多余,既不瞎忙,也不瞎管! 有的管理者啥都不管,结果项目乱成一锅粥;有的领导什么都要过问,事无巨细盯着,结果把…

Open-Sora 2.0 重磅开源!

潞晨科技正式推出 Open-Sora 2.0 —— 一款全新开源的 SOTA 视频生成模型,仅 20 万美元(224 张 GPU)成功训练商业级 11B 参数视频生成大模型。开发高性能的视频生成模型通常耗资高昂:Meta 的视频模型训练需要 6000 多张 GPU 卡片,投入数百万美元。 在多项关键指标上,它与…

homebrew 视化管理工具

安装cakebrew官网brew cask install cakebrew过程如下➜ study brew cask install cakebrew ==> Downloading https://cakebrew-377a.kxcdn.com/cakebrew-1.2.5.dmg ######################################################################## 100.0% ==> Verifying SH…