一条sql语句的奇幻旅程
金蝶云社区-刘映希
刘映希
1人赞赏了该文章 152次浏览 未经作者许可,禁止转载编辑于2023年08月21日 10:21:33

客户端连上了PG实例之后,从执行一条SQL 按下键盘上的回车键到客户端返回给你结果,这之间到底发生了什么?

先上一张图:

image.png

客户端要和数据库建立通信,需要经过连接器,它收到建立连接请求后,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的执行过程就简单很多了。

建立连接后,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方法。

进入处理客户端发送SQL语句的总入口

这步最主要的就是根据不同的语句类型分发到不同代码进行处理。所以称作为traffic corp,也就是交通协调疏导的意思。如果是DML语句或者是select 语句,都属于QUERY,进入QUERY处理逻辑。如果是DDL语句,那么属于Utility Command,进入Utility Command处理逻辑。因为DDL与DML的处理逻辑是非常不一样的。

解析

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。会根据表和索引的统计信息去计算不同路径的可能代价值,最后选出最优者。

执行SQL语句

       执行plan,它会遍历每个节点,以致完成。

查询SQL的结果返回给客户端


本文转载自:公共号金蝶云·天梯

作者:张号

原文链接:公共号金蝶云·天梯

图标赞 1
1人点赞
还没有人点赞,快来当第一个点赞的人吧!
图标打赏
0人打赏
还没有人打赏,快来当第一个打赏的人吧!