今天介绍一个MySQL中的数据类型-JSON,相信大家对JSON都不陌生,在日常工作中使用到的频率也很高,话不多说,直接开始。


何谓JSON

看下RFC文档对于JSON的描述

1.基于 JavaScript 语言的轻量级的数据交换格式

2.基于文本

3.语言无关


JSON应用场景

我大概使用过以下两类:

1.接口的数据交换,比如ajax请求时的application/json、rpc调用时的JSON序列化\反序列化;

2.以JSON格式存储数据,我接触过以下两种:

2.1 以Mongodb为代表的文档型数据库,很好的支持JSON格式的数据存储;

2.2 以MySQL为代表的关系型数据库,5.7.8之前没有JSON这种数据类型,只能以varchar或者text形式变相的支持JSON,存取键值极不方便;5.7.8开始有JSON数据类型,有专门语法支持键值的存取,易用性得到很大提升。

接下来重点聊聊MySQL中如何存取JSON以及存在的一些问题。

                                        


MySQL 存储JSON

熟悉关系型数据库的同学都知道,数据存储在表中,得先有表才能插数据,看一条普通的SQL insert语句

  1. insert into user(id,name,age) values(1,'jack',10);

代表的语义是往user表中插入一条数据,这条数据有三个属性,分别是id、name、age,各自对应MySQL user表中的三个列,如果我们向user表中插入一个不存在的列salary,MySQL会报错

Error Code: 1054. Unknown column 'salary' in 'field list'

结论是要往MySQL表中插入数据,必须提前定义好表结构,表结构包括表名、表字符集、表包含的字段、字段名、字段类型等等。

有什么办法能不给表加物理字段就可以为数据增加属性呢?

给表预置一个扩展字段是一种解决思路,比如extdata,里面存储JSON形式的键值对,形如:

  1. extdata
  2.  
  3. {"salary":1000,
  4. "sex":'女',
  5. "其他key":'其他值'
  6. }

至于存哪些key完全由使用方决定,key的数量不限,value的类型也不限,是不是有很好的扩展性,不管业务怎么变,底层存储都是支持的。

这也就是为什么要在MySQL中存取JSON的目的,主要是为了追求扩展性。

具体到MySQL中怎么实现,前面提到MySQL 5.7.8之前是不支持JSON的,要支持JSON语义,只能以字符串形式来变相实现,比如要修改extdata中的salary为2000,是没有办法直接修改的,需要先在应用层将extdata读出然后反序列化为JSON对象,通过JSON对象的Api来修改salary的值,修改完以后将新的JSON对象序列化为新JSON串,最后整体修改user表中的extdata字段为新JSON串,用代码实现大体如下:

  1. 1.result = db.execute("select extdata from user where id = xxx");
  2. 2.JSONObj = JSONUtil.parse(result.get("extdata"));
  3. 3.JSONObj.put("salary",2000);
  4. 4.extdata_str = JSONObj.toJSONString();
  5. 5.db.execute("update user set extdata=extdata_str where id=xxx");

  

这一套更新操作繁琐且性能低,读取操作也存在类似问题,由于没有原生Api的支持,这一切感觉有点糟糕。

到了MySQL 5.7.8开始,MySQL开始支持JSON这种数据类型,看下官方文档的介绍:

MySQL新增加的原生JSON类型比在字符串列中存储 JSON 格式的字符串相比有两个优点:

1.自动的数据校验,对于JSON类型的列MySQL会校验其合法性;

2.提供了更方便的Api用于存取,避免了繁琐的应用层操作。

看下基于MySQL 5.7.8,如何优雅的存取JSON类型中的键值,依然以修改extdata中的salary为例:

  1. update user set extdata = JSON_SET(user.extdata, '$.salary',2000) where id =1;

读取salary的值:

  1. select JSON_EXTRACT(user.extdata, '$.salary') from user where id =1;

借助JSON_SET和JSON_EXTRACT这两个Api,极大的降低了存取的复杂度,想深入了解MySQL JSON用法的请参考文章最后的推荐阅读内容。

