最近有很多小伙伴在搞信创数据库迁移。从Oracle迁移到国产或开源数据库后,最大的抱怨就是性能下降太多。这似乎与某些厂商声称的性能不符一样。但有时问题并不像某些投诉那么严重。性能下降的问题大部分是执行计划问题,是可以解决的。不过Oracle上也有一些执行计划很好的SQL。迁移到国产数据库或开源数据库后,不能做任何调整。.开始从事信创数据库之后,SQL优化就会成为一件大事。其实SQL最容易出问题的就是表连接和单表访问。即使使用了错误的索引,SQL运行速度也会变慢,影响也不会那么严重。表连接的执行计划有问题,在Oracle上可能秒级生成的SQL可能跑几个小时都跑不了。这两天写了一些云里雾里的文章。今天写点轻松的,和大家聊聊表连接。表连接的类型有很多种,如内连接、左外连接、右外连接、全外连接、半连接(SEMIJOIN)、ANTIJOIN等,要明确这些连接,并准确使用这些连接的连接方式,那么SQL运行起来就不会那么令人沮丧了。为了更好的讲解今天的内容,我先创建了两个测试表。我们今天的测试是在PG12.3内核数据库上执行的。首先我们创建两个测试表,然后我们看一下各个表的连接。innerjoin是最常用的一种,就是取两个表的交集数据。一个典型的语法是:SELECTcount(*)FROMjoin1j1INNERJOINjoin2j2ONj1.id=j2.id;。如果两张表不加过滤条件,几万条数据的JOIN最好的执行方式是HASHJION。这一点从执行计划也能看出来。Innerjoin的等价SQL是:SELECTcount(*)FROMjoin1j1,join2j2wherej1.id=j2.id;如果我们稍微更改此语句,将其更改为SELECTcount(*)FROMjoin1j1,join2j2wherej1.id<>j2.id;我们会发现这条语句的执行效率明显下降,从8.3毫秒下降到将近40秒。从执行计划来看,没有使用HASHJOIN,而是使用了性能较差的NESTEDLOOP。如果国内的数据库因为这种情况导致无法使用HASHJOIN而导致应用性能问题,在语义等价的情况下,看看是否可以使用NOTIN或者NOTEXISTS来改写,参考下面的例子。左外连接返回的结果是整个左行源加上两者的交集。一个典型的SQL是SELECTcount(*)FROMjoin1j1LEFTJOINjoin2j2ONj1.id=j2.id;但是从执行计划来看,这条SQL并没有选择leftjoin的执行计划,而是选择了下面的JION2表为驱动表的rightouterjoin。优化器认为这个执行计划更有效率。右外连接类似于左外连接:SELECTcount(*)FROMjoin2j1RIGHTJOINjoin2j2ONj1.id=j2.id;和上面的例子类似,优化器认为还是改成左外连接比较好。半连接SEMIJOIN和LEFTJOIN是SQL语句中两种不同类型的连接,SEMIJOIN,语句只返回左表中包含右表的行,而且只返回一次。而LEFTJOIN返回左表中的所有行,如果右表中没有匹配的行,则返回NULL值。其典型的SQL为:SELECTcount(*)FROMjoin1j1whereexists(SELECTidFROMjoin2j2WHEREj1.id=j2.id);ANTIJOIN排除了右表中的数据。典型的SQL是:SELECTcount(*)FROMjoin1j1wherenotexists(SELECTidFROMjoin2j2WHEREj1.id=j2.id);还有一些连接方式,比如fullouterjoin等,今天就不讨论了。最后介绍一个从Oracle数据库迁移到国产开源数据库时最容易出现性能问题的SQL:selectj1.*fromjoin1j1,join2j2wherej1.id=j2.idorj1.id=100;.这个SQL的特点是条件中有一个Or条件。在Oracle中,这条SQL的执行效率是没有问题的,但是在很多国产和开源的数据库中,性能是有问题的。这种情况下,重写SQL是最终的解决方案。如果等价的话,我们可以用union来改写这个SQL。selectj1.*fromjoin1j1,join2j2wherej1.id=j2.idunionselectj1.*fromjoin1j1wherej1.id=100;今天早上写到一半,过来一个客户,下午要出差,所以今天就先写到这里。有些问题只是提出来的,没有深入分析。他们只是提供了一个方向,供大家思考。如果大家有兴趣,可以进一步深入分析。解决了这些问题之后,更换国产数据库的工作就会顺利很多。
