B+树并发控制机制

参考:
B+树并发控制机制的前世今生
视频:索引并发

B+树的并发控制机制

基础的并发机制:它采用了两种粒度的锁:
(1)index粒度的S/X锁;
(2)page粒度的S/X锁(本文等同于树节点粒度)。
前者被用来控制对树结构访问及修改操作的冲突,后者被用来控制对数据页访问及修改操作的冲突。

index粒度的S/X锁

索引粒度的S/X锁:对整棵树进行加锁。

/* Algorithm1. 读操作 */
1.   SL(index)
2.   Travel down to the leaf node
3.   SL(leaf)
4.   SU(index)
5.   Read the leaf node
6.   SU(leaf)

读操作在访问树结构的过程中对B+树加的是S锁,所以其它读操作可以并行访问树结构,减少了读-读操作之间的并发冲突。

/* Algorithm2. 悲观写操作 */
1.   XL(index)
2.   Travel down to the leaf node
3.   XL(leaf)   /* lock prev/curr/next leaves */
4.   Modify the tree structure 
5.   XU(index)  
6.   Modify the leaf node 
7.   XU(leaf)

因为写操作可能会修改整个树结构,所以需要避免两个写操作同时访问B+树。悲观写操作通过索引粒度的互斥锁避免这个问题。但悲观写操作在访问树结构的过程中对B+树加的是X锁,所以它会堵塞其它的读/写操作,这在高并发场景下会导致糟糕的多线程扩展性。

/* Algorithm3. 乐观写操作 */
1.   SL(index)
2.   Travel down to the leaf node
3.   XL(leaf)
4.   SU(index)
5.   Modify the leaf node
6.   XU(leaf)

因为每一个树节点页可以容纳大量的键值对信息,所以B+树的写操作在多数情况下并不会触发split/merge等修改树结构的操作。乐观思想假设大部分写操作并不会修改树结构。在访问树结构过程中持有树结构的S锁,从而支持其它读/乐观写操作同时访问树结构,而写操作对叶节点持有X锁。

B+树往往优先执行乐观写操作,只有乐观写操作失败才会执行悲观写操作,从而减少了操作之间的冲突和堵塞。不管是悲观写操作还是乐观写操作,它都通过索引粒度或者页粒度的锁避免相互之间修改相同的数据。

即使其它读写操作访问的是树结构的不同分支,在实际执行过程中不会产生相互间的影响,但是悲观写操作依然会堵塞其它所有读/写操作,直到树结构修改完成,这导致了过高的堵塞开销。

考虑只锁住B+树中被修改的分支,而不是锁住整个树结构?

page粒度的S/X锁

树节点粒度的S/X锁:只对修改的分支加锁。读操作在持有子节点的锁后才释放父节点的锁。

/* Algorithm4. 读操作 */
1.   current <= root
2.   SL(current) 
3.   While current is not leaf do {
4.     SL(current->son)
5.     SU(current)
6.     current <= current->son
7.   }
8.   Read the leaf node 
9.   SU(current)

读操作先获得子节点的S锁,再释放父节点的S锁,这个过程反复执行直到找到某个叶节点。因为读操作在持有子节点的锁后才释放父节点的锁,所以不会读到一个正在修改的树节点,不会在定位到某个子节点后子节点的键值对被移动到其它节点。

/* Algorithm5. 写操作 */
1.   current <= root
2.   XL(current) //写操作同样从根节点出发,首先持有根节点的X锁
3.   While current is not leaf do {
4.      XL(current->son)
5.      current <= current->son//写操作先获得子节点的X锁,
6.      If current is safe do {//然后判断子节点是否是一个安全节点
7.         /* Unlocked ancestors on stack. */
8.         XU(locked ancestors)//子节点是安全节点,写操作立即释放祖先节点的X锁
9.      }     
10.  }
11.  /* Already lock the modified branch. */
12.  Modify the leaf and upper nodes 
13.  XU(current) and XU(locked ancestors) 

写操作判断子节点安全才释放父节点的X锁,反复执行直到找到某个叶节点。当到达了叶节点后,写操作就已经持有了修改分支上所有树节点的X锁,从而避免其它读/写操作访问该分支。

SX锁

写操作在到达被修改分支之前,对途径的树节点加的是X锁,这在一定程度上阻塞了其它操作访问对应的树节点。当这个写操作需要频繁将树节点从磁盘读取到内存产生较高的IO延迟时,这个堵塞开销会更高。

一种位于S锁与X锁之间的SX锁,它可以堵塞其它的SX/X加锁操作(写操作),但可以允许S加锁操作(读操作),并且当它确定要修改该节点时可升级为X锁堵塞其它读写操作。

/* Algorithm6. 写操作 */
1.   current <= root
2.   SXL(current)
3.   While current is not leaf do {
4.      SXL(current->son)
5.      current <= current->son
6.      If current is safe do {
7.          /* Unocked ancestors on stack. */
8.          SXU(locked ancestors)
9.      }     
10. }
11. XL(modified nodes) /* SX->X, top-to-down*/
12. /* Already lock the modified branch. */
13. Modify the leaf and upper nodes 
13. XU(current) and XU(locked ancestors)

