当前位置: 首页 >> 技术文章 >> 分而治之:Oracle 18c 及 12.2 分区新特性的 N 种优化实践(含PPT)
分而治之:Oracle 18c 及 12.2 分区新特性的 N 种优化实践(含PPT)
发布时间:2018-07-12 发布人:杨廷琨 41

本文根据 杨廷琨 2018 年 5 月 11 日在【第九届中国数据库技术大会】上的演讲内容整理而成,关注“数据和云”公众号并回复 “Yangtingkun” 即可获得演讲 PPT 。


讲师介绍:杨廷琨,云和恩墨高级咨询顾问, ITPUB Oracle 数据库管理版版主 ,人称 “杨长老”,十数年如一日坚持进行 Oracle 技术研究与写作,号称 “Oracle 的百科全书”。迄今已经在自己的博客上发表了超过 3000 篇技术文章。2010 年,与 Eygle 共同主编出版了《Oracle DBA 手记》一书,2007 年被 Oracle 公司授予 ACE 称号。


内容概览


这个是本次分享的主要内容


1.分区基本概念

2.分区演进历史

3.分区最佳实践

4.分区最新特性


  1. 分区基本概念

很高兴,又和大家重聚在 DTCC 2018 的数据库性能优化专场。这次我想和大家分享与分区相关的优化特性,主要会和大家介绍一些 Oracle 最新的分区技术以及我们在日常运营中的最佳实践。


Oracle 对于分区的定义是根据内部定义的规则,将一张表的数据拆分到多个数据段中。分区之后,每个分区都是独立的数据段。Oracle 分区的最大好处是透明性,应用无需了解底层数据,访问方式也与之前无异。


杨1.jpg

换句话说,当把表做成分区表之后,程序不做任何的修改调整就可以直接跑,同样如果把分区表改回普通表,也不要做任何调整。但是,这并不意味着做了应用之后我们就不需要了解表是否进行了分区。如果你想要分区带来额外的性能好处,那么分区策略一定是要和应用程序、业务访问方式相结合。


Oracle 提供了几种分区的访问方式,最常用直观的方式是通过分区扩展语句直接指定到某个分区。但我们不推荐这种方式,我们更推荐的方式是通过真正控制访问数据、增加规范条件,让 Oracle 帮你定位到需要访问的某些数据。



1531361307699090471.jpg


分区的好处是什么? 原来所有的操作都是基于一张大表去做的,而分区之后,一张大表化成了多个小单元,我们可以基于这种小单元去做删除、截断、迁移、索引等操作,分区提供了很好的细粒度操作手段。


提到分区,很多人第一反应是它带来的性能优势,但其实我认为分区最大的优势是在可管理性和可维护性方面,管理很多小表或者小数据段的成本要远低于管理一张大表,而且平均维护时间也会变少,但速度却会更快。


第二个好处是可用性的增强,这也是 Oracle 官方宣称的好处。原来,如果一张表对应的某个数据文件出现问题,影响的是整个全表,但现在可能影响的只是其中一个分区,因为它在逻辑上是相隔离的。


基于以上两点,我们才能开始讲性能的提升。在早期的版本中,Oracle 只是直接说性能有一定提升,但其实这种说法是不准确的,因为分区如果设计不好,你的性能是会下降的。后来 Oracle 自身也意识到了,所以从 Oracle 11 版本之后,Oracle 就把性能拆成了 OLTP 和 OLAP 两种不同的情况去考虑。


OLTP 处理的更多的是短时间内的大量并发,所以这时分区能带来的好处是降低共享资源的争用,消除热点块; OLAP 面临的是海量数据的处理,所以我们要更好的利用并行来提升性能。


2.Oracle分区演进历史

杨3.jpg


Oracle 最早是从版本 8 引进分区的,上图这张表给大家列出了各个版本的分区功能,并通过功能、性能和管理性三个角度解读。很多人虽然在用分区,但是据我了解他们应用的很多特性都还集中在 8、9 版本,例如常用的范围分区、历史分区、列表分区等等功能。但其实之后的版本中,Oracle 一直都有很多更好更新的功能提供给我们。

