2020-04-25 07:47
众所周知,自 Oracle 18c开始,Oracle将数据库的版本发布变更为年度发布,以践行敏捷迭代的研发理念。所谓年度发布 也就是按照年份,每年发布一个版本。2018年发布了Oracle 18c,2019年是Oracle 19c,2020年发布Oracle 20c( 20c 云上版本已经在2月14日发布,按照官方计划公众发布是在4月)。
这其中,18c = 12.2.0.2,19c = 12.2.0.3,这两个年度版本仍然处于 12c 的发布序列中, 19c 属于 12c 体系的最后一个版本。由于 11g 已经退出了官方支持序列,越来越多的用户开始升级到 19c 。
随着版本号的变化,补丁策略也随之改变,自18c开始, Oracle推出RU策略(Release Update),每个季度发布一个RU,所以对应18c,我们看到的RU系列版本号就是18.1,18.2等等,RU是一个累积的增量补丁,也就是说后发布的包含之前RU的内容,如果我们从18.1开始,可以直接应用18.4,跳过其间的18.2和18.3版本。
由于每个季度的间隔事实上很大,如果期间用户报告了安全问题或严重的Bug,那么 针对每个RU,Oracle还可能发布最多两个RUR(Release Update Revision)修订版本,其中主要包含安全补丁和重要的回归修复。
下图是新策略下的Oracle数据库版本发布计划表:
这个图,可能会让大多数人看得头晕,但是升级路径有一个原则,记住这个原则就一目了然了。这个原则就是: 主版本号后面的2个数字之和要小于等于目标版本。例如 18.8.2 的低位 8+2 =10,这个版本可以升级到 18.10.0 ,但是不可以升级到 18.9.0,因为 9+0 < 10 。
当然,在非特殊情况下,官方推荐采取 RU 路线的更新策略,这看起来就简化多了:
当我们进行大版本升级时,总是建议用户通过SPA(SQL Performance Analyzer),对源环境和目标版本之间的测试,以避免出现SQL的兼容性问题或者性能问题,通过在源库进行SQL捕获,在目标库(通常是升级的高版本)进行重演,并生成性能对比报告,以指导用户的升级变更:
而对于小版本的升级,往往无法进行如此详尽的测试准备工作,这就需要我们能够预先收集已知问题并做出防范。
在进行数据库升级时,SQL 是最容易受到影响的部分,这些影响包括:
SQL 语法发生改变:原应用SQL出错无法正确执行或执行结果发生改变;
内部函数改变:函数返回值或返回值类型发生变化,导致SQL结果出错或异常;
执行计划改变:因为算法调整,SQL的执行计划发生改变,一部分执行计划变坏,影响性能;
新特性的BUG:往往新特性引入,会带来相应的新BUG,影响系统性能或者稳定性;
次生BUG:因为修复某些BUG,而引入的新BUG,导致SQL可能出现不兼容或其他错误和异常;
本文针对SQL方面的几个 19c 中典型案例,作为范例,希望能够为即将升级到 19c的朋友们提供一些经验借鉴。考虑到这些内容是DBA们知识储备的一部分,所以在一些知识点上做了介绍和扩展。
1. Oracle 19.3 中优化器问题导致的执行结果错误
在 Oracle 数据库跨版本升级时,很多SQL的新特性被引入到数据库中来,不可避免的,一些BUG会潜移默化的发生。而我们认为, 最让人困扰的是那些SQL不出异常,但是查询结果出错的语句,这类SQL很难通过测试发现,只能在人们注意到查询结果出错时,才会追究并发现问题。
以下就是这样一个问题,在 19.3 版本中广泛存在。创建简单的测试表和测试数据:
CREATE TABLE ENMO AS SELECT * FROM DBA_OBJECTS;
UPDATE ENMO SET EDITION_NAME = 'ORA$BASE' WHERE MOD(ROWNUM, 10000) = 0;
CREATE TABLE TECH AS SELECT ROWNUM ID FROM ENMO;
analyze table ENMO compute statistics;
...
此时执行如下查询,可以从结果看到返回了2个零值,这个结果显然是错误的:
set autotrace on
SELECT COUNT(OBJECT_NAME ||','|| OBJECT_TYPE), COUNT(EDITION_NAME) FROM ENMO, TECH WHERE ID = OBJECT_ID;
COUNT(OBJECT_NAME||’,’||OBJECT_TYPE) COUNT(EDITION_NAME)------------------------------------ -------------------0 0
在以下执行计划中,可以看出执行计划的错误出现在第三个步骤,在这个步骤中,通过Filter增加了一个 NOT NULL 的过滤,这就使得大部分数据被抛弃了,进一步向上的查询连接自然就出现了结果集的错误:
...
那么正常的情况应该是如何呢?去掉第二个输出,可以获得以下结果:
SELECT COUNT(OBJECT_NAME ||','|| OBJECT_TYPE) FROM ENMO, TECH WHERE ID = OBJECT_ID;
COUNT(OBJECT_NAME||','||OBJECT_TYPE)------------------------------------71789
...
经过确认,这是一个因为修复BUG而引入的BUG。Oracle最为强大的地方是,几乎为每一个修正都加入了一个开关,如果发现某个修正引起了新的问题,那么通过开关就可以恢复原有的工作方式。
...
以下语句就关闭了这个修复:
alter session set "_fix_control"='24761824:OFF';
Session altered.
...
再来看现在的执行计划,去掉了 NOT NULL 的过滤,SQL执行结果恢复了正常:
...
那么这个BUG是如何发生的呢?
【版權聲明】