当前位置: 代码网 > it编程>编程语言>Javascript > Oracle 数据库中的 JSON性能注意事项(最佳实践)

Oracle 数据库中的 JSON性能注意事项(最佳实践)

2025年04月24日 Javascript 我要评论
本文为白皮书“json in oracle database: performance considerations”的翻译及阅读笔记。目的本文档概述了在 oracle 数据库

本文为白皮书“json in oracle database: performance considerations”的翻译及阅读笔记。

目的

本文档概述了在 oracle 数据库中存储和处理的 javascript 对象表示法 (json) 的性能调优最佳实践。应用这些最佳实践将使开发人员、数据库管理员和架构师能够主动避免性能问题,并确保他们设计的应用程序和系统以最佳性能运行。
本文档中的超链接提供文档、更多信息、示例和免费实践培训的访问。

oracle 数据库中 json 的简介

2014 年,oracle 发布了 oracle 12.1.0.2,在所有 oracle 数据库版本中增加了原生 json 支持这点表示怀疑,应该是oson发布后才算吧)。在此版本之前,json 通常存储在 nosql 数据库中,而 nosql 数据库缺乏功能和数据一致性模型,这迫使开发人员添加额外的代码来确保数据完整性。

为了弥补 nosql 的不足,开发人员开始使用关系数据库或其他数据存储技术,例如运行分析查询。2014 年原生 json 支持功能的加入,消除了对这些额外专用数据存储技术的需求,从而通过减少集成工作、简化部署、降低风险和成本,显著加快了开发速度。(融合数据库的好处)此外,使用标准化 sql 运算符存储、处理和分析 json 显著缩短了上手时间,降低了所需的技能,并使非开发人员也能够轻松处理 json 数据。

oracle 数据库提供原生 json 支持。json 可与所有 oracle 数据库功能兼容,包括选件、oracle 管理包、框架、架构和安全性。存储在 oracle 数据库中的 json 还能受益于 oracle 数据库的性能、可扩展性、可用性、可扩展性、可移植性和安全性。(利用oracle的强大能力也是融合数据库的好处)访问存储在 oracle 数据库中的 json 与访问其他数据库访问方法(包括 oci、.net 和 jdbc)相同。

有关 oracle 数据库中 json 的更多信息,请参阅 json 开发人员指南。

性能特性和技术 – 深入探讨

以下部分更详细地描述了工作负载部分中讨论的功能:

在 oracle 数据库中存储 json 数据以获得最佳性能

json 可以使用数据类型为 varchar2、clob、blob 或 json 的列来存储。无论使用哪种类型,您都可以像操作其他类型的数据一样操作 json 数据。

  • 对于 oracle 21c,建议使用针对查询和高效(部分)更新进行优化的原生 json 类型。可以在 json 列上定义 is json 检查约束,以强制执行正确的 json 语法,如果应用程序能够保证 json 的正确性,则可以禁用(而不是删除)该约束。
  • 对于 oracle 19c,建议使用原生 blob 数据类型,该类型也针对查询和高效更新进行了优化。
  • clob 也受支持,但应避免使用,因为由于 ucs2 编码,clobs 通常需要两倍的存储空间(和磁盘读取)。
  • varchar2 字段也受支持,如果已知 json 文档的最大大小,或者 json 文档已存储在 varchar2 字段中,又或者您更喜欢 varchar2 的简便性,则可以考虑使用 varchar2 字段。varchar2 值最多可容纳 32k个(原文是32,不对)字节。

工作负载类型和数据访问模式

数据库工作负载可分为操作型工作负载和分析型工作负载。操作型工作负载也称为联机事务处理系统 (oltp),它们以事务为导向,拥有众多用户,并且旨在实现即时响应;例如,银行的自动柜员机 (atm)。oltp 系统支持所有数据操作类型。典型的操作涉及使用最少行数插入或更新数据的事务。oltp 系统的性能目标是事务速度、吞吐量和数据库并发性。相比之下,分析型工作负载(例如联机分析处理 (olap)、数据仓库和数据湖)专为数据分析而构建,用户较少,并且旨在处理大量数据。典型的操作包括使用复杂的资源密集型查询处理数千或数百万行数据,这些查询会连接和聚合多个表之间的数据。olap 系统针对查询进行了优化。

