PG体系结构之内存结构
内存结构
PostgreSQL的内存体系结构可以分为两大类:
- Local memory area 本地内存区域——由每个后端进程分配供自己使用。
- Shared memory area 共享内存区域——由PostgreSQL服务器的所有进程使用。
共享内存区域由PostgreSQL服务器在启动时分配。这个区域也被划分为几个固定大小的子区域。
PostgreSQL启动后,会生成一块共享内存,用于做数据块的缓冲区,以便提高读写性能。WAL日志缓冲区和Clog缓冲区也存在共享内存中,除此之外还有全局信息比如进程、锁、全局统计等信息也保存在共享内存中。
其中最重要的组成部分是Shared Buffer和WAL Buffer。
√ Shared Buffer Pool
PostgreSQL将表和索引中的页面从持久存储加载到这里,并直接操作它们。Shared Buffer的目的是减少磁盘IO。为了达到这个目的,必须满足以下规则∶
当需要快速访问非常大的缓存时(10G、100G等)
如果有很多用户同时使用缓存,需要将内容尽量缩小
频繁访问的磁盘块必须长期放在缓存中
对应的设置参数为:shared_buffers
12345678910C:\Users\lhrxxt>psql -U postgres -plhr -h192.168.66.35 -p 15432Password for user postgres:psql (13.3)Type "help" for help.postgres=# show shared_buffers;shared_buffers----------------128MB(1 row)
√ WAL Buffer
WAL Buffer是用来临时存储数据库变化的缓存区域。存储在WAL Buffer中的内容会根据提前定义好的时间点参数要求写入到磁盘的WAL文件中。为了保证数据不因服务器故障而丢失,PostgreSQL支持WAL机制。WAL data(也称XLOG records)是PostgreSQL中的事务日志;WAL buffer是WAL数据写入持久存储之前的缓冲区域。
在备份和恢复的场景下,WAL Buffer和WAL文件是极其重要的。
√ CommitLOG
提交日志(CLOG)保存所有事务(如in_progress,committed,aborted)的状态,用于并发控制(CC)机制。
本地内存(Local memory area )
每个backend进程分配一个本地内存区域用于查询处理;每个区域又分为几个子区域,子区域的大小可以是固定的,也可以是可变的。非全局存储的数据都存在本地内存中。下表列出了主要的子区域。
表 Local memory area
子区域 | 描述 |
---|---|
work_mem | Executor使用此区域按顺序和不同的操作对元组进行排序,并使用merge-join和hash-join操作来连接表。内部排序操作和Hash表在使用临时操作文件之前使用的存储缓冲区。 |
maintenance_work_mem | 在某些维护操作,例如VACUUM(收集表和索引的统计信息,整理表和索引)、CREATE INDEX、ALTER TABLE ADD FOREIGN Key、Reindex等中都会使用这个区域。 |
temp_buffers | Executor使用此区域存储临时表。用于访问临时表的缓冲区。 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | postgres=# show autovacuum_work_mem; autovacuum_work_mem --------------------- -1 (1 row) postgres=# show maintenance_work_mem; maintenance_work_mem ---------------------- 64MB (1 row) postgres=# show work_mem; work_mem ---------- 4MB (1 row) postgres=# show temp_buffers; temp_buffers -------------- 8MB (1 row) |
除了上面提到的本地内存区域和共享内存区域,PostgreSQL还分配了如下几个区域:
用于各种访问控制机制的子区域,例如,信号量、轻量级锁、共享锁和排他锁等
用于各种后台进程的子区域,如checkpointer和autovacuum。
用于事务处理的子区域,比如保存点和两阶段提交。
等等。
与内存相关的参数
shared_buffers
(integer
)
设置数据库服务器将使用的共享内存缓冲区量。默认通常是 128 兆字节(128MB
),但是如果你的内核设置不支持(在initdb时决定),那么可以会更少。 这个设置必须至少为 128 千字节。不过为了更好的性能,通常会使用明显高于最小值的设置。 如果指定值时没有单位,则以块为单位,即BLCKSZ
字节,通常为8kB.(BLCKSZ
的非默认值改变最小值。) 此参数只能在服务器启动时设置。
如果有一个专用的 1GB 或更多内存的数据库服务器,一个合理的shared_buffers
开始值是系统内存的 25%。即使更大的shared_buffers
有效,也会造成一些工作负载, 但因为PostgreSQL同样依赖操作系统的高速缓冲区,将shared_buffers
设置为超过 40% 的RAM不太可能比一个小点值工作得更好。为了能把对写大量新的或改变的数据的处理分布在一个较长的时间段内,shared_buffers
更大的设置通常要求对max_wal_size
也做相应增加。
如果系统内存小于 1GB,一个较小的 RAM 百分数是合适的,这样可以为操作系统留下足够的空间。
huge_pages
(enum
)
控制是否为主共享内存区域请求巨型页。有效值是try
(默认)、on
以及off
。如果huge_pages
被设置为try
,则服务器将尝试请求巨型页,但是如果失败会退回到默认的方式。如果为on
,请求巨型页失败将使得服务器无法启动。如果为off
,则不会请求巨型页。
当前,只有Linux和Windows上支持这个设置。在其他系统上这个参数被设置为try
时,它会被忽略。
巨型页面的使用会导致更小的页面表以及花费在内存管理上的 CPU 时间更少,从而提高性能。更多有关Linux上使用巨型页面的细节请见第 18.4.5 节。
巨型页在Windows上被称为大页面。要使用大页面,需要为运行PostgreSQL的Windows用户账号分配Lock Pages in Memory的用户权限。可以使用Windows的组策略工具(gpedit.msc)来分配用户权限Lock Pages in Memory。为了在命令窗口以单进程(而不是Windows服务)的方式启动数据库服务器,命令窗口必须以管理员身份运行或者禁用用户访问控制(UAC)。当UAC被启用时,普通的命令窗口会在启动时收回用户权限Lock Pages in Memory。
注意这种设置仅影响主共享内存区域。Linux、FreeBSD以及Illumos之类的操作系统也能为普通内存分配自动使用巨型页(也被称为“超级”页或者“大”页面),而不需要来自PostgreSQL的显式请求。在Linux上,这被称为“transparent huge pages”(THP,透明巨型页)。已知这种特性对某些Linux版本上的某些用户会导致PostgreSQL的性能退化,因此当前并不鼓励使用它(与huge_pages
的显式使用不同)。
temp_buffers
(integer
)
为每个数据库会话设置用于临时缓冲区的最大内存.这些是仅用于访问临时表的会话本地缓冲。 如果指定值时没有单位,则以块为单位,即BLCKSZ
字节,通常为8kB。 默认为8兆字节 (8MB
)。(如果BLCKSZ
不是8kB,则默认值按比例缩放。) 这个设置可以在独立的会话内部被改变,但是只有在会话第一次使用临时表之前才能改变; 在会话中随后企图改变该值是无效的。
一个会话将按照temp_buffers
给出的限制根据需要分配临时缓冲区。如果在一个并不需要大量临时缓冲区的会话里设置一个大的数值, 其开销只是一个缓冲区描述符,或者说temp_buffers
每增加一则增加大概 64 字节。不过,如果一个缓冲区被实际使用,那么它就会额外消耗 8192 字节(或者BLCKSZ
字节)。
max_prepared_transactions
(integer
)
设置可以同时处于“prepared”状态的事务的最大数目(见PREPARE TRANSACTION)。把这个参数设置 为零(这是默认设置)将禁用预备事务特性。这个参数只能在服务器启动时设置。
如果你不打算使用预备事务,可以把这个参数设置为零来防止意外创建预备事务。如果你正在使用预备事务,你将希望把max_prepared_transactions
至少设置为max_connections一样大,因此每一个会话可以有一个预备事务待处理。
当运行一个后备服务器时,这个参数必须至少与主服务器上的一样大。否则,后备服务器上将不会执行查询。
work_mem
(integer
)
设置在写入临时磁盘文件之前查询操作(例如排序或哈希表)可使用的基础最大内存容量。 如果指定值时没有单位,则以千字节为单位。默认值是4兆字节 (4MB
)。 注意对于一个复杂查询, 可能会并行运行好几个排序或者哈希操作;每个操作通常都会被允许使用这个参数指定的内存量,然后才会开始写数据到临时文件。 同样,几个正在运行的会话可能并发进行这样的操作。因此被使用的总内存可能是work_mem
值的好几倍,在选择这个值时一定要记住这一点。 ORDER BY
、DISTINCT
和归并连接都要用到排序操作。 哈希连接、基于哈希的聚集以及基于哈希的IN
子查询处理中都要用到哈希表。
与等效的基于排序的操作相比,基于哈希的操作通常对内存可用性更敏感。 可用于哈希表的内存通过将work_mem
乘hash_mem_multiplier
来计算。 这使得基于哈希的操作可以使用超出通常work_mem
基本量的内存。
hash_mem_multiplier
(浮点
)
用于计算基于哈希的操作可以使用的最大内存量。 最终限制通过将work_mem
乘以hash_mem_multiplier
来决定。 默认值为 1.0,这使得基于hash的操作与基于排序的操作一样,都取决于简单的work_mem
最大值。。
考虑在经常发生溢出的环境中增加hash_mem_multiplier
,尤其是在简单增加 work_mem
会导致内存压力(内存压力通常以间歇性的内存退出错误的形式出现)。 设置为 1.5 或 2.0 对混合工作负载有效。在 2.0 - 8.0 或更多范围内的较高设置在已work_mem
已经达到40MB 或更多的环境中可能有效。
maintenance_work_mem
(integer
)
指定在维护性操作(例如VACUUM
、CREATE INDEX
和ALTER TABLE ADD FOREIGN KEY
)中使用的 最大的内存量。 如果指定值时没有单位,则以千字节为单位,其默认值是 64 兆字节(64MB
)。因为在一个数据库会话中,一个时刻只有一个这样的操作可以被执行,并且一个数据库安装通常不会有太多这样的操作并发执行, 把这个数值设置得比work_mem
大很多是安全的。 更大的设置可以改进清理和恢复数据库转储的性能。
注意当自动清理运行时,可能会分配最多达这个内存的autovacuum_max_workers倍,因此要小心不要把该默认值设置得太高。 通过独立地设置autovacuum_work_mem可能会对控制这种情况 有所帮助。
autovacuum_work_mem
(integer
)
指定每个自动清理工作者进程能使用的最大内存量。如果指定值时没有单位,则以千字节为单位。其默认值为 -1,表示转而使用 maintenance_work_mem的值。当运行在其他上下文环境中时, 这个设置对VACUUM
的行为没有影响。
logical_decoding_work_mem
(integer
)
指定逻辑解码要使用的最大内存量,在将某些解码的更改写入本地磁盘之前。 这将限制逻辑流复制连接使用的内存量。它默认为 64 兆字节(64MB
)。 由于每个复制连接仅使用此大小的单个缓冲区,并且安装通常不会同时具有多个此类连接(受 max_wal_senders
的限制),因此将此值设置得明显高于 work_mem
是安全的,从而减少写入磁盘的解码更改数量。
max_stack_depth
(integer
)
指定服务器的执行堆栈的最大安全深度。这个参数的理想设置是由内核强制的实际栈尺寸限制(ulimit -s
所设置的或者本地等价物),减去大约一兆字节的安全边缘。 需要这个安全边缘是因为在服务器中并非所有例程都检查栈深度,只是在关键的可能递规的例程中才进行检查。如果指定值时没有单位,则以千字节为单位。默认设置是两兆字节(2MB
),这个值相对比较小并且不可能导致崩溃。但是,这个值可能太小了,以至于无法执行复杂的函数。只有超级用户可以修改这个设置。
把max_stack_depth
参数设置得高于实际的内核限制将意味着一个失控的递归函数可能会导致一个独立的后端进程崩溃。 在PostgreSQL能够检测内核限制的平台上, 服务器将不允许把这个参数设置为一个不安全的值。不过,并非所有平台都能提供该信息,所以我们还是建议你在选择值时要小心。
shared_memory_type
(enum
)
指定服务器应用于主共享内存区域的共享内存实现,包括 PostgreSQL 的共享缓冲区和其他共享数据。 可能的值为 mmap
(对使用 mmap
分配的匿名共享内存),sysv
(通过 shmget
分配的系统V 共享内存),和windows
(Windows共享内存)。 并非在所有平台上都支持全部值;第一个被支持的选项是该平台的默认选项。 sysv
选项不是任何平台的默认选项,通常不建议使用,因为它通常需要非默认的内核设置来允许大量的地址分配(参见 第 18.4.1 节)。
dynamic_shared_memory_type
(enum
)
指定服务器应该使用的动态共享内存实现。可能的值是posix
(用于使用 shm_open
分配的 POSIX 共享内存)、sysv
(用于通过shmget
分配的 System V 共享内存)、 windows
(用于 Windows 共享内存)、和mmap
(使用存储在数据目录中的内存映射文件模拟共享内存)。并非所有平台上都支持所有值,平台上第一个支持的选项就是其默认值。 在任何平台上mmap
选项都不是默认值,通常不鼓励使用它,因为操作系统会 反复地把修改过的页面写回到磁盘上,从而增加了系统的I/O负载。不过当 pg_dynshmem
目录被存储在一个 RAM 盘时或者没有其他共享内存功能可用时, 它还是有用的。