第四讲 Buffer Pool

问题:DBMS 是如何管理其内存和磁盘之间来回移动数据的?

答案:在决定如何在磁盘中来回移动数据时,我们必须考虑两个关键方面:

空间控制【Spatial Control】:

  • 将页【pages】写在在磁盘的什么地方?
  • 以及如何布局它们,才可以使得我们最大化的利用串行IO,因为磁盘的顺序读取很快,但是随机读取很慢(因此我们的目标是使经常一起访问的页【pages】在磁盘上物理上尽可能靠近)。

时间控制【Temporal Control】:

  • 何时将页面读入内存,何时将其写入磁盘?
  • 目标是最大限度地减少因必须从磁盘读取数据而导致的停顿【stall】数量

解决方法是基于空间与时间的权衡~

面向磁盘的DBMS
面向磁盘的DBMS

注:为什么数据库不适用操作系统的虚拟内存,而要用Buffer Pool ?

因为我们对查询【query】想要做什么有更好的了解,因此我们可以做好准备,并从磁盘读取数据并将其写入内存,而因为操作系统只看到粒度级别的,比如在页【page】上的读【read】和写【write】,不了解实际查询【query】如何访问这些页面,毕竟操作系统对所有的页【pages】都一视同仁,而在数据库系统中,有的页【pages】对应的是所有,有的页【pages】对应的是数据,所以所有的页【page】不因该被视为是一样的。

1 Buffer Pool Manager

1.1 Buffer Pool organization

在高层次上,缓冲池【Buffer Pool】就是一个从操作系统分配得到的一个很大的内存区域【Memory Region】,并基于数据库的页大小(Postgres时8kb,Mysql是16kb),在逻辑上将它们分为固定大小的页。数据库将在它自己的地址空间中管理它,将用它来复制查询【query】所需从磁盘访问的页【page】,并将它们放入缓冲池【buffer pool】中(需要注意的是,内存中帧对应的页不需要像磁盘中那样连续)。

  • 内存区域【Memory Region】被组织为固定大小页【pages】的数组。
  • 该数组中的条目,也被称为帧【frame】,其大小等于页【page】的大小(至于为什么这么叫,因为别的术语都有其含义,比如page,slot,block)。
  • 当 DBMS 请求某个页【page】时,会将该页【page】的一个精确副本放入其中一个帧【frame】中,注意这里一般不涉及marshing或者序列化处理,它只是逐字节复制罢了。
  • 脏页会被缓冲,不会立即写入磁盘,后面我们会在讲持久化和恢复时重温。这里面涉及的时写通【write through】和写回的【write back】(有一个单独的日志文件,叫做预写日志【write ahead log】,它会跟踪我们所做的更改,我们会确保在脏页之前刷新到磁盘中)区别。


 

1.2 Buffer Pool Meta Data

页表【Page Table】(并非操作系统的页表)负责跟踪当前内存中的页【page】。你可以将页表想象为一个哈希Map,它将页ID【Page ID】映射到帧【frame】(其在缓冲池中的位置),如下所示:

当执行引擎请求访问某个页时,页表【page table】需要告知其在缓冲之中的位置,或者告知其不在缓冲池中。在这种情况下,我必须去我的页目录【page direction】找到那个页ID在磁盘上的位置,然后决定把它复制到哪个帧里。

需要注意的是,数据库是一个线程安全的应用,当多个线程同时访问一个页表中不存在的页面时,我们需要避免多线程对页表替换更新。因此,每当我们访问一个页面时,我们需要在页表【page table】中将他钉住【pin】,这特别像一个引用计数器【reference count】,该计数表明当前有多少活跃的查询正在访问该页。我们在页表【page table】中存储这个引用计数器,而不是在页【page】中,因此当某个页被驱逐出去时,我们也不需要额外再去存储其对应的引用计数器,它只会存在于内存中,也就是页表中。

同时页表【pge table】还为每个页【page】维护着附加元数据,这些元数据时负责记录在整个系统中如何使用这些页面:

  • dirty flag:告知有其他查询自加载到内存中后修改了该页【page】
  • pin/reference counter:跟踪需要此页面保留在内存中的工作线程的数量,因此当我们运行驱逐策略时,它不能被驱逐
  • access tracking information:记录谁正在访问过页面,最后一次页面访问的时间等等

