转载:http://bbs.chinaunix.net/thread-1805302-1-1.html
Postgresql内核分析 闫华 postgres_fan@yahoo.com.cn 1前言 一直都想写关于Postgresql实现原理方面的文章,由于这是一个工作量巨大的工作,平时还要工作谋生,很难抽出时间来写,希望能够坚持下来。Postgresql作为历史最悠久的开源数据库管理系统,有关它的详细信息可以到http://www.postgresql.org/去查阅,这里不再赘述。在MysqL被Oracle抓在手中以后,MysqL变得前途未卜,在这种情况下,Postgresql显得越发地重要。 Postgresql作为脱胎于学术研究的数据库管理系统,源代码结构非常地清晰,易于理解,相比之下,MysqL的代码结构就要乱很多(个人之见,MysqL的粉丝不要喷我)。 Postgresql的源代码的规模已经非常地大,文件众多。初学者如果直接去看代码,必然如坠五里雾中,难以理解,所以我的文章着重讲述源代码背后的实现原理,而不是详细地叙述每个函数的输入输出以及每一行代码的作用。让读者能从宏观上把握系统结构,循着系统的主线去阅读源代码要容易地多。 这将是一系列的连载文章,闲言少叙,让我们先从Postgresql的进程结构说起。 2 Postgresql进程结构 Postgresql与Oracle类似,是多进程结构的数据库。在Postgresql中主要有postmaster,postgres,vacuum,bgwriter,pgarch,walwriter,pgstat等进程,下面先简单介绍一下每个进程的作用,然后再详细介绍每个进程的实现原理。 (1)postmaster 负责在启动数据库的时候创建共享内存并初始化各种内部数据结构,如锁表,数据库缓冲区等,该进程在数据库中只有一个。在数据库启动以后负责监听用户请求,创建postgres进程来为用户服务。这一点与Oracle的TNS listener进程类似。 (2)postgres 负责执行用户发出的所有sql语句,该进程在数据库中可能有多个,Oracle中叫shadow process。 (3)vacuum 负责清除数据库中无用的历史数据(已经被删除或更新的记录)。更新优化器的统计信息,确保产生可以接受的查询计划,该进程在数据库可能有多个,Oracle无此种类型进程。 (4)bgwriter 负责将数据缓冲区中已被更新的数据库写入数据库物理数据文件中,Oracle中对应的进程叫DBWR,该进程在数据库中只有一个。 (5)pgarch 负责将系统产生的redo log复制到其他外部存储介质中, Oracle中对应的进程叫Archiver,该进程在数据库中只有一个。 (6)walwriter 负责将系统产生的redo log 写到redo log 文件中(在pg_xlog目录下),Oracle中对应的进程叫LGWR,该进程在数据库中只有一个。 (7)pgstat 负责收集数据库运行中的统计信息,如一个表上面进行了多少次插入与更新操作,该进程在数据库中只有一个,Oracle无此种类型进程。 2.1 Postmaster进程 Postmaster进程是Postgresql启动以后创建的第一个进程,所有的其他进程都是由Postmaster创建的。与Postmaster进程相关的大部分代码都在src\backend\postmaster\postmaster.c文件中。Postmaster进程的入口函数是PostmasterMain。 Postmaster进程在系统启动时首先进行一些初始化工作,创建共享内存和信号量等数据结构,创建pgstat等后台进程,然后调用ServerLoop函数进入监听客户端连接请求的状态,ServerLoop函数的代码是一个无限循环的结构,正常的情况下,Postmaster进程永远都不会退出ServerLoop函数。ServerLoop函数的主要功能是在收到客户端连接请求的情况下,创建一个新的postgres进程来为客户端服务。 因为所有的其他进程都由Postmaster进程创建,所以它们都是Postmaster的子进程。当一个postgres、bgwriter、 walwriter或vacuum进程因为非正常原因崩溃以后,Postmaster进程要进行一些清理工作,释放它们占用的内部资源,或者重新启动这些进程。postmaster.c中的reaper函数就是负责执行这个任务的。 2.2 Postgres进程 Postgres进程负责执行客户端发出的所有的sql语句及自定义函数。与Postgres进程相关的代码在src/backend/tcop/postgres.c文件中,Postgres进程的入口函数是PostgresMain。 PostgresMain首先进行一些初始化工作,然后使用语句for (;进入一个无限循环状态,等待客户端发来命令请求,接受客户端命令,执行客户端命令,将执行结果返回给客户端。 for (;无限循环体首先调用ReadCommand从客户端读取一条命令,然后根据命令类型,调用相应的处理函数。例如,对于可以直接执行的sql语句(simple query),命令类型的代码是“Q”,处理代码如下: case 'Q': /* simple query */ { const char *query_string; /* Set statement_timestamp() */ SetCurrentStatementStartTimestamp(); query_string = pq_getmsgstring(&input_message); pq_getmsgend(&input_message); exec_simple_query(query_string); send_ready_for_query = true; 语句exec_simple_query(query_string)负责解析sql语句,生成查询计划,执行查询计划,将查询结果返回给客户端。 2.3 Vacuum进程 Postgresql使用的是多版本的并发控制机制,历史数据与当前数据都存放在同一个数据库中,当某些历史数据不再有用时,应当把这些历史数据从数据库中删除掉(这个过程叫vacuum),让这些数据所占的存储空间能够被重新利用,否则数据库所占的存储空间会越来越大,最后会消耗掉所有的物理存储空间,导致数据库无法继续正常运行。Vacuum进程负责清除无用的历史数据 Vacuum进程分两种。第一种叫Vacuum控制进程,第二种Vacuum工作进程。自动垃圾Vacuum控制进程在数据库中只有一个,数据库启动以后这个进程就存在,它睡眠一段时间后,就创建一个或多个Vacuum工作进程来进行垃圾收集(Vacuum控制进程不直接创建Vacuum工作进程,而是通过postmaster进程创建Vacuum工作进程),然后再进入睡眠状态,不断地重复这个循环,每次睡眠的时间由参数autovacuum_naptime指定。Vacuum工作进程负责真正的垃圾收集工作,参数autovacuum_max_workers控制能够被同时创建的Vacuum工作进程的个数的最大值。 与Vacuum控制进程相关的大部分代码都在backend/postmaster/autovacuum.c文件中,Vacuum控制进程的入口函数是AutoVacLauncherMain。AutoVacLauncherMain首先进行一些初始化工作,然后使用语句for (;进入一个无限循环状态,睡眠一端时间,检查是否需要创建新的Vacuum工作进程,如果需要,通知Postmaser创建新的Vacuum工作进程,然后继续睡眠,不断地重复这个过程。 |