zoukankan      html  css  js  c++  java
  • 转载:postgresql性能详解

    原文:http://blog.chinaunix.net/uid-8623064-id-2456488.html

    postgresql 性能详解 (2009-04-21 20:10)
    分类: 数据库技术


    一、postgresql简介

    PostgreSQL是一种非常复杂的对象-关系型数据库管理系统(ORDBMS),也是目前功能最强大,特性最丰富和最复杂的自由软件数据库系统。有些特性甚至连商业数据库都不具备。这个起源于伯克利(BSD)的数据库研究计划目前已经衍生成一项国际开发项目,并且有非常广泛的用户。

    二、数据访问模型


    转载:postgresql <wbr>性能详解
    (图1)

    上图从系统各个不同组件之间的call stack关系来反映单个sql transaction的流程,当然也可以从单个sqltransaction中client和server的交互来看考察之,见下图。

    转载:postgresql <wbr>性能详解
    (图2)

    为什么要从不同的角度来看待同一个问题呢?因为不同的角度关注的问题不一样,当把所有的问题都汇总起来之后,就可以看到问题的全貌,进而抽象出问题的本质,从而得出更优的solution。举个例子来说,从图1中我们不会看到client与dbserver之间建立connection的问题,然而在图2中我们自然会考虑到connection得问题,如果postgresql.conf中max_connection_num只有100,而猝发请求中同时有200个client去connectdb server,那么会怎么样呢?显然有100个connectionrequest会fail。这些具体问题我们在后面接着讨论,这里只想抽象这么一个模型给我们一些指引,指导我们下一步该focus在哪些方面,哪些是能做的,具体又该怎么做,这个在图中已有反应,比如说,dbserver在接受请求的时候会调用socket函数族来侦听网络请求,那么这个我们显然是无法控制的,包括kernel层的网络路由,当然理论上我们可以用fiberchannel这些个更烧钱的玩意儿来降低网络延迟。

    三、用例和问题描述
    性能问题,通常是软件项目比较头疼的一类问题,早期、中期不会出现,到后期才会突然冒出来,一方面,这也是由于一些客观原因导致的,比如说,项目起始阶段通常是迅速把预期的功能实现而不考虑性能,为什么呢?这里就会涉及一些非技术因素,比如,你说你做的项目很有“钱途”,但也许管钱的人他不这么想,所以你必须先说服这些管钱的人在你这个项目上砸钱然后你才可能用资金招人并建立自己的团队。怎么样来说服那些管钱的人呢,最直接、最好的办法莫过于showdemo了,所以项目起始阶段通常是赶着先把东西整出来,性能如何以后“再说”。“再说”两个字含义很深远,但一般的理解就是,以后不管了,出了问题再忙着管。另一方面呢,也是项目本身的特点所决定的,因为在项目的早期和中期,连基本的可以跑的系统都没有,那也就无法真正求出一个operation需要的时间。还有可能就是开发者的心理因素了,我个人觉得一般人在从事某些事之前总会往好的方面想,尤其是对性能问题,没有多少人能认识清楚,最后结果估计不足,而误差有余!虽然我们从事的项目最后总能映射为一类可解的计算问题,也就是说,我们的code总能在有穷个时钟周期内跑完,但是如果这有穷个时钟周期长无法接受,那么其实这无异于无穷个时钟周期,所以呢,性能总是有个下限指标。基于以上的分析,因为我们在项目的前中期很难发现性能问题,所以就更需要我们在design和implement的时候加倍注意这些问题,这样不至于在项目的后期才去想法去补救这些问题。

    四、postgresql性能分析
    从我对postgresql的测试和认识来看,postgresql本身的性能还是可堪重负的,对不同的sqloperation(select,insert,update,delete这个case相对比较少不考虑),在猝发式请求中,它的平均响应时间仍然显的很平稳。然后结合到具体的业务逻辑中,事情就变的一下子复杂了,此时要考察的事情就多了很多。

    从图2的视角来考虑:
    [1]考察connection的建立,有几个问题需要考虑,网络延迟,max_connection_num的取值,如果连接上来的client数目超过了max_connection_num,那么是直接refuse这个连接请求呢还是cache这个连接请求。先说网络延迟,在一般的应用中,DBServer通常和application Server(如WebServer)相隔咫尺或者同时位于一台server上,这时的网络延迟就可以忽略不计,当然不排除一些特殊应用,可能需要跨WAN来accessDB Server,这个时候可能就需要在两个location之间各放一个DBServer,所有的client都就近来访问,当然这两个DBServer之间如何保证数据一致又是一个问题,如果是同步做数据迁移,那么在WAN上的延迟仍然是一个问题,如果是异步做数据迁移,那么其中一个DBServer上的data又不能保证实时更新,application得到的数据可能是旧的,application能否接受要视业务逻辑而定。接着我们来看最大连接数,postgresql能接受的最大连接数受hardwareplatform的限制,也就是说受memory这些因素的限制,同时还要看DB Server的负载有多重,一般的应用中DBServer均是dedicated,所以大可以倾系统的memory予postgresql,还有一些场合下可能DBServer又是Application Server/NTP Server/DNSServer,这时就要考虑不能分给DB过多的memory以免对别的service带来副作用。还有一个问题就是当DBServer监听到的connection数大于max_connection_num时,一般来说我们会选择refuse这个连接请求,因为在这之前我们已经慎重选择了max_connection_num,如果peak_connection_num大于max_connection_num,那么我们可以认为这时DBServer已经overload,如果这时依然满足client的连接请求,那么显然会影响当前的transaction的完成,这时就会发生像TCPCongestion那样的情况,已有的要设法走完自己的path,新来的也要抢着开始自己的path,情况就变的越来越糟!然而,在这种情况下,application这个层次就需要fine-grainederror handlingmechanism,如果application遇到这种case,那么就需要提示customer详细的信息以显示“系统繁忙中”,而不是很笼统的“服务暂不可用”,如果是后者那么可能会给customer造成一个假象,系统crash了。可能还有些usecase要求即使peak_connection_num大于max_connection_num,也不能refuse这个连接请求,比如说某个请求需要report一个critical的alert,也许别的请求不处理也无所谓,但要是这个请求被忽略,也许就会造成系统的risk,目前postgresql对所有的请求都一视同仁,即使某个transaction携带的内容非常critical,postgresql本身也不知道,如果postgresql本身提供一些像TCP那样区分服务的feature就好了,当然这并不说“毕其功于一役”,还是同样需要application来显示指定。

    [2]考察authentication的需要,authentication是通过加/解密来完成的,加/解密是很耗时间的,最有名的可能就属RSA了。然而考虑一下我们的usecase,在一般的系统部署中,通常Web Server是处于网络边界的,尽管Web Server外还有一层firewall,而DBServer是在处于“内部”的,通常会位于DMZ中。从这个角度上来说,把DBServer和别的Server放在一台机器上,像这样的部署方式通常是非常不reasonable的。从军事角度来看,这样的部署方式和打“防御战”的普遍战法类似,就是所谓的“大纵深”的防御战术,尽管说纵深层次越多,防御性就越好,然而战术始终是理论,真正的“防御战”,统帅所用的兵力毕竟是有限的,从我们的usecase来看,我们可用的资源总是有限的。假设现在我们的网络被hack了,hacker已经进入我们的DMZ了,那我们的DBServer能够幸免吗?显然不可能。因此,从这个角度来看,个人觉得没有必要在postgresql.conf里面再设置认证方式,只是简单地设置成trust就已足够,当然可以设置一下ipfilter限定一下connection的source。如果说applicationcode在建立连接之后就会保持一个长连接,那么authentication大会也无妨,因为也就刚开始那会认证一下,反之,如果每次执行完一个transaction就把connection断掉,那么就需要慎重考虑一下是否必须authentication。

    [3] 考察sql session的初始化,首先我并没有仔细读过postgresql的sourcecode,因此没法精确评估session初始化正真消耗的时间,我们从普遍的角度来考虑,一般初始化需要申请一些必须的内存空间,然后建立相关数据结构之间的联系,然后通知中央模块来处理这些数据结构。除非我们添加硬件元素,比如说CPU,或者memory,否则我们无法加快这步session初始化过程。

    [4] 考察sqlstatement的执行时间,这一步是我们最应该focus的地方。第一,我们来看一下我们直接使用的sql语句,先看一下下面2中语义相同但语法不同的sql语句,假设我们有个表叫test,testtable有个index叫id:
        (1) select *from test order by id; 
        (2) select *from test where id >= 1 and id <= (select max(id) from test)order by id;
    一般我们如果要枚举某个table的所有记录那必定会选择(1),选择(2)的人如果不是爱因斯坦的智商那么就是变态,然而,如果你用postgresql的explain来分析对比一下这2个sqlstatement也许会看出差别,只是也许具体要取决于你用的版本号。(1)也许用比较土的sequencescan方式,而(2)必定会用indexscan,这一下就明显了吧。这里的示例中index的使用会提升performance,因为这里是select,如果是insert呢,那么postgresql为了indexconsistent就会在整个table上加上exclusivelock,如果是update则只要在对应的row上加上exclusivelock,可以看出index对于insert的performance还是有影响的,因此在建立dbschema的时候要考虑相应的table是read多还是write多,如果write多那么建立index可能反而不合适。第二,lock的获取和释放,postgresqllock的默认值是"read committed",因为大部分应用都是一个transaction只包含一个sqlstatement,因此这个lock级别已足够了,没有必要在applicationcode显示调用LOCK,这样做反而带来deadlock的风险。从lock角度来看,个人觉得应当慎用外键查询。因为postgresql还有其他的数据库均采用MVCCmodel,因此即使在大规模并发访问情况下,lock对performance的影响也不是那么明显,当然也正因为此,我们需要定期地runVACUUM来回收那些存储了过期数据但尚未被归还给free_space_map的空间,当然我们也可以在postgresql.conf里面配置autovacuum使之在一些访问量比较少的时间段运行,比如说凌晨3点左右,这样free_space_map就可以像JavaVM的memory那样定期被回收,free_space_map是用来记录postgresql当前仍然free的存储空间,定期回收free_space有助于insert和update的performance。第三,WAL(write ahead log),为了保证dataconsistent,postgresql在commit某个修改回先写log,由于默认情况下,WAL的目录pg_xlog是postgresql的data目录的子目录,那么postgresql在写data和写log的时候就会竞争同一个disk的I/O,如果把pg_xlog定向到单独的disk(比如用一个符号链接),那么dataI/O和logI/O可以“独善其身”,相互不干扰。还有些配置项和WAL有关,这就是checkpoint,checkpoint是对WAL的一次校验,在datarecovery的时候postgresql会读取checkpoint来决定从哪一个log开始redo/undo,我们适当地降低一下checkpoint的频率,事实上checkpoint没有必要做那么频繁,因为没有那么频繁的datacorruption。第四,trun offfsync,fsync打开后,postgresql会把每次更新的data都flush到disk上去,如果fsync off了,那么就由底层的OS来决定何时执行flush操作。尽管这个选项关闭以后会对performance有所提升,但是如果DBServer突然down掉了,那么可能会导致WAL不完整,或者checkpoint不完整,那当DBServer重启时,postgresql可能会报出类似“log不完整”的错误,此选项慎用!第五,虽然postgresql本身没有提供loadbalance这个feature,但是有第三方工具弥补了这项空缺,这就是Pgpool-II。既然要做loadbalance那至少需要run两个DB Server,这样select就可以主要集中在其中一个DB Server,而另一个DBServer就主要cover update和insert操作。

    [5] 考察sqlsession的释放。通常我们会忽略这一步,因为这一步无非就是释放一些缓存,close一些file。没错!但是如果说DBServer和client之间的connection已经断了呢,尽管linux sysctl里面有相关的TCP配置选项可以配置TCPtimeout,但如果说setsocketopt函数提供了设置保持长连接的选项,也许kernel的TCP配置就会被bypass(我不知道setsockopt是否一定支持这么做,这只是我的猜测),但既然postgresql提供了这样的配置项,我们没有理由不用!这些配置项就是:
       tcp_keepalives_idle
       tcp_keepalives_interval
       tcp_keepslaves_count
    一旦配置这几个选项,即使client和server之间断了联系,那么过了预定时间这个connection所占的资源也会收回了,同时client也会收到相应的错误信息,而不是永远地hang在那不动!

    五、High Availablity trade off

    在很多应用中DB都处于centralized的位置,一旦down掉就会对整个系统造成影响,而如今的系统动不动就说要满足至少99.99%的availablity,如果在一个系统中只有一个DB,就会造成单点故障,于是就要求一个系统中至少要有2个DB,而这2个DB之间的数据需要保持一致性!这里2个DB之间的数据一致性和3个乃至多个DB之间的数据一致性是一个问题,因此我们只就2个DB之间的数据一致性来讨论一下。2个DB之间的数据要保持一致性,就需要把一个DB上的数据更新往另一个DB上迁移,迁移无非有2中策略:同步或者异步。同步更新,可以保证2个DB之间的数据时刻都是一致的,这样其中一个DBdown以后,另一个马上就能扛起担子,如果2个DB都是OK的,那么所有的operation都可以发往其中任一个DB,但正如前面分析的,这样做会给performance带来副所用,因为一个transaction要等所有的DB都完成了操作才能正真commit,client才能收到返回消息。异步更新,可以保证performance不受影响,但是这时2个DB之间是有master/slave关系的,master所有的更新不会立即更新到slave上,因此master上所有的操作都可以做,而slave上就只能做一些select操作,因为slave上的data没法保证是最新的。无论哪一种方案都有利弊的,当然以上说的solution都是applicationlevel的solution,还有想kernel level的solution,比如说LinuxDRDB。个人比较倾向这种kernellevel的solutionn,首先kernel里面的code对所有的application都是透明的,其次kernel里面做东西performance相对比userspace里面的要好,最后就是我个人的kernel情结。当然,kernellevel的solution也有其弊端,因为kernel只能看到filesystem的信息,没法知道postgresql的transaction这种上层的概念,kernel只能傻瓜式的替你备份你写的东西然后往另一个机上迁移,这样copy出来的只是文件,不是DB动态的信息,这样做为备份的那台机器只能做备份,除了灾难恢复的时候有用外,其余时间几乎不会用到,这比之同步备份和异步备份就显得有些浪费了。

    六、相关project

    [1] cybercluster 

    [2] slony

    [3] pgpool-II
  • 相关阅读:
    ACM学习历程—HDU5410 CRB and His Birthday(动态规划)
    ACM学习历程—HDU1028 Ignatius and the Princess III(递推 || 母函数)
    ACM学习历程—HDU5396 Expression(递推 && 计数)
    ACM学习历程—HDU 5317 RGCDQ (数论)
    ACM学习历程—HDU 5326 Work(树形递推)
    ACM学习历程—SNNUOJ 1110 传输网络((并查集 && 离线) || (线段树 && 时间戳))(2015陕西省大学生程序设计竞赛D题)
    jquery开发js插件
    requireJS
    java进阶之-Maven,svn,git,maven合拼多个项目
    java 项目中每个jar包的作用总结
  • 原文地址:https://www.cnblogs.com/leeeee/p/7276630.html
Copyright © 2011-2022 走看看