HASHJOIN是大数据规模连接中最常用的方法。与最常用的NESTEDLOOP相比,其应用场景有所不同。对于两个表的连接,NESTEDLOOP适用于查询电话号码等应用场景。如果领导给你一个列表,让你查几家公司的电话号码,你要做的就是找一个电话号码本,根据公司名称索引,一一查,就可以了。即将完成。这种方法就是著名的NESTEDLOOP。通过几次快速循环,完成两个行源(待查清单、电话簿)的关联操作。如果这个任务改了,领导给你的名单上有几万家公司,那我们就不傻一个一个去查了。这时候不适合使用NESTEDLOOP循环,HASHJOIN是更快的解决方案。很多SQL执行计划有错误,很大一部分是NESTEDLOOP和HASHJOIN的错误使用。因此,现在一些CBO优化器对NESTEDLOOP和HASHJOIN都有主动纠偏技术。Oracle19C的可调整执行计划主要是在NESTEDLOOP执行过程中,一旦发现循环次数超过评估预期,就动态更改为HASHJOIN。一开始有点牵强。今天我们的重点不是讨论NL和HASHJOIN的区别,而是向大家展示PG数据库的HASHJOIN执行计划中一些容易被忽视的点。在查看执行计划的时候,如果能更好的把握这些关注点,对SQL优化会有很大的帮助。可能有小伙伴要说说,反正都是HASHJOIN,执行计划都差不多,没什么看的。那么我们来看看上面执行计划中红框内的内容,Batches:32,这是什么意思呢?如果你之前是OracleDBA,那么你应该对优化排序、one-pass排序、multi-pass排序等概念还是有印象的。当需要排序或者HASHTABLE中的数据量过大,超过了SORTAREASIZE的限制时,排序/HASHjoin不能一次性完成,必须分成多个分区,一个一个完成。在PG的HASHJOIN中,将HASHJOIN分成多个BATCHES。因为一个BATCH在一个batch完成后需要暂存在一个临时文件中,所以我们一般可以看到这种情况下写的temp的内容,我也用红框标出了这部分内容。排序区域不足导致的这种问题会带来什么样的性能问题?我们看这个例子,BATCHES:1,即不需要分区,此时使用了4540KB的WORK_MEM。其实我在给大家演示这个案例的时候,第一个例子使用了256KB的work_mem设置,当然不能满足超过4M的内存需求。在第二个示例中,我使用了一个巨大的work_mem(256MB)。当然,实际的内存使用以执行计划为准。一次在内存中完成HASHJOIN有什么好处?当然是执行效率。我们可以看到第二次执行只用了90毫秒,而分成32个BATCH的执行用了239毫秒。看到这里,可能有朋友要说了,既然效果这么好,那我们可以把WORK_MEM参数设置的足够大。其实,设置过大的WORK_MEM是有隐患的。如果我们的物理内存不是很大,设置过大的WORK_MEM可能会导致极端情况下物理内存消耗过大,导致更严重的问题。WORK_MEM参数可以在会话级别动态设置。如果我们的一些需要排序或者HASHJOIN的SQL可以在应用层面进行设置,在执行大SQL的时候设置一个较大的值,SQL执行完后再重新设置参数。这样WORK_MEM的使用效率最高。否则,为了满足大规模SQL的需要,我们需要设置一个更大的值。当然,虽然我们设置了WORK_MEM,但并不一定会消耗那么多内存,但是活跃会话数*WORK_MEM还是需要注意的,以保证我们的物理内存有那么多空闲空间可用(参考availablememory,notFREEmemory)是非常必要的。如果我们不确定系统的最大内存使用量,而物理内存比较吃紧,则需要设置较大的SWAP。在极端情况下,可以保证系统不会因为OOM而导致重大问题。上面的执行计划也是我们经常看到的。PG数据库支持并行HASHJOIN,默认开启。如果我们系统的CPU资源足够,那么enable_parallel_hash参数保证开启。ParallelHASHJOIN通过并行seq扫描和并行hashjoin两种机制可以进一步提高HASHJOIN的性能。我们可以看到,通过并发,进一步提升了这条SQL的执行效率。但凡事都有优点和缺点。如果你的服务器的CPU资源非常紧张,那么过多的并行HASHJOIN可能会导致你的CPU资源经常不足而引发其他问题。如果是这种情况,关闭parallelHASHJOIN也是一种策略,让每次HASHJOIN稍微慢一点,但是要保证CPU资源不超载。