在写操作将影响分支上的锁升级为X锁前,所有读操作都可以访问被这个写操作访问过的非叶节点,从而减少了线程之间的冲突。由于SX锁的存在,不会出现多个写操作修改同一个分支的情况。

减少锁的使用

前文中的并发控制机制在很大程度上减少了线程间的冲突,但是依然存在一个问题:不论读/写操作,它们在访问一个树节点前都需要对树节点加S/SX/X锁。频繁加锁操作在多核处理器上会产生Coherence Cache Miss过高的问题。

前文中所述的并发机制,往往采用自顶向下的加锁策略,在安全地获取到子节点的锁后释放父节点的锁。然而我们很容易发现,这种加锁方式依然是十分悲观的:大部分获取到的锁其实是无意义的,尤其在树的上层,因为离根节点越近的树节点被更新的概率越低。因此,如果存在一种自底向上加锁的策略,只有在树节点分裂或者合并或者删除的情况下向上加锁,只对被修改的树节点加锁,就可以在很大程度上减少加锁的范围和频率,从而提高B+树的多线程扩展性。为了实现这个目标,我们首先需要支持在不持有锁的状态下从根节点访问到叶节点的功能。

Blink树,对后世影响深远的多线程B+树

Blink树假设访问树节点的读写操作是原子性的,读操作不会读到写操作修改到一半的状态,但写操作之间修改同一份数据时会出现冲突。

/* Algorithm7. 读操作 */
1.   current <= root
2.   While current is not leaf do {
3.      current <= scanNode(current, v)
4.      current <= current->son
5.   }
6.   /* Keep move right if necessary. */
7.   /* Deal with the leaf node. */
/*  scanNode函数 */
8.  Func scanNode(node* t, int v) {
9.     If t->next->key[0] <= v do 
10.       t <= scanNode(t->next, v)
11.   return t;
12. }

读操作从根节点出发,遍历整个树结构,直到找到某个叶子节点(step1-5),在这个过程中,读操作并不持有锁。特殊之处在于在每到达一个子节点后,它都会调用scanNode函数,这个函数就是Blink树的精髓所在。因为读操作在遍历树结构的过程中不持有锁,这导致它访问的某个树节点可能被其它写操作所分裂或者删除。

Blink树提出为每一个树节点配置一个右指针,这个右指针为读操作提供了另一种方式去访问子节点的右兄弟节点。Blink树规定树的分裂操作顺序必然是从左至右,因此目标键值对只有可能被分裂到子节点的右兄弟节点。

scanNode函数:读操作会判断子节点的右兄弟节点的最小值是否大于它正在查找的目标键值,如果不是说明目标键值对在右兄弟节点或者更右边的节点,指针就会往右走,直到找到某个右兄弟节点的最小值大于目标键值。

当发生删除操作时,它采用index粒度的X锁,堵塞其它读/写操作,避免了dangling pointer错误的发生。

/* Algorithm8. 写操作 */
1.   current <= root                                  
2.   While current is not leaf do {             
3.      current <= scannode(current, v)     
4.      stack <= current                           
5.      current <= current->son                 
6.   }                                                          
7.   XL(current)   /* lock the current leaf */ 
8.   moveRight(current)                             
9.   DoInsertion:                                        
10.  If current is safe do                                       
11.    insert(current) and XU(current)              
12.  else {
13.    allocate(next)
14.    shift(next) + link(next)
15.    modify(current)
16.    oldnode <= current
17.    current <= pop(stack)
18.    XL(current)
19.    moveRight(current) 
20.    XU(oldnode)
21.    goto DoInsertion; 
22. }

写操作使用和读操作类似的方式定位到目标叶节点current并加锁(step1-8)。为了支持自底向上加锁,写操作遍历过程中将访问到的树节点压入栈stack中。如果叶节点是安全节点,直接插入后释放锁就可以了(step10-11)。如果叶节点不是安全节点,就分配一个新的next节点,将叶节点的数据移动到next节点,修改current节点并将右指针指向next节点(step13-15)。然后,写操作从栈中弹出上一层的父节点并加锁(step16-18)。由于父节点也可能被分裂,所以也需要通过moveRight函数移动到正确的上一层节点(step19),然后重复上述的DoInsertion过程。moveRight与scanNode相似,主要的区别在于前者是在加锁状态下向右走,拿到右节点的锁后可释放当前结点的锁。写操作通过树节点粒度的锁,避免了多个写操作同时修改同一个树节点。

虽然Blink树有效减少了加锁频率,但是它依然存在两个问题:1. 不实际的假设:读写树节点的操作是原子性的;2. 删除操作竟然需要锁住整个索引结构,效率太差了。

OLFIT树,版本号你值得拥有

OLFIT树,在Blink树基础上引入了版本号的概念。

