网易首页 > 网易号 > 正文 申请入驻

MySQL + JSON = 王炸!!

0
分享至

关系型的结构化存储存在一定的弊端,因为它需要预先定义好所有的列以及列对应的类型。但是业务在发展过程中,或许需要扩展单个列的描述功能,这时,如果能用好 JSON 数据类型,那就能打通关系型和非关系型数据的存储之间的界限,为业务提供更好的架构选择。

当然,很多同学在用 JSON 数据类型时会遇到各种各样的问题, 其中最容易犯的误区就是将类型 JSON 简单理解成字符串类型 。 但当你看完这篇文章后,会真正认识到 JSON 数据类型的威力,从而在实际工作中更好地存储非结构化的数据。

JSON 数据类型

JSON(JavaScript Object Notation)主要用于互联网应用服务之间的数据交换。MySQL 支持RFC 7159定义的 JSON 规范,主要有 JSON 对象JSON 数组 两种类型。下面就是 JSON 对象,主要用来存储图片的相关信息:

{
"Image": {
"Width": 800,
"Height": 600,
"Title": "View from 15th Floor",
"Thumbnail": {
"Url": "http://www.example.com/image/481989943",
"Height": 125,
"Width": 100
},
"IDs": [116, 943, 234, 38793]
}
}

从中你可以看到, JSON 类型可以很好地描述数据的相关内容,比如这张图片的宽度、高度、标题等(这里使用到的类型有整型、字符串类型)。

另一种 JSON 数据类型是数组类型,如:

[
{
"precision": "zip",
"Latitude": 37.7668,
"Longitude": -122.3959,
"Address": "",
"City": "SAN FRANCISCO",
"State": "CA",
"Zip": "94107",
"Country": "US"
},
{
"precision": "zip",
"Latitude": 37.371991,
"Longitude": -122.026020,
"Address": "",
"City": "SUNNYVALE",
"State": "CA",
"Zip": "94085",
"Country": "US"
}
]

到目前为止,可能很多同学会把 JSON 当作一个很大的字段串类型,从表面上来看,没有错。但本质上,JSON 是一种新的类型,有自己的存储格式,还能在每个对应的字段上创建索引,做特定的优化,这是传统字段串无法实现的。

JSON 类型的另一个好处是无须预定义字段,字段可以无限扩展。而传统关系型数据库的列都需预先定义,想要扩展需要执行 ALTER TABLE ... ADD COLUMN ... 这样比较重的操作。

需要注意是,JSON 类型是从 MySQL 5.7 版本开始支持的功能,而 8.0 版本解决了更新 JSON 的日志性能瓶颈。如果要在生产环境中使用 JSON 数据类型,强烈推荐使用 MySQL 8.0 版本。

讲到这儿,你已经对 JSON 类型的基本概念有所了解了,接下来,我们进入实战环节:如何在业务中用好JSON类型?

业务表结构设计实战 用户登录设计

在数据库中,JSON 类型比较适合存储一些修改较少、相对静态的数据,比如用户登录信息的存储如下:

DROP TABLE IF EXISTS UserLogin;

CREATE TABLE UserLogin (
userId BIGINT NOT NULL,
loginInfo JSON,
PRIMARY KEY(userId)
);

由于当前业务的登录方式越来越多样化,如同一账户支持手机、微信、QQ 账号登录,所以这里可以用 JSON 类型存储登录的信息。

接着,插入下面的数据:

SET @a = '
{
"cellphone" : "13918888888",
"wxchat" : "破产码农",
"QQ" : "82946772"
}
';

INSERT INTO UserLogin VALUES (1,@a);

SET @b = '
{
"cellphone" : "15026888888"
}
';

INSERT INTO UserLogin VALUES (2,@b);

从上面的例子中可以看到,用户 1 登录有三种方式:手机验证码登录、微信登录、QQ 登录,而用户 2 只有手机验证码登录。

而如果不采用 JSON 数据类型,就要用下面的方式建表:

SELECT
userId,
JSON_UNQUOTE(JSON_EXTRACT(loginInfo,"$.cellphone")) cellphone,
JSON_UNQUOTE(JSON_EXTRACT(loginInfo,"$.wxchat")) wxchat
FROM UserLogin;
+--------+-------------+--------------+
| userId | cellphone | wxchat |
+--------+-------------+--------------+
| 1 | 13918888888 | 破产码农 |
| 2 | 15026888888 | NULL |
+--------+-------------+--------------+
2 rows in set (0.01 sec)

