project4踩坑经历

本篇文章记录本人对实验中各个板块的理解以及踩坑, 如果您发现我过多的涉及到了实验的内容, 有违学术诚信, 请告诉我!

正文

project4整体实现了一个OCC版本的MVCC, 即乐观版本的多版本并发控制为事务提供支持。

  • Task1要求实现时间戳和水印;
  • Task2要求元组重构方法和撤销日志收集方法, 以及重构project3中实现的SeqScan执行器。
  • Task3要求实现MVCC执行器, 分成一个个小任务就是:重构insert、delete、Update执行器, 实现Commit提交操作, Undolog生成和更新函数、 垃圾回收机制。
  • Task4引入主键, 并实现主键更新和插入
  • Bonus1为abort事务回滚, Bonus2为Serializable Verification序列化验证。

Task1

首先要看一下读取时间戳到底怎么工作。 对于事务来说, 事务有一个事务id, 一个读取时间戳, 一个提交时间戳。 事务被创建时, 会被事务管理器分配一个递增的事务id,用来唯一标记事务, 这个事务管理器就是DBMS里面管理所有事物的对象。 事务启动时, 也会被分配一个读取时间戳,这个读取时间戳是整个全局中, 上一个事务的提交时间戳。 而提交时间戳, 同样是由事务管理器分配, 并且每次事务提交都会递增, 保证全局唯一。

如上图, 每一次有事务提交, 那么last_commits_ts_就会加一, 同时一个事务启动, next_txn_id也会加一。 然后读取时间戳就可以在启动的时候直接赋值last_commit_ts即可。

Task1中需要理解的是watermark水位线。水位线用来判断DBMS中有哪些数据版本不再被所有活跃事务需要。其中current_reads里面包括了当前活跃事务中,读取时间戳与读取时间戳个数的对应关系。如果获取水位线, 那么就返回current_reads中的最小值, 如果current_reads中没有数据, 直接返回。

我需要做的就是, 在Commit函数中, 提交时间戳加一,并将事务的提交时间戳设置为该时间戳。然后更新水位线中的commits设置为提交时间戳。最后要移除水位线中的该事务read_ds计数。在Begin函数中设置read_ds即可。

Task2

Task2的主要任务有两个, 一个是元组重构方法Reconstruct、一个是重构顺序扫描方法SeqScan。

Reconstruct

理解元组重构就要先理解undolog。undolog就是将元组改变前和改变后的“差异“保存下来, 比如当T1将A的四列元组改变了两列, 则将:改变了哪些列、改变之前该列的值、该槽位的版本链、当前事务的事务ID保存为一个undolog(撤销日志), 然后将这个撤销日志保存到当前的事务里面, 并将该撤销日志添加到对应槽位的版本链中。注意该版本连由当前DBMS的事务管理器维护。就是下图中的version_map:

Reconstruct就是拿到一组撤销日志(undolog), 根据undolog中的差异, 将basetuple恢复成旧元组并返回。

具体实现细节就是:

  • 如果undolog的is_delete标记为true, 说明旧元组为空,那么就构建一个空tuple。
  • 否则依次根据拿到undolog中的差异列的值, 和无差异列的值。 构建出一个旧元组。 最后遍历整个”undo_logs", 直到遍历完为止。

SeqScan

我认为SeqScan是所有的算子的基础, 因为除了Insert, 其他的算子的最底层都是SeqScan或者IndexScan。 大部分算子都是从SeqScan拉取数据。 SeqScan中要处理:哪些元组看的见、哪些元组看不见。

因为我之前p3实现的是无MVCC的扫描, 扫描每一个槽位时, 该槽位是什么拿到什么就可以了, 如果为空, 则继续遍历下一个槽位即可。 但是对于MVCC控制的SeqScan, 同样是遍历每一个槽位, 但是每个槽位还要深入遍历它的版本链, 即undolog链。