杨4.jpg

Oracle 12 和 18c 中新增了很多非常实用的新功能,极大的简化了大家的日常操作,接下来我会着重为大家介绍这些功能。


3.分区最佳实践

杨5.jpg


接下来,我们介绍一下分区的最佳使用场景。


范围分区的最佳使用场景是针对具有天然时间属性的数据。例如,系统里记录的订单时间、生成时间、启动时间等等。当然并不是只要有时间属性数据就可以用范围分区,还要看业务对时间是否有可见性的要求。另外,业务如果更关心近期数据就再好不过了,这样范围分区对性能提升以及过期历史数据清理都会是很好的帮助。

杨6.jpg

随着数据的不断积累,我们都会担心数据库越来越大。如果业务对于数据的生命周期有明确要求的话,那么我们可以通过清理数据来让现有系统维持在一个相对稳定的状态。

如何去清理数据呢? 传统的 Delete 数据清理会面临很多问题,例如效率低下、无法释放空间等等。但如果我们的数据存储有一个明确的时间限定条件,那么分区就是一个很好的选择。

分区的清理成本和速度都很值得期待。在清理数据时,我们建议尽可能建立全局索引。数据清除操作是会影响全局索引的使用度,甚至导致索引失效。但如果我们是定期做分区数据清理的话,那么就不会影响全局索引的作用。

范围分区的好处是什么呢? 首先,数据分布是相对平均的,因为是按照时间等分的,数量也是可控的; 通过 DDL 清理数据的速度很快,不会产生大量 redo、undo 的问题。同时,在设计时还要考虑尽量让一个查询集中在

一个分区中完成,提升查询效率。通过定期 DDL 方式清理分区,可以保证分区、表的大小维持在稳定的量级,同时索引也不会随着时间迅速增长。

刚才我们介绍了范围分区是最常用的清理过期数据的方式,但是在真正的产品环境中,我们会面临各种不同的场景。例如,我们不能把所有数据都简单的删掉,因为删除的数据中可能有少量的数据是需要保留的。

面对这样的场景,如果使用 Delete 方式去删除,你会发现虽然我们使用了分区,但是却没有享受到分区的好处。而且数据清理使用了原来的方式,那么必然会碰到原来的问题。

杨7.jpg



那我们有没有更好的解决办法呢? 我们可以用 Insert+Exchange 的方式来做,我先把这部分数据从表中删掉,如果这其中有少量需要的数据,再插回来就可以了。这种方式既保证了效率,也避免了遇到之前的问题。


上图是我通过代码简单的给大家演示一下整个过程。


这里有一张 T_PART 表和 P(3) 分区,假设这其中有七八千条数据都是不重要的,但其中可能需要保留 30 条数据,那么我就会采用之前提到的 Insert+Exchange 方式。这里还有一个小窍门,我们是先 Insert,再 Exchange。为什么这样做,如果这张表一致性非常重要,我们就要在操作之前,先把这张表锁起来,避免别人对它进行操作,然后把需要保留的数据 Insert 到一张临时表中,之后再去做 Exchange。通过这种方式我们可以时刻保持数据的一致性。

杨6.jpg


除了上面的挑战,我们可能还会面临主子表的挑战。假设我的主表和子表都做了分区,那我们可能会面临以下挑战,首先子表可能不存在主表的分区时间列,例如,有订单表和订单详细表两个表,并且两者是主子表关系。

其中订单表是以订单时间来分区的,这时订单详细表的分区时间列就会产生争议,如果是按照订单明细的创建时间,那么它和订单时间可能是不一致的,且二者本身就是一对多的关系,所以在数据清理的时候,可能主表清理不了,如果要用订单时间去分区,那么你就需要在表中冗余订单时间。

