PostgreSQL 硬件性能调优
PostgreSQL是一个由世界范围内开发人员在互联网上开发的对象-关系型数据库。她是商业数据库如Oracle和Informix一个开源替代。
PostgreSQL最初由加大伯克利分校开发。在1996年,一个团队开始在互联网上开发这个数据库。他们通过邮件来交流思想并且通过文件服务器来共享代码。PostgresSQL现在在专业特性、性能、可靠性上比得上商业数据库。她具有事务、视图、存储过程、引用一致性约束。她支持许多编程接口,包括ODBC、Java(JDBC)、TCL/TK、PHP、Perl和Python。得益于一大批有才干的网络开发者,PostgreSQL 继续以惊人的速度提升 。
性能概念
有两个方面的数据库性能调优。一个是提高数据库服务器使用的CPU、内存、磁盘驱动。第二个是优化发送到数据库的查询。这篇文章讨论硬件方面的性能调优。查询优化可以通过SQL命令如 CREATE INDEX、VACUUM 、VACUUM FULL、ANALYZE、CLUSTER 和 EXPLAIN 来完成。这些在《PostgreSQL:Introduction and Concepts》中讨论在 http://momjian.us/main/writings/pgsql/aw_pgsql_book/。
为了理解硬件调优问题,理解计算机里正在发生什么是非常重要的。简单来说,计算机可以想象成一个被存储包围着的CPU。在和CPU同一芯片上是几个用来存储中间结果和不同指针和计数的CPU寄存器。在这些周围是用来保存最近访问信息的CPU缓存。在CPU缓存外面是大量的用来保存正在执行程序和数据的随机访问主存(RAM)。在主存的外面是存储更多信息的磁盘驱动。磁盘驱动是唯一的持久存储区,所以任何要在关机后保持的信息都必须放在那里。总之,在CPU的周围有这些存储:
存储区域 | 度量单位 |
---|---|
CPU 寄存器 | bytes |
CPU 缓存 | kilobytes |
RAM | megabytes |
磁盘驱动 | gigabytes |
你可以看到距离CPU越远存储越大。理论上,大量的持久存储可以放在CPU旁边,但是会特别慢且比较昂贵。在实践中,经常被使用的信息存储在CPU旁边,不经常访问的信息存储远离CPU,当使用时被带到CPU。
保持信息靠近CPU
在不同存储区之间移动信息是自动发生的。编译器决定哪些信息保存在寄存器中。CPU芯片逻辑保证最近使用的信息保存在CPU缓存中。操作系统控制哪些信息存储在RAM并在磁盘驱动器间来回穿梭。
CPU寄存器和CPU缓存不能被数据库管理员有效的调优。有效的数据库调优涉及在RAM中增加有用的信息,来尽可能的阻止磁盘访问。
你可能认为这很容易,但是不是。计算机的RAM包含很多东西:
- 正在执行的程序
- 程序数据和栈
- PostgreSQL shared buffer cache
- 内核磁盘缓存
- 内核
合适的调优包括在RAM中尽可能多的保存数据库信息,同时不影响操作系统的其他区域
PostgreSQL Shared Buffer Cache
PostgreSQL 不直接更改磁盘上的信息。相反,它请求读数据到PostgreSQL shared buffer cache。PostgreSQL后台来读和写这些块,并且最终刷到磁盘上。
需要访问表的后台先在缓存中查找需要的块。如果它们已经存在,可以马上继续处理。如果没有,一个操作系统请求被发送去加载这些块。这些块从内核磁盘缓存或者直接从磁盘加载。这可能是昂贵的操作。
多大算太大?
你可能想,“我把全部的RAM都给PostgreSQL shared buffer cache”。然而,如果你那样做,会没有其他的空间分配给内核和其他任何要运行的程序。合适的PostgreSQL shared buffer cache 是不产生其他不良活动的最大空间。
为了理解不良活动,你需要去理解UNIX 操作系统怎么管理内存。如果有足够的内存去装进所有的程序和数据,只需要很少的内存管理。然而,如果所有东西不能装进内存,内核开始强制内存刷出到叫swap磁盘区域。它把最近没有使用的内存页移出。这个操作叫做swap pageout。Pageouts 不是问题因为它们发生在非活动时间段。糟糕的是当这些页必须从swap返回时,意为着移到swap的旧页必须移入RAM。这种叫 swap pagin。这种情况糟糕因为当页从swap移动时,程序必须暂停直到pagein完成。
Pagein 活动可以被系统分析工具如vmstat和sar展示,并且暗示没有足够的内存去有效运行。不要混淆swap pageins和ordinary pageins。它包含的页是作为的正常的操作系统读取文件系统的一部分。如果你不能发现swap pageins,许多pageouts 是一个好的暗示,你也正在进行swap pageins。
缓存大小的影响
你可能好奇为什么缓存大小如此重要。首先,假设PostgreSQL shared buffer cache 足够大去装下整个表。这张表的重复顺序扫描不需要访问磁盘,因为所有的数据已经在缓存中了。现在假设缓存比表小一个块大小。这个表的顺序扫描需要加载所有的表数据块进入缓存直到最后一个。当需要块时,最老的块被移出,这种情况就是表的第一个块。当另外一个顺序扫描发生时,第一个块已经不在缓存中,需要把它重新装入,这时最老的块,也就是这个表的第二个块被移出缓存。这又会把下一个需要的块继续推出去直到这个表的最后。这是一个极端的例子,但是你可以明白减少一个块可以把缓存的效率从100%变为0%。它展示了找到正确的缓存大小可以极大的影响性能。
合适的Shared Buffer Cache大小
理想的,PostgreSQL shared buffer cache 应该是:
- 足够大能装入经常访问的表
- 足够小能避免swap pageins活动
记住postmaster分配所有的共享内存在它启动的时候。这片区域保持相同的大小即使没有一个人访问数据库。一些操作系统pageout不涉及共享内存,一些锁定主存中的共享内存。锁定共享内存是推荐的。PostgreSQL管理员指南有关于各种操作系统内核参数配置信息。
批量排序内存大小
另外一个调优的参数是批量排序使用的内存大小。当排序大表或者结果集,PostgresSQL会分成几部分排序它们,把中间结果放到临时文件中。这些文件合并并且重新排序直到所有的行完成排序。增加批量大小能减少临时文件且经常能加快排序。然而,如果批量排序太大,它们会引起pageins,因为在排序时一部分批量排序pageout到swap区。这种情况下使用更小的批量排序和更多的临时文件是更快的。所以再次,swap pageins 指示已经分配了太多的内存。记住每个后台排序如ORDER BY,CREATE INDEX,或者merge join都使用这个参数。有几个并行的排序会使用几倍的这个内存大小。
缓存大小和排序大小
缓存大小和排序大小都影响内存使用,所以你不可能最大化一个而不影响另一个。记住缓存大小在postmaster启动是分配,而排序大小由正在执行的排序操作的数量决定。通常,缓存大小比排序大小更重要。然而,可预见的使用ORDER BY ,CREATE INDEX或者merge joins的确定查询可以被大的批量排序大小加速。
同时,很多操作系统限制了多少共享内存可以分配。增加这些限制需要操作系统相关的知识去重新编译或者重新配置内核。更多的信息可以在PostgreSQL管理员指南中找到。
作为调优的起点,如果只有一些大的会话,用15%的主存作为缓存大小,2-4%作为排序大小。如果有很多小的会话可以调的更小。你可以尝试增加他们去看是否有性能提升同时没有swap发生。如果shared buffer cache太大,你会浪费维护太多缓存的管理开销,并且占用其他进程和额外的内核磁盘缓存的RAM。
一个有价值的参数是effective_cache_size。调优者使用这个参数去估计内核磁盘缓存的大小。在不固定缓存大小的内核中,这个参数可以设置成未使用RAM的平均大小,因为这些内核使用未使用的RAM去缓存最近访问的磁盘页。在其他固定大小磁盘缓存的内核中,通常应该把你的内核缓存设置成10%RAM大小。
磁盘局限性
磁盘驱动的物理特性导致了不同于文章中提到的其他存储区域的性能特点。其他的存储区域可以以相同速度访问任何字节。磁盘驱动有它自己的自旋盘和移动头,访问在磁头附近的数据比距离远的数据快的多。
移动磁头去盘面的另外磁柱需要花费一些时间。Unix开发者知道这个特性。当排序磁盘上的大文件,他们尝试把文件的各个条放在一起。例如,假设一个文件需要10个磁盘块。操作系统可能把1-5块放在一个磁柱上,6-10块放在另外磁柱上。如果这个文件需要从头读到未,只需要磁头移动两次。--一次获取1-5块的磁柱,一次获取6-10块。然而,如果文件不是顺序读,如块1,6,2,7,3,8,4,9,5,10;需要10次磁头移动。你可以看到,在磁盘上,顺序读比随机读快很多。这是为什么如果一个表的大部分内容需要被读,PostgreSQL偏向于顺序扫描而不是索引扫描。这也凸显了缓存的价值。
多磁盘Spindles
磁头在数据库活动期间移动相当多。如果产生太多的读写请求,驱动会变得饱和,导致性能不佳。(vmstat和sar可以提供每个磁盘活动量的信息)
一个磁盘饱和的解决方法是移动一些PostgreSQL的数据文件到其他磁盘。记着,移动文件到同一磁盘的其他文件系统没有帮助。所有在同一磁盘的文件系统使用同一个磁头。
通过磁盘驱动的数据库访问可以通过以下几个方法加速:
移动数据库、表、索引
表空间允许你在不同的驱动上创建对象。
移动WAL
initdb -X 和符号连接可以用来移动pg_xlog目录到不同的磁盘驱动。不像其他的写操作,PostgreSQL 写日志必须在事务完成前刷到磁盘。缓存不能用来推迟这些写操作。为写日志使用单独的磁盘可以允许磁头停留在当前日志的磁柱而没有移动延迟。(你可以使用postgres -F 参数来阻止日志刷到磁盘,但是一个操作系统灾难需要从备份中恢复)
其他的选项包括使用磁盘阵列,通过多个磁盘驱动加速一个文件系统。镜像会降低数据库写速度,但是会加速数据库读,因为数据可以从每个驱动中获得。许多站点使用RAID1+0或RAID0+1,因为它同时提供了性能和可靠的优势。RAID5对于6个或者更多驱动来说是更快的。理论上,RAID 5将有一个由电池支持的高速缓存,因此写入可以以一种有效的方式刷新到磁盘,而不是在编写文件的时候减慢应用程序的速度。
磁盘驱动缓存
现代磁盘驱动都有读和写缓存。读缓存保证最近请求的磁盘块在磁盘内存中可用。写缓存保存写请求直到它们可以有效的存储到盘面上。你可能意识到这可能产生问题--如果磁盘断电了,当它持有的写数据还没写到磁盘上怎么办?一些磁盘驱动和RAID控制器有后备电池写缓存来保证数据安全,且当电源完全恢复时把数据写到盘面。然而,大部分驱动没有这种特性,因此它们是不可靠的。
幸运的是,在大多数驱动你可以关闭写缓存。SCSI驱动默认关闭写缓存。IDE设备写缓存通常是打开的,但是可以从操作系统命令关闭如hdparm -W0,sysctl hw.ata.wc=0或者scsimd。但是,一些IDE驱动虽然报告写缓存关闭了,但是仍在使用它,这是不可靠的。没有精确的测试很难判断哪个驱动在撒谎。
因为PostgreSQL每个事务提交时使用fsync()写WAL日志到磁盘,并且等待写完成。当使用写缓存时用户能看到巨大的性能提升。因此,为了性能和可靠性,如果PostgreSQL能使用有后备电池的写缓存是一个理想的方案。
SCSI vs IDE
SCSI驱动通常比IDE驱动贵很多。SCSI驱动有一个协议用来在操作系统和控制器间通信,然而IDE驱动简单的多且同一时刻只能接受一个请求。有标记队列的SCSI驱动可以接受多个请求并且重新排列它们提高效率。这是为什么当单用户或者单文件IO时SCSI和IDE驱动有相似的性能特性,但是当多个用户或者进程请求时SCSI比IDE性能更好。因此,SCSI更适合重负载数据库服务器。
实际上,SCSI或IDE是唯一的方法区分两种主要的驱动:企业驱动,为高性能和高可靠设计。个人电脑驱动,为最小耗费设计。这篇文章http://www.seagate.com/content/docs/pdf/whitepaper/D2c_More_than_Interface_ATA_vs_SCSI_042003.pdf,做了优秀的工作来描述在生产基于性能可靠性或者减小耗费的驱动。它是一个优秀的指导在选择基于这些特性的驱动。
文件系统
一些操作系统支持多磁盘文件系统。在这种情况,可能很难知道哪个文件系统表现最好。PostgreSQL通常在传统的Unix文件系统表现最好,像很多操作系统支持的BSD UFS/FFS等。UFS默认的8K块大小和PostgreSQL的页大小一样。你可以运行在日志和基于日志的文件系统,但是这些会在WAL的fsync执行期间引起额外的开销。老的基于SvR3的文件系统变的太碎片难以有好的性能。
因为有许多文件系统可以选择并且没有一个是最优的,Linux上的文件系统选择是特别困难的:ext2不是完全的灾难安全的,ext3,XFS和JFS是基于日志的且Reiser是对小文件优化且记录日志。虽然ext2比日志文件系统块很多,但是当要求灾难恢复时ext2是不可选的。如果必须使用ext2,使用sync enabled挂载。一些人推荐使用ext3时用data=writeback挂载。
不推荐PostgreSQL使用NFS和其他远程文件系统。NFS和本地文件系统没有一样的文件系统特性,这些不一致可能引起数据可靠和灾难恢复问题。
多CPU
PostgreSQL使用多进程模型,意为着每个数据库连接拥有它自己的unix进程。因此,所有多CPU操作系统可以通过可用的CPU来加速多数据库连接。然而,如果只有一个数据库连接是活动的,它只能使用一个CPU。PostgreSQL没有使用多线程模型从而允许一个进程使用多个CPU。
检查点
当WAL文件充满时,一个检查点被执行来强制所有的脏缓存刷到磁盘,从而使日志文件可以循环。检查点也会在固定的时间间隔被执行,通常是5分钟。如果有很多的数据库写活动,WAL段会很块被充满,导致所有磁盘缓存被刷到磁盘引起系统过度的缓慢。
检查点
检查点应该每几分钟发生一次。如果在一分钟发生几次,性能会变差。在服务日志中查找"checkpoint_warning"消息来决定你的检查点是否执行太频繁。如果30秒内检查点执行超过一次,会出现这个消息。
减小检查点的频率包括增加在data/pg_xlog中WAL文件的数量。每个文件是16 megabyte,所以它影响磁盘使用率。默认的配置使用了最小数量的日志文件。为了减小检查点频率,你需要增加这个参数:
checkpoint_segments=3
默认值是3。增加它直到检查点几分钟出现一次。另外一个可能出现的消息是:
LOG:XLogWrite:new log file created - consider increasing WAL_FILES
这个消息暗示postgresql.conf中的wal_files参数需要增加。
结论
幸运的是,PostgreSQL 不需要太多调优。大部分参数自动调整到最佳性能。缓存大小和排序大小是两个参数管理员能够控制去更好的利用可用内存。磁盘访问可以通过多个驱动加速。其他参数可能设置在share/postgresql.conf.sample。你可以copy到data/postgresql.conf去实验其他PostgreSQL更奇异的参数。