当然了,每次写 JSON_EXTRACT、JSON_UNQUOTE 非常麻烦,MySQL 还提供了 ->> 表达式,和上述 SQL 效果完全一样:

SELECT
userId,
loginInfo->>"$.cellphone" cellphone,
loginInfo->>"$.wxchat" wxchat
FROM UserLogin;

当 JSON 数据量非常大,用户希望对 JSON 数据进行有效检索时,可以利用 MySQL 的 函数索引 功能对 JSON 中的某个字段进行索引。

比如在上面的用户登录示例中,假设用户必须绑定唯一手机号,且希望未来能用手机号码进行用户检索时,可以创建下面的索引:

ALTER TABLE UserLogin ADD COLUMN cellphone VARCHAR(255) AS (loginInfo->>"$.cellphone");

ALTER TABLE UserLogin ADD UNIQUE INDEX idx_cellphone(cellphone);

上述 SQL 首先创建了一个虚拟列 cellphone,这个列是由函数 loginInfo->>"$.cellphone" 计算得到的。然后在这个虚拟列上创建一个唯一索引 idx_cellphone。这时再通过虚拟列 cellphone 进行查询,就可以看到优化器会使用到新创建的 idx_cellphone 索引:

EXPLAIN SELECT * FROM UserLogin
WHERE cellphone = '13918888888'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: UserLogin
partitions: NULL
type: const
possible_keys: idx_cellphone
key: idx_cellphone
key_len: 1023
ref: const
rows: 1
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)

当然,我们可以在一开始创建表的时候,就完成虚拟列及函数索引的创建。如下表创建的列 cellphone 对应的就是 JSON 中的内容,是个虚拟列;uk_idx_cellphone 就是在虚拟列 cellphone 上所创建的索引。

CREATE TABLE UserLogin (
userId BIGINT,
loginInfo JSON,
cellphone VARCHAR(255) AS (loginInfo->>"$.cellphone"),
PRIMARY KEY(userId),
UNIQUE KEY uk_idx_cellphone(cellphone)
);

用户画像设计

某些业务需要做用户画像(也就是对用户打标签),然后根据用户的标签,通过数据挖掘技术,进行相应的产品推荐。这份架构师图谱建议看看,少走弯路。

比如:

  • 在电商行业中,根据用户的穿搭喜好,推荐相应的商品;

  • 在音乐行业中,根据用户喜欢的音乐风格和常听的歌手,推荐相应的歌曲;

  • 在金融行业,根据用户的风险喜好和投资经验,推荐相应的理财产品。

在这,我强烈推荐你用 JSON 类型在数据库中存储用户画像信息,并结合 JSON 数组类型和多值索引的特点进行高效查询。假设有张画像定义表:

CREATE TABLE Tags (
tagId bigint auto_increment,
tagName varchar(255) NOT NULL,
primary key(tagId)
);

SELECT * FROM Tags;
+-------+--------------+
| tagId | tagName |
+-------+--------------+
| 1 | 70后 |
| 2 | 80后 |
| 3 | 90后 |
| 4 | 00后 |
| 5 | 爱运动 |
| 6 | 高学历 |
| 7 | 小资 |
| 8 | 有房 |
| 9 | 有车 |
| 10 | 常看电影 |
| 11 | 爱网购 |
| 12 | 爱外卖 |
+-------+--------------+

可以看到,表 Tags 是一张画像定义表,用于描述当前定义有多少个标签,接着给每个用户打标签,比如用户 David,他的标签是 80 后、高学历、小资、有房、常看电影;用户 Tom,90 后、常看电影、爱外卖。

若不用 JSON 数据类型进行标签存储,通常会将用户标签通过字符串,加上分割符的方式,在一个字段中存取用户所有的标签:

+-------+---------------------------------------+
|用户 |标签 |
+-------+---------------------------------------+
|David |80后 ; 高学历 ; 小资 ; 有房 ;常看电影 |
|Tom |90后 ;常看电影 ; 爱外卖 |
+-------+---------------------------------------

这样做的缺点是:不好搜索特定画像的用户,另外分隔符也是一种自我约定,在数据库中其实可以任意存储其他数据,最终产生脏数据。

用 JSON 数据类型就能很好解决这个问题:

DROP TABLE IF EXISTS UserTag;
CREATE TABLE UserTag (
userId bigint NOT NULL,
userTags JSON,
PRIMARY KEY (userId)
);

