postgres服务并发测试

存在的问题

  • 执行大表的全表查询(select *)进程被kill;
  • 创建GIST、GIN等索引进程被kill;

原因:

  • Linux 内核根据应用程序的要求分配内存,绝大部分应用程序不会同时使用全部内存,为了提高性能,内核采用(over-commit memory)内存分配办法来间接利用这部分 “空闲” 的内存,提高整体内存的使用效率。
  • 当大多数应用程序都消耗完分配的内存时,所有应用已分配的内存超出了物理内存时(包括 swap),内核(OOM killer)会kill一些进程保障系统正常运行。OOM killer通过计算oom score选取进程进行删除。

解决方法

  • 增加物理内存或调整SWAP交换空间;
  • 调整work_mem、max_connections参数;
  • 使用更严格的内存提交策略overcommit_memory:
  • 通过oom_adj干预oom killer;

调整work_mem、max_connections参数

按照官方文档建议: work_mem * max_connections <= RAM + SWAP


加载

  • 测试数据:2G
  • 并发数: 11~99
  • 测试环境:RAM 32G,swap 32G
  • pg数据库配置:
    max_connections=100
    shared_buffers=8G
    work_mem=320M
    maintenance\_work_mem=64M
  • 测试结果:并发达到连接上限(100),持续测试两天无异常;
    将work_mem翻倍,测试参数边界:并发执行成功,无内存异常;

索引

  • 测试环境:物理内存32G,swap 15G
  • 测试数据:6G*10个表
  • pg数据库配置:

    max_connections=100
    shared_buffers=8G
    work_mem=320M
    maintenance_work_mem=320M

  • 测试结果:99个并发创建gist索引,未出现kill状况;

  • 待测试:测试数据较小未达到内存瓶颈,增加测试数据持续测试;

查询

测试用例

  • 全表导出: select * from pgtest;
  • 全表排序+聚集:select count() from (select from pgtest order by id)t;
  • 测试环境:RAM:64G, swap 32G
  • 测试数据:6.4G

配置1

max_connections=1000
work_mem=1GB
shared_buffers=16G
maintenance_work_mem=1G

测试结果

  • 全表导出:40个并发,36个psql进程被kill;
  • 全表排序聚集:执行成功;

按照文档推测,work_mem*并发数远大于物理内存,使用推荐配置测试。

配置2

max_connections=100
shared_buffers=16GB
work_mem=640MB
maintenance_work_mem=4GB

测试结果

  • 全表导出:36个并发,30个psql进程被kill;
  • 全表排序聚集:执行成功;

通过系统oom日志及pg数据库日志,确定由于psql占用内存过多,被OOM killer关闭,服务端则失去客户端连接;
可通过远程访问,但网络压力较大,传输结果时间较长,待测试。

修改overcommit_memory:

  • 多并发时,若系统资源不足,客户端抛出异常:out of memory**;
  • 不足:不适合生产场景,资源使用效率降低;

更改进程oom_adj:失败

  • 修改oom_adj后,相应进程不会被OOM kill;
  • 部分情况下,系统资源枯竭,出现宕机

结论

  • 加载多并发、小结果集查询,正常执行;
  • 查询大表select *,psql占用内存达到系统上限,大部分被kill;
  • 创建索引,6G数据多并发执行成功;
  • 由于进程一般不会用尽申请的所有内存,加上客户端psql进程占用内存的影响,无法准确测试服务进程并发上限。通过测试发现,使用建议在建议配置下,执行创建索引、加载、查询,未出现pg**服务进程**被kill情况;
  • 查询结果集大导致的psql占用大量内存,会在系统资源不足时被oom kill,目前数据库级别没有对psql内存限制的参数;
  • psql被kill的根本原因是并发数*单进程内存>物理内存,系统资源不足。通过修改OOM参数可以避免psql进程被kill,但无法改变系统负载过大资源不足的问题。

待测部分

  • 索引测试表数据量不足,增加数据测试;
  • 查询客户端被kill,尝试psql远程执行查询,测试服务端进程是否存在被kill情况;

参考
https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server
oracle、mysql等数据库也会出现类似问题
http://www.oracle.com/technetwork/articles/servers-storage-dev/oom-killer-1911807.html

附录

内存相关参数

work_mem
指定每个子查询(aggregate、sort)等操作使用的内存数量,单个查询可能使用几倍的work_mem;
建议:最大连接数work_mem每个查询平均子查询数 应该与物理内存+swap接近;

shared_buffers
数据库服务器共享内存缓冲区大小。默认值为128M,该参数只能在数据库服务启动时设定。
建议该值设为物理内存的1/4,较大的值并不一定能提高效率,因为postgres依赖系统缓冲cache,而缓冲占用内存;
增加本参数的同时,应该修改checkpoint_segments。

temp_buffers
会话级别参数,设定每个数据库会话最大的temporary memory。
该参数需要在使用临时表之前设定。

maintenance_work_mem
指定维护类操作(VACUUM, CREATE INDEX, ALTER TABLE ADD FOREIGN KEY)等可用的最大内存,默认是16M。由于一个会话只执行一个操作,且并发操作较少,该值可以比work_mem稍大。
注意:若已启动autocacuum,则最大可能分配autovacuum_max_workers*maintenance_work_mem的内存。

effective_cache_size
设定单个查询可用的磁盘缓存大小,对查询计划有影响。若缓存大则可能使用索引,缓存小则使用全盘扫描。需要和以下几个参数同时考虑:共享内存(shared buffers)、查询并发数、系统缓存等。

OOM score 计算

  • 根据进程使用的内存(包含SWAP)计算,内存使用越多,得分越高;
  • 若进程属于root用户,则将得分适当减少;
  • 将得分与进程的oom_score_adj进行相加;

调整OOM killer

// 调整进程oom_score  
echo -1000 > /proc/self/oom\_score_adj  
echo -17 > /proc/self/oom_adj  
// 调整pg编译选项,设置子进程默认值  
-DLINUX_OOM_SCORE_ADJ=-1000 或-DLINUX_OOM_ADJ=-17  
// 使用严格内存提交模式(不建议生产环境)  
sysctl -w vm.overcommit_memory=2  

你可能感兴趣的:(postgres服务并发测试)