所以,当我们访问某个不存在于缓冲池中的页【page】时,那么它需要将这个位置锁存【latch】起来,然后去后台检出该页【page】,更新页表,并释放锁存【latch】。

如果我要读它,我会把它钉住,即使我要给它写入的话,我也会把它钉住,但如果我只是读它,我不会保留任何额外的信息,一旦我完成了读取,我就可以把引脚扔掉,如果我在写它,我必须维护一个脏标志,表示我修改了这个东西,这是为了防止数据库系统把它写回磁盘时直接扔掉它,因为它知道当它访问时,其他查询修改了页面因此我们必须确保这些更改最终会回到磁盘。

1.3 Lock 和 Latch

锁【lock】:

  • 保护数据库的逻辑内容免受其他事务的影响。
  • 在事务期间内持有
  • 需要能够回滚更改

锁存器【latch】:

  • 护 DBMS 内部数据结构的关键部分免受其他线程的影响
  • 在操作期间持有
  • 不需要能够回滚更改

简单的讲,锁是用户空间下的,用户保护一个高级概念,比如元组【tuple】,表【table】,数据库【database】等,需要注意的是,锁是有死锁检查以及其他保护策略的,而锁存器是系统空间下,他是低级的原语,我们主要用它保护数据库系统内的临界区,主要是数据库开发使用,他本质上等同于mutext,而且它没有死锁检查以及其他保护策略。

1.3 页表和页目录

页目录【page dictionary】是从页ID【Page ID】到数据库文件中页位置【page location】的映射。 

  • 所有更改都必须记录在磁盘上,以便 DBMS 在重新启动时能够找到它


页表【page table】是从页ID【Page ID】到缓冲池的帧中该页副本的映射。 

  • 这是一个不需要存储在磁盘上的内存数据结构

2 分配策略

全局政策【Global Policy】:

  • 为所有活跃的查询做出决策

局部政策【Local Policy】:

  • 将帧分配给特定查询,而不考虑并发查询的行为
  • 但仍然需要支持共享页面

这两种策略各有优势,现代数据库是基本都是这两种策略的组合。

2.1 缓冲池优化
  • 多个缓冲池【Multi Buffer Pool】
  • 预取【Pre-Fetching】
  • 扫描共享【Scan-Sharing】
  • 缓冲池绕过【Buffer Pool Bypass】
2.1.1 多个缓冲池

DBMS 并不总是为整个系统提供一个缓冲池。

  • 多个缓冲池实例
  • 每个数据库一个缓冲池
  • 每种页类型一个缓冲池

将内存分区到多个池有助于减少锁存器【Latch】争用(因为锁存器是锁整个页表,所以他会成为性能瓶颈),并提高局部性。

方法1 Object ID --- SQL Server

在记录 id 【record ids】中嵌入对象标识符【object identifier】,然后维护从对象到特定缓冲池的映射。

方法2 Object ID --- MySQL 
  • 哈希页 ID 以选择要访问的缓冲池。

2.1.2 预取

DBMS还可以根据查询计划预取页面:

  • 顺序扫描 
  • 索引扫描

顺序扫描预取前(栗子里是游标分页):

顺序扫描预取后:

索引扫描预取前:

索引扫描预取前后:

根据查询计划,我们 需要页0,页1,而根据条件,我们需要检出页3,以及预取页5,而操作系统是无法做到的,因为页3和页5是不连续的。数据系统能知道这样的原因是,每一个页节点都有一对兄弟节点的指针,页3的兄弟节点是页5,因此数据库系统可以提前预取。

2.1.3 扫描共享(也叫同步扫描)

这里的基本思想是出现了一堆查询,它们想要访问同一个表,其中一个开始,它开始扫描页面,,我们可以识别出它们需要相同的数据,我们背靠背,我们的游标连接到它们的游标上,我们就可以同时读取相同的页。