INSERT INTO UserTag VALUES (1,'[2,6,8,10]');
INSERT INTO UserTag VALUES (2,'[3,10,12]');

其中,userTags 存储的标签就是表 Tags 已定义的那些标签值,只是使用 JSON 数组类型进行存储。

MySQL 8.0.17 版本开始支持 Multi-Valued Indexes,用于在 JSON 数组上创建索引,并通过函数 member of、json_contains、json_overlaps 来快速检索索引数据。 所以你可以在表 UserTag 上创建 Multi-Valued Indexes:

ALTER TABLE UserTag
ADD INDEX idx_user_tags ((cast((userTags->"$") as unsigned array)));

如果想要查询用户画像为常看电影的用户,可以使用函数 MEMBER OF:

EXPLAIN SELECT * FROM UserTag
WHERE 10 MEMBER OF(userTags->"$")\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: UserTag
partitions: NULL
type: ref
possible_keys: idx_user_tags
key: idx_user_tags
key_len: 9
ref: const
rows: 1
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)

SELECT * FROM UserTag
WHERE 10 MEMBER OF(userTags->"$");
+--------+---------------+
| userId | userTags |
+--------+---------------+
| 1 | [2, 6, 8, 10] |
| 2 | [3, 10, 12] |
+--------+---------------+
2 rows in set (0.00 sec)

如果想要查询画像为 80 后,且常看电影的用户,可以使用函数 JSON_CONTAINS:

EXPLAIN SELECT * FROM UserTag
WHERE JSON_CONTAINS(userTags->"$", '[2,10]')\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: UserTag
partitions: NULL
type: range
possible_keys: idx_user_tags
key: idx_user_tags
key_len: 9
ref: NULL
rows: 3
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)

SELECT * FROM UserTag
WHERE JSON_CONTAINS(userTags->"$", '[2,10]');
+--------+---------------+
| userId | userTags |
+--------+---------------+
| 1 | [2, 6, 8, 10] |
+--------+---------------+
1 row in set (0.00 sec)

如果想要查询画像为 80 后、90 后,且常看电影的用户,则可以使用函数 JSON_OVERLAP:

EXPLAIN SELECT * FROM UserTag
WHERE JSON_OVERLAPS(userTags->"$", '[2,3,10]')\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: UserTag
partitions: NULL
type: range
possible_keys: idx_user_tags
key: idx_user_tags
key_len: 9
ref: NULL
rows: 4
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)

SELECT * FROM UserTag
WHERE JSON_OVERLAPS(userTags->"$", '[2,3,10]');
+--------+---------------+
| userId | userTags |
+--------+---------------+
| 1 | [2, 6, 8, 10] |
| 2 | [3, 10, 12] |
+--------+---------------+
2 rows in set (0.01 sec)


总结

JSON 类型是 MySQL 5.7 版本新增的数据类型,用好 JSON 数据类型可以有效解决很多业务中实际问题。

最后,我总结下今天的重点内容:

  • 使用 JSON 数据类型,推荐用 MySQL 8.0.17 以上的版本,性能更好,同时也支持 Multi-Valued Indexes;

  • JSON 数据类型的好处是无须预先定义列,数据本身就具有很好的描述性;

  • 不要将有明显关系型的数据用 JSON 存储,如用户余额、用户姓名、用户身份证等,这些都是每个用户必须包含的数据;

  • JSON 数据类型推荐使用在不经常更新的静态数据存储。

转自:「已注销」 链接:https://blog.csdn.net/java_pfx/article/details/116594654

- EOF -

点赞和在看就是最大的支持❤️

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
太难了,2024年大学生就业率崩了?

太难了,2024年大学生就业率崩了?

请叫我教育君
2024-06-07 08:25:52
官宣!德国欧洲杯名单出炉:德甲最佳门将落选 3大巨星迎最后一舞

官宣!德国欧洲杯名单出炉:德甲最佳门将落选 3大巨星迎最后一舞

阿超他的体育圈
2024-06-08 05:11:30
星舰试飞成功后,热搜为何消失了?

星舰试飞成功后,热搜为何消失了?

木蹊说
2024-06-07 16:05:52
“塞尔维亚正成为中国附庸”?武契奇反驳:塞是自由国家,中国尊重我们

“塞尔维亚正成为中国附庸”?武契奇反驳:塞是自由国家,中国尊重我们

环球网资讯
2024-06-08 10:25:14
彻底凉凉:全网下架,永久封存!公司深夜回应:已辞退相关负责人

