PgSQL · 特性分析· jsonb类型解析

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
简介: PG 9.4版本里面,增强了对json数据的支持,受到了很大关注。9.4之前,PG已经原生支持json数据类型了,但只是用字符串的形式存储和处理。这样做天然有性能上的缺点:每次对json字符串里面的数据进行查询,一般需要全表扫描加字符串匹配,效率很低。当然也可以在存储json的字符串字段上创建GI.

PG 9.4版本里面,增强了对json数据的支持,受到了很大关注。9.4之前,PG已经原生支持json数据类型了,但只是用字符串的形式存储和处理。这样做天然有性能上的缺点:每次对json字符串里面的数据进行查询,一般需要全表扫描加字符串匹配,效率很低。当然也可以在存储json的字符串字段上创建GIN索引,但需要对查询中用到的json的key或value创建单独索引,造成要被动维护很多索引。所以,这种json类型,只适用于把PG单纯作为数据存储,只读入读出数据,不对数据进行限定key或value查询的场景。

PG 9.4中引入了jsonb类型。其特点是,将json数据中的key和value进行解析,转换为PG的基本数据类型,包括数字,字符串和布尔类型等;同时,增加了对应的GIN处理函数,可以将json中的所有key和value转换为GIN索引的key。这样,只用一个GIN索引,即可实现对所有key或value的条件查询。下面我们分析一下jsonb的使用方法和内核实现。

使用

创建含jsonb类型的表方法如下所示:

 

创建GIN索引的方法如下:

 

可以使用下面的查询得到含有<product, ProgreSQL>键值对的行:

 


内核实现

先分析一下jsonb是如何从字符串,变成特殊的二进制形式存入磁盘的。追踪一下jsonb插入的过程,可以看到PG所调用的函数流程如下。

 

其中,pg_parse_json先把用户输入的字符串,通过编译器转换为一个树形结构(每个节点的类型为JsonbValue)。然后JsonbValueToJsonb在这个结构基础上,转换为存入磁盘的格式。从convertJsonbObject函数可以看出,转换为磁盘格式的策略为:从树形结构的根部开始遍历,递归进行广度优先遍历。对于同一父亲下面的子键值,将所有键名(字符串)长度写入buffer中预留的头部,随后将键名依次写入buffer中。最后再以相似的方式写入键所对应的所有值(值如果是json对象,则递归调用)。这样,读入buffer的头部,就可以遍历出所有键名的位置,得到键名。再从读第一个键值开始,读入对应的值或子键,最终得到整个树(见JsonbIteratorNext)。

采用这种存储方式,jsonb所占用的存储空间比原来支持的json类型要多一些。其实,jsonb的核心优势在于快速和灵活的索引。从前面创建index的语句可以看到,jsonb支持两种特有的GIN索引jsonb_ops和jsonb_path_ops。我们知道,GIN索引建立时,会先通过内建函数从表中每行数据的索引字段的值中,抽取键(key),一个字段值一般可抽取多个key。然后,将每个key与含有此key的所有行的ID组成键值对,再将它们插入b树索引供查询。那么这两种GIN索引有什么区别呢?

它们的区别在于,生成GIN key的方式不同。jsonb_ops调用gin_extract_jsonb函数生成key,这样每个字段的json数据中的所有键和值都被转成GIN的key;而jsonb_path_ops使用函数gin_extract_jsonb_path抽取:如果将一个jsonb类型的字段值看做一颗树,叶子节点为具体的值,中间节点为键,则抽取的每个键值实际上时每个从根节点到叶子节点的路径对应的hash值。

不难推测,jsonb_path_ops索引的key的数目和jsonb的叶子节点数有关,用叶子节点的路径做查询条件时会比较快(这也是这种索引唯一支持的查询方式);而jsonb_ops索引的key的数目与jsonb包含的键和值(即树形结构的所有节点)的总数有关,可以用于路径查询之外的其他查询。


目录
相关文章
|
14天前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
32 3
|
16天前
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
35 3
|
18天前
|
开发框架 供应链 监控
并行开发模型详解:类型、步骤及其应用解析
在现代研发环境中,企业需要在有限时间内推出高质量的产品,以满足客户不断变化的需求。传统的线性开发模式往往拖慢进度,导致资源浪费和延迟交付。并行开发模型通过允许多个开发阶段同时进行,极大提高了产品开发的效率和响应能力。本文将深入解析并行开发模型,涵盖其类型、步骤及如何通过辅助工具优化团队协作和管理工作流。
51 3
|
18天前
|
缓存 JavaScript 前端开发
Vue3与Vue2生命周期对比:新特性解析与差异探讨
Vue3与Vue2生命周期对比:新特性解析与差异探讨
67 2
|
7天前
|
缓存 监控 网络协议
|
2天前
|
Kubernetes Cloud Native 调度
云原生批量任务编排引擎Argo Workflows发布3.6,一文解析关键新特性
Argo Workflows是CNCF毕业项目,最受欢迎的云原生工作流引擎,专为Kubernetes上编排批量任务而设计,本文主要对最新发布的Argo Workflows 3.6版本的关键新特性做一个深入的解析。
|
6天前
|
存储 消息中间件 NoSQL
Redis数据结构:List类型全面解析
Redis数据结构——List类型全面解析:存储多个有序的字符串,列表中每个字符串成为元素 Eelement,最多可以存储 2^32-1 个元素。可对列表两端插入(push)和弹出(pop)、获取指定范围的元素列表等,常见命令。 底层数据结构:3.2版本之前,底层采用**压缩链表ZipList**和**双向链表LinkedList**;3.2版本之后,底层数据结构为**快速链表QuickList** 列表是一种比较灵活的数据结构,可以充当栈、队列、阻塞队列,在实际开发中有很多应用场景。
|
5天前
|
Dart 安全 编译器
Flutter结合鸿蒙next 中数据类型转换的高级用法:dynamic 类型与其他类型的转换解析
在 Flutter 开发中,`dynamic` 类型提供了灵活性,但也带来了类型安全性问题。本文深入探讨 `dynamic` 类型及其与其他类型的转换,介绍如何使用 `as` 关键字、`is` 操作符和 `whereType&lt;T&gt;()` 方法进行类型转换,并提供最佳实践,包括避免过度使用 `dynamic`、使用 Null Safety 和异常处理,帮助开发者提高代码的可读性和可维护性。
62 1
|
10天前
|
PHP 数据安全/隐私保护 开发者
PHP 7新特性解析与实践
【10月更文挑战第20天】本文将深入浅出地介绍PHP 7的新特性,包括性能提升、语法改进等方面。我们将通过实际代码示例,展示如何利用这些新特性优化现有项目,提高开发效率。无论你是PHP新手还是资深开发者,都能从中获得启发和帮助。
|
18天前
|
JavaScript 前端开发 索引
JavaScript ES6及后续版本:新增的常用特性与亮点解析
JavaScript ES6及后续版本:新增的常用特性与亮点解析
17 4

推荐镜像

更多