扫描共享实际上处于访问方法【access method】最低的物理层。

如果一个查询想要扫描表,而另一个查询已经在执行此操作,则 DBMS 会将第二个查询的游标附加到现有游标上。
例子:

  • 完全支持 DB2、MSSQL、Teradata 和 Postgres,即对于不完全相同但读取相同页【page】的查询,这种完全扫描共享。
  • Oracle 仅支持相同查询的游标共享,简而言之,他是通过对查询座字符串hash,因此任何不一样,都会的导致无法命中。

栗子:

我们针对A表的val字段做加和【sum】,

它把游标【Cursor】移到页面上,开始读取它们,把它们取到缓冲池中,首先它得到第0页,它不在那里,所以它把它放到内存中,然后是第1页,以此类推。现在我们到第三页,我们还没有讨论过驱逐政策,但第0页是最后一个被使用的,所以我们继续把第0页驱逐出去,以放置第3。

现在Q2出现了,操作相同的表,但Q2不是加和,而是求平均值,最笨的办法,就是让 Q2 的游标到最开始。而聪明的办法是让Q2的游标附着到Q1的游标上,随着Q1指定到最后并结束,Q2会再回过头来把前面漏掉的页重新检索出来计算。

但是如果Q2里面有limit或者窗口函数,那么共享就会变得棘手!!

还有一种极端的扫描共享,叫做持续扫描共享【CONTIONOUS  SCAN SHARING】,但是没人实现过。

2.1.4 缓冲池绕过

顺序扫描操作符不会将获取的页面存储在缓冲池中以避免开销,而是放在一小块砖为该查询创建的一个工作内存中。
→ 内存被看作是运行的查询【query】的本地内存【local memory】。
→ 如果操作符需要读取磁盘上连续的大量页面,则效果很好。
→ 也可用于临时数据【temporary data】(排序【sort】、连接【join】)。
在 Informix 中称为“轻扫描【Light Scan】”

3 替换策略

当 DBMS 需要释放帧以为新页面腾出空间时,它必须决定从缓冲池中逐出哪个页面。
替换策略的目标:

  • 正确性【Correctness】
  • 准确度【Accuracy】
  • 速度【Speed】
  • 元数据开销【Meta-data overhead】:维护元数据的成本很高,我们需要跟踪页面的访问方式,这样我们就可以决定删除哪些内容
     

3.1 LRU

维护每个页面上次访问时间的单个时间戳。 当 DBMS 需要逐出一页时,选择时间戳最旧的一页。

  • 保持页面排序,以减少驱逐时的搜索时间

3.2 C LO C K

它类似于LRU,但是它不用为每一个页维护一个时间戳:

  1. 每一页都有一个引用位
  2. 当某个页被访问时,对应的引用位设置为1

用“时钟指针”组织环形缓冲区【circular buffer】中的页面:

  • 扫描时,检查页面的位是否设置为 1。
  • 如果是,则设置为0。 如果不是,那就驱逐。

缺点:

LRU + CLOCK 替换策略容易受到顺序洪泛【sequential flooding】的影响。

  • 比如某个查询【query】执行了一个读取每个页面的顺序扫描
  • 这会污染缓冲池,因为页面被读取一次,然后就不再读取了
  • 在 OLAP 工作负载中,最近使用的页面通常是最好驱逐的页面。

LRU + CLOCK 仅跟踪页面上次访问的时间,但不跟踪页面访问的频率。

顺序洪泛:

Q1访问页1

Q2顺序访问所有页,当缓冲池不足时,页1由于是最早访问的页面,因此会被驱逐

Q3访问页1,而刚被驱逐!!!

3.3 LRU-K

以时间戳的形式跟踪对每个页面的最后 K 次引用的历史记录,并计算后续访问之间的间隔。

  • 可以很好地区分引用类型

然后,DBMS 使用此历史记录来估计下次访问该页面的时间。

  • 逐出具有最长预期间隔的页面。
  • 为最近被驱逐的页面维护一个临时的内存缓存,以防止它们总是被驱逐。

3.4 MYSQL近似LRU-K

