客户端连上了PG实例之后,从执行一条SQL 按下键盘上的回车键到客户端返回给你结果,这之间到底发生了什么?
先上一张图:
客户端要和数据库建立通信,需要经过连接器,它收到建立连接请求后,postgres主进程会fork出一个子进程来完成SQL执行操作,由客户端发起的SQL经过解析器-->优化器-->执行器等阶段后返回查询结果到客户端。
Postmaster进程
postmaster 后台进程就是封装了PG所有后台进程的入口。这里为什么只是说“入口”呢,因为实现由各自的代码文件实现。这里不负责。如像checkpoint进程,bgwriter进程,wal writer进程,autovacuum laucner进程,stats collector进程以及archiver进程等。这些进程都是由postmaster fork出来。另外postgres用户进程,vacuum worker进程等也是由postmaster进程负责fork。除此之外,postmaster 还负责监听用户请求,将用户请求从监听端口中接收过来,fork出一个postgres进程,专门负责处理用户SQL语句。
Fork的核心进程:
postgres: logger 负责写日志相关进程
postgres: checkpointer checkpoint进程
postgres: background writer 刷数据进程
postgres: walwriter WAL日志进程
postgres: autovacuum launcher vacuum launcher 进程
postgres: stats collector 统计信息进程
postgres: logical replication launcher 逻辑复制launcher进程
Postgres进程
postgres是用户进程,专门负责处理用户请求。也就是通常所说的backend进程。用户终端在上一步postmaster fork出postgres backend进程后,直接与此postgres 进程交互。无论是后台进程还是backend 进程,都由postmaster来维护。postmaster在全局级别维护了一个数组,为每个进程分配一个slot。
共享内存区
shared_buffers(共享缓冲区)
它表示数据缓冲区中的数据块的个数,每个数据块的大小是8KB。数据缓冲区位于数据库的共享内存中,它越大越好,不能小于128KB。这个参数只有在启动数据库时,才能被设置。默认值是128MB。
wal_buffers(日志缓存区的大小)
可以降低IO,如果遇上比较多的并发短事务,应该和commit_delay一起用。存放WAL数据的内存空间大小,系统默认值是64K。
进程私有内存区
temp_buffers(临时缓冲区)
用于存放数据库会话访问临时表数据,系统默认值为8M。可以在单独的session中对该参数进行设置,尤其是需要访问比较大的临时表时,将会有显著的性能提升
work_mem (工作内存或者操作内存)
其负责内部的sort和hash操作,合适的work_mem大小能够保证这些操作在内存中进行。
maintenance_work_mem (维护工作内存)
主要是针对数据库的维护操作或者语句。针对VACUUM,CREATE INDEX,ALTER TABLE ADD FOREIGN KEY等操作。
在对整个数据库进行VACUUM或者较大的index进行重建时,适当的调整该参数非常必要。postresql文档提示在启用了autoacuum功能的情况下,该参数不能配置的过大。
弄清楚了postgresql的整个架构,再来看sql的执行过程就简单很多了。
1 建立连接后,postmaster进程fork出处理sql的子进程。
postgres进程被fork后,接受用户并处理用户语句。因此这里首先会识别用户所发出的语句。如insert,delete,update,select等DML语句,另外还有如create table,create index等DDL语句。
只是识别语句的类型,并不会做其他任何处理。然后由traffic cop 代码块进行分发。
Postgresql 执行insert、delete、update、select都是通过postgres.c里面的exec_simple_query方法。
2 进入处理客户端发送SQL语句的总入口
这步最主要的就是根据不同的语句类型分发到不同代码进行处理。所以称作为traffic corp,也就是交通协调疏导的意思。如果是DML语句或者是select 语句,都属于QUERY,进入QUERY处理逻辑。如果是DDL语句,那么属于Utility Command,进入Utility Command处理逻辑。因为DDL与DML的处理逻辑是非常不一样的。
3 解析
3.1进行语法分析,生成语法树
只是简单的产生raw parse tree,这个里面不涉及语义检查。只是做语法扫描。
3.2语义分析和查询重写
这里会进行语义分析,会访问数据库中的对像,需要持有锁。这个过程会将简单的一个select 语句拆分成多个部分,将parse tree(语法树)转换成query tree,生成parse tree。如将整个select语句转换成:from 部分,where条件部分,group by 部分,order by 部分以及having 部分等。是任何数据库都需要操作的,并且非常重要的一环。
后面再经过analyze和rewrite,生成query tree,即查询树。查询树的生成需要进行语义检查。这一步会调用optimizer,并根据cost选择一种最优的执行路径。
4 SQL 生成执行计划树
根据上面的query tree产生执行计划和执行树。这部分核心代码在planner.c中,是PG的Query Optimizer。会根据表和索引的统计信息去计算不同路径的可能代价值,最后选出最优者。
5 执行SQL语句
执行plan,它会遍历每个节点,以致完成。
6 查询SQL的结果返回给客户端
本文转载自:公共号金蝶云·天梯
作者:张号
原文链接:公共号金蝶云·天梯
推荐阅读