如果只是冗余订单时间,相信很多人都是可以接受的,但是挑战还不止于此,一旦有了外健约束,主表无法执行 truncate 操作,必须先将约束 disable 掉,给运维增加很多不便。

如何解决这个问题? Oracle 11g 就给我们提供了一个新功能叫参考分区,它是这么解决的:主表的字段还是按照主表时间列去分区,但子表不再需要冗余主表字段,而是直接依赖与主表的主外键关系去做分区。

杨7.jpg


参考分区适用于主子表建立相同的数据策略,同时子表没有合适的分区字段,且主子表经常关联访问的场景。另外,Oracle 12 还对此做了增强,支持级联,换句话说,当我有主子表的情况时,不用先去子表做 truncate,直接在主表做 truncate,它就会递归的把所有子表 truncate。

杨8.jpg

哈希分区相对来说比较简单,它的适用场景是没有时间属性、缺少区分数据的业务字段的场景。如果系统面临着共享资源的争用,也可以使用哈希分区。

我建议哈希分区键值列尽量选择重复度不高的字段,这样不容易导致数据分布不均; 分区数量最好是 2 的幂次方,这也是为了避免分区数量分布不均; 针对没有时间属性和明确业务属性的表,通常不会去做定期清理的策略,我更建议使用全局索引; 另外,哈希分区索引可以有效的解决索引热点块的问题。

有人可能会有这样的疑问,既然我的数据没有业务特点,为什么要分区呢? 我们之前碰到过这样一个案例,客户的表量级非常大,400T 的数据可能有 395T 的数据都在同一张表中 ,客户面临的问题是表可能存不下这么多数据,Oracle 对于表容量没有限制,但是对于表空间的容量是有限制的,这时你会发现如果不用分区,这个问题就是无解的。

杨9.jpg

哈希分区可以解决的一个问题是热点块问题。为什么会产生热点块问题呢? 对于 OLTP 系统来说,会有大量的数据插入。插入数据的类型一般有两种,一种是主键,另一种是时间类。这两组数据的共同点是新插的数据永远是最大的,当多人同时做插入时,因为表是无序的,所以没有影响,但索引是有序的,所以更新索引时会产生资源争用。如果你是 RAC 架构,由于 GC 多节点之间的相互争用,会导致热点块问题进一步加剧。

传统的解决方案是利用 Oracle 提供的逆键索引,把键值反过来,分散热点块。但逆键索引有一个很大的缺点,就是它虽然可以解决热点问题,但却不支持范围扫描。

哈希分区如何解决呢? 我们创建索引,指定索引按照哈希方式分区,然后指定分区数量。这样做的好处是什么呢? 原来我是一个索引,只有一个最高值,大家都去争抢这一个最高值,但是现在我变成了 32 个分区,有 32 个索引最高值,这时的资源争抢就会少很多。当然,技术都是有两面性的,在这种情况下,你再做索引范围扫描时,就不只是要扫一棵索引树,而是 32 棵索引树,引入的额外开销就会大许多。所以分区数量的选择也是十分有讲究的。

列表分区最常见的是针对有某些业务属性数据的场景。我们可以根据明确的业务特点去做分区。


地区字段是列表分区常见的候选键值列,数据分布和访问方式确定分区键值划分,同时我还建议设定一个 DEFAULT 分区,假设,我们把国内的大部分省到列出来了,但是有一天有个没有对应分区的省的数据进来了,如果没有 DEFAULT 分区就会直接报错,而有了这个分区,数据就有容身之所了。

列表分区与业务的匹配度更好,业务可以清楚的知道数据存在哪里,并且高效的找到,不用太多的使用 Oracle 内部的关联和查询。

杨11.jpg


常见的索引有两种,一种是全局索引,一种是本地索引。分区的全局索引和单表上的索引没有区别,不管表上有多少个分区,只有一棵索引树,所有的数据来自分区。这样做的好处了,即使表做了分区,访问代价也不会增加,但缺点是如果你对下面的分区表做了一些 DDL 操作,那么很容易导致索引失效。

杨12.jpg