它使用单个 LRU 链表,但有两个入口点(“okd”与“young”)。
→ 新页面总是插入到old列表的头部。
→ 如果old链表中的页面再次被访问,则插入到young'链表的头部。

此时,我要访问页1,而它不在buffer pool中,我们将其加载到内存中,因为没空间了,我们需要驱逐页8,并将页1放在old链表的头部:

假设只页1再次被访问,我们可以识别到它已经在jold链表里了,因此我们将其放到young的头部,并滑动所有链表:

该方案于LRU不同点在于它ing不是维护之前访问之间的间隔, 但只要知道它在old和young链表的界限之内,那很可能是最近被访问的。

3.5 本地化LO C A L I Z AT I O N

DBMS 根据每个查询【Query】选择要驱逐的页面。 这最大限度地减少了每个查询对缓冲池的污染。

  • 跟踪每个查询【Query】访问过的页面

示例:Postgres 维护一个查询专用的小型环形缓冲区【ring buffer】。

3.6 优先级提示【Priority Hint】

DBMS 了解查询【Query】执行期间每个页面的上下文。
它可以向缓冲池提供有关某个页面是否重要的提示。

3.7 脏页

快速路径【Fast Path】:如果缓冲池中的页面不是脏的,那么 DBMS 可以简单地“删除”它。
慢速路径【Slow Path】:如果页脏了,则 DBMS 必须写回磁盘以确保其更改得以保留。


快速驱逐【n fast evictions】与将来不会再次读取的脏写页面之间的权衡。

3.8 后台写【Backgroung Write】

DBMS 可以定期遍历页表并将脏页写入磁盘。
当脏页被安全写入时,DBMS 可以逐出该页或只是取消设置dirty标志。
需要小心的是,系统在写入日志记录之前不会写入脏页......

3.9 问题

操作系统/硬件尝试通过重新排序和批处理 I/O 请求来最大化磁盘带宽。
但他们不知道哪些 I/O 请求比其他请求更重要。
许多 DBMS 告诉您切换 Linux 以使用截止日期【deadline】或 noop (FIFO) 调度程序。
→ 示例:Oracle、Vertica、MySQL

3.10 DISK I/O SCHEDULING

很多数据库在操作系统之上还有一个小的填充层,它将负责跟踪缓冲池管理器关于读取和写入的未完成的请求(DBMS 维护内部队列来跟踪来自整个系统对页的读/写请求),并决定如何将它们组合在一起以优化性能。


它本质上类似于基于不同的因素,决策不同IO请求的优先级:

  • 顺序 I/O 与随机 I/O
  • 关键路径任务【Critical Path Task】与后台任务【Background Task】
  • 表、索引、日志、临时数据【Table vs. Index vs. Log vs. Ephemeral Data】
  • 事务信息【Transaction Information】
  • 基于用户的 SLA

操作系统不知道这些事情并且可能会妨碍。

3.11 OS PAGE CACHE

大多数磁盘操作都是通过操作系统 API 进行的。 除非 DBMS 告诉它不要这样做,否则操作系统会维护自己的文件系统缓存(也称为页面缓存【page cache,】、缓冲区缓存【buffer cache】)。


大多数 DBMS 使用直接 I/O (O_DIRECT) 来绕过操作系统的缓存。我们不希望操作系统缓存我们读写的页面,因为我们想要自己来处理。如果我们使用OS的缓存,那么带来的问题有:

  • 页面的冗余副本:OS的页缓存【page cache】和数据库的buffer pool都有相同的页
  • 不同的驱逐政策。
  • 失去对文件 I/O 的控制:如果你不小心,你也会失去对磁盘上的内容何时被刷新的控制

3.12 FSYNC PROBLEMS

如果 DBMS 掉用 fwrite 会发生什么?

文件会被写入 OS 的页缓存【page cache】中,因为 OS 尝试智能,以使得速度变得更快,因此当 fwrite。返回时,页不一定被写入到磁盘中,它可能还在页缓存【page cache】中,至于什么时候会被写入磁盘,那完全是操作系统决定的。

那么如果 DBMS 调用 fsync 呢?

