latch与lock区别

来源:互联网 发布:网络直播的弊端 议论文 编辑:程序博客网 时间:2024/06/05 17:29
 【原文参考https://github.com/sdg-sysdev/bdb-study/blob/master/btree_locking.txt】   B-TREE locking需要区分两种情况: 1. 并发的数据库事务在查询或修改数据库内容时对B-TREE索引访问的并发控制 2. 并发的线程对内存中的B-TREE数据结构进行访问时的并发控制   事务并发控制使用Lock,线程并发控制使用Latch   Lock和Latch的区别:   Lock Latch 分离... 用户事务 线程 保护... 数据库内容 内存中的数据结构 存活周期... 整个事务 临界区 模式... Shared, exclusive, update, 读,写, intention, escrow, schema, etc. (可能)更新 死锁... 检测和分解 避免 ...通过... 分析waits-for图,超时,事务中止, 编码规范,"lock leveling" 部分回滚,lock降级 保存于... Lock管理器的哈希表 被保护的数据结构     在分布式事务中,当等待全局事务协调器的决策时,需要保持Locks,以保证本地事务可以 遵守全局协调器的最终决策。但Latches在这时不需要被保持。   在没有新事务并发执行的崩溃恢复阶段,不需要Locks。因为在系统崩溃之前的并发控制已经 保证活跃事务之间不会冲突。而Latches则总是被需要。   physiological日志是根据页面中记录的标识来引用它,而不是根据字节位置。因此,在页面 内移动记录是为数不多的不需要写恢复日志的操作。   在页面内整理空闲空间和记录空间不需要写日志,除非在移除非法记录之后改变了某些记录的标识, 例如它们在页面内的slot numbers   从系统崩溃中恢复时,如果在日志分析的同时获取Lock,那么可以允许在重做操作及进行补偿操作 的同时继续事务处理。在此过程中获取的Lock可以不同于原始事务执行过程中获取的Lock。   Latch则仅限于保护那些修改B-TREE结构,但不改变其逻辑内容的操作。典型的例子就是分拆B-TREE节点 以及在B-TREE相邻节点间均衡负载。其它影响整个B-TREE索引的例子包括压缩,碎片整理,和其它 形式的重组。影响范围小于一整个节点的例子则是创建和移除幽灵记录。   存在的问题: 1. 当一个线程在读取缓冲池中的某个页面时,不能被另外一个线程修改。 2. 当跟随指针(页面标识符)从一个页面到另一个时,例如从B-TREE索引中的一个父节点到一个子节点, 该指针必须不能被另外一个线程废止(invalidate) 3. 不仅父-子指针之间有"指针追踪"的问题,在相邻指针之间也存在。不同的线程可能根据升序或降序 进行索引扫描,这会引起死锁。 4. 在B-TREE中执行插入操作时,一个子节点可能会因溢出而需要在父节点中进行插入。在极端情况下, B-TREE的旧根节点会溢出,而不得不进行分拆,并使用一个新的根节点代替。从叶子节点往根节点 访问在单线程环境下不会出问题,但在多线程环境下有死锁的可能。     对于第一个问题,数据库实现了只读latch和读-写latch。   解决第二个问题的方法是使用"lock coupling",即保留父节点的latch直到获取了子节点的latch。 如果子节点当前不在缓冲池中,因而需要相对缓慢的磁盘I/O,则在从磁盘读取子节点的过程中 应该释放父节点上的latch。这种情况下,可以通过获取缓冲池中所选页面对应的描述符结构的latch 来实现lock coupling。 另外,为了防止B-TREE在此期间的变化,该I/O应该重新遵循从根到叶子的遍历方式。这么做看起来会 比较昂贵,但是通常可以直接基于之前的搜索结果。例如,验证在将子节点页面读取到缓冲池中时 所有父页面上的LSN都没有变化。   第三个问题和第二个类似,只有两点不同。从积极的一面来说,异步read-ahead可以缓解该问题的发生。 由于B-TREE索引中的大规模read-ahead通常不能依赖于相邻指针,而必须依赖于叶子节点父节点中的指针 来预读,甚至是祖父节点。从消极的一面来说,要避免相反方向扫描的死锁,latch代码必须提供一种立即 失败模式。当在向前或向后遍历时发生了获取latch失败的情况,则必须释放所有叶子节点所对应页面的 latch。   第四个问题是最复杂的。它影响所有的更新,包括插入,删除,甚至记录的更新。 一个方法是在查找受影响的叶子节点时,将从根节点到叶子节点的遍历过程中碰到的所有节点都加上排他(exclusive)latch。 这种方法显然的问题是会碰到并发上的瓶颈,尤其在根节点。 另一个方法是在从根到叶子的搜索过程中使用共享(shared)latch,当有需要的时候将共享latch升级成排他latch。 在能够允许升级并且没有死锁风险的情况下这种方法很好,但是实际上由于它可能会失败,因此在实现中不能只 使用该方法。 第三个方法是在通常的共享和排他latch之外,使用"更新"("update")或"升级"("upgrade") latch。"更新"latch兼容共享 latch,但是彼此之间不兼容,这导致对于多个更新来说B-TREE根节点还是一个瓶颈。 以上三个方法可以有个改进,就是当碰到一个更低层的未满节点,如果它能保证拆分操作不会传递到更高层的节点,则可以释放 从根到父节点这条路径上的所有latch。另一方面,变长B-TREE记录和变长键值会使得决定需要多少空闲空间才能做出该保证 变得很困难甚至不可能。有趣的是,该问题可以通过以下方法来解决:在释放父节点的latch之前,可以知道如果子节点因为该 插入操作而需要拆分,则哪个键值会被添加到父节点中去。 第四个方法在为插入操作从根到叶子的遍历过程中主动拆分节点。该方法避免了第一个方法的瓶颈问题和第二个方法的升级 失败问题。它的不利之处是在真正需要之前拆分,浪费了一些空间,更重要的是在变长记录和键值的情况下,可能不可能在任何 情况下都能够主动拆分。 第五个方法是在初次根到叶子的搜索过程中使用共享latch,当一个节点需要拆分的时候中止该过程。然后启动一个新的过程, 并且在到达需要拆分的节点时,获取一个排他latch然后拆分。该方法能够受益于之前讨论过的基于之前路径的快速搜索机制。   BLINK-TREE [Lehman and Yao 1981].BLINK-TREE的优势是分配一个新的节点并初始接入到树中是一个本地步骤,仅仅影响一个 已存在的节点。劣势是搜索可能会稍微慢一点点。在密集插入的情况下,需要有方法来防止形成过长的相邻节点列表。 (PostgreSQL实现的也是BLINK-TREE索引) [Jaluta et al. 2005] 中有更详细的描述。   "key range locking", next-key locking"   在许多B-TREE实现中,当用户事务请求删除记录时实际上并没有实际删除。它只是将该记录标记为非法记录,称为幽灵记录。 因此,每次查询过程中如果碰到记录头中设置了"ghost bit"的记录,需要将它们从查询结果中剔除。 在插入一个新的B-TREE键值时,如果一个拥有相同键值的幽灵记录已经存在,则该操作会转化为一个对已有(幽灵)记录的更新操作。 对幽灵记录的回收在一个异步的清理事务中进行。在被清理之前,它们的键值像通常记录的键值一样参与并发控制和key rangelocking。   Locking in non-unique indexes Increment lock modes
0 0
原创粉丝点击