本地索引和表分区是一一对应的关系,当你在对表做数据操作时,Oracle 同时也会对索引分区做操作,不会导致分区索引的不可用,同时本地索引还支持并行扫描和创建。


本地索引的缺点是如果你要把主键创建成本地索引,那么主键必须包含在索引里面,而且无法保证每棵独立的索引树之间的数据一致性,所以必须把键值列加入其中来保证唯一性,但是这样未限定分区的查询将扫描全部索引分区,这会增加额外的开销。

杨13.jpg

最后,我们看一下用来消除热点块的哈希分区索引,索引虽然也会做分区,但是与表数据没有任何关联关系,任何一个索引分区都可以去访问所有的表,它的优势就是分散热点。但缺点是访问索引时需要访问索引的每个分区才能得到完整记录。


4. 12C分区最新特性

杨14.jpg


Oracle 12 提供了很多新特性,这些新特性对于日常维护是非常有价值的,所以我们来为大家详细介绍一下。

杨15.jpg


首先,我们来介绍部分分区。刚才我们讲到常用的索引有两种,全局索引和本地索引,那么部分分区的索引是什么呢? 在实际环境中,我没必要对所有分区都创建索引,很多历史数据分区的访问频度是非常小的,没有必要关注,部分分区索引刚好满足这样的应用场景,它可以把索引范围压缩到一个合理范围内。


假设创建了一张分区表,里面有 9 个分区,分别存储了 17 年和 18 年的数据,其中存储 17 年数据的四个分区我们做了 INDEX OFF 操作,在创建索引时,我们会发现虽然创建了 9 个索引段,但做了操作的四个分区是不可用的,实际只创建了 5 个索引段。


当你访问部分分区索引时,它会直接把执行计划分成两部分,一部分是在索引段里扫描,还有一部分是在对应内容里扫描。部分分区索引其实最大的改变是它可以分辨哪些索引是可用的,哪些是不可用的。


如果在指定键值的同时再加上一个时间,那这个时间就是我们分区线。当我们访问数据时,Oracle 就可以根据时间知道我要访问哪个区,其它无需访问的区虽然也是有数据的,但 Oracle 会在这里有个设置恒为假条件的 filter,当访问到这里时会直接跳到下一部分。

杨15.jpg


Oracle 12.1 非常强大的一个功能点是索引异步维护。当创建范围分区时,由于经常要做 DDL 的清理,所以不建议大家去建全局索引。但是 Oracle 12 解决了这个问题,假设 1 月 4 日对应数据分区里有 31 万条记录,3 月 5 日对应的数据分区有近 30 万条记录,当我们去做创建分区操作时,花费时间大约为 0.18 秒,然后再检查状态时,不出意外,全局索引已经失效了。




重建之后,我们在分区创建时加上一个 Update Index 的操作,因为要同步 DDL 操作,它会更新所有状态,很多人认为它会变得非常慢,其实它只用了 0.17 秒,比之前的 0.18 秒还要快。这是因为 Oracle 12 之后,它会自动给数据打标识,并不是真正同步去维护,而是在后台异步维护,不仅提高了索引的可用性,同时还提高了效率。

杨16.jpg

Oracle 12.2 很有意思的一个特性是自动列表分区,我们之前建议在列表分区时一定要加上 DEFAULT 值,否则数据插入会报错,尤其是当一张表只有一个 Keyboard,在自动列表分区中插入数据是非常困难的。而 Oracle 提供的很方便的功能是在线把一张普通表转换成分区表,这样我就不会因为是分区表而引入停机、维护等。


Oracle 18c 中针对这种情况提供了更强的改变,例如我创建了一个分区表和三个索引,当我从普通表变成分区表之后,但我对分区表策略不满意,可以直接通过 Oracle 语句来改变数据表策略,而且这个操作可以在 DDL 发生时在线去做。


最后一个功能是针对变更之后的索引,我们可以在线去做分区的 MERGE 操作,通过在线的方式把其中几个分区合并。