CMU15445-2021-FALL-Project4-自用笔记

本实验将实现bustub中的锁管理器,其负责跟踪数据库中使用的元组级锁,以使得数据库支持并发的查询计划执行。

TASK #1 - LOCK MANAGER + DEADLOCK PREVENTION

在本实验中,将使用两阶段锁策略实现具体的元组级锁,具体的锁定解锁策略应当由事务的隔离级别决定。当一个事务需要读取或写入元组时,其需要根据隔离级别尝试获得元组对应的读锁或写锁,并在适当的时刻将其释放。

事务及隔离级别

transaction.h

 private:
  /** The current transaction state. */
  TransactionState state_;
  /** The isolation level of the transaction. */
  IsolationLevel isolation_level_;
  /** The thread ID, used in single-threaded transactions. */
  std::thread::id thread_id_;
  /** The ID of this transaction. */
  txn_id_t txn_id_;

  /** The undo set of table tuples. */
  std::shared_ptr<std::deque<TableWriteRecord>> table_write_set_;
  /** The undo set of indexes. */
  std::shared_ptr<std::deque<IndexWriteRecord>> index_write_set_;
  /** The LSN of the last record written by the transaction. */
  lsn_t prev_lsn_;

  /** Concurrent index: the pages that were latched during index operation. */
  std::shared_ptr<std::deque<Page *>> page_set_;
  /** Concurrent index: the page IDs that were deleted during index operation.*/
  std::shared_ptr<std::unordered_set<page_id_t>> deleted_page_set_;

  /** LockManager: the set of shared-locked tuples held by this transaction. */
  std::shared_ptr<std::unordered_set<RID>> shared_lock_set_;
  /** LockManager: the set of exclusive-locked tuples held by this transaction. */
  std::shared_ptr<std::unordered_set<RID>> exclusive_lock_set_;
};

bustub中事务由Transaction以及TransactionManager管理。Transaction中维护了事务的全部信息,包括事务ID、事务隔离级别、事务的状态(锁扩张、锁收缩、COMMITABORT)、事务的元组修改记录及索引修改记录、事务的页面修改记录、以及事务当前所拥有的锁。

TransactionManager中进行事务的实际行为,如BEGINCOMMITABORT,并可以通过其获得对应ID的具体事务。

死锁预防策略

在本实验中,将通过Wound-Wait策略实现死锁预防,其具体方法为:当优先级高的事务等待优先级低的事务的锁时,将优先级低的事务杀死;当优先级低的事务等待优先级高的事务的锁时,优先级低的事务将阻塞。在bustub中,事务的优先级通过其事务ID确定,越小的事务ID将代表更高的优先级。

在锁管理器中,使用lock table管理锁,lock table是以元组ID为键,锁请求队列为值的哈希表。其中,锁请求中保存了请求该元组锁的事务ID、请求的元组锁类型、以及请求是否被许可;通过队列的方式保存锁保证了锁请求的先后顺序:

LockShared()

  • 在LockShared中,事务txn请求元组ID为rid的读锁

  • 进行前置判断,当事务的状态为ABORT时,直接返回假

  • 如当前的事务状态为锁收缩时,调用获取锁函数将导致事务ABORT并抛出异常

  • 当事务的隔离级别的READ_UNCOMMITTED时,其不应获取读锁,尝试获取读锁将导致ABORT并抛出异常

  • 如前置判断通过,则将当前事务状态置为GROWING,并获得互斥锁保护锁管理器的数据结构

  • 然后,获取对应元组ID的锁请求队列及其相关成员,并将当前事务的锁请求加入队列

  • 在这里txn_table_为保存<事务ID、事务>的二元组

  • 需要注意,在该请求被加入队列时将就应当调用GetSharedLockSet()将该元组ID加入事务的持有锁集合,使得该锁被杀死时能将该请求从队列中删除

  • 为了避免死锁,事务需要检查当前队列是否存在使得其阻塞的锁请求

  • 如存在则判断当前事务的优先级是否高于该请求的事务,如是则杀死该事务

  • 如非则将can_grant置为false表示事务将被阻塞

  • 如该事务杀死了任何其他事务,则通过锁请求队列的条件变量cv唤醒其他等待锁的事务,使得被杀死的事务可以退出请求队列

  • 如存在阻塞该事务的其他事务,且该事务不能将其杀死,则进入循环等待锁

  • 队列中在该事务之前的锁请求是否存在活写锁(状态不为ABORT),如是则继续阻塞

  • 如能遍历到最后,说明前方无阻塞事务,则将该请求的granted_置为真,并返回

  • 如果遍历完还是存在阻塞事务,事务调用条件变量cv的wait阻塞自身,并原子地释放锁。

  • 检查该事务是否被杀死,如是则抛出异常

LockExclusive()

LockExclusive使得事务txn尝试获得元组ID为rid的元组写锁。

  • 进行前置检查,如当前事务状态为ABORT返回假

  • 如当前锁在收缩阶段,则将其状态置为ABORT并抛出异常

  • 更新事务状态为GROWING

  • 获取锁请求队列

  • 并将该请求插入队列,以及将该锁加入事务的拥有锁集合,插入事务表

  • 查询是否有将该事务锁请求阻塞的请求,当获取写锁时,队列中的任何一个锁请求都将造成其 阻塞,当锁请求的事务优先级低时,将其杀死

  • 如存在不能杀死的请求,则该事务将被阻塞

  • 当杀死了任一事务时,将唤醒该锁等待队列的所有事务

  • 等待锁可用,每当事务被唤醒时,检查其是否被杀死,如被杀死则抛出异常

  • 如未被杀死,则检查队列前是否有任意未被杀死的锁请求

  • 如没有则获得锁并将锁请求granted_置为真

LockUpgrade()

LockUpgrade用于将当前事务txn所拥有的元组ID为rid的读锁升级为写锁

  • 判断当前事务是否被杀死,以及该元组的锁请求序列是否已经存在等待升级锁的其他事务,如是则杀死事务并抛出异常。

  • 如通过检验,则将当前锁请求队列的upgrading_置为当前事务ID,以提示该队列存在一个等待升级锁的事务

  • 在Wound Wait中并未提及有关更新锁的行为,在这里将其每次唤醒尝试升级锁视为一次写锁获取, 即每次其尝试升级锁时都将杀死队列前方将其阻塞的事务。

  • 其具体方法为,每次事务被唤醒时,先检查其是否被杀死,然后遍历锁请求队列在其前方的请求

  • 如其优先级较低则将其杀死,如其优先级较高则将can_grant置为假,示意其将在之后被阻塞。

  • 如杀死任意一个事务,则唤醒其他事务。

  • 如can_grant为假则阻塞事务,

  • 如为真则更新锁请求的 lock_mode为EXCLUSIVE

  • 并将upgrading_初始化设置为INVALID_TXN_ID。

  • 当升级成功时,更新事务的拥有锁集合,擦除读锁记录,添加写锁记录

Unlock()

Unlock函数使得事务txn释放元组ID为rid元组上的锁。

需要注意,当事务隔离级别为READ_COMMIT时,事务获得的读锁将在使用完毕后立即释放,因此该类事务不符合2PL规则.

为了程序的兼容性,在这里认为READ_COMMIT事务在COMMIT或ABORT之前始终保持GROWING状态,对于其他事务,将在调用Unlock时转变为SHRINKING状态。

  • 在释放锁时,遍历锁请求对列(通过txn_id_确定事务)并删除对应事务的锁请求

  • 然后唤醒其他事务

  • 并在事务的读锁与写锁集合中删除该锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值