按键检索 json 文档 (oltp)

您的工作负载会根据关系列(键)选择单个 json 文档,并将 json 数据存储在第二个(负载)列(可以认为是key/value中的value)中。键列上的主键约束强制键值唯一,并为其创建索引以便快速查找。如果键不是随机的(例如,使用序列或标识列),则索引可能会成为高事务性系统中的热点,因为并发/后续插入会命中同一个索引块。基于键列对索引进行哈希分区常见的分散热点的做法)会将插入操作均匀分布到所有分区。soda 和 mongodb 集合自动具有主键列——基于键的文档查找无需进一步操作。

按字段值检索 json 文档 (oltp)

在这里,通过 json 文档中的字段值选择一个或几个文档。json_value 或 json_exists 运算符中的路径表达式定义了这些值。如果重复使用相同的路径表达式,建议使用 json_value 的基于函数的索引。对感兴趣的字段值进行索引,可以将数据检索的全表扫描替换为索引查找,从而确保最佳性能。

虽然在 json 文档中索引单个字段很容易,但数组索引却更具挑战性。基于函数的索引无法索引数组值(函数每个 json 数据只能返回一个值);在 oracle 21c 之前的版本中,可以使用物化视图作为替代方案:物化视图将数组扩展为具有多个行条目的关系列,然后将其作为普通列进行索引。oracle 全面的查询重写框架会自动重写针对 json 文档的 sql 语句,以便使用物化视图进行快速数据检索。在 oracle 21c 中,您可以使用此版本中引入的全新多值索引功能,原生地为 json 数组中的值创建索引。

使用全文搜索检索 json 文档 (oltp、olap)

某些工作负载仅知道感兴趣的值,而不知道 json 文档中字段的路径表达式,例如对任意文档的临时查询。oracle 提供了 json 搜索索引来提升此类工作负载的性能。借助 json 搜索索引,sql/json 运算符 json_textcontains 允许根据文本搜索条件(包括词干提取和模糊搜索)选择行。

提取 json 值用于报告或分析 (olap)

在报告或分析用例中,json 数据会映射到关系模型,以便使用 sql 进行进一步处理。常用的 sql 操作包括连接(与其他 json 或关系数据)、聚合(求和、求平均值、窗口函数)或机器学习(分类、预测)。sql/json 运算符 json_table 允许从 json 映射到关系模型。oracle 数据库会尽可能将多个 json 查询运算符优化为单个 json_table 语句(显示在查询执行计划中)。