彻底凉凉:全网下架,永久封存!公司深夜回应:已辞退相关负责人

21世纪经济报道
2024-06-07 22:46:09
禁电动车后,广州三元里店铺成片倒闭,街道办终于服软了!

禁电动车后,广州三元里店铺成片倒闭,街道办终于服软了!

财话连篇
2024-06-07 09:43:14
北京一军官娶海军美女少校为妻,2年后才发现妻子的真实身份

北京一军官娶海军美女少校为妻,2年后才发现妻子的真实身份

星辰故事屋
2024-06-05 12:16:48
网友:比亚迪员工内部信流出网上直接炸锅了,这应该就是梦想吧!

网友:比亚迪员工内部信流出网上直接炸锅了,这应该就是梦想吧!

火山诗话
2024-06-08 05:07:19
月壤分给美国更符合利益?嫦娥六号还没回家,中国已同意欧洲请求

月壤分给美国更符合利益?嫦娥六号还没回家,中国已同意欧洲请求

莫将离
2024-06-07 23:16:02
亡国,是一面照妖镜

亡国,是一面照妖镜

无心镜
2024-06-07 08:05:36
曝广东一公司月薪6千招前台,要求外出应酬:跟不是男朋友以外的人做

曝广东一公司月薪6千招前台,要求外出应酬:跟不是男朋友以外的人做

可达鸭面面观
2024-06-08 11:12:56
99%的人,已经无法再通过高考改变命运了

99%的人,已经无法再通过高考改变命运了

听风听你
2024-06-08 00:03:53
难哭了!今年高考数学让学霸破防,普通考生更是直接哭着出考场!

难哭了!今年高考数学让学霸破防,普通考生更是直接哭着出考场!

请叫我教育君
2024-06-08 00:06:48
妈妈为送考穿上旗袍减重25斤,双向奔赴的努力,却引来各种嘲讽

妈妈为送考穿上旗袍减重25斤,双向奔赴的努力,却引来各种嘲讽

北方青木
2024-06-07 12:58:37
笑不活了,中俄免签的第一批受害者出现了,要被评论区笑死了

笑不活了,中俄免签的第一批受害者出现了,要被评论区笑死了

奇特短尾矮袋鼠
2024-06-07 15:54:13
全国少工委就“红领巾系法”发声:未对是否折叠作硬性要求

全国少工委就“红领巾系法”发声:未对是否折叠作硬性要求

南方都市报
2024-06-07 11:14:20
“俄罗斯也可以军援美国敌人,就像美国援乌那样”

“俄罗斯也可以军援美国敌人,就像美国援乌那样”

鲁中晨报
2024-06-07 14:07:04
消息人士称黎巴嫩真主党武装已提升战备状态

消息人士称黎巴嫩真主党武装已提升战备状态

界面新闻
2024-06-08 09:38:14
菲律宾撤离部分搁浅登陆舰人员,只能出不能进,局势出现新转折点

菲律宾撤离部分搁浅登陆舰人员,只能出不能进,局势出现新转折点

说天说地说实事
2024-06-08 08:10:11
A股:重磅!证监会,沪深交易所齐发声!事关量化,全是重点!

A股:重磅!证监会,沪深交易所齐发声!事关量化,全是重点!

兵哥闲聊
2024-06-08 08:23:57
2024-06-08 12:30:44
程序员乔戈里
程序员乔戈里
本人百度java工程师
1633文章数 8903关注度
往期回顾 全部

科技要闻

今年数学到底有多难?大模型:我也不太会

头条要闻

男生744分考上清华获校长接见 如今已是清华博士生

头条要闻

男生744分考上清华获校长接见 如今已是清华博士生

体育要闻

她拯救了WNBA,却为何被疯狂针对?

娱乐要闻

汤唯抵达巴黎将担任奥运火炬手

财经要闻

重磅详解:为什么美国经济还没有衰退?

汽车要闻

上汽大通大家9售26.99万起 综合续航1300km+

态度原创

本地
时尚
房产
教育
公开课

本地新闻

我和我的家乡|踏浪营口,心动不止一夏!

奔四奔五的中年女人,夏天少穿“花裙、廉价裙”,否则显油腻

房产要闻

顶流地段+顶级户型!香港半山豪宅,已成为高净值人群的资产压舱石!

教育要闻

高考首日 我省39.5万人迎来大考

公开课

近视只是视力差?小心并发症

无障碍浏览 进入关怀版