/* Algorithm9. 树节点的写操作 */
1.   XL(current)
2.   Update the node content
3.   INCREASE(version)
4.   XU(current)
/* Algorithm10. 树节点的读操作 */
1.   RECORD(version)
2.   Read the node content
3.   If node is lock, go to step1
4.   If version differs, go to step1

Algorithm 9-10显示版本号相关的具体操作。

Algorithm 9显示写操作在每个树节点上的执行过程:它首先锁住这个节点(step1),接着更新这个节点的内容(step2),然后递增树节点的版本号(step3),最后释放这个节点的锁(step4)。因为读操作在读取某个树节点时树节点可能被修改/分裂/删除,写操作通过锁告知读操作这个树节点正在被修改,通过版本号告知读操作这个树节点已经被修改。

Algorithm 10显示读操作在每个树结点上的执行过程:它首先记录这个树节点的版本号(step1),再读取这个树节点的内容(step2),在读操作结束后再次读取节点的锁和版本号。如果节点的锁或者版本号发生变化,它判断自己读取的树节点可能处于不一致的中间状态,因此从(step1)重新开始执行。

/* Algorithm11. OLFIT树的读操作 */
1.   current <= root
2.   While current is not leaf do {
3.      RECORD(version)
4.      next <= scanNode(current, v)
5.      If version/lock is not modified do
6.          current <= next            
7.   }
8.   /* Keep move right if necessary. */
9.   /* Deal with the leaf node. */

Algorithm 11显示了读操作的完整过程。(step1-7)的过程与Blink树相似,区别在于OLFIT树在访问每个节点时根据版本号/锁的状态判断自己是否读到正确的数据,从而避免读到修改到一半的树节点。

对于删除操作,为了避免读操作正在访问的节点被其它写操作删除,OLFIT树可以采用epoch-based reclamation机制。原理简单来说就是将删除操作分为逻辑删除和物理删除两个步骤,只有在确保一个树节点没有被任何操作访问时才可以回收这个树节点的物理空间。

Masstree,B+树优化机制的集大成者