说到这儿,借助MySQL的原生JSON类型以及相关的Api存取扩展数据在易用性方面已经没什么问题了,接下来从性能角度思考下是否有待提升。

  1. /*找出salary等于2000的user*/
  2. select * from user where JSON_EXTRACT(user.extdata, '$.salary') =2000;

在我自己的pc机上,user表中共300万条数据,执行这条SQL花费接近3秒,不谈快慢,就论是否有优化空间,贴个执行计划出来

面对大名鼎鼎的全表扫描如何优化呢?


优化JSON查询

按照过往的思路,我们只要设计合理的索引就能避免全表扫描,但这次面对JSON似乎有点黔驴技穷了,别担心,大名鼎鼎的MySQL早已帮你做了既生瑜又生亮的美事,看看官方怎么说。

  1. JSON类型列无法直接索引;

  2. 可以基于JSON创建一个生成列,然后基于生成列创建索引,从而达到对JSON类型列加索引的效果。

接着看下何谓生成列

生成列的值在插入数据时不需要设置,MySQL会根据生成列关联的表达式自动计算填充,生成列的定义方式如下:

  1. col_name data_type [GENERATED ALWAYS] AS (expr)
  2. [VIRTUAL | STORED] [NOT NULL | NULL]
  3. [UNIQUE [KEY]] [[PRIMARY] KEY]
  4. [COMMENT 'string']

AS (expr)指示生成列并定义用于计算列值的表达式,可以在前面加上GENERATED ALWAYS明确的表示这是一个生成列。  

回归到我们的场景中,分三步进行优化:

  1. 1.创建一个生成列v_salary,计算列值表达式为extdata->"$.salary",代表提取extdata中的salary
  2. ALTER TABLE `user` ADD COLUMN `v_salary` DECIMAL(10,2) as (extdata->"$.salary") AFTER `extdata`;
  3. 2.针对v_salary创建索引
  4. ALTER TABLE `user` ADD INDEX `idx_salary` (`v_salary`) ;
  5. 3.替换查询语句中JSON_EXTRACT(user.extdata, '$.salary')为v_salary;
  6. select * from user where v_salary =2000

select * from user where v_salary =2000,执行耗时为0.047s,这个优化效果非常显著。

看下现在的执行计划已经使用了索引


总结

任何新技术的引入一定要有一个比较全面的认识,充分理解其利弊,不能只看到其光鲜的一面,而忽略其带来的弊端,对于弊端要有应对措施,知己知彼。


推荐阅读

https://dev.mysql.com/doc/refman/5.7/en/json.html

https://dev.mysql.com/doc/refman/5.7/en/json-modification-functions.html#function_json-set

https://dev.mysql.com/doc/refman/5.7/en/create-table-secondary-indexes.html

https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html

rfc7159 (ietf.org)