具体实现细节如下:

  • 对于每一个槽位, 先看当前tuple能否看到

    • 能看到并且不为空, 就返回; 能看到但是为空, 检查下一个槽位。
  • 如果看不到, 那么就检查undolog。

  • 一边回溯版本链, 一遍收集undolog, 直到当前undolog能够看到。

    • 如果最旧的undolog也看不到, 那么就检查下一个元组。
  • 利用收集到的undolog构建旧元组, 返回即可。

这里要注意的是, 文档中说解释了CollectUndolog,并要求实现了。

所以我可以实现该函数,方便SeqScan的编写。

实现流程就是

  • 从DBMS的事务管理器中拿到整个该槽位的版本链。
  • 遍历整个版本链, 如果看不到, 则加入到“return_undologs(返回undolog数组)”中, 如果能看到,则返回该数组就可。

Task3

Task3要求实现MVCC版本的执行器, 前面实现的SeqScan也是MVCC版本的执行器的一部分, Task3中要求实现Insert、Update、Delete的MVCC版本执行器。 并且要实现一下垃圾回收机制以及提交。

Insert

Insert不需要构建undolog, 因为Insert只是在一个新的槽位插入新元组。 与p3不同的是, 要注意设置该槽位的时间戳为事务ID, 并且要把该槽位的修改记录添加到事务中。

这里不需要注意并发, 因为InsertTuple是在一个新槽位插入数据。 并发冲突只存在一种情况: 多个事务同时向内部插入数据。而InsertTuple本身为原子的, 所以无需注意并发控制。

Commit

关于Commit, 要注意一点就是要先更新事务以及更新元组的提交实践戳。 然后才能更新全局的提交时间戳以及watermark里面的最新提交时间戳commit_ts。 因为对于一个事务,如果先更新了全局提交时间戳, 那么此时另一个事务就会获得这个全局提交时间戳作为read_ts。

另外,Commit一定是原子的, 并且更新元组的提交时间戳一定是原子的。当前一个事务修改了元组后,假如一共修改了几个元组, 但是只更新了一个元组的提交时间戳。 那么另一个新事物, 就永远不能读取这些没有更新提交时间戳的元组了。所以要么Commit完成, 要么没有Commit, 不存在Commit一半的情况。

TxnMgrDbg

TxnMgrDbg就是回溯整个版本链, 对于版本连上的每一个版本, 都要打印出对应的元组。 一直到版本链结束。该函数为辅助调试函数,可以不实现, 但是建议实现, 方便调试。

GenerateNewUndoLog And GenerateUpdateUndoLog

GenerateNewUndoLog和GeenerateUpdateUndoLog用来当Update和delete对元组进行改变时, 保存旧元组与新元组的差异。 保存的方法就是创建undolog, 并将undolog连接到版本链中。

GenerateNewUndolog的参数为basetuple和targettuple, 根据两者构建一个undolog。

GenerateUpdateUndoLog的参数为basetuple、targettuple、undolog。 需要先利用undolog和basetuple构建出旧元组, 然后根据旧元组和targettuple生成新undolog并返回。

具体实现细节如下:

  • GenerateNewUndoLog

    • 如果basetuple为空, 那么创建一个undolog的is_delete为true, 并且modifys_filed为schema.columns.size()。 并且每个列对应的modify_value为空。链接版本链后返回即可。
    • 如果targettuple为空, 那么创建undolog的is_delete为false, 并且modify_filed为schema.columns.size()。 并且每个对应的modify_value为basetuple对应列的值, 链接版本链后返回。
    • 如果两者都不为空, 则遍历两tuple的每一列,标记每一列是否有差异, 并将有差异的列的basetuple对应的列收集起来,构造undolog。
  • GenerateUpdateUndoLog

    • 先构造旧元组。

    • 然后调用重复一遍GenerateNewUndoLog的逻辑即可。

Update And Delete

update和delete在执行主逻辑之前, 我们要检查一下 写写 冲突。