Masstree融合了大量B+树的优化策略,包括单线程场景下和多线程场景下的。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验一 Linux系统的安装及用户界面的使用 一.实验目的 1. 了解Linux系统的安装、熟悉系统的启动过程和使用环境。 2. 掌握Linux环境下vi编辑器的使用方法。 3. 掌握Linux系统中编辑、编译、调试、运行一个C语言程序的全过程。 二.实验内容 1、实验要求 1. 在VMWare虚拟机环境或真实物理机器上,安装一个Linux操作系统。 2. 体验Linux操作系统中X Windows系统的使用。 3. 尝试Linux系统键盘命令的使用,并熟练掌握常用的基本命令。 4. 掌握命令行方式下vi编辑器的使用。 5. 编写一段C程序,使用系统调用fork()创建两个子进程。各进程显示不同的信息,如父进程显示字符“a”,子进程分别显示字符“b”和“c”。多次运行观察显示结果,并分析产生这种执行效果的原因。 实验二 Linux进程控制 一.实验目的 1. 掌握进程的概念,明确进程和程序的区别。 2. 认识和了解并发执行的实质。 二.实验内容 1、实验要求 1. 编写一段程序,使用系统调用fork()创建两个子进程。各进程显示不同的信息,如父进程显示字符“a”,子进程分别显示字符“b”和“c”。多次运行观察显示结果,并分析产生这种执行效果的原因。 2. 修改上面编写的程序,将每个进程的输出由单个字符改为循环输出一句话,如父进程显示:“parent:”加上进程ID,子进程分别显示:“Child1:”(或“Child2:”)加上自己的进程ID。再观察程序执行时屏幕上出现的现象,并分析原因。 3.一个父进程创建一个子进程,子进程通过exec系统调用执行另一个文件。各自的代码中显示不同的信息,观察其运行结果,分析两个进程并发执行的效果。 4. 编写程序创建如图所示的进程树,在每个进程中显示当前进程ID和父进程ID。 实验三 Linux进程间通信 一.实验目的 (1) 分析进程争用临界资源的现象,学习解决进程互斥的方法; (2) 学习如何利用进程的“软中断”、管道机制进行进程间的通信,并加深对上述通信机制的理解; (3) 了解系统调用pipe( )、msgget( )、msgsnd( )、msgrcv( )、msgctl( )、shmget( )、shmat( )、shmdt( )、shmctl( )的功能和实现过程,利用共享存储区机制进行进程间通信。 二、实验内容 1、实验要求 (1) 进程的控制 修改已编制的程序,将每个进程输出一个字符修改为每个进程输出一句话,再观察程序执行时屏幕上出现的现象,并分析出现问题的原因,进一步理解各个进程争夺临界资源的情况。 如果在程序中使用系统调用locking( )来给每一个进程加锁,可以实现进程之间的互斥,试观察并分析出现的现象。 (2) 进程的软中断通讯 编制一段程序,实现进程的软中断通讯:使用系统调用fork( )创建两个子进程;再使用系统调用signal( )让父进程捕捉键盘上来的中断信号(即按Del键);在捕捉到中断信号后,父进程用系统调用kill( )向两个子进程发信号;子进程捕捉到信号后分别输出下列信息后终止: Child process1 is killed by parent! Child process2 is killed by parent! 父进程等待两个子进程都终止以后,输出如下信息后终止: Parent process in killed! (3) 进程的管道通讯 编制一段程序,实现进程的管道通讯:使用系统调用pipe( )建立一条管道线;两个子进程分别循环向这条管道写一句话: Child 1 is sending a message! Child 2 is sending a message! 而父进程则循环从管道中读出信息,显示在屏幕上。 实验报告 内含源代码
作者: Hector Garcia-Molina, Jeffrey D. Ullman, Jennifer Widom 本书是斯坦福大学计算机科学专业数据库系列课程第二门课的教科书。书中对数据库系统实现原理进行了深入阐述,并具体讨论了数据库管理系统的三个主要成分—存储管理器、查询处理器和事务管理器的实现技术。书中还对信息集成的最新技术,例如数据仓库、OLAP、数据挖掘、Mediator、数据立方体系统等进行了介绍。本书适合于作为高等院校计算机专业研究生的教材或本科生的教学参考书,也适合作为从事相关研究或开发工作的专业技术人员的高级参考资料 译者序 前言 第1章 DBMS实现概述 1.1 Megatr on 2000数据库系统介绍 1.1.1 Megatr on 2000实现细节 1.1.2 Megatron 2000如何执行查询 1.1.3 Megatron 2000有什么问题 1.2 数据库管理系统概述 1.2.1 数据定义语言命令 1.2.2 查询处理概述 1.2.3 主存缓冲区和缓冲区管理器 1.2.4 事务处理 1.2.5 查询处理器 1.3 本书梗概 1.3.1 预备知识 1.3.2 存储管理概述 1.3.3 查询处理概述 1.3.4 事务处理概述 1.3.5 信息集成概述 1.4 数据库模型和语言回顾 1.4.1 关系模型回顾 1.4.2 SQL回顾 1.4.3 关系的和面向对象的数据 1.5 小结 1.6 参考文献 第2章 数据存储 2.1 存储器层次 2.1.1 高速缓冲存储器 2.1.2 主存储器 2.1.3 虚拟存储器 2.1.4 第二级存储器 2.1.5 第三级存储器 2.1.6 易失和非易失存储器 习题 2.2 磁盘 2.2.1 磁盘结构 2.2.2 磁盘控制器 2.2.3 磁盘存储特性 2.2.4 磁盘访问特性 2.2.5 块的写入 2.2.6 块的修改 习题 2.3 辅助存储器的有效使用 2.3.1 计算的I/O模型 2.3.2 辅助存储器中的数据排序 2.3.3 归并排序 2.3.4 两阶段多路归并排序 2.3.5 扩展多路归并以排序更大的关系 习题 2.4 改善辅助存储器的访问时间 2.4.1 按柱面组织数据 2.4.2 使用多个磁盘 2.4.3 磁盘镜像 2.4.4 磁盘调度和电梯算法 2.4.5 预取和大规模缓冲 2.4.6 各种策略及其优缺点 习题 2.5 磁盘故障 2.5.1间断性故障 2.5.2 校验和 2.5.3 稳定存储 2.5.4 稳定存储的错误处理能力 习题 2.6 从磁盘崩溃中恢复 2.6.1 磁盘的故障模型 2.6.2 作为冗余技术的镜像 2.6.3 奇偶块 2.6.4 一种改进:RAID 2.6.5 多个盘崩溃时的处理 习题 2.7 小结 2.8 参考文献 第3章 数据元素的表示 3.1 数据元素和字段 3.1.1 关系型数据库元素的表示 3.1.2 对象的表示 3.1.3 数据元素的表示 3.2 记录 3.2.1 定长记录的构造 3.2.2 记录首部 3.2.3 定长记录在块中的放置 习题 3.3 块和记录地址的表示 3.3.1 客户机-服务器系统 3.3.2 逻辑地址和结构地址 3.3.3 指针混写 3.3.4 块返回磁盘 3.3.5 被固定的记录和块 习题 3.4 变长数据和记录 3.4.1 具有变长字段的记录 3.4.2 具有重复字段的记录 3.4.3 变格式的记录 3.4.4 不能装入一个块中的记录 3.4.5 BLOBS 习题 3.5 记录的修改 3.5.1 插入 3.5.2 删除 3.5.3 修改 习题 3.6 小结 3.7 参考文献 第4章 索引结构 4.1 顺序文件上的索引 4.1.1 顺序文件 4.1.2 稠密索引 4.1.3 稀疏索引 4.1.4 多级索引 4.1.5 重复键的索引 4.1.6 数据修改期间的索引维护 习题 4.2 辅助索引 4.2.1 辅助索引的设计 4.2.2 辅助索引的应用 4.2.3 辅助索引中的间接 4.2.4 文档检索和倒排索引 习题 4.3 B树 4.3.1 B树的结构 4.3.2 B树的应用 4.3.3 B树中的查找 4.3.4 范围查询 4.3.5 B树的插入 4.3.6 B树的删除 4.3.7 B树的效率 习题 4.4 散列表 4.4.1 辅存散列表 4.4.2 散列表的插入 4.4.3 散列表的删除 4.4.4 散列表索引的效率 4.4.5 可扩展散列表 4.4.6 可扩展散列表的插入 4.4.7 线性散列表 4.4.8 线性散列表的插入 习题 4.5 小结 4.6 参考文献 第5章 多维索引 5.1 需要多维的应用 5.1.1 地理信息系统 5.1.2 数据立方体 5.1.3 SQL多维查询 5.1.4 使用传统索引执行范围查询 5.1.5 利用传统索引执行最邻近查询 5.1.6 传统索引的其他限制 5.1.7 多维索引结构综述 习题 5.2 多维数据的类散列结构 5.2.1 网格文件 5.2.2 网格文件的查找 5.2.3 网格文件的插入 5.2.4 网格文件的性能 5.2.5 分段散列函数 5.2.6 网格文件和分段散列的比较 习题 5.3 多维数据的类树结构 5.3.1 多键索引 5.3.2 多键索引的性能 5.3.3 kd树 5.3.4 kd树的操作 5.3.5 使kd树适合辅存 5.3.6 四叉树 5.3.7 R树 5.3.8 R树的操作 习题 5.4 位图索引 5.4.1 位图索引的诱因 5.4.2 压缩位图 5.4.3 游程长度编码位向量的操作 5.4.4 位图索引的管理 习题 5.5 小结 5.6 参考文献 第6章 查询执行 6.1 一种查询代数 6.1.1 并、交和差 6.1.2 选择操作符 6.1.3 投影操作符 6.1.4 关系的积 6.1.5 连接 6.1.6 消除重复 6.1.7 分组和聚集 6.1.8 排序操作符 6.1.9 表达式树 习题 6.2 物理查询计划操作符介绍 6.2.1 扫描表 6.2.2 扫描表时的排序 6.2.3 物理操作符计算模型 6.2.4 衡量代价的参数 6.2.5 扫描操作符的I/O 代价 6.2.6 实现物理操作符的迭代器 6.3 数据库操作的一趟算法 6.3.1 一次多元组操作的一趟算法 6.3.2 全关系的一元操作的一趟算法 6.3.3 二元操作的一趟算法 习题 6.4 嵌套循环连接 6.4.1 基于元组的嵌套循环连接 6.4.2 基于元组的嵌套循环连接的迭代器 6.4.3 基于块的嵌套循环连接算法 6.4.4 嵌套循环连接的分析 6.4.5 迄今为止的算法的小结 习题 6.5 基于排序的两趟算法 6.5.1 利用排序去除重复 6.5.2 利用排序进行分组和聚集 6.5.3 基于排序的并算法 6.5.4 基于排序的交和差算法 6.5.5 基于排序的一个简单的连接算法 6.5.6 简单排序连接的分析 6.5.7 一种更有效的基于排序的连接 6.5.8 基于排序的算法小结 习题 6.6 基于散列的两趟算法 6.6.1 通过散列划分关系 6.6.2 基于散列的消除重复算法 6.6.3 基于散列的分组和聚集算法 6.6.4 基于散列的并、交、差算法 6.6.5 散列连接算法 6.6.6 节省一些磁盘I/O 6.6.7 基于散列的算法小结 习题 6.7 基于索引的算法 6.7.1 聚簇和非聚簇索引 6.7.2 基于索引的选择 6.7.3 使用索引的连接 6.7.4 使用有排序索引的连接 习题 6.8 缓冲区管理 6.8.1 缓冲区管理结构 6.8.2 缓冲区管理策略 6.8.3 物理操作符选择和缓冲区管理的关系 习题 6.9 使用超过两趟的算法 6.9.1 基于排序的多趟算法 6.9.2 基于排序的多趟算法的性能 6.9.3 基于散列的多趟算法 6.9.4 基于散列的多趟算法的性能 习题 6.10 关系操作的并行算法 6.10.1 并行模型 6.10.2 一次一个元组的并行操作 6.10.3 全关系操作的并行算法 6.10.4 并行算法的性能 习题 6.10 小结 6.11 参考文献 第7章 查询编译器 7.1 语法分析 7.1.1 语法分析与语法分析树 7.1.2 SQL的一个简单子集的语法 7.1.3 预处理器 习题 7.2 用于改进查询计划的代数定律 7.2.1 交换律与结合律 7.2.2 涉及选择的定律 7.2.3 下推选择 7.2.4 涉及投影的定律 7.2.5 有关连接与积的定律 7.2.6 有关重复消除的定律 7.2.7 涉及分组与聚集的定律 习题 7.3 从语法分析树到逻辑查询计划 7.3.1 转换成关系代数 7.3.2 从条件中去除子查询 7.3.3 逻辑查询计划的改进 7.3.4 结合/分配运算符的分组 习题 7.4 操作代价的估计 7.4.1 中间关系大小的估计 7.4.2 投影大小的估计 7.4.3 选择大小的估计 7.4.4 连接大小的估计 7.4.5 多连接属性的自然连接 7.4.6 多个关系的连接 7.4.7 其他操作的大小估计 习题 7.5 基于代价的计划选择介绍 7.5.1 大小参数估计值的获取 7.5.2 统计量的增量计算 7.5.3 减少逻辑查询计划代价的启发式 7.5.4 枚举物理计划的方法 习题 7.6 连接顺序的选择 7.6.1 连接的左右变元的意义 7.6.2 连接树 7.6.3 左深连接树 7.6.4 通过动态编程来选择连接顺序和分组 7.6.5 带有更具体的代价函数的动态编程 7.6.6 选择连接顺序的贪婪算法 习题 7.7 物理查询计划选择的完成 7.7.1 选取选择方法 7.7.2 选取连接方法 7.7.3 流水线操作与物化 7.7.4 一元流水线操作 7.7.5 二元流水线操作 7.7.6 物理查询计划的符号 7.7.7 物理操作的顺序 习题 7.8 小结 7.9 参考文献 第8章 系统故障对策 8.1 可回复操作的问题和模型 8.1.1 故障模式 8.1.2 关于事务的进一步讨论 8.1.3 事务的正确执行 8.1.4 事务的原语操作 习题 8.2 undo日志 8.2.1 日志记录 8.2.2 undo日志规则 8.2.3 使用undo日志的恢复 8.2.4 检查点 8.2.5 非静止检查点 习题 8.3 redo日志 8.3.1 redo日志规则 8.3.2 使用redo日志的恢复 8.3.3 redo日志的检查点 8.3.4 使用带检查点的redo日志的恢复 习题 8.4 undo/redo日志 8.4.1 undo/redo规则 8.4.2 使用undo/redo日志的恢复 8.4.3 undo/redo日志的检查点 习题 8.5 防备介质故障 8.5.1 备份 8.5.2 非静止转储 8.5.3 使用备份和日志的恢复 习题 8.6 小结 8.7 参考文献 第9章 并发控制 9.1 串行调度和可串行化调度 9.1.1 调度 9.1.2 串行调度 9.1.3 可串行化调度 9.1.4 事务语义的影响 9.1.5 事务和调度的一种记法 习题 9.2 冲突可串行性 9.2.1 冲突 9.2.2 优先图及冲突可串行性判断 9.2.3 优先图测试发挥作用的原因 习题 9.3 使用锁的可串行性实现 9.3.1 锁 9.3.2 封锁调度器 9.3.3 两阶段封锁 9.3.4 两阶段封锁发挥作用的原因 习题 9.4 用多种锁方式的封锁系统 9.4.1 共享锁与排他锁 9.4.2 相容性矩阵 9.4.3 锁的升级 9.4.4 更新锁 9.4.5 增量锁 习题 9.5 封锁调度器的一种体系结构 9.5.1 插入锁动作的调度器 9.5.2 锁表 习题 9.6 数据库元素层次的管理 9.6.1 多粒度的锁 9.6.2 警示锁 9.6.3 幻象与插入的正确处理 习题 9.7 树协议 9.7.1 基于树的封锁的动机 9.7.2 访问树结构数据的规则 9.7.3 树协议发挥作用的原因 习题 9.8 使用时间戳的并发控制 9.8.1 时间戳 9.8.2 物理上不可实现的行为 9.8.3 脏数据的问题 9.8.4 基于时间戳调度的规则 9.8.5 多版本时间戳 9.8.6 时间戳与封锁 习题 9.9 使用有效性确认的并发控制 9.9.1 基于有效性确认的调度器的结构 9.9.2 有效性确认规则 9.9.3 三种并发控制机制的比较 习题 9.10 小结 9.11 参考文献 第10章 再论事务管理 10.1 读未提交数据的事务 10.1.1 脏数据问题 10.1.2 级联回滚 10.1.3 回滚的管理 10.1.4 成组提交 10.1.5 逻辑日志 习题 10.2 视图可串行性 10.2.1 视图等价性 10.2.2 多重图与视图可串行性的判断 10.2.3 视图可串行性的判断 习题 10.3 死锁处理 10.3.1 超时死锁检测 10.3.2 等待图 10.3.3 通过元素排序预防死锁 10.3.4 时间戳死锁检测 10.3.5 死锁管理方法的比较 习题 10.4 分布式数据库 10.4.1 数据的分布 10.4.2 分布式事务 10.4.3 数据复制 10.4.4 分布式查询优化 习题 10.5 分布式提交 10.5.1 分布式原子性的支持 10.5.2 两阶段提交 10.5.3 分布式事务的恢复 习题 10.6 分布式封锁 10.6.1 集中封锁系统 10.6.2 分布式封锁算法的代价模型 10.6.3 封锁多副本的元素 10.6.4 主副本封锁 10.6.5 局部锁构成的全局锁 习题 10.7 长事务 10.7.1 长事务的问题 10.7.2 saga(系列记载) 10.7.3 补偿事务 10.7.4 补偿事务发挥作用的原因 习题 10.8 小结 10.9 参考文献 第11章 信息集成 11.1 信息集成的方式 11.1.1 信息集成的问题 11.1.2 联邦数据库系统 11.1.3 数据仓库 11.1.4 Mediator 习题 11.2 基于Mediator系统的包装器 11.2.1 查询模式的模板 11.2.2 包装器生成器 11.2.3 过滤器 11.2.4 其他在包装器上进行的操作 习题 11.3 联机分析处理 11.3.1 OLAP应用 11.3.2 OLAP数据的多维视图 11.3.3 星型模式 11.3.4 切片和切块 习题 11.4 数据立方体 11.4.1 立方体操作符 11.4.2 通过物化视图实现立方体 11.4.3 视图的格 习题 11.5 数据挖掘 11.5.1 数据挖掘的应用 11.5.2 关联规则的挖掘 11.5.3 A-Priori算法 11.6 小结 11.7 参考文献
面试高级开发的期间整理的面试题目,记录我面试遇到过的数据库题目以及答案,比如说mvvc还有数据库调优,索引。 目录如下 数据库 数据库事务隔离级别; 事务的并发导致的问题; 数据库事务设置不同的隔离级别会导致的不同并发问题 当前读和快照读的意义; mvcc:多版本控制: 指的是一种提高并发的技术,其解决问题是什么; MVCC实现过程; mvcc三大组件; RC、RR级别下的InnoDB快照读有什么不同:17 mysql面试题01.vep 描述一下mysql的乐观锁和悲观锁,以及mysql锁的种类; mysql如何做分库分表的; mysql描述一下mysql主从复制的机制的原理;mysql主从复制主要有几种模式 在mysql开启Binlog(为了其他非事务引擎复制所以引入binlog,比如说主从)后,如何保证binlog和InnoDB redo日志的一致性呢; binlog和redo log和区别; mysql涉及到的log有哪些; 阿里要求尽量少用join,为什么; 索引 索引分类 索引的数据结构; b树和b+树的区别 索引失效的情况: 数据库优化 查询计划的结果中看哪些关键数据; mysql 如何调优; 索引是建的越多越好吗; 在表查询中,一律不要使用 * 作为查询的字段列表,原因是什么; 描述一下InnoDB和MyISAM的区别; 当前读和快照读的意义; 13 mvcc:多版本控制: 指的是一种提高并发的技术,其解决问题是什么; 14 MVCC实现过程; 14 mvcc三大组件; 15 RC、RR级别下的InnoDB快照读有什么不同:17 mysql面试题01.vep 17 描述一下mysql的乐观锁和悲观锁,以及mysql锁的种类; 17 mysql如何做分库分表的; 18 mysql描述一下mysql主从复制的机制的原理;mysql主从复制主要有几种模式 19 在mysql开启Binlog(为了其他非事务引擎复制所以引入binlog,比如说主从)后,如何保证binlog和InnoDB redo日志的一致性呢; 20 binlog和redo log和区别; 22 mysql涉及到的log有哪些; 23 阿里要求尽量少用join,为什么; 23 索引 24 索引分类 24 索引的数据结构; 24 b树和b+树的区别 26 索引失效的情况: 26 数据库优化 27 查询计划的结果中看哪些关键数据; 27 mysql 如何调优; 27 索引是建的越多越好吗; 29 在表查询中,一律不要使用 * 作为查询的字段列表,原因是什么; 29 描述一下InnoDB和MyISAM的区别; 29
今年9月份参加计算机二级考试的同学请转载 作者:天使~之戀 已被分享1次 评论(0) 复制链接 分享 转载 删除 下面的138道题目,在二级考试中命中率极高。 一、选择题 (1) 下面叙述正确的是(C) A. 算法的执行效率与数据的存储结构无关 B. 算法的空间复杂度是指算法程序中指令(或语句)的条数 C. 算法的有穷性是指算法必须能在执行有限个步骤之后终止 D. 以上三种描述都不对 (2) 以下数据结构中不属于线性数据结构的是(C) A. 队列 B. 线性表 C. 二叉树 D. 栈 (3) 在一棵二叉树上第5层的结点数最多是(B) 注:由公式2k-1得 A. 8 B. 16 C. 32 D. 15 (4) 下面描述中,符合结构化程序设计风格的是(A) A. 使用顺序、选择和重复(循环)三种基本控制结构表示程序的控制逻辑 B. 模块只有一个入口,可以有多个出口 C. 注重提高程序的执行效率 D. 不使用goto语句 (5) 下面概念中,不属于面向对象方法的是 (D) 注:P55-58 A. 对象 B. 继承 C. 类 D. 过程调用 (6) 在结构化方法中,用数据流程图(DFD)作为描述工具的软件开发阶段是(B) A. 可行性分析 B. 需求分析 C. 详细设计 D. 程序编码 (7) 在软件开发中,下面任务不属于设计阶段的是(D) A. 数据结构设计 B. 给出系统模块结构 C. 定义模块算法 D. 定义需求并建立系统模型 (8) 数据库系统的核心是(B) A. 数据模型 B. 数据库管理系统 C. 软件工具 D. 数据库 (9) 下列叙述中正确的是(C) A.数据库是一个独立的系统,不需要操作系统的支持 B.数据库设计是指设计数据库管理系统C.数据库技术的根本目标是要解决数据共享的问题 D.数据库系统中,数据的物理结构必须与逻辑结构一致 (10) 下列模式中,能够给出数据库物理存储结构与物理存取方法的是(A) 注:P108 A. 内模式 B. 外模式 C. 概念模式 D. 逻辑模式 (11) 算法的时间复杂度是指(C) A. 执行算法程序所需要的时间 B. 算法程序的长度 C. 算法执行过程中所需要的基本运算次数 D. 算法程序中的指令条数 (12) 算法的空间复杂度是指(D) A. 算法程序的长度 B. 算法程序中的指令条数 C. 算法程序所占的存储空间 D. 算法执行过程中所需要的存储空间 (13) 设一棵完全二叉树共有699个结点,则在该二叉树中的叶子结点数为(B) 注:利用公式n=n0+n1+n2、n0=n2+1和完全二叉数的特点可求出 A. 349 B. 350 C. 255 D. 351 (14) 结构化程序设计主要强调的是(B) A.程序的规模 B.程序的易读性 C.程序的执行效率 D.程序的可移植性 (15) 在软件生命周期中,能准确地确定软件系统必须做什么和必须具备哪些功能的阶段是(D) 注:即第一个阶段 A. 概要设计 B. 详细设计 C. 可行性分析 D. 需求分析 (16) 数据流图用于抽象描述一个软件的逻辑模型,数据流图由一些特定的图符构成。下列图符名标识的图符不属于数据流图合法图符的是(A) 注:P67 A. 控制流 B. 加工 C. 数据存储 D. 源和潭 (17) 软件需求分析阶段的工作,可以分为四个方面:需求获取、需求分析、编写需求规格说明书以及(B) 注:P66 A. 阶段性报告 B. 需求评审 C. 总结 D. 都不正确 (18) 下述关于数据库系统的叙述中正确的是(A) A. 数据库系统减少了数据冗余 B. 数据库系统避免了一切冗余 C. 数据库系统中数据的一致性是指数据类型的一致 D. 数据库系统比文件系统能管理更多的数据 (19) 关系表中的每一横行称为一个(A) A. 元组 B. 字段 C. 属性 D. 码 (20) 数据库设计包括两个方面的设计内容,它们是(A) A. 概念设计和逻辑设计 B. 模式设计和内模式设计 C. 内模式设计和物理设计 D. 结构特性设计和行为特性设计 (21) 下列叙述中正确的是(A) A. 线性表是线性结构 B. 栈与队列是非线性结构 C. 线性链表是非线性结构 D. 二叉树是线性结构 (22) 下列关于栈的叙述中正确的是(D) A. 在栈中只能插入数据 B. 在栈中只能删除数据 C. 栈是先进先出的线性表 D. 栈是先进后出的线性表 (23) 下列关于队列的叙述中正确的是(C) A. 在队列中只能插入数据 B. 在队列中只能删除数据 C. 队列是先进先出的线性表 D. 队列是先进后出的线性表 (24) 对建立良好的程序设计风格,下面描述正确的是(A) 注:P48 A. 程序应简单、清晰、可读性好 B. 符号名的命名要符合语法 C. 充
b树的并发控制是指在多个并发操作同时对b树进行读写时,需要确保数据的一致性和并发操作的正确性。为了实现并发控制,可以采用以下几种方法: 1. 锁机制:通过加锁来保护b树节点的访问,确保同一时间只有一个操作可以对节点进行读写。常见的锁包括读锁和写锁,读锁可以同时被多个操作获取,而写锁则只能被一个操作获取。 2. 乐观并发控制:在并发读操作中,使用乐观并发控制算法来避免加锁带来的开销。乐观并发控制假设冲突的情况较少,允许多个操作同时读取数据,但在写操作时需要检测是否有其他操作对数据进行了修改。 3. 事务机制:通过事务的方式来管理并发操作,保证操作的原子性、一致性、隔离性和持久性。事务可以通过锁机制或者乐观并发控制来实现,并且可以使用日志记录来恢复和回滚操作。 b树的并发控制是数据库系统中的重要问题,不同的数据库系统可能采用不同的并发控制方法来保证数据的一致性和并发操作的正确性。详细的实现细节和算法原理可以参考引用和中的文章。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [MySQL · 引擎特性 · B+树并发控制机制的前世今生](https://blog.csdn.net/jc_benben/article/details/83110868)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [一文彻底搞懂MySQL基础:B树和B+树的区别](https://blog.csdn.net/a519640026/article/details/106940115)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值