调用会阻塞,直到硬件返回数据已经被持久化,但是,硬件也会耍一些小游戏。

如果 fsync 失败(EIO),会发生什么?
→ Linux 将脏页标记为干净页,因为这并不是linux的panic,
→ 如果DBMS再次调用fsync,那么Linux告诉你flush成功了,但它其实在撒谎.

4 其他内存池

DBMS 需要内存来存储元组【tuples】和索引【index】以外的内容。


这些其他内存池可能并不总是由磁盘支持。取决于实现。
→ 排序 + 连接缓冲区【Sorting + Join Buffers】
→ 查询缓存【Query Caches】
→ 维护缓冲器【Maintenance Buffers】
→ 日志缓冲区【Log Buffers】
→ 字典缓存【Dictionary Caches】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/477902.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

线性表:关于链表(主要以单链表为例)的相关理解和应用

多清澈这天空 晴雨相拥 同心逐梦! 坚守我信心 一路出众!! 首先,按照惯例,欢迎大家边听歌边观看本博客 ▶ 紫荆花盛开 (163.com)(建议复制链接,浏览器打开,csdn打开太慢了&#x…

吉时利keithley 2604B数字源表

181/2461/8938产品概述: Keithley 2604B 源表可让您比以前更快、更轻松、更经济地进行精密直流、脉冲和低频交流源测量测试。Keithley 2604B 的 IV 功能测试测试速度是竞争产品的两到四倍,其结合了: Keithley 的高速第三代源测量单元 (SMU)…

Linux学习-进程

目录 进程基本概念 进程相关命令 进程的创建 进程的调度 进程相关函数接口 进程的消亡 实例:创建九个子进程 目录 进程基本概念 进程相关命令 进程的创建 进程的调度 进程相关函数接口 进程的消亡 实例:创建九个子进程 exec函数…

Typecho博客后台登陆界面美化

登录界面: 食用方法: 备份 admin 目录 压缩包内容上传到 admin 目录内。 结构:网站根目录 /admin/login.php 结构:网站根目录 /admin/style 修改 login.php 第35行,把“季春二九管理后台”替换成自己的信息 清理缓存,开始体验新的…

罐头鱼AI矩阵获客批量混剪运营系统介绍

罐头鱼AI矩阵——智能运营系统助力抖音视频创作 随着社交媒体的普及,视频内容创作成为了企业营销的重要手段。为了帮助您更高效地进行视频内容创作和发布,我们推出了罐头鱼AI矩阵,一款集智能混剪、关键词生成、发布管理等功能于一体的全新运营…

交易的成功并非仅依赖于拥有强大的工具,而在于如何用好你的工具

任何领域伟大的成就往往源于个人不屈不挠地克服重重障碍的能力,这种毅力和决心并非普通人所具备,因此他们往往只能停留在普通和平凡的层面。 而对于那些渴望在交易领域取得卓越成就的人来说,坚持采用一套经过验证且有效的交易系统&#xff0c…

ArtDD 一键下载 掘金、CSDN、开源中国、博客园文章 文章的Chrome 插件

有想一键下载博客园、掘金、CSDN、开源中国等博客自己的文章到本地的可以使用这个插件 插件会根据当前页面链接自动分辨出文章所属平台,可能有些文章详情链接是不支持的,例如博客园存在很多个版本的文章详情页链接格式,想要支持所有的链接是…

设计模式之抽象工厂模式解析

抽象工厂模式 1)问题 工厂方法模式中的每个工厂只生产一类产品,会导致系统中存在大量的工厂类,增加系统的开销。 2)概述 a)产品族 和 产品等级结构 产品等级结构:产品的继承结构; 产品族&…

武汉星起航引领跨境电商新潮流,一站式孵化平台助力合作伙伴腾飞

在全球经济一体化的大趋势下,跨境电商以其独特的优势逐渐成为连接各国市场的桥梁。随着市场竞争的加剧,传统的经营模式已难以满足日益复杂的业务需求,合作伙伴迫切需要更为全面、专业的指导和支持。正是在这样的背景下,武汉星起航…

