关系结构化存储有一定的缺点,因为它需要预先定义所有的列及其对应的类型。但在业务发展过程中,可能需要扩展单个栏目的描述功能。这时候,如果能用好JSON数据类型,就可以打通关系型和非关系型数据存储的边界,为业务提供更好的服务。架构选择。当然,很多同学在使用JSON数据类型的时候都会遇到各种各样的问题。最常见的错误是简单地将JSON类型理解为字符串类型。但是看完这篇文章,你就会真正体会到JSON数据类型的强大,从而在实际工作中更好的存储非结构化数据。JSON数据类型JSON(JavaScriptObjectNotation)主要用于互联网应用服务之间的数据交换。MySQL支持RFC7159定义的JSON规范,主要有两种类型:JSON对象和JSON数组。下面是JSON对象,主要用来存储图片的相关信息:{"Image":{"Width":800,"Height":600,"Title":"Viewfrom15thFloor","Thumbnail":{"Url":"http://www.example.com/image/481989943","Height":125,"Width":100},"IDs":[116,943,234,38793]}}从中可以看出,JSON类型可以很好的描述数据的相关内容,比如这张图片的宽、高、标题等(这里使用的类型包括整型和字符串类型).除了支持JSON对象中的字符串、整型、日期类型,JSON内嵌字段还支持数组类型,比如上面代码中的IDs字段。另一种JSON数据类型是数组类型,如:[{"precision":"zip","Latitude":37.7668,"Longitude":-122.3959,"Address":"","City":"SANFRANCISCO","State":"CA","Zip":"94107","Country":"US"},{"precision":"zip","Latitude":37.371991,"Longitude":-122.026020,"Address":"","City":"SUNNYVALE","State":"CA","Zip":"94085","Country":"US"}]上面的示例显示了一个包含2个JSON对象的JSON数组。至此,很多同学可能会把JSON看成是一种大字段字符串类型。从表面上看,没有任何问题。但本质上,JSON是一种新的类型,有自己的存储格式。它还可以在每个对应的字段上创建索引并进行特定的优化,这是传统字段字符串无法实现的。JSON类型的另一个优点是不需要预先定义字段,字段可以无限扩展。但是,传统关系数据库的列需要预先定义。如果要扩容,需要进行ALTERTABLE...ADDCOLUMN...等繁重的操作。需要注意的是JSON类型是MySQL从5.7版本开始支持的功能,8.0版本解决了更新JSON的日志性能瓶颈。如果要在生产环境中使用JSON数据类型,强烈推荐MySQL8.0版本。说到这里,你已经对JSON类型的基本概念有所了解了。接下来我们进入实战部分:如何在业务中用好JSON类型?业务表结构设计实际用户登录设计在数据库中,JSON类型更适合存储一些修改较少、相对静态的数据。例如,用户登录信息的存储如下:DROPTABLEIFEXISTSUserLogin;CREATETABLEUserLogin(userIdBIGINTNOTNULL,loginInfoJSON,PRIMARYKEY(userId));由于现在的业务登录方式越来越多样化,比如同一个账号支持手机、微信、QQ账号登录,所以这里可以将登录信息以JSON类型存储。接下来插入如下数据:SET@a='{"cellphone":"13918888888","wxchat":"破产码农","QQ":"82946772"}';INSERTINTOUserLoginVALUES(1,@a);SET@b='{"cellphone":"15026888888"}';插入用户登录值(2,@b);从上面的例子可以看出,用户1的登录方式有3种:手机验证码登录、微信登录、QQ登录,而用户2只有手机验证码登录。如果不使用JSON数据类型,则需要通过以下方式创建表:SELECTuserId,JSON_UNQUOTE(JSON_EXTRACT(loginInfo,"$.cellphone"))cellphone,JSON_UNQUOTE(JSON_EXTRACT(loginInfo,"$.wxchat"))wxchatFROM用户登录;+--------+------------+------------+|用户名|手机|wxchat|+--------+------------+------------+|1|13918888888|破产码农||2|15026888888|NULL|+--------+------------+------------+2行中的集合(0.01秒)当然可以,每次都要写JSON_EXTRACT和JSON_UNQUOTE,很麻烦。MySQL还提供了>>表达式,与上述SQL的作用完全一样:SELECTuserId,loginInfo->>"$.cellphone"cellphone,loginInfo->>"$.wxchat"wxchatFROMUserLogin;当JSON数据量很大,用户想要有效的检索JSON数据时,可以使用MySQL的函数索引功能,对JSON中的某个字段进行索引。比如上面的用户登录例子,假设用户必须绑定一个唯一的手机号,希望以后使用手机号进行用户检索,可以创建如下索引:ALTERTABLEUserLoginADDCOLUMNcellphoneVARCHAR(255)AS(loginInfo->>"$.cellphone");更改表用户登录添加唯一索引idx_cellphone(cellphone);上面的SQL首先创建了一个虚拟列cellphone,通过函数loginInfo->>"$.cellphone"计算得到。然后在这个虚拟列上创建一个唯一索引idx_cellphone。然后通过虚拟列cellphone查询,可以看到优化器会使用新创建的idx_cellphone索引:EXPLAINSELECT*FROMUserLoginWHEREcellphone='13918888888'\G***************************1.行***************************id:1select_type:SIMPLEtable:UserLoginpartitions:NULLtype:constpossible_keys:idx_cellphonekey:idx_cellphonekey_len:1023ref:constrows:1filtered:100.00Extra:NULL1rowinset,1warning(0.00sec)当然,我们可以在开始时创建表在那至此,虚拟列和功能索引的创建完成。下表创建的列cellphone对应JSON中的内容,为虚拟列;uk_idx_cellphone是在虚拟列cellphone上创建的索引。创建表UserLogin(userIdBIGINT,loginInfoJSON,cellphoneVARCHAR(255)AS(loginInfo->>"$.cellphone"),PRIMARYKEY(userId),UNIQUEKEYuk_idx_cellphone(cellphone));有些业务需要做用户画像设计(即给用户打标签),然后根据用户的标签,利用数据挖掘技术做出相应的产品推荐。例如:在电商行业,根据用户的穿着喜好推荐相应的商品;在音乐行业,根据用户喜欢的音乐风格和经常听的歌手推荐相应的歌曲;在金融行业,根据用户的风险偏好和投资经验,推荐相应的金融产品。这里强烈推荐大家使用JSON类型在数据库中存储用户画像信息,结合JSON数组类型和多值索引的特点进行高效查询。假设有一张画像定义表:CREATETABLETags(tagIdbigintauto_increment,tagNamevarchar(255)NOTNULL,primarykey(tagId));从标签中选择*;+--------+--------------+|标签编号|标记名|+--------+------------+|1|70后||2|80后|3|90后||4|00后||5|热爱运动||6||爱网购||12|爱外卖|+--------+------------+可以看到,表Tags是一个纵向定义表,用于描述当前定义了多少个标签,然后标记每个用户。比如用户David,他的标签是80后、高学历、小资、有房、经常看电影;用户Tom,90后,经常看电影,爱外卖。如果不使用JSON数据类型进行标签存储,用户标签通常通过字符串传递,并添加分隔符访问一个字段中的所有用户标签:+--------+---------------------------------------+|用户|标签|+------+-------------------------------------------+|大卫|80后;高学历;小资产阶级;有房子;经常看电影||汤姆|90后;经常看电影;爱外卖|+--------+-----------------------------------这样做的缺点是不容易搜索到特定画像的用户。此外,分隔符也是一种自我约定。其实数据库中可以任意存放其他数据,造成脏数据。使用JSON数据类型可以很好的解决这个问题:DROPTABLEIFEXISTSUserTag;CREATETABLEUserTag(userIdbigintNOTNULL,userTagsJSON,PRIMARYKEY(userId));INSERTINTOUserTagVALUES(1,'[2,6,8,10]');INSERTINTOUserTagVALUES(2,'[3,10,12]');其中,userTags中存储的标签是Tags表中定义的那些标签值,但是以JSON数组类型存储。MySQL8.0.17版本开始支持Multi-ValuedIndexes,用于在JSON数组上创建索引,通过memberof、json_contains、json_overlaps函数快速获取索引数据。因此,您可以在表UserTag上创建多值索引:ALTERTABLEUserTagADDINDEXidx_user_tags((cast((userTags->"$")asunsignedarray)));如果想查询经常看电影的用户画像,可以使用函数MEMBEROF:EXPLAINSELECT*FROMUserTagWHERE10MEMBEROF(userTags->"$")\G**************************1.行***************************ID:1select_type:SIMPLEtable:UserTagpartitions:NULLtype:refpossible_keys:idx_user_tagskey:idx_user_tagskey_len:9ref:constrows:1filtered:100.00Extra:在集合中使用where1行,1个警告(0.00秒)SELECT*FROMUserTagWHERE10MEMBEROF(userTags->"$");+---------+--------------+|用户名|用户标签|+--------+-------------+|1|[2,6,8,10]||2|[3,10,12]|+------+-------------+2rowsinset(0.00sec)如果要查询post-的用户80后经常看电影,可以用函数JSON_CONTAINS:EXPLAINSELECT*FROMUserTagWHEREJSON_CONTAINS(userTags->"$",'[2,10]')\G****************************1.行**************************id:1select_type:SIMPLEtable:UserTagpartitions:NULLtype:rangepossible_keys:idx_user_tagskey:idx_user_tagskey_len:9ref:NULL行:3过滤:100.00额外:使用集合中的where1行,1个警告(0.00秒)SELECT*FROMUserTagWHEREJSON_CONTAINS(userTags->"$",'[2,10]');+--------+--------------+|userId|userTags|+--------+------------+|1|[2,6,8,10]|+--------+----------------+1rowinset(0.00sec)如果要查询纵向用户经常看电影的80后、90后可以使用函数JSON_OVERLAP:EXPLAINSELECT*FROMUserTagWHEREJSON_OVERLAPS(userTags->"$",'[2,3,10]')\G****************************1.行*****************************id:1select_type:SIMPLEtable:UserTagpartitions:NULLtype:rangepossible_keys:idx_user_tagskey:idx_user_tagskey_len:9ref:NULL行:4过滤:100.00额外:使用集合中的where1行,1个警告(0.00秒)SELECT*FROMUserTagWHEREJSON_OVERLAPS(userTags->“$”,'[2,3,10]');+--------+------------+|用户名|用户标签|+--------+----------------+|1|[2,6,8,10]||2|[3,10,12]|+-------+----------------+2rowsinset(0.01sec)SummaryJSONtypeisanewdatatypein集合MySQL5.7版本。使用JSON数据类型可以有效解决业务中的很多实际问题。最后总结一下今天的重点内容:使用JSON数据类型,推荐MySQL8.0.17以上版本,性能更好,同时支持多值索引;JSON数据类型的优点是不需要预先定义列,数据本身就非常具有描述性;不要用JSON存储明显的关系数据,比如用户余额、用户名、用户身份证等.这些是每个用户必须包含的数据;对于不经常更新的静态数据存储,建议使用JSON数据类型。版权声明:本文为博主原创文章,遵循CC4.0BY-SA版权协议,转载请附上原文出处链接及本声明。本文链接:https://blog.csdn.net/java_pf...近期热点文章推荐:1.1,000+Java面试题及答案(2022最新版)2.精彩!Java协程来了。..3.SpringBoot2.x教程,太全面了!4.不要用爆破爆满画面,试试装饰者模式,这才是优雅的方式!!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!
