CMU15445 Concurrency Control
qps
- Project #1 - Buffer Pool Manager, qps, 2323.73636
- Project #2 Checkpoint #2: B+Tree, qps, 151510.33
- Project #3 - Query Execution, time, 4812.76133s
- Project #4 - Concurrency Control, update qps, 10.6529, read qps 10.3618, weighted qps 10.5946
Lock Manager
lock
- 检查事务的隔离级别是否符合锁的要求
REPEATABLE_READ:
The transaction is required to take all locks.
All locks are allowed in the GROWING state
No locks are allowed in the SHRINKING state
READ_COMMITTED:
The transaction is required to take all locks.
All locks are allowed in the GROWING state
Only IS, S locks are allowed in the SHRINKING state
READ_UNCOMMITTED:
The transaction is required to take only IX, X locks.
X, IX locks are allowed in the GROWING state.
S, IS, SIX locks are never allowed
-
是否是重复的锁请求
-
是否为锁升级
-
将锁请求加入队列
-
尝试获取锁,看这个请求与已经分配资源(granted)的请求(如有其他线程在读被分配了读锁)是否互斥或者说兼容;如果满足的话,其次看优先级,满足 fifo;
- 判断兼容性。遍历请求队列,查看当前锁请求是否与所有的已经 granted 的请求兼容。需要注意的是,在我的实现中 granted 请求不一定都在队列头部,因此需要完全遍历整条队列。锁兼容矩阵可以在 Lecture slides 中查看。若全部兼容,则通过检查。否则直接返回 false。当前请求无法被满足。
- 判断优先级。锁请求会以严格的 FIFO 顺序依次满足。只有当前请求为请求队列中优先级最高的请求时,才允许授予锁。优先级可以这样判断:
如果队列中存在锁升级请求,若锁升级请求正为当前请求,则优先级最高。否则代表其他事务正在尝试锁升级,优先级高于当前请求。- 若队列中不存在锁升级请求,则遍历队列。如果,当前请求是第一个 waiting 状态的请求,则代表优先级最高。如果当前请求前面还存在其他 waiting 请求,则要判断当前请求是否前面的 waiting 请求兼容。若兼容,则仍可以视为优先级最高。若存在不兼容的请求,则优先级不为最高
这里有个问题,尝试获取锁的时候,确定了当前 request 不是升级锁的请求,且判断了之前的 waiting 的 request 都与当前的兼容,那么可以获取锁(具有相同的最高优先级),那么之前 waiting 的 request 呢?
这里这段话来自 shiyi
unlock
- 对于 table lock,释放时检查所有的 rowlock 是否已经释放
- 获取对应的 lock request queue
- 遍历请求队列,找到 unlock 对应的 granted 请求
当隔离级别为 REPEATABLE_READ 时,S/X 锁释放会使事务进入 Shrinking 状态。当为 READ_COMMITTED 时,只有 X 锁释放使事务进入 Shrinking 状态。当为 READ_UNCOMMITTED 时,X 锁释放使事务 Shrinking,S 锁不会出现。
deadlock dection
实验说明中提到,要使用 MakeEagerIterator 而不是 MakeIterator 获取迭代器。这貌似是一个 Halloween Problem?
死锁的检测与解除,这个就是检测资源是否冲突。
- 遍历不同的 request queue,建立有向图,对于同一个 request 里面,应该构建任意 waiting 到 任意 granted 事务的边;应该只有一个有向图,但可能不是单连通,因为可能一个事务访问多个表,有可能只访问一个表,表就可以看出资源(或者表上的锁)
- 搜索是否有环,如果有环,找到 txn_id 最大的事务(最年轻的事务),将此事务的 state 设置为 abort
- 找到事务正在请求资源的 request queue,notify_all 将 request_queue 上所有线程唤醒。
- 如果事务唤醒后发现状态为 abort (这里需要修改一下 上文的等待条件) ,在 request_queue 中将自己删除,抛异常,返回。(对应于 locktable 和 lockrow 中 while(!CanLockUpgrade))此时相当于环断了
- 在 graph 中删除 所有与 txn_id 相关的边,可能会有多条边,因为会同时在等待多个事务。
- 重复这个过程,因为可能有多个环。
Concurrent Query Execution
实际上就是并发的实现之前的 excutor 算子
seq scan
- 对于写,lockmode 是 IX,看是否能用 IX 锁住表
- 对于读,如果是 READ_UNCOMMITED,就不需要任何锁,否则需要 S 锁,但这里不能用 S,否则后面的 MixedTest 会有问题,所以需要 IS,如果当前事务已经申请了 IX 锁,那么就没有必要 IS 锁了。
- 每读取一行需要 row lock(ru 不需要 S,rc 和 rr 都需要),即使这一行是被删除的,如果是有效的行就 row unlock(ru 级别不需要加读锁,那么读锁也就不需要 unlock,rc 级别都需要,rr 释放锁在 SHRIKING), 无效就 unlock 看下一行。如果发现到了表的结尾,那么需要 unlock table(如果是 delete 操作,seqcan 并没有真正完成 delete 操作,因此不能 unlock table,只有当是读操作时,才可以 unlock table)
insert && delete
aborted 时会 undo 之前所有的操作,因此每个 transaction 维护了一个 table_write_set_ 和一个 index_write_set_,插入删除时需要记录一下。
并且,插入和删除是写操作,需要申请 row lock,不需要手动释放,因为这似乎是实验需要满足的 2pl。
** insert 获取锁**
- 获取 IX 表锁
- 获取 X 行锁
事务在被 abort 后,lock_manger 会自动释放相关资源,所以我们不需要添加冗余的逻辑去释放已有的锁。
https://zhuanlan.zhihu.com/p/600001968
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通