判断条件就是当前要修改的元组的提交时间戳大于自己的读取时间戳, 并且提交时间戳不为自己的事务ID。 那么就说明这个元组要么正在被其他事务修改, 要么已经被其他事务修改并且事务已经提交了。 发生了写写冲突。

  • Delete: 当槽位处的提交时间戳为当前的事务ID, 那么更新undolog,直接更新槽位处的元组即可。如果槽位处的提交时间戳为某个已经提交的事务的提交时间戳, 那么就以target为nullptr, 原元组为basetuple构建undolog, 注意要添加写集合与将undolog添加到当前事务中。最后更新当前槽位处元组。
  • Update:当槽位处的提交时间戳为当前的事务ID, 那么更新undolog, 并且更新槽位处的元组即可。 如果槽位处的提交时间戳为某个已经提交的事务的提交时间戳, 那么以target为newtuple, 原元组为basetuple创建undolog, 并且更新undolog。然后更新当前槽位元组即可。

需要注意的是更新元组的函数中Check参数填成nullptr即可。 OCC的并发控制不需要加锁, 只需要在需要并发控制时添加一个Check函数即可。

垃圾回收

对于垃圾回收就是回收某个不需要的事务。 如果该事务内部所有的undolog都不会被当前活跃事务看到, 那么该事务以及内部所有的undolog就可以被回收了。

实现策略为:

  • 遍历DBMS中所有的已经提交事务, 并将事务修改的table和rid保存下来。
  • 遍历所有table以及rid,拿到对应槽位的版本连上面的提交时间戳ts。 查看watermark, 如果存在watermark > ts, 则该时间戳对应的已提交事务不应被回收, 进行标记。

最后回收所有未被标记为不可回收的提交事务。

Task4

Task4引入主键, 并且引入并发控制。

Index Insert

如果存在主键,此时进行插入要考虑插入数据的主键列所对应的Value(以后叫作Key)是否已经存在索引中。 如果已经存在索引中, 则要拿到索引中Key指向的槽位,看看是否槽位处为delete, 如果为delete,则更新该槽位的数据和元数据即可。同时要注意并发控制。如果不为delete, 说明有数据, 那么就是 写写 冲突。

如果不在索引中, 则类似于无主键索引的插入。

并发控制

OCC的并发控制是在提交阶段之前进行验证,验证是否与之前已经提交的事务有 写写冲突。这里要做的就是先拿到该槽位原本的元数据, 将这个元数据作为Check的固定参数。 Check函数就是检查原本的元数据是否与新拿到的元数据是否相同。

Index Scan

Index Scan和SeqScan实现的逻辑相同, 对于每一个元组要检查版本链。

  • 如果版本可见并且不为delete, 则返回, 如果版本可见但是delete, 检查下一个槽位。
  • 如果版本不可见, 遍历到版本链的下一个位置, 当版本连为末位, 则检查下一个槽位。

Delete、Update并发控制

Task4中我需要重构Delete、Update, 将两者设计为支持并发控制的版本。 我需要改动的地方很少, 只需要类似于Insert设计一个绑定了元组元数据的可调用对象作为Check即可。 并且Check内部逻辑为比较新的元数据是否与旧的元数据是否相同。

我这里理解的意思就是, 当前事务在修改该槽位的过程中, 如果其他事务已经对该槽位进行了修改, 即便没有提交。此时就类似于当前事务正在修改一个已经被修改的元组,该元组不应该被当前事务可见, 相当于刚刚进入函数时写写冲突的判断, 直接设置abort退出即可。

主键更新

主键更新分为三种情况, insert、delete、update。

  • insert时如果新元组的索引列对应的Value不存在主键索引中,还需要将Value添加到索引中。 并指向新的RID。
  • delete时对于主键列不进行删除。
  • update时, 如果主键更改, 那么要先将所有更改的元组删除, 然后再将所有新元组添加。这里删一个插入一个的话, 会导致新插入的元组与已经存在的元组索引列冲突, 出现逻辑错误。

Bonus 1

发生写写冲突后, 调用abort函数回滚。

