2008年10月20日星期一

PostgreSql源码学习(4)

PG源码之parser(1)

解析sql语句是数据库服务器需要做的第一步工作。通常来说首先需要建立数据库链接,而建立数据库链接的程序可能是psql,jdbc,odbc等等。解析阶段主要是检查应用程序提交的sql的正确性以及创建查询语法树。接下来才是查询重写和执行计划优化,最后才是执行相应的查询。先分析查询解析器的原因主要是它是处理查询的第一步,必须对它了解清楚。还有一点小小的私心,据我所知,parser这部分的内容是当年由两个加大伯克利分销的中国学生完成的(用伟光正的话说是奠定了坚实的基础,当然肯定改进了不少)。

pgsql始终是以一用户一进程的c/s模式来实现建立链接。pgsql的开发者们各处的解释是:”由于我们并不清楚需要建立多少个链接,所以我们必须使用一个管理者进程来为每一个链接请求创建一个新的服务器进程。“这种方式很类似于ORACLE的专用服务器模式,似乎对WEB环境下的应用支持的不是很好,而且由于linux/unix本身的进程开销不大,所以在*nix上使用似乎比在windows上使用pgsql更好。当然在windows上pgsql始终是一个进程,并且使用线程来处理各个问题,性能如何没有详细的测试报告。所谓的管理者进程在pg7的时候是指postmaster进程,这个进程会在指定端口监听数据库链接请求。在pg8以后这个进程是postgres进程,在*nix上通常是:/urs/local/pgsql/bin/postgres -D DATA DIR。比如(在fedora上使用ps -aef |grep postgres):

postgres 4162 1 0 13:44 ? 00:00:00 /usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data
postgres 4164 4162 0 13:44 ? 00:00:00 postgres: writer process
postgres 4165 4162 0 13:44 ? 00:00:00 postgres: wal writer process
postgres 4166 4162 0 13:44 ? 00:00:00 postgres: autovacuum launcher process
postgres 4167 4162 0 13:44 ? 00:00:00 postgres: stats collector process
postgres 5269 4162 0 14:04 ? 00:00:00 postgres: fy fangyuan [local] idle

可以看到实际上第一个进程是其他所有进程的父进程,因此这个进程就是管理者进程。每一个postgres进程之间使用共享内存和信号量来保证数据一致性(这个方法有点像ORACLE的SGA,只是SGA里有共享池用于存放数据,而PG只有每个进程的私有数据)。

解析器(parser)首先检查查询语句(实际上是一个ASCII字符串)的语法正确性。如果语法是正确的则会建立一个parse tree,否则就返回语法错误。在这一步里,pgsql的开发者们使用lex和yacc来进行语法解析。文件scan.l(src/bankend/parser目录下)主要用于识别相应的标示,比如sql关键字,操作符,PLSQL关键字等等。lex会为每一个关键字或者标示符创建一个token并且返回给parser。gram.y里定义来parser并且包含了一系列的语法规则和不符合/符合规则就会触发的动作(可以用来给出语法错误或者建立语法树)。在编译的时候scan.l会被编译为scan.c而gram.y则产生gram.c文件,最终被编译入postgres程序中。

由于本文并不是对lex以及yacc的详细介绍,在此不再详述。如果有兴趣的话,可以参考《lex和yacc(第二版)》(机械工业出版社,翻译的老美的书,质量很高,并且专门有一章来讲解析sql),另外IBM的网站上也有好些文章介绍这两个工具,可以搜来看看。要理解parser是如何工作的必须对这两个工具有深入的认识,否则根本无法理解parser部分的源码。另外lex和yacc也是老美讲授编译原理的时候必定会讲到的工具,因此花一点时间来学习这两个工具还是物超所值。

parser还没有写完,下次接着写。
fanngyuan于2008/10/19 15:12

没有评论: