如何确定 PostgreSQL 中池大小和数据库连接之间的最佳位置

lor*_*zor 15 postgresql

我们在处理高峰时段到我们的数据库服务器的流量时遇到问题。我们正在研究改进硬件(请参阅有关该方面的问题),但我们也希望进行池配置和服务器调整。

我们正在开发的应用程序是一款用于智能手机的回合制多人游戏,其后端由RailsunicornPostgreSQL 9.1作为数据库组成。我们目前有 600 000 名注册用户,并且由于游戏状态存储在数据库中,每隔几秒钟就会进行数千次写入。我们使用PgBadger分析了来自PostgreSQL的日志文件,在关键时间我们得到了很多

FATAL: remaining connection slots are reserved for non-replication superuser connections
Run Code Online (Sandbox Code Playgroud)

解决这个问题的天真解决方案是增加postgresql.conf 中的max_connections(当前为 100) 。我已经阅读了http://wiki.postgresql.org/wiki/Number_Of_Database_Connections这表明这可能不是正确的做法。在前面提到的文章中,它提到了在 max_connections 和pool size之间找到最佳位置。

为了找到这个甜蜜点,可以做些什么?是否有任何好的工具可以测量max_connectionspool size 的不同值的 I/O 性能?

我们当前的设置是 4 个游戏服务器,每个服务器有 16 个独角兽工人,池大小为 5。

以下是我们使用的非默认 postgres 设置:

version                      | PostgreSQL 9.1.5 on x86_64-unknown-linux-gnu,compiled by gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3, 64-bit
checkpoint_completion_target | 0.9
checkpoint_segments          | 60
checkpoint_timeout           | 6min
client_encoding              | UTF8
effective_cache_size         | 2GB
lc_collate                   | en_US.UTF-8
lc_ctype                     | en_US.UTF-8
log_destination              | csvlog
log_directory                | pg_log
log_filename                 | postgresql-%Y-%m-%d_%H%M%S.log
log_line_prefix              | %t
log_min_duration_statement   | 200ms
log_rotation_age             | 1d
log_rotation_size            | 10MB
logging_collector            | on
max_connections              | 100
max_stack_depth              | 2MB
server_encoding              | UTF8
shared_buffers               | 1GB
ssl                          | on
TimeZone                     | localtime
wal_buffers                  | 16MB
work_mem                     | 8MB
Run Code Online (Sandbox Code Playgroud)

Cra*_*ger 15

这里的简短回答是“以监控和性能指标为指导的反复试验”。

有一些一般的经验法则可以帮助您找到应该开始的模糊领域,但它们非常通用。广泛的指导方针“CPU 数量加上独立磁盘的数量”经常被引用,但这只是一个非常粗略的起点。

您真正需要做的是为您的应用程序获得强大的性能指标。开始记录统计数据。

对此,集成工具的方式并不多。有诸如 nagioscheck_postgres脚本、Cacti 系统性能计数器日志记录、PostgreSQL 统计数据收集器等之类的东西……但没有太多东西可以将它们放在一起。可悲的是,你必须自己做那一点。对于 PostgreSQL 端,请参阅PostgreSQL 手册中的监控。存在一些第三方选项,例如EnterpriseDB 的 Postgres Enterprise Monitor

对于此处提到的应用程序级指标,您需要将它们记录在共享数据结构或外部非持久数据库(如 Redis)中,并在记录它们时或将它们写入 PostgreSQL 数据库之前聚合它们。尝试直接记录到 Pg 会因记录测量产生的开销而扭曲您的测量,并使问题变得更糟。

最简单的选择可能是每个应用服务器中的单例,用于记录应用程序统计信息。您可能希望不断更新 min、max、n、total 和 mean;这样你就不必存储每个统计点,只需存储聚合。这个单例可以每 x 分钟将其聚合统计数据写入 Pg,该速率足够低,性能影响最小。