对于高选择性分析(根据字段过滤条件仅选择少量 json 文档),可以使用索引优化访问。如果访问的行数较多(但并非全部),且索引的选择性不再足够,则应考虑对数据进行分区,以从查询中剔除不相关的分区。此外,处理大数据量时,建议充分利用并行执行。sql/json 运算符 json_table 可以并行化,且没有任何限制。(三个优化手段:索引,分区,并行

假设您反复运行相同的 json 到关系型数据库转换,例如每日报告或仪表板查询。在这种情况下,物化视图通过在视图中物化中间结果,可以完全避免在运行时重复执行相同的 json_table 转换。json_table 物化视图支持快速刷新,因此在插入或更新后能够高效自动地刷新。物化视图还可以与 oracle database in-memory 结合使用,以受益于内存列压缩和快速 simd 扫描。这可以显著提升性能,尤其是对于分析查询而言。(物化视图(主)+ in-memory(辅)优化

json 生成 (oltp、olap)

oracle 数据库新增了 sql/json 运算符,(可能指的是json_array和json_object)用于从关系数据和查询结果生成新的 json 数据。典型的用例是修改某个 json 文档的结构,或将分析查询的结果作为 json 数据提取返回。当仅访问少量行时,索引可以提供快速访问。如果 json 生成基于多行数据,则应考虑使用物化视图,但需要注意的是,json 生成的快速刷新功能仅在有限情况下才支持。

性能特性和技术 – 深入探讨

以下部分将更详细地介绍与性能相关的功能,并附上示例。通常,常规的 sql 调优技巧是适用的:您可以利用已有的技能。这缩短了学习曲线,并消除了 dba 和数据库管理员对在 oracle 数据库中采用 json 的担忧。这些调优技巧背后的主要理念是减少需要读取和处理的数据量:

基于函数的索引

基于函数的索引可以基于特定键或键组合创建,并优化使用 sql/json 运算符对相同键进行的查询操作。基于函数的索引使用 json_value 运算符构建,并支持位图和 b 树索引格式。

以下示例在示例 json 文档的 ponumber 键上创建了一个(唯一的)函数索引,该索引可通过路径表达式“$.ponumber”访问。本示例假设 json 数据存储在名为“purchaseorder”的表的“data”列中。

create unique index po_number_idx on purchaseorder po(
json_value(po.data, '$.ponumber' returning number
null on empty error on error));

ponumber 值将被提取(并编入索引)为数字。这会影响范围查询(按数字排序而非字母排序),并避免在运行时进行数学运算或比较的数据类型转换。缺失值将被编入索引,作为 sql null 值。

以下查询使用了简化的 json 语法。由于使用了 number() 项方法,因此将使用索引进行数据检索,如计划所示。

select data from purchaseorder po
where po.data.ponumber.number() = 200;
-----------------------------------------------------
| id | operation | name |
-----------------------------------------------------
| 0 | select statement | | 
| 1 | table access by index rowid| purchaseorder | 
|* 2 | index unique scan | po_number_idx |
-----------------------------------------------------

多值索引

如果路径表达式可以选择多个值,则建议使用多值索引——这在访问 json 数组中的值时很常见。以下代码在示例 json 文档的 json 数组“lineitems”中的字段“upccode”上创建了多值索引。这些值以字符串形式进行索引。

create multivalue index upccode_index on purchaseorder po
( po.data.lineitems.part.upccode.string());

多值索引也使用 b 树,但由于生成的 rowid 需要去重,因此速度比函数索引略慢。因此,如果已知路径表达式最多返回一个值,则应优先使用基于函数的索引。多值索引是在 oracle 21c 中引入的(出于早期的原因,可以使用物化视图来加速对数组的访问)。

以下查询使用了多值索引:

select data from purchaseorder po where
po.data.lineitems.part.upccode.string() = '13131092705'; 
-------------------------------------------------------------
| id | operation 							| name 			| 
|  0 | select statement 					| 				| 
|* 1 | table access by index rowid batched	| purchaseorder | 
|* 2 | index range scan (multi value) 		| upccode_index |
-------------------------------------------------------------

json 搜索索引

oracle 数据库支持使用基于 oracle 全文索引的搜索索引来索引整个 json 文档。该搜索索引不仅包含所有值,还包含其字段名称,并允许进行全文搜索。以下示例在“purchaseorder”上创建了一个 json 搜索索引。

create search index po_full_idx on purchaseorder po (po.data) for json
parameters('sync (every "freq=secondly; interval=1") dataguide off');

“parameters”子句指定索引是异步的,每秒同步一次。也可以在每次事务提交时同步索引,但这会增加索引维护成本并降低并发 dml 的吞吐量。json 搜索索引还可以通过名为 json dataguide 的功能在 dml 操作期间发现模式更改——例如,它允许自动生成 json_table 视图。“dataguide off”子句禁用此模式发现功能,从而降低 json 搜索索引在 dml 操作期间的成本。

json 搜索索引的底层数据结构是发布列表,通常比 b 树索引慢。如果 json 搜索索引与基于函数的索引或多值索引一起使用,则优化器会尽可能优先选择这些索引。由于 json 搜索索引会索引整个 json 数据,因此其大小将显著大于其他索引,通常在原始数据的 20%-30% 左右。json 搜索索引支持 json 数组中的值以及全文搜索操作。以下示例选择所有“description”字段同时包含单词“magic”和“christmas”的文档。除了“{and}”,还可以使用“{near}”或“{not(…)}”。有关 json 搜索索引功能的更多信息,请参阅文档

select data from purchaseorder po
where json_textcontains(po.data, '$.lineitems.part.description', 'magic
{and} christmas');

查询执行计划将 json 搜索索引显示为“域索引”:

---------------------------------------------------------
| id  | operation 						| name			|
---------------------------------------------------------
| 	0 | select statement 				| 				|
| 	1 | table access by index rowid		| purchaseorder |
|* 	2 | domain index 					| po_full_idx	|
---------------------------------------------------------

对于包含大量 dml 操作的工作负载,使用单个 json 搜索索引来替代大量的函数索引和多值索引可能会有所帮助,从而减少索引维护量(dml 操作后的索引同步)。更多优化策略请参阅右侧引用的博客。

物化视图

您可以使用物化视图来提升频繁访问多行的查询(而非基于索引驱动的键值查找)的性能。物化视图会持久保存查询结果。部分或完全匹配物化视图查询的后续查询无需重新运行原始查询即可访问物化数据(以空间换取速度)。

在本文档中,我们主要关注 json_table 物化视图。以下代码将创建一个物化视图,其中包含示例 json 文档中“lineitems”数组的值。如前所述,使用物化视图可以在 oracle 19c 中索引 json 数组值,因为该版本不支持多值 json 索引。

create materialized view po_mv build immediate
refresh fast on statement with primary key as
 select po.id, jt.* from purchaseorder po, json_table(po.data, '$' error on error null on empty
 columns (
 	 po_number number path '$.ponumber', 
 	 userid varchar2(10) path '$.user', 
 	 nested path '$.lineitems[*]'
 	 	columns ( itemno number path '$.itemnumber',
 	 	description varchar2(256) path '$.part.description',
 	 	upc_code number path '$.part.upccode',
 	 	quantity number path '$.quantity',
 	 	unitprice number path '$.part.unitprice')))
jt;

在我们的物化视图中将数组值转换为多行,使我们能够在 json 数组的字段上创建附加(辅助)索引,如下所示:

create index mv_idx on po_mv(upc_code, quantity);

现在,基表上的 sql/json 查询将透明地重写,以尽可能使用物化视图及其索引。以下查询是一个示例,其中 oracle 自动重写查询以使用物化视图及其二级索引,如执行计划中所示:

select data from purchaseorder po
where json_exists(po.data, '$.lineitems[*]?(@.part.upccode == 1234)');
-----------------------------------------------------------------
| id | operation							| name 				|
 … 	
| 4 | mat_view access by index rowid batched| po_mv 			|
|* 5 | index range scan 					| mv_idx 			|
-----------------------------------------------------------------

为了使物化视图 (mv) 与底层数据(dml 操作后)保持同步,我们将物化视图设置为“语句快速刷新”。这可以自动执行刷新过程,并始终保持物化视图和基表数据一致。本文不深入讨论物化视图的各种刷新机制。更多详细信息,请参阅相关文档。

oracle 分区

您可以像平常一样对包含文档的表进行分区,以提高性能:分区可以将表和索引细分为单独的较小物理对象,即所谓的分区。**分区表中的数据位置由分区键标识。此键可以是关系列,也可以是 json 数据中的字段。**从应用程序的角度来看,分区表与非分区表完全相同。

以下示例创建一个范围分区表,其分区键从存储在“data”列中的 json 文档中提取,并使用基于 json_value 的虚拟列“po_num_vc”;

create table part_j (id varchar2 (32) not null primary key,
data json,
po_num_vc number generated always as
(json_value (data, '$.ponumber' returning number)))
partition by range (po_num_vc)
	 (partition p1 values less than (1000), 
	 	partition p2 values less than (2000));

对 json 字段“$.ponumber”(用作虚拟列分区键的 json 字段)进行过滤的查询将明显受益于 oracle 分区功能:一种称为分区修剪的优化技术会自动排除所有不相关的分区,即已知不包含任何与查询相关的数据的分区。
以下示例查询只需访问第一个分区,因为查询的相等谓词只能在此分区中找到匹配的记录。执行计划中显示了这一点,其中 pstart 列和 pstop 列均为 1。

select data from part_j
where json_value (data, '$.ponumber' returning number) = 500; -----------------------------------------------------------------
| id 	| operation 			| name | time | pstart| pstop |
-----------------------------------------------------------------
| 0 	| select statement 		| 			| 00:00:01 | 	| 	|
| 1 	| partition range all	| 			| 00:00:01 | 1 	| 1 |
|* 2 	| table access full 	| part_j 	| 00:00:01 | 1 	| 1 |
-----------------------------------------------------------------

oracle 分区有多种机制来对表进行分区,由于篇幅原因,这里不再赘述。更多详情请参阅文档。通常,对于较大的 json 文档(平均 > 32kb),使用关系列作为分区键在 dml 操作期间通常比使用 json_value 虚拟列性能更高,因为后者需要在写入正确分区之前从 json 中提取分区键。

并行执行

通过使用多个进程处理 json 文档,可以并行化 json 操作(例如查询或批量更新)。这可以更高效地利用硬件资源,是大规模数据处理的关键。

大型数据仓库应始终使用并行执行来实现良好的性能oltp 应用程序中的特定操作(例如批处理操作)也可以从并行执行中显著受益
并行执行支持查询和 dml(插入、更新)。有多种方法可以启用和配置并行执行。例如,oracle 自治数据库会根据为连接选择的消费者组自动选择并行度。对于手动控制并行度的数据库,您可以在会话级别启用并行度或装饰单个对象。例如,以下代码为我们的表“purchaseorder”启用了 8 级并行度。

alter table purchaseorder parallel 8;

如果使用并行执行,则执行计划将显示带有“px”的行。

| 1 | px coordinator || 2 | px send qc (order)|

oracle 内存列式存储

json 数据可以存储在内存列存储(im 列存储)中,从而提升查询性能。最大可达 32 kb 的 json 值可以与其他关系列一起直接加载并在内存中处理。通常,json 文档中的值并非都与分析查询相关。在这种情况下,只需在内存中分别移动相关的 json 字段即可更高效地利用内存:可以使用虚拟列或中间物化视图。

以下示例向表“purchaseorder”添加了一个虚拟列,该列用于从订单地址中提取“zipcode”字段。虚拟列已添加,并且表已启用内存处理。

alter table purchaseorder add (zip varchar2(4000) generated always as
(json_value(data, '$.shippinginstructions.address.zipcode.number()')));
alter table purchaseorder inmemory;

以下分析示例查询按 zipcode 计算订单数量,并利用快速内存​​处理,如执行计划所示。

select zip, count(1) from purchaseorder group by zip ;
-----------------------------------------------------
| id 	| operation 				| name 			|
-----------------------------------------------------
| 0 	| select statement 			| 				|
| 1 	| hash group by 			| 				|
| 2 	| table access inmemory full| purchaseorder |
-----------------------------------------------------

oracle exadata 数据库云服务器

exadata 加速 json 性能:包含表和索引扫描的查询可以将数据搜索和检索处理卸载到 exadata 存储服务器。对于 json 运算符(例如,在查询的 where 子句中使用 json_value 或 json_exists),此卸载操作会自动且透明地进行最大 4kb 的 json 文档可以卸载到 exadata 存储服务器。更大的文档将在数据库中处理。

执行计划中的 storage 术语表明卸载已完成:

-----------------------------------------------------
| id | operation 				| name 			|
-----------------------------------------------------
|* 3 | table access storage full| purchaseorder |
-----------------------------------------------------

oracle 真正应用集群

oracle 真正应用集群 (rac) 允许客户在多台服务器上运行单个 oracle 数据库,以最大限度地提高可用性并在访问共享存储时实现水平可扩展性

使用 oracle real application clusters 对于 json 文档的处理是透明的,并且任何 sql/json 处理都会自动受益。

oracle 分片

oracle 分片也是一种水平扩展技术,但与 rac 不同,它采用无共享架构。分片技术允许 json 文档扩展到海量数据和事务处理,并支持数据主权。json 文档根据分片键(可以是关系列或 json 字段)分发到各个数据库表分片中。

使用 oracle 分片技术对于分片 json 文档的处理是透明的。对于许多操作而言,分片文档的处理仅在拥有特定分片的数据库上进行,而跨分片查询将透明地收集并聚合来自所有相关分片的结果数据。

oracle 数据库中 json 性能调优特性到此结束。以下总结了 json 文档存储 api(mongodb 集合和 soda 集合)的性能相关主题:

性能技巧soda 系列

oracle 数据库提供允许以集合形式访问 json 数据的 api:适用于 mongodb 的 oracle 数据库 api 和简单 oracle 文档访问 api -soda。从概念上讲,json 集合将 json 数据(称为文档)存储在自动生成的表中(以便也可以通过 sql 访问)。soda 支持与包含 json 数据的常规表相同的存储选项,并且适用相同的建议:在 oracle 19c 上使用 blob,在 oracle 21c 上使用原生 json 类型。

用户通常使用原生语言驱动程序(例如,java 版 soda 或 python 版 soda)处理 json 集合。soda 原生语言驱动程序通常比 rest 驱动程序(rest 版 soda)提供更高的吞吐量(每秒操作数)。

建议按如下方式配置 soda 驱动程序:

  • 启用 soda 元数据缓存

soda 驱动程序需要了解每个 json 集合的元数据(列名、类型等)。启用元数据缓存可以减少与数据库的往返次数,从而提高延迟和吞吐量。

  • 启用语句缓存

语句缓存通过缓存重复使用的可执行语句(例​​如在循环中或在重复调用的方法中)来提高性能。对于 java,语句缓存是使用 jdbc 启用的。

  • 对于负载平衡系统:关闭 dns 缓存

负载平衡允许将 soda 操作分布到不同的节点。如果启用了 dns 缓存,则所有连接都可能使用同一节点,从而导致负载平衡失效。对于 java,应设置以下系统属性:inet.addr.ttl=0

数据库性能调优技术也适用于 soda:例如,soda 集合可以分区或分片,并且可以使用索引和/或物化视图加速查询。soda 操作会自动转换为等效的 sql 操作:例如,soda 查询将转换为在 where 子句中使用 json_exists 运算符的 select 语句。

可以从 v$sql 数据库视图中检索 sql 操作,也可以通过直接在 soda 驱动程序中启用日志记录来检索 sql 操作:在 java 中,使用标准日志记录包 - 可以为 soda 启用它,如下所示:

java -classpath "..." -doracle.soda.trace=true -djava.util.logging.config.file=logging.properties <program>
  • ‘oracle.soda.trace=true’ 启用 sql 语句的日志记录。
  • ‘logging.java.util.logging.config.file’ 定义 java.util.logging 配置文件的路径,该文件允许不同的日志记录级别:finest 是最详细的日志记录级别。

更多信息 – 链接

  • oracle xe
  • oracle standard edition
  • oracle enterprise edition
  • oracle exadata cloud service
  • oracle exadata cloud at customer
  • oracle exadata database machine
  • oracle database cloud service
  • oracle autonomous json
  • oracle autonomous transaction processing
  • oracle autonomous data warehouse

到此这篇关于oracle 数据库中的 json性能注意事项(最佳实践)的文章就介绍到这了,更多相关oracle 数据库json内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com