PostgreSQL 打开文件句柄优化 - 百万以上实体对象初始化优化

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

标签

PostgreSQL , ulimit , nofile , fs.nr_open , setrlimit


背景

数据库是一种非常庞大的应用系统,就拿PostgreSQL来说,每个实体对象(物化视图、表、序列、索引、TOAST表、TOAST索引)都需要至少1个数据文件,还需要fsm, vm等辅助文件。

当数据库中有很多实体对象时,文件数会非常庞大,而对于Linux操作系统来说,单个进程的打开文件数是有限制的。PostgreSQL数据库新增了一层文件句柄管理,使用文件句柄池来管理文件的打开和关闭。

但是如果需要打开或关闭的文件数特别多(例如 某个业务一下子创建了几十万张表,然后做检查点),文件句柄池就会不停的切换,成为系统的瓶颈。

本文结合Linux 的文件句柄限制,以及PostgreSQL的文件句柄相关配置,讲一下如何优化这类应用场景。

Linux 文件句柄部分介绍 转载自

http://qujunorz.blog.51cto.com/6378776/1703295

Linux下设置最大文件打开数nofile及nr_open、file-max说明

在开发运维的时候我们常常会遇到类似“Socket/File: Can’t open so many files”,“无法打开更多进程”,或是coredump过大等问题,这些都可以设置资源限制来解决。今天在教某位客户设置最大文件数方法时,搜索网上的方法时发现各家说法并不一致,便写了这篇文档。

通常对linux某个用户设置系统资源,我们都已经知道可以用ulimit命令来查看和设置。
表 1. ulimit 参数说明

选项 [options] 含义 例子
-H 设置硬资源限制,一旦设置不能增加。 ulimit – Hs 64;限制硬资源,线程栈大小为 64K。
-S 设置软资源限制,设置后可以增加,但是不能超过硬资源设置。 ulimit – Sn 32;限制软资源,32 个文件描述符。
-a 显示当前所有的 limit 信息。 ulimit – a;显示当前所有的 limit 信息。
-c 最大的 core 文件的大小, 以 blocks 为单位。 ulimit – c unlimited; 对生成的 core 文件的大小不进行限制。
-d 进程最大的数据段的大小,以 Kbytes 为单位。 ulimit -d unlimited;对进程的数据段大小不进行限制。
-f 进程可以创建文件的最大值,以 blocks 为单位。 ulimit – f 2048;限制进程可以创建的最大文件大小为 2048 blocks。
-l 最大可加锁内存大小,以 Kbytes 为单位。 ulimit – l 32;限制最大可加锁内存大小为 32 Kbytes。
-m 最大内存大小,以 Kbytes 为单位。 ulimit – m unlimited;对最大内存不进行限制。
-n 可以打开最大文件描述符的数量。 ulimit – n 128;限制最大可以使用 128 个文件描述符。
-p 管道缓冲区的大小,以 Kbytes 为单位。 ulimit – p 512;限制管道缓冲区的大小为 512 Kbytes。
-s 线程栈大小,以 Kbytes 为单位。 ulimit – s 512;限制线程栈的大小为 512 Kbytes。
-t 最大的 CPU 占用时间,以秒为单位。 ulimit – t unlimited;对最大的 CPU 占用时间不进行限制。
-u 用户最大可用的进程数。 ulimit – u 64;限制用户最多可以使用 64 个进程。
-v 进程最大可用的虚拟内存,以 Kbytes 为单位。 ulimit – v 200000;限制最大可用的虚拟内存为 200000 Kbytes。

当然我们都知道linux大部分的命令设置都是临时生效,而且ulimit命令只对当前终端生效,如果需要永久生效的话,我们有两种方法,一种是将命令写至profile和bashrc中,在有些网页中说写到rc.local,其实是不对的;还有一种就是在limits.conf中添加记录(需重启生效,并且在/etc/pam.d/中的seesion有使用到limit模块)。接下来讨论的就是在limits.conf用户最大文件打开数限制的相关内容。

针对用户打开最大文件数的限制, 在limits.conf对应的nofile,不管是man手册还是文件中说明都只是一句话“maximum number of open files",它其实对应是单个进程能打开的最大文件数,通常为了省事,我们想取消它的限制,根据man手册中,“values -1, unlimited or infinity indicating no limit”,-1、unlimited、infinity都是表明不做限制,可是当你实际给nofile设置成这个值,等你重启就会发现无法登录系统了。

由此可见,nofile是有一个上限的,同时用ulimit测试:

#ulimit -n unlimited  
bash: ulimit: open files: cannot modify limit: 不允许的操作  

写一个简单的for循环得出:

#for  V in `seq  100000  10000000`;do ulimit -n $V;[[ $? != 0 ]]&&break;done  

再执行ulimit -n ,可以看到1048576就是nofile的最大值了,但为什么是这个值?1048576是1024*1024,当然这并没有什么卵用。。。再跟踪一下我们就会发现这个值其实是由内核参数nr_open定义的:

# cat /proc/sys/fs/nr_open   
1048576  

到此我们就要说起nr_open,与file-max了,网上在说到设置最大文件数时偶尔有些帖子也说到要修改file-max,字面上看file-max确实像是对应最大文件数,而在linux内核文档中它们两的解释是:

file-max:   

The value in file-max denotes the maximum number of file-  
handles that the Linux kernel will allocate. When you get lots  
of error messages about running out of file handles, you might  
want to increase this limit  

执行:grep -r MemTotal /proc/meminfo | awk '{printf("%d",$2/10)}',可以看到与file-max是相近的;

nr_open:  

This denotes the maximum number of file-handles a process can  
allocate. Default value is 1024*1024 (1048576) which should be  
enough for most machines. Actual limit depends on RLIMIT_NOFILE  
resource limit.  

额,到此笔者有点晕了,file-handles(即文件句柄),然后相比而言在UNIX/LINUX中我们接触更多是file discriptor(FD,即文件描述符),通过百度搜索,似乎file-handle在windows中是一个类似file discrptor的东东,但是我们讨论的是linux,再google一下,我们可以精确到c语言中这两个概念的区别,据他们的讨论file-handle应该是一个高层的对象,使用fopen,fread等函数来调用,而FD是底层的一个对象,可以通过open,read等函数来调用。

到此,我们应该可以下一个大致的结论了,file-max是内核可分配的最大文件数,nr_open是单个进程可分配的最大文件数,所以在我们使用ulimit或limits.conf来设置时,如果要超过默认的1048576值时需要先增大nr_open值(sysctl -w fs.nr_open=100000000或者直接写入sysctl.conf文件)。当然百万级别的单进程最大file-handle打开数应该也够用了吧。。

由于笔者水平有限,内容难免有错漏之处,还有file-max的默认值为何会比nr_open值要小也没有再深入追究,欢迎大家来讨论交流。

小结

ulimit 的nofiles限制不能超过sysctl设置的fs.nr_open参数。

所以如果要加大单个进程打开的文件数,首先要扩大fs.nr_open。

另外再/etc/security/limits.conf中设置的nofiles,绝对不要超过fs.nr_open,否则会无法登陆系统。

PostgreSQL 相关配置

max_files_per_process = 1000         # min 25  

max_files_per_process (integer)  

Sets the maximum number of simultaneously open files allowed to each server subprocess.   

The default is one thousand files.   

If the kernel is enforcing a safe per-process limit, you don't need to worry about this setting.   

But on some platforms (notably, most BSD systems), the kernel will allow individual processes to open many more files than the system can actually support if many processes all try to open that many files.   

If you find yourself seeing "Too many open files" failures, try reducing this setting.   

This parameter can only be set at server start.  

PostgreSQL test case

环境:基于流复制的主备,采用异步流复制。

在主库创建一个模板库,然后在模板库中创建45万张表,每张表有若干索引,约束,序列。

观察主备延迟的情况。

1. 创建模板库

create database tmp1 with template template0;  

2. 连接到模板库,创建测试单表

\c tmp1  

create table public.test(  
c0 serial  unique  check(c0>0),   
c1 serial  unique  check(c1>0),   
c2 serial  unique  check(c2>0),  
c3 serial  unique  check(c3>0),   
c4 serial  ,  
c5 serial  ,   
c6 serial  ,  
c7 serial  ,   
c8 serial  ,  
c9 serial  ,   
c10 serial  ,   
c11 serial  ,   
c12 serial  ,  
c13 serial  ,   
c14 serial  ,  
c15 serial  ,   
c16 serial  ,  
c17 serial  ,   
c18 serial  ,  
c19 serial  ,   
c20 serial  ,   
c21 serial  ,   
c22 serial  ,  
c23 serial  ,   
c24 serial  ,  
c25 serial  ,   
c26 serial  ,  
c27 serial  ,   
c28 serial  ,  
c29 serial  ,   
c30 serial  ,   
c31 serial  ,   
c32 serial  ,  
c33 serial  ,   
c34 serial  ,  
c35 serial  ,   
c36 serial  ,  
c37 serial  ,   
c38 serial  ,  
c39 serial    
);  