遍历该事务的写集合, 拿到每个槽位。 拿到槽位对应的元组。 此时会有三种情况:

  • 如果槽位没有版本链, 说明该事务在该槽位进行了插入, 则将该位置设置为delete。

    • 否则为delete或者update。 拿到undolog, 重构tuple并进行滚滚即可。 即利用tuple更新槽位即可。

Bonus 2

检查每一个槽位, 以及槽位对应的版本链; 对于序列化来说, 要保证事务读取元组的顺序和事务按照启动顺序序列化执行结果相同。快照隔离的实现中已经处理 写 - 写冲突 。 所以要保证序列化, 就要处理 写倾斜 。

所以要认识什么是写倾斜, 下面是关于写倾斜的理解, 对比 写写冲突 来理解:

  • 写 - 写冲突 :事务T1正在修改表1中的第三行, 事务T2稍后启动, 也想要修改表1中的第三行。 此时T1尚未提交, 或者已经提交但T1.commit_ts > T2.read_ts, T2直接abort。

  • 写倾斜 :写写冲突是针对同一行, 写倾斜则是针对不同行。 假如一个表中有四行数据:

    • 
      
      col1 | col2 | 1 | 0 | | 1 | 0 | | 0 | 0 | | 0 | 0 |
    • 现在T1想要将col1全部设置为0, T2想要将col!全部设置为1。 T1先执行 -> "update t1 set col1 = 0 where col1 != 0; 然后底层seq_scan执行器通过遍历 + 过滤读取到了第一二行元组。元组通过管道回传给上层 update执行器。然后执行器更新了表中的第一二行。 然后T2执行 -> "update t1 set col1 = 1 where col1 != 1" , 由于T1未提交, 所以表中的一二行被锁定,T2的seq_scan执行器只能读取到第三四行元组并且交给update执行器, 然后update执行器更新三四行。 最后的表中的结果就如下:
    • 
      
      col1 | col2 | 0 | 0 | uncommited -> modified by T1 | 0 | 0 | uncommited -> modified by T1 | 1 | 0 | uncommited -> modified by T2 | 1 | 0 | uncommited -> modified by T2
    • 但是我们想要的是什么呢? 我们想要的是先执行T1, 然后T1把表中所有数据修改为0, 然后执行T2, T2把表中所有数据修改为1, 最后表中的数据应该全都为1:
    • 
      
      col1 | col2 | 1 | 0 | | 1 | 0 | | 1 | 0 | | 1 | 0 |

处理写倾斜

了解了实现序列化要解决写倾斜的问题。 然后下面具体谈如何处理写倾斜。

我们从上面的例子中就能看到, 其实写倾斜发生的条件就是在T1的修改的元组里面, 有些元组其实满足了T2的过滤条件, 但是由于uncommited, 所以无法被T2修改。 此时T1先执行,能够正确修改, 但是T2因为uncommited不能正确修改, 有写倾斜。 所以如何判断写倾斜? 就是判断可能冲突的事务修改的元组里面, 如果有能够被我们正在判断的事务所成功过滤的, 但是又因为uncommited, 所以没有读取到的这些元组。 那么就是发生了写倾斜。

我们处理的验证逻辑就是, 在提交的时候判断是否与序列化的结果一致。 因为如果冲突事务T2修改过某个元组, 并且这个元组符合检查事务T1的过滤条件, 又uncommited无法被检查事务T1读取, 那么就不符合序列化。

  • 所以在提交阶段, 先拿到所有的冲突事务。即uncommit的事务。
  • 判断每一个冲突事务的修改过的元组(写集合), 是否满足检查事务的过滤条件。
  • 如果满足, 说明发生写倾斜,事务中止。 如果所有的元组都不满足, 则说明没有发生写倾斜, 可以正常提交。
