带你认识存储的本质 - 状态 | 青训营笔记

138 阅读4分钟

这是我参与「第五届青训营 」笔记创作活动的第10天

基于代价的优化器一般需要考虑哪些代价?

基于代价优化的的主要思路是计算所有可能的执行计划的代价,并挑选代价最小的执行计划的路径。

执行器的执行模型,除了本课中提到的火山模型是否还有其他模型?相比于火山模型有什么优劣势?

一、迭代模型/火山模型(Iterator Model)

又称 Volcano Model 或者 Pipeline Model。 Iterator Model 该计算模型将关系代数中每一种操作抽象为一个 Operator,将整个 SQL 构建成一个 Operator 树,查询树自顶向下的调用next()接口,数据则自底向上的被拉取处理。 火山模型的这种处理方式也称为拉取执行模型(Pull Based)。 大多数关系型数据库都是使用迭代模型的,如 SQLite、MongoDB、Impala、DB2、SQLServer、Greenplum、PostgreSQL、Oracle、MySQL 等。 火山模型的优点在于:简单,每个 Operator 可以单独实现逻辑。 火山模型的缺点:查询树调用next()接口次数太多,并且一次只取一条数据,CPU 执行效率低;而 Joins, Subqueries, Order By 等操作经常会阻塞。

二、物化模型(Materialization Model)

Materialization Model 物化模型的处理方式是:每个 operator 一次处理所有的输入,处理完之后将所有结果一次性输出。 物化模型更适合OLTP负载,这些查询每次只访问小规模的数据,只需要少量的函数调用。

三、向量化/批处理模型(Vectorized / Batch Model)

Batch Model 向量化模型 和 火山模型 类似,每个 operator 需要实现一个 next() 函数,但是每次调用 next() 函数会返回一批的元组(tuples),而不是一个元组,所以向量化模型也可称为批处理模型。 向量化模型是火山模型和物化模型的折衷。 向量化模型比较适合 OLAP 查询,因为其大大减少了每个 operator 的调用次数,也就简单减少了虚函数的调用。 Presto、snowflake、SQLServer、Amazon Redshift等数据库支持这种处理模式。 Spark 2.x 的 SQL 引擎开始也支持向量化执行模型。 在 Hive 中使用 向量化执行的方式: 1、必须以 ORC 格式来存储数据, 2、将 hive.vectorized.execution.enabled 参数设置为 true

InnoDB 的 buffer pool 是怎么实现页面管理和淘汰的?

如上所述,buffer pool 最主要的功能便是加速读和加速写。

加速读就是当需要访问一个数据页的时候,如果这个页已经在缓存池中,那么就不再需要访问磁盘,直接从缓冲池中就能获取这个页面的内容。

加速写就是当需要修改一个数据页的时候,先将这个页在缓冲池中进行修改,记下相关的 redo log,这个页的修改就算已经完成了。至于这个被修改的页什么时候真正刷新到磁盘,这个是 buffer pool 后台刷新线程来完成的。

在实现上面两个功能的同时,需要考虑客观条件的限制,因为机器的内存大小是有限的。通常情况下,当数据库的数据量比较大的时候,缓存池并不能缓存所有的数据页,所以也就可能会出现,当需要访问的某个页时,该页却不在缓存池中的情况,这个时候就需要从磁盘中将这个页读出来,加载到缓存池,然后再去访问。这样就涉及到随机的物理 IO,也就增加了操作页所消耗的时间。

这样的情况是一个 bad case,是需要尽量避免的——因此需要想办法来提高缓存的命中率。innodb buffer pool 采用经典的 LRU 算法来进行页面淘汰,以提高缓存命中率。与传统的 LRU 算法相比,buffer pool 中的 LRU 列表其中间位置被打了一个 old 标识,可以简单的理解为将 LRU 列表分为两个部分,这个标记到 LRU 列表头部的数据页称为 young 数据页池,这个标志到 LRU 列表尾部的数据页称之为 old 数据页池。当一个页从磁盘上加载到缓存池的时候,会将它放在 old 标识之后的第一个位置,也就是说放在了 old 池子中(“中点插入策略”)。这个机制保证了在做大表的一次性全表扫描时,即使有大量新进来的数据页,也会被存放在 old 池子中,当 old 池子的大小不够缓存新进来页面的时候,也只是在 old 池子中进行循环冲洗,这样就不会冲洗 young 池子中的热点页,从而保护了热点页。这就是 buffer pool LRU 算法的简单机制。