关于关系型数据库中使用json字段的建议

1. 前言

1.1 概述

当前,一些应用程序在数据库层使用 JSON格式的字段。JSON 有很好的灵活性,它可以自由地包含不同键。然后,关系型数据库对JSON的处理能力天生不足。因此,在关系型数据库中使用JSON时应当遵循一定的思想,从而既能受益于JSON的灵活性,又能发挥关系型数据库的强大功能。

本文根据实际工作中的经验,结合一些国内外现有的资料,总结了一些在关系型数据库中使用JSON 的设计思想和注意事项。文章旨在指导读者更好地进行应用的数据库设计。

本文使用的数据库是PostgreSQL。

1.2一些术语

1.2.1 JSON

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

 

JSON建构于两种结构:

1.名称/对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),纪录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 associative array)。

2. 值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。

JSON对象是一个无序的名称/对集合。一个对象以{ (左括号) 开始,以} (右括号) 结束。每个名称后跟一个 :(冒号);名称/对之间使用 ,(逗号 )分隔。例如:

{"a": 1, "b": [1, 2, 3]}

JSON数组是值(value)的有序集合。一个数组以[” 左中括号 开始, ]右中括号 结束。值之间使用, 逗号分隔。例如:

[1, 2, "3", {"a": 4}]

JSON的值(value)可以是双引号(””)括起来的字符串(string)、数值(number)truefalse null、对象(object)或者数组(array)。这些结构可以嵌套。

 

2 JSON使用注意事项

2.1 JSON字段设计思想

1. JSON 只能作为一种辅助型的数据格式,不要试图用JSON 去取代表中大多数字段,也不要将不合适的属性放在JSON中[2]。

2. JSON 格式的字段对数据库最好是透明的[2]。传统的关系型数据库并不提供JSON处理功能。近年来Oracle,PostgreSQL 和 MySQL 引入了JSON 处理功能,但功能有限。另外,关系型数据库的第一范式,要求所有字段都是原子性的(虽然我们不必严格遵守),这个规则应尽量遵守。

 

3. 如果因业务需求,一些 JSON 格式的字段要用数据库处理,应该保证,JSON 结构尽量是单层的,由简单的键值对组成。这样便于读取,修改和扩展。如果它有嵌套结构,也应保证内层结构对数据库尽可能透明。

 

例如,这里的JSON是单层的。

{

    "doorNo": "1",

    "secret": "15F50xyeyEE3nLqop7ub7fd7d7R1zPYgMNBg/D+6t8+el8gnxl4Wc0ZBZlkgkMdb",

    "readerInOrOut": "1",

    "controllerLevel": "3",

    "devEngModeEnable": null,

    "secParentIndexCode": null,

    "acsReaderCardCapacity": "0",

    "ReaderFaceCapacity": "0",

    "ReaderFingerCapacity": "0"

}

 

2.2 JSON字段设计思想

使用关系型数据库查询JSON 中的值时,应该明确地给定JSON 键的路径。不能在键的未知的情况下,通过值来获取它的键。就如同在关系型数据库,我们不能在字段未知的情况下,通过值来获取字段名一样。

 

上面的例子也说明,不是所有的属性都适合放在JSON 中。

2.3 什么样的属性不适合放在 JSON 中?

不是所有属性都适合放在JSON中。不合理,不周全的设计会不仅会会影响程序的性能,还会给软件的开发和维护造成困难。因此,我们应该在追求JSON 的灵活性和发挥关系型数据库的功能之间取得平衡。

下面是不适合放在JSON中的属性:

1.作为重要的选择条件,或用于连接(join)、排序(使用了order by)、去重(使用了distinct)或分组(使用了group by)运算的属性[3][5]。尽管我们可以通过数据库提供的函数或操作符获取 JSON 中的属性,并通过它进行上述运算,但处理较复杂,性能不佳,从2.2节的案例中就可以看出。因此,这样的属性应当作为原子性的字段存在。

以下面的人员表tb_person为例:

create table tb_person

(

         person_id varchar(48) default gen_random_uuid() not null,

        person_index_code varchar(48),

        person_name varchar(64),

        sex integer,

        organization_indexcode varchar(48),

       organization_name varchar(48),

        status integer,

        phone varchar(64),

        email varchar(64),

        age integer,

        birthday date,

        create_time timestamp with time zone default current_timestamp(3),

        update_time timestamp with time zone default current_timestamp(3),

        extended_attribute jsonb,

        constraint uk_tb_person_person_index_code unique (person_index_code, status),

        constraint pk_tb_person primary key (person_id, status)

);