罐头鱼AI矩阵运营系统|视频批量混剪|矩阵获客

罐头鱼AI传单功能操作说明 个性化首页展示 登录状态一目了然灵活绑定账号数量快速查看最新上传视频素材和素材列表 抖音账号轻松绑定 明晰显示登录账号和已绑定账号灵活控制可绑定账号数量一键授权绑定抖音账号 账号管理列表 清晰管理查看绑定抖音账户列表 上传视频素材便捷管理…

洛谷_P2678 [NOIP2015 提高组] 跳石头_python写法

P2678 [NOIP2015 提高组] 跳石头 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) d, n, m map(int,input().split())data [0] for i in range(n):value int(input())data.append(value) data.append(d)def check(mid):now 0cnt 0for i in range(1,n2):if abs(data[now]-da…

Excel的数据分析工具

Excel的数据分析工具 会生成新的工作簿 全选--数据透视表 插入切片器 使用作图工具 可以继续插入条件切片

CodeMeter案例分享-工业自动化领域

德国威步遵行于“完美保护、完美授权、完美安全”的企业宗旨,为各个领域的软件商、设备商的数字资产提供强有力的保护,有效防止软件盗版、逆向工程、代码篡改、恶意破坏以及商业间谍活动,帮助企业把全新的商业模式与业务流程完美整合并获得可…

桌面日程安排软件,笔记本电脑常用软件有哪些?

在如今快节奏的社会环境中,时间仿佛成了比金子还要珍贵的资源。我们每个人都像是一名指挥官,需调度好每一刻光阴,使其发挥最大效益。而在这场与时间的较量中,一款优秀的桌面日程安排软件就像您的私人助理,帮您精准规划…

武汉星起航引领跨境电商新潮流,深耕亚马逊打造全方位合作新模式

在全球化的浪潮下,跨境电商已成为连接各国市场的重要桥梁,为无数企业带来了前所未有的发展机遇。在这一领域,武汉星起航电子商务有限公司以其独特的战略眼光和实战经验,成为引领行业发展的佼佼者。公司自2017年起便深耕亚马逊平台…

应用案例 | 基于三维机器视觉的销轴销套深框抓取上料解决方案

随着科技的不断发展,机器人技术不断革新,并逐渐渗透到各行业的各个环节。在机器人技术的帮助下,各行业的生产力不断增强,生产效率也得到了极大的提高。 销轴销套深框抓取是指在机械装配或生产过程中,对销轴和销套进行抓…

【数据可视化】Echarts官方文档及常用组件

个人主页 : zxctscl 如有转载请先通知 文章目录 1. 前言2. Echarts官方文档介绍3. ECharts基础架构及常用术语3.1 ECharts的基础架构3.2 ECharts的常用术语3.2.1 ECharts的基本名词3.2.2 ECharts的图表名词 4. 直角坐标系下的网格及坐标轴4.1 直角坐标系下的网格4.2…

今日问题:动态分配内存出错

2024.3.22 在搜素了许多文章和查阅了许多博客后依然没有找到问题所在&#xff0c;最后无意之间翻看以前的关于动态内存管理的代码后发现&#xff1a; 没加头文件&#xff1a;#include<stdlib.h> 苦笑不得了属于是 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio…

漏洞挖掘技术综述与人工智能应用探索:从静态分析到深度学习,跨项目挑战与未来机遇

在网络安全和软件工程领域中&#xff0c;将机器学习应用于源代码漏洞挖掘是一种先进的自动化方法。该过程遵循典型的监督学习框架&#xff0c;并可细化为以下几个关键步骤&#xff1a; 数据预处理&#xff1a; 源代码解析与清理&#xff1a;首先对源代码进行文本解析&#xff…

mysql 如何设计分库分表

在MySQL中设计分库分表的方法通常涉及到水平拆分与垂直拆分两种主要方式。 水平拆分&#xff1a; 按照某一列进行水平拆分&#xff1a; 可以根据某一列&#xff08;如用户ID、时间等&#xff09;的取值范围将数据拆分到不同的数据库或表中。基于哈希值的水平拆分&#xff1a;…