3. 在模板库创建3000个schema

for ((i=1;i<=3000;i++)) ; do psql tmp1 -c "create schema s$i" ; done  

1分钟内完成

观察到主备无延迟

select pg_size_pretty(pg_xlog_location_diff(pg_current_xlog_insert_location(),sent_location)),  pg_size_pretty(pg_xlog_location_diff(pg_current_xlog_insert_location(),replay_location)) from pg_stat_replication ;  

\watch 1  

4. 每个schema创建150张表(基于模板表),开启150个并发连接开始建表

vi test.sh  

for ((x=1;x<=150;x++)) ; do (for ((i=1;i<=3000;i++)) ; do psql tmp1 -c "create table if not exists s$i.test$x (like public.test including all)" ; done) & done  

chmod 500 test.sh  

nohup ./test.sh >/dev/null 2>&1 &  

37分钟完成

除了初始化日志,autovacuum也会产生大量的日志

观察主备延迟如下

发送延迟5.6GB,应用延迟15GB

发送延迟多久平复:与 主备网络环境、主库XLOG读取速度、备库XLOG写入速度 有关,测试环境耗时约2分钟。

应用延迟多久平复:与 备库apply速度有关(关闭备库fsync将达到0延迟目的), 测试环境耗时约9分钟。

创建完成后

模板库对象数

select count(*) from pg_class;  

225 万   

模板库大小

\l+  

47 GB   

5. 主库执行检查点

checkpoint;  

观察主备延迟,备库checkpoint进程,CPU 100%,维持了数十个小时。

strace -p checkpoint观察到缓慢的close(xxx)。

6. 创建数据库(使用前面创建的模板库)

create database test with template tmp1;  

耗时4分钟 。

备库XLOG延迟360字节 。

备库XLOG延迟平复时间 6分钟 。

备库startup进程strace跟踪,涉及大量文件操作,因为这个库涉及到的文件数有2252981个。    

lstat("base/16384/12976402", {st_mode=S_IFREG|0600, st_size=0, ...}) = 0  
open("base/16384/12976402", O_RDONLY)   = 12  
open("base/24795111/12976402", O_RDWR|O_CREAT|O_EXCL, 0600) = 13  
read(12, "", 65536)                     = 0  
close(13)                               = 0  
close(12)                               = 0  
lstat("base/16384/305700", {st_mode=S_IFREG|0600, st_size=8192, ...}) = 0  
open("base/16384/305700", O_RDONLY)     = 12  
open("base/24795111/305700", O_RDWR|O_CREAT|O_EXCL, 0600) = 13  
read(12, "\0\0\0\0\30p\234+\0\0\0\0000\0\360\37\360\37\4 \0\0\0\0b1\5\0\2\0\0\0"..., 65536) = 8192  
write(13, "\0\0\0\0\30p\234+\0\0\0\0000\0\360\37\360\37\4 \0\0\0\0b1\5\0\2\0\0\0"..., 8192) = 8192  
sync_file_range(0xd, 0, 0x2000, 0x2)    = 0  
read(12, "", 65536)                     = 0  
close(13)                               = 0  
close(12)                               = 0  
lstat("base/16384/639437", {st_mode=S_IFREG|0600, st_size=8192, ...}) = 0  

7. 删除单个schema

\c test  

drop schema s1 cascade;  

耗时3秒 。

备库XLOG延迟773字节 。

备库XLOG延迟平复时间 27秒 。

相差9倍的时间。所以你需要注意这一点。

8. 删除刚刚创建的数据库

drop database  test;  

耗时1分钟 。

备库XLOG延迟176字节 。

备库XLOG延迟平复时间 30秒,实际上没有差别,因为drop database结束后,才产生XLOG,然后这笔XLOG还需要在备库apply,所以drop database实际上是没有延迟的 。

优化

1. 修改操作系统打开文件限制

vi /etc/sysctl.conf  

fs.nr_open=10240000  

sysctl -p  
vi /etc/security/limits.conf  

* soft    nofile  10240000  
* hard    nofile  10240000  

2. 修改数据库打开文件限制

vi postgresql.conf  

max_files_per_process = 2048000  

3. 使用新的ulimit值,重启数据库

通常退出Linux会话,重新登录即可

pg_ctl restart -m fast  

4. 备库关闭 fsync 将缩短至几乎0延迟

在有多个备库的情况下,如果考虑性能,关闭某些备库的fsync也未尝不可。