从...开始:

  • 什么是请求延迟?换句话说,应用程序从收到客户端请求到响应客户端需要多长时间。在一段时间内汇总记录,而不是作为单独的记录。按请求类型分组;说,按页。

  • 应用程序执行的每个查询或查询类型的数据库访问延迟是多少?从向数据库询问信息/存储信息到完成并可以继续下一个任务需要多长时间?同样,在应用程序中聚合这些统计信息,并且只将聚合信息写入数据库。

  • 你的吞吐量怎么样?在任何给定的 x 分钟内,您的应用程序执行的每个主要类的查询有多少由数据库提供服务?

  • 在 x 分钟的同一时间范围内,有多少客户端请求?

  • 每隔几秒采样一次并在数据库中的相同 x 分钟窗口上聚合,有多少个数据库连接?其中有多少是闲置的?有多少人活跃?在插入?更新?选择?删除?那段时间有多少交易?请参阅统计收集器文档

  • 在相同的时间间隔内再次采样和聚合,主机系统的性能指标如何?多少读取和多少写入磁盘 IO/秒?每秒兆字节的磁盘读写?CPU使用率?平均负载?内存使用?

您现在可以通过关联数据、绘制图表等来开始了解您的应用程序的性能。您将开始看到模式,开始发现瓶颈。

您可能会了解到,尽管磁盘 I/O 非常低(以兆字节/秒为单位),但您的系统仍处于瓶颈状态INSERT并且UPDATE处于高事务率。这暗示您需要使用电池支持的回写缓存 RAID 控制器或一些高质量的电源保护 SSD 来提高磁盘刷新性能。如果可以synchronous_commit = off在服务器崩溃和/或 a 中丢失一些事务,您也可以使用,以减轻commit_delay一些同步负载。

当您根据并发连接数绘制每秒事务数并校正应用程序看到的不同请求率时,您将能够更好地了解您的吞吐量最佳点在哪里。

如果您没有快速刷新存储(BBU RAID 或快速耐用的 SSD),您将不会需要更多的主动写入连接,最多可能是您拥有的磁盘数量的 2 倍,根据 RAID 安排可能更少,磁盘性能等。在这种情况下,它甚至不值得反复试验;只需将您的存储子系统升级到具有快速磁盘刷新的存储子系统

pg_test_fsync一个工具,会帮助你确定这可能是你的问题。大多数 PostgreSQL 软件包都将此工具安装为 contrib 的一部分,因此您不需要编译它。如果您的每秒操作数少于几千次,则pg_test_fsync迫切需要升级您的存储系统。我的配备 SSD 的笔记本电脑有 5000-7000。我的工作站使用 7200rpm SATA 磁盘的 4 磁盘 RAID 10 阵列和直写(非写入缓存)在 中获得大约 80 次f_datasync操作/秒,对于fsync();它慢了数百倍。比较:笔记本电脑,SSD VS工作站直写(非写入缓存)RAID 10. 这台笔记本电脑的 SSD 很便宜,我不一定相信它会在断电时刷新写入缓存;我保留了良好的备份,不会将其用于我关心的数据。优质 SSD 的性能即使不是更好,也同样出色,并且具有写入耐用性。

对于您的申请,我强烈建议您查看:

  • 具有快速刷新的良好存储子系统。我怎么强调这一点都不为过。优质电源故障安全 SSD 和/或具有电源保护回写缓存的 RAID 控制器。
  • 使用UNLOGGED表来存储您可以承受的数据丢失。定期将其聚合到日志表中。例如,将正在进行的游戏保存在未记录的表中,并将分数写入普通的持久表。
  • 使用commit_delay(对于快速刷新存储不太有用 - 提示)
  • 关闭synchronous_commit您可以承受丢失的事务(对于快速刷新存储不太有用 - 提示提示)
  • 分区表,尤其是数据“老化”并被清理的表。不是从分区表中删除,而是删除分区。
  • 部分索引
  • 减少您创建的索引数量。每个索引都有写入成本。
  • 将工作分成更大的事务
  • 使用只读热备副本来减轻主数据库的读取负载
  • 使用 memcached 或 redis 之类的缓存层来处理更改较少或可以承受陈旧的数据。您可以使用LISTENNOTIFY使用 PostgreSQL 表上的触发器来执行缓存失效。

如果有疑问:http : //www.postgresql.org/support/professional_support/