人员表tb_person这些属性中,人员id(person_id),编码(person_index_code),所属组织的编码(organization_indexcode),当前状态(status)和创建时间(create_time)都是业务中重要的选择条件;而所属组织的编码(organization_indexcode)是人员表和组织表的关联字段,也做为分组运算的条件(我们可以根据组织对人员分组);创建时间(create_time)是常用的排序字段。它们都需要作为独立的字段,而不适合放在JSON中。

 

2.某个属性的读取和更新频率比其他属性频繁很多,则它们不适合放在同一个 JSON 字段中。因为 JSON 字段占用空间很大,读写代价都很高。因此为提高性能,应将读写频繁的属性作为单独的字段。

例如,对于一个对于一个停车场表来说,它的属性,总车位数和当前可用车位数就不能放在同一个JSON中,如果使用JSON的话。因为前者通常是固定的,而后者是动态变化的。

3.为了提高查询速度而设置的冗余属性。这样的属性通常依赖于某些非主属性,放在JSON中则不符合提高性能的目的,而且不易修改。

例如,人员表tb_person中,非主属性有组织编码(organization_indexcode)和组织名称(organization_name)。组织名称依赖于组织编码,会随组织编码的变更而变更,不适合放置在JSON中。

4.本身定义不合理的,与其他属性重复的,或可能会被弃用的属性。从数据库的层面看,JSON是一个整体,单个属性的缺陷就是整个JSON的缺陷。而且修改或删除这样的有缺陷属性,代价会比修改或删除单独字段的代价高。因此,这一类属性,应当予以删除或改造。

2.4 什么样的属性可以用JSON 表示?

1. 用户定义的属性,可以放在 JSON 中[4]。

2. 若属性是一个集合,则可以用JSON数组表示[2][4]。

3. 若表中有较大比例的行没有该属性,或该属性为空,则可以将该属性放在JSON字段中。

4. 对数据库透明,仅由上层程序处理的属性,可以放在JSON字段中。这样的JSON 字段一般很少在数据库中修改,而JSON 作为一个整体在程序中传输[3]。

 

2.5 将独立字段合并为JSON 的注意事项

如果因业务原因,需要将表中的几个字段合并为JSON,除了前几节的事项外,还需要注意以下几点:

1. JSON 结构尽量简单,理想的情况下,合并后的JSON 的键和值分别对应原有的字段和它的值。

2. 用来合成 JSON 的原字段最好是原子性的,或者对数据库是透明的。这样的字段便于合并,在 JSON 中也容易解析。

3. 最后,如果你需要用一些非原子性的字段构造一个复杂的 JSON,则应该详细地写出构造的方法步骤,再进行编码。

 

2.6 修改JSON字段的数据的注意事项

如果需要修改数据库中 JSON 字段的数据,除了上面的事项,还需要注意以下几点:

1. 增加,修改或删除JSON 字段中的键值对时,应该明确地给定表上的选择条件和JSON 键的路径,从而使我们能够直接通过给定条件获取表中需要更改的行,并能够根据 JSON 键的完整路径来添加,修改或删除键值对。如果新增的值,或者修改后的值依赖于 JSON 中某个键的值,则被依赖的键的路径也应该是明确的。

2. 修改JSON的结构应该慎重。JSON 字段的结构在确定之后,不应该发生较大变化。

参考文献

     [1] 介绍 JSON

     [2] Colin M. Answer: Storing JSON in database vs. having a new column for each key?. 2017-09-15.  

     [3]  Vishal Kumar. Answer: Is it okay to use JSON as a database. 2018-06-26.

     [4] Arun B Chandrasekaran. Using JSON Datatype In Relational Database To Develop Flexible/Configurable Software. 2018-11-11

    [5] Bill Karwin. Answer: Why not use relational databases to store JSON data (like a primary key field, and a BLOB field for JSON data in a MySQL table) instead of using NoSQL databases (MongoDB, etc.)?. 2019-04-14

 

你可能感兴趣的:(数据库,数据库,json,关系型数据库)