例如内存数据库voltdb,就是类似的。

参考

1. man setrlimit

       RLIMIT_NOFILE  
              Specifies  a  value  one  greater  than the maximum file descriptor number that can be opened by this process.  Attempts (open(2), pipe(2), dup(2), etc.)  to exceed this limit yield the error EMFILE.  
              (Historically, this limit was named RLIMIT_OFILE on BSD.)  

2. kernel-doc/sysctl/fs.txt

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
6月前
|
SQL 关系型数据库 测试技术
沉浸式学习PostgreSQL|PolarDB 20: 学习成为数据库大师级别的优化技能
在上一个实验《沉浸式学习PostgreSQL|PolarDB 19: 体验最流行的开源企业ERP软件 odoo》 中, 学习了如何部署odoo和polardb|pg. 由于ODOO是非常复杂的ERP软件, 对于关系数据库的挑战也非常大, 所以通过odoo业务可以更快速提升同学的数据库优化能力, 发现业务对数据库的使用问题(如索引、事务对锁的运用逻辑问题), 数据库的代码缺陷, 参数或环境配置问题, 系统瓶颈等.
762 1
|
18天前
|
存储 JSON 关系型数据库
PostgreSQL Json应用场景介绍和Shared Detoast优化
PostgreSQL Json应用场景介绍和Shared Detoast优化
|
3月前
|
弹性计算 关系型数据库 数据库
开源PostgreSQL在倚天ECS上的最佳优化实践
本文基于倚天ECS硬件平台,以自顶向下的方式从上层应用、到基础软件,再到底层芯片硬件,通过应用与芯片的硬件特性的亲和性分析,实现PostgreSQL与倚天芯片软硬协同的深度优化,充分使能倚天硬件性能,帮助开源PostgreSQL应用实现性能提升。
|
7月前
|
SQL 弹性计算 测试技术
如何在PolarDB-X中优化慢SQL
《PolarDB-X动手实践》系列第六期,本场景带您体验如何使用PolarDB-X提供的解决慢SQL的相关工具。
733 0
|
8月前
|
存储 Java 测试技术
深度优化 | PolarDB-X 基于向量化SIMD指令的探索
本文将介绍PolarDB-X对于向量化SIMD指令的探索和实践,包括基本用法及实现原理,以及在具体算子实现中的思考和沉淀。
|
8月前
|
关系型数据库 测试技术 分布式数据库
PolarDB | PostgreSQL 高并发队列处理业务的数据库性能优化实践
在电商业务中可能涉及这样的场景, 由于有上下游关系的存在, 1、用户下单后, 上下游厂商会在自己系统中生成一笔订单记录并反馈给对方, 2、在收到反馈订单后, 本地会先缓存反馈的订单记录队列, 3、然后后台再从缓存取出订单并进行处理. 如果是高并发的处理, 因为大家都按一个顺序获取, 容易产生热点, 可能遇到取出队列遇到锁冲突瓶颈、IO扫描浪费、CPU计算浪费的瓶颈. 以及在清除已处理订单后, 索引版本未及时清理导致的回表版本判断带来的IO浪费和CPU运算浪费瓶颈等. 本文将给出“队列处理业务的数据库性能优化”优化方法和demo演示. 性能提升10到20倍.
595 4
|
9月前
|
存储 缓存 NoSQL
[译]解锁TOAST的秘密:如何优化PostgreSQL的大型列存储以最佳性能和可扩展性
[译]解锁TOAST的秘密:如何优化PostgreSQL的大型列存储以最佳性能和可扩展性
129 0
|
10月前
|
SQL 关系型数据库 MySQL
深度解析PolarDB DDL锁的优化和演进
DDL是数据库所有SQL操作中最繁重的一种,本文总结介绍了云原生数据库PolarDB中DDL全链路MDL锁治理的经验和进展,持续优化用户的使用体验,为用户打造最佳的云原生数据库。
|
11月前
|
SQL 关系型数据库 API
【PostgreSQL】PostgreSQL扩展:pg_stat_statements 优化SQL
【PostgreSQL】PostgreSQL扩展:pg_stat_statements 优化SQL
|
11月前
|
SQL 存储 缓存
开源分布式数据库PolarDB-X源码解读——PolarDB-X源码解读(十二):谈谈in常量查询的设计与优化
开源分布式数据库PolarDB-X源码解读——PolarDB-X源码解读(十二):谈谈in常量查询的设计与优化
185 0

相关产品

  • 云原生数据库 PolarDB