<think>好的,用户现在问的是如何拥有出色的项目经历,并希望得到具体的实现方法。之前用户已经询问过关于职业生涯规划的建议,结合了八字五行、MBTI、教育背景等信息,现在这个问题应该是用户想要进一步补充自己在项目经历上的不足,以便在求职或升学时更具竞争力。 首先,我需要回顾用户的历史信息。用户目前是大二学生,计算机科学与技术专业,绩点50%,无项目和实习经历,但有大作业完成经验。用户的目标是进入人工智能、网络安全或技术产品经理等领域。因此,用户需要具体的项目指导,尤其是在缺乏现有项目的情况下如何起步。 接下来,用户的需求是获得具体的实现方法,所以需要分步骤、分阶段的建议,可能包括如何选择项目、获取资源、积累经验、展示成果等。用户作为INTJ类型,可能更偏好结构清晰、逻辑性强的计划,因此需要将建议组织得有条理,每个步骤都有明确的目标和行动项。 此外,用户之前的分析中提到五行需要补木火,适合南方城市和火属性行业,因此在项目选择上可能需要推荐与人工智能(属火)、网络安全(属火)相关的项目,以符合之前的建议。同时,用户可能需要兼顾提升技术深度和积累实际经验,以弥补当前无项目的短板。 可能需要注意用户的时间安排,用户目前大二,接下来还有两年时间,需要合理规划每个阶段的任务,比如短期、中期、长期目标。同时,考虑到用户尚未有项目经验,需要从基础开始,逐步提升复杂度,并利用现有资源如学校实验室、在线平台、竞赛等。 还要考虑如何将大作业转化为项目经历,指导用户如何优化和扩展现有的大作业,使其更具竞争力。另外,开源贡献和实习也是积累项目经验的重要途径,需要给出具体的方法,如如何选择开源项目,如何准备实习申请等。 展示项目同样重要,用户需要知道如何有效地在GitHub、技术博客、简历中展示自己的项目,以吸引招聘方的注意。同时,提升沟通能力,特别是对于INTJ类型可能需要加强的部分,如项目演示和团队协作。 最后,需要提醒用户保持持续学习和技术敏感度,关注行业动态,定期更新项目,保持竞争力。同时,结合之前的健康建议,注意平衡压力和时间管理,避免过度劳累。</think>以下是打造**高质量项目经历**的系统方法,涵盖**选题策略、执行技巧到成果展示**全流程,结合你的专业背景和现状针对性设计: --- ### 一、项目选题策略 #### 1. **梯度进阶法**(适合零基础起步) - **Level 1:课程作业升级** 将现有大作业迭代为完整项目: ✔️ 用Flask/Django给数据结构课设加Web可视化界面(如动态展示最短路径算法) ✔️ 操作系统课设扩展为简易分布式系统(基于Raft协议) ▶️ 优势:节省启动时间,已有代码复用率超60% - **Level 2:痛点驱动型项目** 聚焦细分场景问题: ✔️ 开发"实验楼课程表冲突检测工具"(解决校内选课痛点) ✔️ 用YOLOv5实现"图书馆座位手机摄像头占座识别系统" ▶️ 关键技术:Web爬虫+规则引擎/目标检测+Flask部署 - **Level 3:前沿技术验证** 选择有学术价值的方向: ✔️ 《基于知识图谱的COVID-19药物副作用推理系统》 ✔️ 《联邦学习框架下的信用卡欺诈检测模型优化》 ▶️ 资源渠道:Arxiv最新论文+Kaggle相关数据集 --- ### 二、项目执行核心技巧 #### 1. **MVP开发法则** - **第一周**:用Streamlit快速搭建可视化Demo(比Django节省70%时间) - **第二周**:核心功能实现(优先完成数据处理+核心算法) - **第三周**:添加日志监控(Prometheus+Grafana)、单元测试(Pytest) - **第四周**:撰写技术文档(必备README结构见下图) ```markdown ## Project Title ![技术架构图] ### 创新点 - 提出XX算法优化传统方案(对比实验表格) ### 使用指南 ```bash git clone xxx pip install -r requirements.txt ``` ### 性能指标 | 模型 | 准确率 | 响应时间 | |------|-------|---------| | 传统方案 | 82% | 300ms | | 本系统 | **91%** | **150ms** | ``` #### 2. **技术组合拳提升含金量** - **基础版**:Python+MySQL+Echarts - **进阶版**: ✔️ 容器化:Docker封装模型服务+GitHub Actions自动化构建 ✔️ 云部署:AWS EC2免费额度部署+域名备案(学生认证) ✔️ 性能优化:Redis缓存热点数据+Nginx负载均衡 #### 3. **数据证明价值** - 量化指标: ✔️ "优化算法使查询速度提升40%"(附JMeter压测报告) ✔️ "系统被XX学院3个实验室采用"(收集用户感谢邮件) - 可视化呈现: ✔️ 用Pyecharts制作系统流量监控大屏(截图放GitHub) ✔️ 技术架构图使用draw.io绘制(突出微服务设计) --- ### 三、项目包装与传播 #### 1. **GitHub爆款仓库打造** - **标题优化**: ✖️ 普通命名:"Course Design" ✔️ 流量密码:"AutoCV-Generator:基于LaTeX的智能简历生成系统" - **Star增长技巧**: ✔️ 在Reddit的r/coolgithubprojects发帖(美东时间早10点发) ✔️ 参与Hacktoberfest活动(PR被合并可获限量版T恤) #### 2. **技术博客写作模版** ```markdown # 如何用BERT实现法律文书争议点挖掘 ## 痛点场景 (律师审阅合同时需人工标注风险条款,耗时易错) ## 技术方案 (图:BERT+BiLSTM-CRF模型架构) ## 记录 - 教训:原始数据未去隐私导致训练中断 - 解决方案:用Faker库生成模拟数据 ## 商业价值 (已与XX律所达成POC测试,预计节省200小时/年) ``` ▶️ 发布平台:掘金+优快云+Medium(英文版) #### 3. **面试话术设计** - **STAR法则升级版**: ✔️ Situation:传统方法存在XX问题(用行业报告数据佐证) ✔️ Task:我负责攻克XX技术难点(突出个人贡献) ✔️ Action:尝试A方案失败→改用B方案(展示思考过程) ✔️ Result:指标提升X%+获得XX认可(量化+背书) --- ### 四、资源加速包 #### 1. **快速出项目工具集** - 低代码平台: ✔️ 前端:Vercel(部署React项目5分钟) ✔️ 后端:Supabase(替代Firebase,国内访问更快) - 数据集: ✔️ 中文NLP:CLUE基准(https://www.cluebenchmarks.com) ✔️ 计算机视觉:OpenDataLab(https://opendatalab.com) #### 2. **抄袭检测与合规** - 代码查重:MOSS系统(斯坦福大学开源) - 版权规避: ✔️ 使用CC0协议数据集(https://commoncrawl.org) ✔️ 商业项目避免直接调用ChatGPT API(改用LangChain封装) #### 3. **项目灵感库** - 每日浏览: ✔️ GitHub Trending(筛选Python/JavaScript分类) ✔️ Papers With Code(最新AI落地案例) - 创意来源: ✔️ Product Hunt(跟踪新锐SaaS产品) ✔️ 国务院《十四五数字经济规划》(挖掘政策支持方向) --- ### 五、实战时间表(6个月速成) | 阶段 | 目标 | 里程碑 | |--------|-------------------------------|----------------------------------------| | 第1-2月 | 完成2个改良型项目 | GitHub收获50+ Star,1篇优快云博客 | | 第3-4月 | 产出1个创新项目+1篇顶会论文 | 项目被教授推荐至大学生创新创业大赛 | | 第5-6月 | 通过项目斩获大厂实习offer | 简历项目栏可写满3个深度参与项目 | --- **关键提示**:立即注册GitHub学生包(https://education.github.com/pack),获取免费AWS额度+JetBrains全家桶,项目开发效率提升3倍以上。今晚就可以从改造你的课程设计开始!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值