抛砖系列之-MySQL中的数据类型JSON的更多相关文章

  1. 抛砖系列之redis监控命令

    前言 redis是一款非常流行的kv数据库,以高性能著称,其高吞吐.低延迟等特性让广大开发者趋之若鹜,每每看到别人发出的redis故障报告都让我产生一种居安思危,以史为鉴的危机感,恰逢今年十一西安烟雨 ...

  2. MySQL中的数据类型及创建

    MySQL创建: 1.创建数据库create database test2; 2.删除数据库drop database test2;3.创建表create table ceshi(    ids in ...

  3. 【个人笔记】《知了堂》MySQL中的数据类型

    MySQL中的数据类型 1.整型 MySQL数据类型 含义(有符号) tinyint(m) 1个字节  范围(-128~127) smallint(m) 2个字节  范围(-32768~32767) ...

  4. MySQL中各种数据类型的长度及在开发中如何选择

    接触MySQL这个数据库大概快要两年了,不过由于没有特别深入系统的去学习,大多也是停留在一知半解的状态.今天在工作中刚好碰到了表设计的问题,顺便写篇博客,把MySQL中数据类型和字段类型选择这方面给弄 ...

  5. 存储引擎和表的操作(mysql中的数据类型、完整性约束)

    一.存储引擎 .概念 MySQL中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平并且最终提供广泛的不同的功能和能力. 通过选择不同的技术 ...

  6. Sql Server中的数据类型和Mysql中的数据类型的对应关系(转)

    Sql Server中的数据类型和Mysql中的数据类型的对应关系(转):https://blog.csdn.net/lilong329329/article/details/78899477 一.S ...

  7. MySQL中的数据类型 [数值型、字符串型、时间日期型]

    MySQL中的数据类型 [数值型.字符串型.时间日期型] MySQL中各数据类型 1. 数值类型(整型) 类型 数据大小 类型 (无符号:unsigned) 数据大小 存储空间 tinyint -12 ...

  8. 存储引擎,MySQL中的数据类型及约束

    存储引擎,MySQL中的数据类型及约束 一.存储引擎 1.不同的数据应该有不同的处理机制 2.mysql存储引擎 ​ Innodb:默认的存储引擎,查询速度叫myisam慢,但是更安全 ​ 支持事务, ...

  9. mysql中的数据类型长度

    “mysql中的数据类型长度是固定的 数据类型后面改的只是展示长度 没用的 int就是四个字节 2的31次方减一是最大值 所以改这个长度没用 只能改数据类型”

随机推荐

  1. OpenStack之三: 安装MySQL,rabbitmq, memcached

    官网地址:https://docs.openstack.org/install-guide/environment-sql-database-rdo.html #:安装mysql [root@mysq ...

  2. 监测linux系统负载与CPU、内存、硬盘、用户数的shell脚本

    本节主要内容: 利用Shell脚本来监控Linux系统的负载.CPU.内存.硬盘.用户登录数. 一.linux系统告警邮件脚本 # vim /scripts/sys-warning.sh #!/bin ...

  3. 【编程思想】【设计模式】【结构模式Structural】组合模式composite

    Python版 https://github.com/faif/python-patterns/blob/master/structural/composite.py #!/usr/bin/env p ...

  4. web端 - 返回上一步,点击返回,跳转上个页面 JS

    1.方法一: <script language="javascript" type="text/javascript"> window.locati ...

  5. Map集合的认识和理解

    java.util.Map(k,v)集合* Map的特点:* 1.Map集合是一个双列集合,一个元素包含两个值(一个是key,一个是Value)* 2.Map集合中的元素,key和value的类型可以 ...

  6. 【C/C++】最长公共子序列(LCS)/动态规划

    晴神这个的最巧妙之处,在于用dp[i][0] = dp[0][j] = 0的边界条件 这样从1的下标开始填数组的时候,递推公式dp[i-1][j-1]之类的不会报错 #include <iost ...

  7. 基于Web的质量和测试度量指标

    直观了解软件质量和测试的完整性 VectorCAST/Analytics可提供便于用户理解的web仪表盘视图来显示软件代码质量和测试完整性指标,让用户能够掌握单个代码库的趋势,或对比多个代码库的度量指 ...

  8. 【Linux】【CentOS】【FTP】FTP服务器安装与配置(vsftpd、lftp)

    [初次学习.配置的笔记,如有不当,欢迎在评论区纠正 -- 萌狼蓝天 @ 2021-12-02] 基本概念 FTP访问方式 实体账号:本地账户 来宾账户:guest 匿名登录:anonymous fp ...

  9. Iphone5, 6 and 6Plus尺寸

    1.iPhone5分辨率320x568,像素640x1136,@2x 2.iPhone6分辨率375x667,像素750x1334,@2x 3.iPhone6 Plus分辨率414x736,像素124 ...

  10. Linux编译安装、压缩打包、定时任务管理

    编译安装 压缩打包 定时任务管理 一.编译安装 使用源代码,编译打包软件 1.特点 1.可以定制软件 2.按需构建软件 2.编译安装 1.下载源代码包 wget https://nginx.org/d ...