我认识的很多Oracle DBA都在学习PG,不论是国内还是国外。数据库多元化发展,拥抱开源是一个趋势性的东西。对于Oracle DBA来说,PG这样的学院派数据库无论从架构上还是从功能上都有一定的相似性,学习起来会感到比较亲切。虽然如此,PG数据库和Oracle是差异很大的,哪怕一些看似近似的概念,因为两种数据库的实现方式不同,在实际应用中的差异很大。今天我就通过几个容易引发误解的问题,给大家提个醒。
曾经有人和我讨论PG的WORK_MEM,他觉得PG的WORK_MEM就有点类似于Oracle的PGA手工管理。粗粗一看,好像还真的像,不过如果你完全按照使用Oracle PGA的*_area_size一样来使用WORK_MEM,那是早晚会遇到坑的。我们知道,PG数据库缺少HASH ANTI JION算子,因此在处理一些NOT IN类型的子查询时往往使用Filter算子,这个算子通过对子查询的结果集生成HASH表,然后由外表来循环探测,最终完成NOT IN过滤。
图片
这种执行计划,虽然外表比较大的时候效率不高,不过总体执行性能大多数情况下还是能接受的。如果我们继续加大T2表符合条件的结果集的数据量,再来执行一下这条SQL。
图片
当内表的结果集需要生成的HASH表的大小大于WORK_MEM的时候,执行计划居然变了。在不使用HASH表的情况下,这条SQL的执行时间恶化到75146毫秒。我的测试环境是PG 14,起码在这个当前使用量比较大的版本中,PG是不会使用多个WORK_MEM,或者采用类似Oracle的2-PASS HASH TABLE的机制来动态处理的。在这种情况下,我们只能通过会话级临时加大WORK_MEM来解决问题。
图片
上面的例子可以看出,当WORK_MEM加大到128MB的时候,又开始使用HASH表了,执行效率也回来了。
对Oracle DBA的第一个认知挑战是,WORK_MEM设置的改变会影响SQL的执行计划。可能现在的Oracle DBA已经忘记PGA手工管理了,在当年服务器内存资源有限,磁盘IO性能极差的年代里,设置合理的PGA参数并非易事。目前面对PG,可能这个问题会更加严峻,在一些大型关键企业应用系统中,设置适当的WORK_MEM的重要性比PGA手工管理时代更加重要。设置过大的WORK_MEM,有可能导致物理内存不足,设置太小,会让有些SQL跑不出来。对于特殊的SQL,设置会话级的WORK_MEM可能是更好的解决方案。
第二个容易让学习PG的Oracle DBA困惑的是CURSOR SHARING机制。在Oracle数据库中,CURSOR SHARING的性能十分关键,对于高并发的应用来说,这一点尤为重要。如果硬解析过多,会消耗不必要的CPU资源,严重时会引发性能危机。与Oracle不同的是,PG虽然学习了Oracle的CURSOR共享机制,采用了一种类似的方法来解决共享CURSOR和避免多种最优执行计划导致的SQL性能问题。PG没有类似Oracle的共享池机制,因此不可能有全局CURSOR的概念。PG的CURSOR共享是会话级的,不是实例级的。在PG的一个会话中,一条SQL的前五次执行,每次都会重新生成执行计划,如果前五次编译发现存在通用执行计划,那么这个通用执行计划就会被共享。
与Oracle的CURSOR SHARING相比,这种机制要难管理多了,对于类似的SQL,很可能因为绑定变量的不同,导致不同的会话中某条SQL 的执行计划是不同的,执行效率存在很大的差异。在Oracle数据库中如果我们发现某个SQL的执行计划因为BIND PEEKING 等方面的原因发生了错误,那么我们在共享池中将这个CURSOR PURGE掉,下一次SQL被执行时就会重新生成执行计划,大概率会纠正以前的错误了。不过在PG里,因为没有全局共享CURSOR而变得十分困难了。当然通过对相关表做DDL会让所有会话的执行计划失效,或者杀掉某个存在问题的会话可以解决这个问题。
实际上在一个并发相当高的PG数据库中要对执行频繁的表做DDL,其副作用也远远大于Oracle数据库,突如其来的SQL解析风暴很可能打爆服务器的CPU,从而引发性能故障。
基于上面的一些问题,作为DBA,应该尽可能为自己的PG数据库申请更多的CPU和物理内存资源。在服务器资源相对充足的系统中,上述两个问题带来的影响都可以得到一定程度的控制。
今天的时间关系,我们先讨论这两个问题,如果PG数据库承担了高负载的关键业务系统,那么DBA也许真的会在生产环境中遇到这些问题,希望今天的文章对于正在学习或者转型到PG数据库的Oracle DBA有所帮助。也给这些DBA提个醒,有些表面上的概念,实际上PG和Oracle是有本质差异的。比如流复制VS DATAGUARD ,WAL VS REDO,分区表,CHECKPOINT,等等等等。