CMU 15-445/645-Note2-数据库存储(下)

0.写在前面

based on CMU 15-445/645 2020fall, Lecture #5.

1: Buffer Pool Manager(BMP)

一般数据库的标准配置是这样的
在这里插入图片描述

  1. 前端 OLTP 数据库
  2. 后端 OLAP 大型数据仓库(back-end data warehouse)
    这两者有时候被称为 Data Silo,即数据孤岛,数据存储相互独立,彼此之间不会真的交流,然后就可以进行 ETL(Extract Transform Load) 的操作,即将业务系统的数据经过抽取、清洗、转换之后加载到数据仓库的过程。
    以 Zynga 这家游戏公司为例,Zynga 收购了很多游戏初创公司,比如 FarmVille,它买下这些公司时,会去运行他们自己的前端 OLTP 数据库,只有他们将这些数据放入他们的后端大型仓库时,他们才能更高地分析出怎么做才能让你们在 FarmVille 上买东西,等后端 OLAP 数据仓库拿到分析后的信息数据后,就推到前端 OLTP 数据库中,并对外暴露。所以 HTAP 基本上在做一些平常只能在 OLAP 端所做的,可以直接在前端的 Data Silo 里面做这些,并且不用等数据传回到后端数据仓库了。
    这可以用 MySQL、PostgreSQL、mongoDB 来做前端数据库,可以用 Hadoop、Spark、Greenplum、Vertica 来做后端数据仓库。

面向磁盘的 DBMS :
在这里插入图片描述
如果执行引擎在请求 page 时,内存中没有足够的空余来容纳我们需要的那个 page,这个时候就必须要决定哪个 page 进行写出操作,即 “remove”,而这正是 Buffer 池要解决的问题
系统的其他部分无须知道或者关系哪些东西在内存里,哪些东西不在,它只会等它需要拿到的东西,然后返回一个指针给到执行引擎。
Buffer 池需要我们在数据库系统内部分配一块很大的内存,就是调用 malloc,拿到一些内存块,并将从磁盘中读取到的所有的 pages 都放到这个里面去。注意这段内存完全是由数据库系统来控制,而不是交给操作系统来做。
然后,这段内存区域被分成一个个固定大小的 chunk,它被称为 frame,frame 对应的是之前提到的 slot(slot 存储的虽然是对应 tuple 的 offset,但对外来看,相当于指代了一段存储区域),即 Buffer 池内存区域中的区块或者是 chunk,frame 里存放的是 page。

1.1 BUFFER POOL META-DATA

在这里插入图片描述
page 表其实就是一个 hash 表,用来跟踪在内存中有哪些 page,可以通过 page 表和 page ID 找到在 frame 中对应的那个 page
数据库系统必须维护一些额外的元数据,以此来跟踪当前 Buffer 池中的 page 发生了什么:

  1. Dirty Flag
    这个 flag 表示的是,从磁盘读取到这个 page 后,这个 page 是否被修改过,比如查询或者事务对其修改过
  2. Pin/引用计数
    这个表示 想要使用该 page 的当前线程数量/正在对该 page 进行的 查询 的数量,page 目前还在被强引用,说明此时不应该将 page 写出到磁盘上。
1.2 Locks vs. Latches

1)Lock(Transaction,databse content,update|intention|shared|exclusive):以数据库的语义来讲,lock 是某种高级逻辑原语(high-level logical primitive),它会去保护数据库中的逻辑内容,比如 tuple、表以及数据库,事务在运行时会去持有这个 lock
latch(Thread,memory data structure,read|write):latch 是一种低级保护原语(low-level protection primitive),它用来保护数据库系统内部的关键部分,比如物理数据结构、内存区域 。latch 就相当于 mutex。

1.3 BUFFER POOL OPTIMIZATIONS

1)Multiple Buffer Pools:
使用多个Buffer Pool的好处有两个:
不同的任务的需要是不同的,所以可能需要针对性的设置不同的置换策略
提升并发能力,因为不同的buffer pool可以并发的访问
既然有了多个buffer pool,那么如何将数据分片呢?
有两种方案:
object ID:提交在record ID(tuple 的唯一ID)中硬编码一个标识符
hash:通过hash record ID来分片

2)Pre-Fetching:当处理完 page1 后,如果要继续请求 page2 或 page3 的数据,通过 pre-fetching 的方式,它已经在 Buffer 池中了,那么也无序停顿去从磁盘中把相关 page 取出然后写入到 Buffer 池内存中。
在这里插入图片描述

3)Scan Sharing:这个优化策略想的是,查询之间可以利用彼此的结果,某个查询可以复用另一个查询从磁盘中读取到的数据,也可以将该数据用于其他的查询。
4)Buffer Pool Bypass:思路是,分配一小块内存给执行查询的那条线程,当它从磁盘中读取 page 时,如果该 page 不在 Buffer 池中,那么它将会把这个 page 从磁盘写入本地内存而不是 Buffer 池。当查询完成时,所有的这些 page 都会被丢弃掉。这么做是为了避免去 page 表中进行查询所带来的开销,因为 page 表中对应的条目是带锁的。
注意,只有当操作的是中间结果以及查询扫描的量不大的场景才适用

Q:为什么其他主流数据库系统不依赖操作系统 page 缓存呢?
因为假设在 OS page cache 中放入一个 page,而在 Buffer 池中放入该 page 的一个副本,如果这个时候要对 Buffer 池中修改这个 page 部分,那么 OS page cache 和 Buffer 池中的 page 就不再是同一个东西了,OS page cache 中存的就是老 page 数据,那么这个数据就冗余了。

2: Replacement Policies

非常高端的、价格很昂贵的企业级数据库和开源数据库之间有什么区别呢?

  1. 高端数据库拥有非常复杂的替换策略,它们会跟踪统计 page 的相关使用数据,会尝试从查询实际所做的事情中推断出最好的决策
  2. 开源数据库的某些比较新的系统中,它们在这方面并没有做的像高端数据库那么好,它们就只能做些简单版的东西了

置换算法是计算机系统中常见的议题。例如LRU/ CLOCK / LRU-k等置换算法也可以用在BPM中。

2.1 Least-Recently Used(LRU)and Clock

LRU:一种使用起来超简单的技术就是 LRU,即跟踪一个 page 最后一次被访问时的时间戳,然后只要看哪个 page 拥有的时间戳是最老的,哪个就应该要被移除掉。
那么此时就可以维护一个单独的数据结构,比如队列,根据 page 的时间戳进行排序。

Clock: 在 Clock 中,没必要去追踪每个 page 的时间戳,相反,唯一需要去跟踪的信息是每个 page 的标志位(reference bit),它会告诉你自从上次检查过该 page 后,这个 page 是否被访问了,所以你需要将你的 page 弄成一个环形的 Buffer,like a clock。在一圈时钟这个时间段内,如果标志位没有变化,就可以从 Buffer 池中移除这个 page。
使用 Clock 算法的优点是,在移除 page 时,不会精确地移除最近最少使用的那个 page。
在某些查询上表现得很好,比如点查询(point query)时访问单个东西。
缺点:Clock 和 LRU 都容易受到 sequential flooding 带来的影响。

2.2 LRU-K

思路是,将最近使用过 1 次的判断标准扩展为最近使用过 K 次,没有达到 K 次访问的数据并不会被缓存。访问记录不能无限记录,当访问次数达到 K 次后,将数据索引从历史队列移到缓存队列中。
相比于 LRU 比较时间戳,LRU-K 看的是时间戳时间的间隔,统计哪一个 page 的上一次和下一次的访问的时间间隔最长,那么哪一个 page 就是最近最少使用的。

2.3 本地化(Localization)

使用多个 Buffer 池,让每个查询本地化,即每个 Buffer 池能对应住一个查询,那么对于单个查询来讲,只会去移除对于这个查询而言最近最少使用的那个 page,而不是对于整个查询而言最近最少使用的那个 page。

2.4 优先级提示(Priority Hints)

有索引时,就知道查询时如何进行扫描的,也能知道哪些不同的 page 被访问了,可以使用这个信息来判断该移除哪些 page
例子,假设有一个 Insert 的操作 Q1,对应的表里面有一个全局的计数器,每次 Insert 时,它就会加 1。
在这里插入图片描述

如果根据这个 id 从小到大排序,意味着遍历始终是沿着树的右侧往下走,去拿到这些 page 的。
因此这个时候就应该提示 Buffer 管理器,这些被红色框圈起来的 page 就应该试着待在内存中。

2.5 Dirty Pages

在 page 上有一个 dirty bit,它表示自从上次它被放入 Buffer 池中后,是否还有查询对该 page 的内容做了修改。
快的方式:如果一个 page 在 Buffer 池中不是 dirty 的,直接 drop 掉它就好 。
慢的方式:如果一个 page 在 Buffer 池中是 dirty 的话,在将该空间重新用于新的 page 之前,必须将这个 page 安全地写回磁盘。
而这是有 trade-off 的,因为一大堆不是 dirty 的 page,可能最近需要用到它,所以直接 drop 掉会带来后面重新读取磁盘并写入内存的 I/O 影响。

2.6 后台写操作(Background Writing)

为了避免 必须立即 将 page 写出以便在 Buffer 池中释放可用空间的问题,在数据库系统中有一条执行定时任务的线程,它会在 Buffer 池中找出那些被标记为 dirty 的 page,将它们写出到磁盘上,然后可以将这些 page 标记为 clean。那么当使用替换策略去决定该移除哪个 page 时,就有一堆 clean 的 page 可以 drop 掉了。
但是这个操作要小心,因为在该 dirty page 对应的修改操作写入日志之前,我们不希望将这些 dirty page 写出到磁盘
同样的,这也是 mmap 无法做到的事情。

参考:
https://www.bilibili.com/read/cv14580880?spm_id_from=333.999.0.0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值