内存架构
本节讨论 Redrock Postgres 数据库实例的内存架构。
共享内存区是一个实例级可读写的内存区,与服务器后台进程一起组成数据库实例。所有服务器进程,都可以读取和修改实例共享内存区中的信息。
共享缓冲区是用于存储从数据文件读取的数据块副本的内存区域。缓冲页是缓冲区管理器用来暂时缓存当前或最近使用的数据块的主内存地址。共享缓冲区可以通过参数 shared_buffers 设置该内存区的大小。Redrock Postgres 将表、索引和回滚段中的页从持久性存储加载到此处,并直接操作它们。
数据库使用内部算法来管理共享缓冲区中的缓冲页。缓冲页可以处于下列互斥状态之一:
-
未使用的
缓冲页可供使用,因为它从未使用过,或者当前未使用。这种类型的缓冲页是数据库最容易使用的。
-
干净的
此缓冲页在之前曾被使用过,而现在包含某个数据块在某个时间点的读取一致版本。块包含数据但是干净的,因此它不需要将执行检查点操作。数据库可以钉住该块并重用它。
-
脏的
缓冲页包含已修改、但尚未写入到磁盘的数据。数据库在重用该缓冲页之前必须对其执行检查点操作。
每个缓冲页带有一种访问模式:钉住的或空闲的 (未钉住)。缓冲页被"钉住"在缓存中,以便当其被某个用户会话访问时,它不会因为内存不足被换出内存。多个会话不能在同一时间修改某个已被钉住的缓冲页。
当一个客户端请求数据时,Redrock Postgres 以下列两种模式之一从共享缓冲区中检索缓冲页:
-
当前模式
当前模式获取,这是一种对当前已出现在共享缓冲区中的块的检索。例如,如果一个未提交事务已更新某个块中的两行,则当前模式获取会检索这个具有未提交行的块。数据库最常使用当前模式获取的情况是在修改语句期间,它只需更新块的当前版本。
-
一致模式
一致读取获取,是对某个块的一致读取版本的检索。此检索可能会使用撤消数据。例如,如果一个未提交事务已更新某个块中的两行,而在另一个独立会话中的查询请求该块,则数据库使用撤消数据来创建该块的一个读取一致版本(称为一致读取克隆),它不包括未提交的更新。通常,查询以一致模式检索块。
逻辑I/O,也称为缓冲页I/O,指的是读取和写入共享缓冲区中的缓冲页。当在内存中找不到请求的缓冲页时,数据库将执行一个物理 I/O,将缓冲页从存储设备复制到内存,然后再执行一个逻辑 I/O,以读取缓存的缓冲页。
用于缓存还未写入磁盘的预写式日志的 WAL 缓冲区,可通过参数 wal_buffers 设置该内存区的大小。
在每次事务提交时,WAL 缓冲区的内容被写出到磁盘,因此 WAL 缓冲区过大不可能提供显著的收益。不过,把它设置为几个兆字节可以在一个繁忙的服务器(其中很多客户端会在同一时间提交)上提高写性能。
本地内存区是特定于一个操作系统进程或线程的内存区,且不和系统上的其他进程或线程共享。由于本地内存区是特定于进程的,所以它不会在共享内存区中分配。
用于给数据库会话缓存本地临时表数据的本地缓冲区,可通过参数 temp_buffers 限制该内存区的最大使用量。一个会话会按照temp_buffers
给出的限制根据需要分配本地缓冲区。
用于在写入临时磁盘文件之前查询操作(例如排序或哈希表)可使用的工作内存区,可通过参数 work_mem 限制该内存区的最大使用量。
注意对于一个复杂查询,可能会并行运行好几个排序或者哈希操作;每个操作通常都会被允许使用这个参数指定的内存量,然后才会开始写数据到临时文件。同样,几个正在运行的会话可能并发进行这样的操作。因此被使用的总内存可能是work_mem
值的好几倍,在选择这个值时一定要记住这一点。ORDER BY
、DISTINCT
和归并连接都要用到排序操作。哈希连接、基于哈希的聚集以及基于哈希的IN
子查询处理中都要用到哈希表。
用于维护性操作(例如VACUUM
、CREATE INDEX
和ALTER TABLE ADD FOREIGN KEY
)可使用的维护工作内存区,可通过参数 maintenance_work_mem 限制该内存区的最大使用量。
当数据库执行一条 SQL 语句时,需要解析 SQL 生成相应的执行计划,并按照执行计划执行返回结果。在访问表时,需要表的模式信息,比如表的列属性、对象编号、统计信息等。PostgreSQL 将表的模式信息存放在系统表中,因此要访问表,就需要首先在系统表中取得表的模式信息。对于一个 PostgreSQL 实例来说,对于系统表和普通表模式的访问是非常频繁的。为了提高这些访问的效率,PostgreSQL 引入了本地高速缓存。本地高速缓存主要有以下部分:
-
执行计划缓存
用于缓存最近使用过的 SQL 语句的执行计划。在运行存储过程或函数的过程中,会缓存执行过的 SQL 语句的执行计划。大多数驱动程序都提供了预备查询的接口,您可以创建带参数的预备语句,绑定参数值执行查询。这些预备查询的 SQL 语句,它们的执行计划也会被缓存起来。PostgreSQL 提供了 SQL 命令
DISCARD PLANS
,用于释放会话中已缓存的执行计划。 -
表模式信息缓存
用于缓存最近访问过的表的模式信息。您在连接会话中访问过的表的模式信息,都会被缓存起来。如果访问的表是被分区过的表,那么这个表下面的分区表的模式信息,也会被缓存起来。
-
系统表元组缓存
用于缓存最近使用过的系统表的元组。