[读]Database · 理论基础 · 高性能B-tree索引

原文地址: Database · 理论基础 · 高性能B-tree索引

本文是
Database · 理论基础 · 高性能B-tree索引
的阅读笔记。(其实就是 CV ,/捂脸 )

Details

1. 前言

要实现高性能,需要在 B-tree 的基础上解决什么问题?

  1. 保证原子性
  2. 能够高效恢复
  3. 支持高并发

更加复杂的描述(要考虑和解决的问题远不止这些):

  • 如何加锁才能支持高并发?
  • 如何对索引页进行更改才能使对并发任务的影响最小化?
  • 如何记录日志才能保证系统发生故障时能够高效地恢复?
  • 如何保证没有提交的 SMO 操作在系统发生故障的时候能够恢复到一致性状态?
  • 如何保证已经提交的 SMO 操作不会因事务的失败而回滚?
  • 如何保证索引遍历遇到了正在进行 SMO 操作的索引页也依然能正常工作(而非阻塞)?
  • 如何保证事务T1插入到页P1而后又被 SMO 操作移动到页P2上的数据能够回滚?

2. 锁同步

数据部分用行锁 row lock ,索引部分使用 latch .

关于 latch

  • 轻量级的锁,分为shared和exclusive两种模式
  • 持有时间短
  • 只保证物理一致性(physical consistency
  • 不保证逻辑一致性(logical consistency
  • 无死锁检测

3. Ladder locking

遍历是自上而下的;有自下而上的操作,如索引页分裂和索引页删除(统称为 SMOStructure Modification Operation

自上而下的遍历,加锁采用 ladder locking (又叫 lock coupling )的方式,顾名思义,类似于我们下梯子的过程,即,刚开始两只脚都在第一层,一只脚先下到第二层,等站稳后,另外一只脚离开第一层,以此类推,直至到达目标层。

过程如下,

  • 先对根节点加 latch ,从根结点读取数据并找到下一层的叶子节点(即孩子节点);
  • 对下一层的孩子节点加 latch ,成功后,释放父节点上的 latch ;如果无法立即获得孩子节点上的 latch ,等待,直到加 latch 成功,才能释放父节点上的 latch
  • 重复此操作,直到到达叶子层节点。

要高并发,需要减少加锁的范围,上面的过程可以将加锁范围限制到 2 个索引页。

4. 逻辑删除(logical delete) vs 物理删除(physical delete)

逻辑删除的好处

  • 简单高效
  • 简化并发控制
  • 保证事务正常回滚

5. SMO 和 Nested Top Action

SMOStructure Modification Operation)包括索引页的分裂(split)和删除(shrink).
SMO原子性.

分裂过程:

  • 分配一个新的索引页
  • 移动目标索引页上的部分索引项(分裂点后面的所有索引项)到新索引页
  • 连接新分配的索引页到目标索引页后面(修改相邻索引页的 prev/next 指针)
  • 在上一级索引页(父节点)中添加指向新索引页的索引项,如果上一级索引页没有足够空间,继续分裂,直到受影响的非叶子节点完成更新。

问题在哪? a. 多级分裂代价高 b. 回滚代价高 c. 并发事务可能丢失数据(如其他事务插入到新增页上后本事务回滚了)。
那么,索引页分裂的最终结果不应该依赖外层事务的状态

Nested Top Action

A nested top action, for our purposes, is taken to mean any subsequence of actions of a transaction which should not be undone once the sequence is complete and some later action which is dependent on the nested top action is logged to stable storage, irrespective of the outcome of the enclosing transaction.

A transaction execution performing a sequence of actions which define a nested top actions consists of the following steps:

(1) ascertaining the position of the current transaction’s last log record;

(2) logging the redo and undo information associated with the actions of the nested top action;

(3) and on completion of the nested top action, writing a dummy CLR whose UndoNxtLSN points to the log record whose position was remembered in step (1).

dummy CLR 日志,其 UndoNxtLSN 指向 nested top action 开始前的最后一条日志.(日志的链式结构)

6. SMO和并发

在释放了叶子层节点上的latch之后,但是找到需要修改的父节点之前,如果一个任务正在遍历索引且正好访问到其父节点,继续往下遍历的时候将可能找不到对应的记录(比如,被移动到了新分裂出来的索引页上),这就是索引的不一致状态。

ARIES/IM 提出使用 exclusive tree latch 解决这个问题,即,SMO 开始前对当前索引加 exclusive tree latch,并在所有受SMO影响的索引页上设置 SM 标记 (SPLITSHRINK),以便其它访问这些索引页的任务能够知道SMO操作正在进行。一旦SMO结束,清除之前设置的SM标记,并释放 exclusive tree latch

虽然tree latch能够解决这个问题,但是其粒度较大,对高并发不友好。

SAP ASE则使用了一些更加巧妙的思路,包括,仅在受SMO影响的索引页上加address lock(基于缓存页地址加锁),且支持在这些索引页上做查询(即使SMO未完成)。

7. 日志和恢复(logging and recovery)

这里所讨论的日志和恢复基于商业数据库中广泛使用的 ARIES (WAL based)。

物理日志和逻辑日志。主流数据库更多采用的是Physiological logging,physical to a page, logical within a page”,日志里面记录数据所在的页面号和行号,不需要记录页内偏移量。

恢复.原始的数据插入位置由于索引页分裂会发生变化,导致数据插入时产生的日志中记录的位置信息失效,所以对于索引来讲,这些记录的位置信息是不可靠的。ARIES/IM 采用了逻辑回滚(logical undo)的方法。

8. 结束语

新技术和新硬件的出现(如NUMA架构,多核CPU和闪存),一些数据库厂商提出了提升B-tree索引性能的新思路,如latch free B-tree, cache-line friendly B-tree等,比较有代表性的有MS SQL Server的Bw-tree,其使用latch free的方式对index page进行delta update,消除了因加latch引起的开销(index page contention,cache line invalidation等)。

information

ARIES是一个恢复算法,由一系列的论文组成

  • 主论文 ARIES: A Transaction Recovery Method Supporting Fine-Granularity Locking and Partial Rollbacks Using Write-Ahead Logging
  • 索引方面 ARIES/KVL : A Key-Value Locking Method for Concurrency Control of Multiaction Transactions Operating on B-Tree Indexes
  • ARIES/IM An Efficient and High Concurrency index Management Method Using Write-Ahead Logging
  • 一篇不错的综述 Repeating History Beyond ARIES

发现一个有趣的东西(与上面内容无关 /捂脸): Let's Build a Simple Database - Writing a sqlite clone from scratch in C

posted @ 2020-06-24 17:08  菁芜  阅读(330)  评论(0编辑  收藏  举报