Mysql存儲(chǔ)json格式的實(shí)現(xiàn)
一、前言
最近做的一個(gè) 填報(bào)項(xiàng)目,由于填報(bào)的字段比較多于是便在數(shù)據(jù)庫(kù)當(dāng)中使用了
longtext
類型的字段直接存儲(chǔ)json
數(shù)據(jù)。
為什么選擇直接存儲(chǔ)json數(shù)據(jù)?
業(yè)務(wù)比較復(fù)雜,涉及到很多 一對(duì)多 的多表關(guān)聯(lián),假如拆分存到各個(gè)業(yè)務(wù)表,那么每次查詢修改是非常繁瑣的,存儲(chǔ)json一張表即可解決。字段全部由前端來(lái)定,減少了前后端聯(lián)調(diào)時(shí)間。避免填報(bào)內(nèi)容需求頻繁調(diào)整而不斷修改數(shù)據(jù)庫(kù)結(jié)構(gòu),比如加字段,減字段這些都是經(jīng)常有的事,完全可以讓前端全權(quán)負(fù)責(zé),你存什么我就給你返回什么。
但是這里我也是有一點(diǎn)好奇,既然存儲(chǔ)為json
,為什么不直接將字段類型設(shè)置為json
?由于最近經(jīng)常會(huì)用到數(shù)據(jù)庫(kù)存json串,也是下定決心徹底把這塊相關(guān)的知識(shí)給梳理一遍。
從5.7開(kāi)始,MySQL開(kāi)始支持json類型,用于存儲(chǔ)JSON數(shù)據(jù)。關(guān)于json類型,mysql官網(wǎng)介紹以及使用,本篇文章也是重點(diǎn)整理的官網(wǎng)相關(guān)知識(shí):https://dev.mysql.com/doc/refman/8.0/en/json.html#json-values
json數(shù)據(jù)類型提供了以下優(yōu)勢(shì):
插入數(shù)據(jù)的時(shí)候自動(dòng)驗(yàn)證數(shù)據(jù)是否是標(biāo)準(zhǔn)的json數(shù)據(jù)格式,如果不是會(huì)報(bào)異常!假如使用longtext
存儲(chǔ)json并不會(huì)有這種校驗(yàn)。
優(yōu)化存儲(chǔ)格式。存儲(chǔ)在json列中的JSON文檔被轉(zhuǎn)換為內(nèi)部格式,允許對(duì)文檔元素進(jìn)行快速讀取訪問(wèn)??梢灾苯油ㄟ^(guò)鍵或數(shù)組索引查找子對(duì)象或嵌套值,而無(wú)需讀取json中所有值。
注意:
- 存儲(chǔ)在JSON列中的任何JSON文檔的大小都受限于最大允許的數(shù)據(jù)包系統(tǒng)變量的值,可以使用JSON_STORAGE_SIZE()獲取到j(luò)son長(zhǎng)度
- 在MySQL 8.0.13之前,JSON列不能有非null的默認(rèn)值。
- 存儲(chǔ)JSON文檔所需的空間與LONGBLOB或LONGTEXT大致相同
- Json類型不支持索引,但是可以在Json當(dāng)中的列上使用索引!在MySQL 8.0.17及以后的版本中,InnoDB存儲(chǔ)引擎支持JSON數(shù)組上的多值索引。
- MySQL優(yōu)化器還會(huì)在匹配JSON表達(dá)式的虛擬列上尋找兼容的索引。
- 在MySQL 8.0中,MySQL 優(yōu)化器可以對(duì)JSON列執(zhí)行局部就地更新,而不是刪除舊文檔并將整個(gè)新文檔寫(xiě)入該列。(在后面會(huì)重點(diǎn)講解這一部分)
二、什么是 JSON
JSON 是 JavaScript Object Notation(JavaScript 對(duì)象表示法)的縮寫(xiě),是一個(gè)輕量級(jí)的,基于文本的,跨語(yǔ)言的數(shù)據(jù)交換格式。易于閱讀和編寫(xiě)。
JSON 的基本數(shù)據(jù)類型如下:
- 數(shù)值:十進(jìn)制數(shù),可以為負(fù)數(shù)或小數(shù)。
- 字符串:字符串是由雙引號(hào)
""
包圍的任意數(shù)量Unicode字符的集合,特殊符號(hào)使用反斜線轉(zhuǎn)義。 - 布爾值:true,false。
- 數(shù)組:一個(gè)由零或多個(gè)值組成的有序序列。每個(gè)值可以為任意類型。數(shù)組使用方括號(hào)
[]
括起來(lái),元素之間用逗號(hào),
分隔。譬如:
[1, "abc", null, true, "10:27:06.000000", {"id": 1}]
對(duì)象:一個(gè)由零或者多個(gè)鍵值對(duì)組成的無(wú)序集合。其中鍵必須是字符串,值可以是對(duì)象、數(shù)組、數(shù)字、字符串或者三個(gè)字面值(false、null、true)中的一個(gè)
。值中的字面值中的英文必須使用小寫(xiě)。
對(duì)象使用花括號(hào){}
括起來(lái),鍵值對(duì)之間使用逗號(hào) ,
分隔,鍵與值之間用冒號(hào) :
分隔。譬如:
{"name": "John Doe", "age": 18, "address": {"country" : "china", "zip-code": "10000"}}
空值:null。
一些合法的JSON的實(shí)例:
{"a": 1, "b": [1, 2, 3]} [1, 2, "3", {"a": 4}] 3.14 "plain_text"
JSON 與 JS 對(duì)象的關(guān)系
很多人搞不清楚 JSON 和 JS 對(duì)象的關(guān)系,甚至連誰(shuí)是誰(shuí)都不清楚。其實(shí),可以這么理解:
JSON 是 JS 對(duì)象的字符串表示法,它使用文本表示一個(gè) JS 對(duì)象的信息,本質(zhì)是一個(gè)字符串。如
var obj = {a: 'Hello', b: 'World'}; //這是一個(gè)對(duì)象,注意鍵名也是可以使用引號(hào)包裹的 var json = '{"a": "Hello", "b": "World"}'; //這是一個(gè) JSON 字符串,本質(zhì)是一個(gè)字符串
JSON 和 JS 對(duì)象互轉(zhuǎn)
要實(shí)現(xiàn)從JSON字符串轉(zhuǎn)換為JS對(duì)象,使用 JSON.parse() 方法:
var obj = JSON.parse('{"a": "Hello", "b": "World"}'); //結(jié)果是 {a: 'Hello', b: 'World'}
要實(shí)現(xiàn)從JS對(duì)象轉(zhuǎn)換為JSON字符串,使用 JSON.stringify() 方法:
var json = JSON.stringify({<!--{C}%3C!%2D%2D%20%2D%2D%3E-->a: 'Hello', b: 'World'}); //結(jié)果是 '{"a": "Hello", "b": "World"}'
簡(jiǎn)單地說(shuō),JSON 可以將 JavaScript 對(duì)象中表示的一組數(shù)據(jù)轉(zhuǎn)換為字符串,然后就可以在網(wǎng)絡(luò)或者程序之間輕松地傳遞這個(gè)字符串,并在需要的時(shí)候?qū)⑺€原為各編程語(yǔ)言所支持的數(shù)據(jù)格式,例如在 Java中,可以將 JSON 還原為數(shù)組或者一個(gè)基本對(duì)象。
XML本質(zhì)上也可以作為跨語(yǔ)言的數(shù)據(jù)交換格式,JSON和XML的可讀性可謂不相上下,一邊是簡(jiǎn)易的語(yǔ)法,一邊是規(guī)范的標(biāo)簽形式,很難分出勝負(fù)。
三、Mysql當(dāng)中json函數(shù)
http://chabaoo.cn/database/2878197v4.htm
四、JSON值部分更新
4.1.使用 Partial Updates 的條件
在MySQL 8.0中,優(yōu)化器可以對(duì)JsoN列執(zhí)行局部就地更新,而不是刪除舊文檔并將整個(gè)新文檔寫(xiě)入該列。此優(yōu)化可以在滿足以下條件的更新中執(zhí)行:
- 要更新的列被聲明為JSON。
- UPDATE語(yǔ)句使用JSON_SET()、JSON_REPLACE()或JSON_REMOVE()這三個(gè)函數(shù)中的任意一個(gè)來(lái)更新列。直接賦值列值(例如,UPDATE mytable SET jcol = '{"a": 10, "b": 25}')不能作為部分更新執(zhí)行。MySQL只能對(duì)使用上面列出的三個(gè)函數(shù)更新值的列執(zhí)行部分更新。
- 輸入列和目標(biāo)列必須是同一列,像UPDATE mytable SET jcol1 = JSON_SET(jcol2, '$.a', 100)這樣的語(yǔ)句不能作為部分更新執(zhí)行。
- 所有的更改都用新的值替換現(xiàn)有的數(shù)組或?qū)ο笾?,并且不向父?duì)象或數(shù)組添加任何新元素。
- 被替換的值必須至少與替換值一樣大。換句話說(shuō),新值不能大于舊值。當(dāng)先前的部分更新為較大的值留下了足夠的空間時(shí),可能會(huì)出現(xiàn)此需求的異常。您可以使用JSON_STORAGE FREE()函數(shù)查看JSON列的任何部分更新釋放了多少空間。
- JSON文檔的部分更新只能在列值上執(zhí)行。對(duì)于存儲(chǔ)JSON值的用戶變量,該值總是被完全替換,即使使用JSON_SET()執(zhí)行更新:
JSON_STORAGE_FREE(更新后釋放的空間)
- 描述:主要是記錄JSON_SET()、JSON_REPLACE()或JSON_REMOVE()使用這三個(gè)函數(shù)進(jìn)行就地更新后,其二進(jìn)制表示形式釋放了多少存儲(chǔ)空間。
- 語(yǔ)法:
JSON_STORAGE_FREE(json_val)
返回值:
- 如果參數(shù)是一個(gè)JSON列值,并按照前面的描述進(jìn)行了更新,則返回一個(gè)正的非零值,這樣它的二進(jìn)制表示比更新之前占用的空間更少。
- 假如其二進(jìn)制表示與以前相同或更大,或者如果更新無(wú)法利用部分更新,則返回0
- 假如沒(méi)有使用那三個(gè)函數(shù),也會(huì)返回0
創(chuàng)建測(cè)試表
mysql> CREATE TABLE jtable (jcol JSON); Query OK, 0 rows affected (0.38 sec) mysql> INSERT INTO jtable VALUES -> ('{"a": 10, "b": "wxyz", "c": "[true, false]"}'); Query OK, 1 row affected (0.04 sec) mysql> SELECT * FROM jtable; +----------------------------------------------+ | jcol | +----------------------------------------------+ | {"a": 10, "b": "wxyz", "c": "[true, false]"} | +----------------------------------------------+ 1 row in set (0.00 sec)
現(xiàn)在我們使用JSON_SET()更新列值,這樣就可以執(zhí)行部分更新;在本例中,我們將c鍵所指向的值(數(shù)組[true, false])替換為占用更少空間的值(整數(shù)1):
mysql> UPDATE jtable -> SET jcol = JSON_SET(jcol, "$.a", 10, "$.b", "wx", "$.c", 1); Query OK, 1 row affected (0.03 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> SELECT JSON_STORAGE_FREE(jcol) FROM jtable; +-------------------------+ | JSON_STORAGE_FREE(jcol) | +-------------------------+ | 16 | +-------------------------+ 1 row in set (0.00 sec)
4.2.如何在 binlog 中開(kāi)啟 Partial Updates
這種部分更新可以使用壓縮格式寫(xiě)入二進(jìn)制日志,以節(jié)省空間;這可以通過(guò)將 binlog_row_value_options
選項(xiàng)系統(tǒng)變量設(shè)置為PARTIAL_JSON
來(lái)啟用。
binlog_row_value_options
參數(shù)是MySQL 8.0.3 版本引入的新參數(shù),該參數(shù)主要用于JSON類型的字段更新時(shí),只記錄更新的那部分?jǐn)?shù)據(jù)到binlog,而不是記錄完整的JSON數(shù)據(jù),這樣能夠顯著減少JSON字段更新產(chǎn)生的binlog文件大小。
需要注意的是,binlog 中使用 部分更新(Partial Updates),只需滿足存儲(chǔ)引擎層使用 Partial Updates 的前幾個(gè)條件,無(wú)需考慮變更前后,JSON 文檔的空間使用是否會(huì)增加。
4.3.關(guān)于 Partial Updates 的性能測(cè)試
首先構(gòu)造測(cè)試數(shù)據(jù),t 表一共有 16 個(gè)文檔,每個(gè)文檔近 10 MB。
create table t(id int auto_increment primary key, json_col json, name varchar(100) as (json_col->>'$.name'), age int as (json_col->'$.age')); insert into t(json_col) values (json_object('name', 'Joe', 'age', 24, 'data', repeat('x', 10 * 1000 * 1000))), (json_object('name', 'Sue', 'age', 32, 'data', repeat('y', 10 * 1000 * 1000))), (json_object('name', 'Pete', 'age', 40, 'data', repeat('z', 10 * 1000 * 1000))), (json_object('name', 'Jenny', 'age', 27, 'data', repeat('w', 10 * 1000 * 1000))); insert into t(json_col) select json_col from t; insert into t(json_col) select json_col from t;
接下來(lái),測(cè)試下述 SQL:update t set json_col = json_set(json_col, '$.age', age + 1);
在以下四種場(chǎng)景下的執(zhí)行時(shí)間:
- MySQL 5.7.36
- MySQL 8.0.27
- MySQL 8.0.27,binlog_row_value_options=PARTIAL_JSON
- MySQL 8.0.27,binlog_row_value_options=PARTIAL_JSON + binlog_row_image=MINIMAL
以 MySQL 5.7.36 的查詢時(shí)間作為基準(zhǔn):
- MySQL 8.0 只開(kāi)啟存儲(chǔ)引擎層的 Partial Updates,查詢時(shí)間比 MySQL 5.7 快 1.94 倍。
- MySQL 8.0 同時(shí)開(kāi)啟存儲(chǔ)引擎層和 binlog 中的 Partial Updates,查詢時(shí)間比 MySQL 5.7 快 4.87 倍。
- 如果在 2 的基礎(chǔ)上,同時(shí)將 binlog_row_image 設(shè)置為 MINIMAL,查詢時(shí)間更是比 MySQL 5.7 快 102.22 倍。
當(dāng)然,在生產(chǎn)環(huán)境,我們一般很少將 binlog_row_image 設(shè)置為 MINIMAL。
但即使如此,只開(kāi)啟存儲(chǔ)引擎層和 binlog 中的 Partial Updates,查詢時(shí)間也比 MySQL 5.7 快 4.87 倍,性能提升還是比較明顯的。
五、如何對(duì) JSON 字段創(chuàng)建索引
http://chabaoo.cn/database/287839lwd.htm
六、mybatis取json類型的數(shù)據(jù)
http://chabaoo.cn/program/2878405ct.htm
七、總結(jié)
- 使用longtext存儲(chǔ)json也可以使用函數(shù)等操作,既然mysql出了json數(shù)據(jù)類型,那我們盡量就使用json數(shù)據(jù)類型來(lái)存儲(chǔ)json,而且該數(shù)據(jù)類型還有數(shù)據(jù)校驗(yàn)。
- Mysql提供了大量的json相關(guān)函數(shù),基于此,我們針對(duì)于一些需求完全可以在sql層面使用函數(shù)解決問(wèn)題,而不需要將數(shù)據(jù)拿到業(yè)務(wù)層,然后通過(guò)業(yè)務(wù)代碼來(lái)解決問(wèn)題。
- 盡量使用8.0以上的mysql來(lái)使用json數(shù)據(jù)類型存儲(chǔ)json
- mysql提供了JSON_SET()、JSON_REPLACE()或JSON_REMOVE()三個(gè)函數(shù)可以進(jìn)行值部分更新。其效率是5.7版本的5倍!雖然5.7版本也有這幾個(gè)函數(shù),但是并不是部分更新!
- 涉及到根據(jù)json當(dāng)中某個(gè)字段查詢,我們可以通過(guò)虛擬列進(jìn)行建立索引。同時(shí)在MySQL 8.0.17及以后的版本中,InnoDB存儲(chǔ)引擎支持JSON數(shù)組上的多值索引。
- 假如使用的是mybatis框架,如果圖省事可以直接使用JSONObject來(lái)接受前端的值,也可以作為返回值使用,當(dāng)然也可以自定義json的Java對(duì)象,但是都需要設(shè)置typeHandler,好處是中間不會(huì)出現(xiàn)轉(zhuǎn)義問(wèn)題,而使用String不需要設(shè)置typeHandler,但是會(huì)存在轉(zhuǎn)義的問(wèn)題。
到此這篇關(guān)于Mysql存儲(chǔ)json格式的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Mysql存儲(chǔ)json格式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL中的數(shù)據(jù)類型binary和varbinary詳解
binary和varbinary與char和varchar類型有點(diǎn)類似,不同的是binary和varbinary存儲(chǔ)的是二進(jìn)制的字符串,而非字符型字符串。下面這篇文章主要給大家介紹了關(guān)于MySQL中數(shù)據(jù)類型binary和varbinary的相關(guān)資料,介紹的非常詳細(xì),需要的朋友可以參考學(xué)習(xí)。2017-07-07mysql密碼正確無(wú)法登陸(host的問(wèn)題)
本文主要介紹了mysql密碼正確無(wú)法登陸(host的問(wèn)題),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05解決net start mysql--服務(wù)無(wú)法啟動(dòng) 服務(wù)沒(méi)有報(bào)告任何錯(cuò)誤問(wèn)題
這篇文章主要介紹了解決net start mysql--服務(wù)無(wú)法啟動(dòng) 服務(wù)沒(méi)有報(bào)告任何錯(cuò)誤問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12基于mysql數(shù)據(jù)庫(kù)的密碼問(wèn)題詳解
本篇文章是對(duì)mysql數(shù)據(jù)庫(kù)的密碼問(wèn)題進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-062022最新版MySQL 8.0.30 安裝及配置教程(小白入門(mén))
這篇文章主要介紹了2022最新版MySQL 8.0.30 安裝及配置教程,安裝過(guò)程算是比較簡(jiǎn)單的,今天給大家分享的此文比較適合mysql數(shù)據(jù)庫(kù)的小白,需要的朋友可以參考下2022-09-09navicat無(wú)法遠(yuǎn)程連接mysql的解決方法
這篇文章主要介紹了navicat無(wú)法遠(yuǎn)程連接mysql的解決方法,需要的朋友可以參考下2014-04-04Mysql中int(1)、int(20)的區(qū)別小結(jié)
本文主要介紹了Mysql中int(1)、int(20)的區(qū)別小結(jié),int后的數(shù)字表示最大顯示寬度,一般int后面的數(shù)字M要配合zerofill一起使用才有效,下面就來(lái)具體介紹一下,感興趣的可以了解一下2025-03-03Mysql 實(shí)現(xiàn)向上遞歸查找父節(jié)點(diǎn)并返回樹(shù)結(jié)構(gòu)的示例代碼
通過(guò)mysql 8.0以下版本實(shí)現(xiàn),一個(gè)人多角色id,一個(gè)角色對(duì)應(yīng)某個(gè)節(jié)點(diǎn)menu_id,根節(jié)點(diǎn)的父節(jié)點(diǎn)存儲(chǔ)為NULL, 向上遞歸查找父節(jié)點(diǎn)并返回樹(shù)結(jié)構(gòu),今天通過(guò)本文給大家介紹Mysql遞歸查找父節(jié)點(diǎn)并返回樹(shù)結(jié)構(gòu),感興趣的朋友一起看看吧2022-09-09