Mysql存儲(chǔ)json格式的實(shí)現(xiàn)
一、前言
最近做的一個(gè) 填報(bào)項(xiàng)目,由于填報(bào)的字段比較多于是便在數(shù)據(jù)庫當(dāng)中使用了
longtext類型的字段直接存儲(chǔ)json數(shù)據(jù)。
為什么選擇直接存儲(chǔ)json數(shù)據(jù)?
業(yè)務(wù)比較復(fù)雜,涉及到很多 一對(duì)多 的多表關(guān)聯(lián),假如拆分存到各個(gè)業(yè)務(wù)表,那么每次查詢修改是非常繁瑣的,存儲(chǔ)json一張表即可解決。字段全部由前端來定,減少了前后端聯(lián)調(diào)時(shí)間。避免填報(bào)內(nèi)容需求頻繁調(diào)整而不斷修改數(shù)據(jù)庫結(jié)構(gòu),比如加字段,減字段這些都是經(jīng)常有的事,完全可以讓前端全權(quán)負(fù)責(zé),你存什么我就給你返回什么。
但是這里我也是有一點(diǎn)好奇,既然存儲(chǔ)為json,為什么不直接將字段類型設(shè)置為json?由于最近經(jīng)常會(huì)用到數(shù)據(jù)庫存json串,也是下定決心徹底把這塊相關(guān)的知識(shí)給梳理一遍。
從5.7開始,MySQL開始支持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)行快速讀取訪問。可以直接通過鍵或數(shù)組索引查找子對(duì)象或嵌套值,而無需讀取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è)新文檔寫入該列。(在后面會(huì)重點(diǎn)講解這一部分)
二、什么是 JSON
JSON 是 JavaScript Object Notation(JavaScript 對(duì)象表示法)的縮寫,是一個(gè)輕量級(jí)的,基于文本的,跨語言的數(shù)據(jù)交換格式。易于閱讀和編寫。
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)
[]括起來,元素之間用逗號(hào),分隔。譬如:
[1, "abc", null, true, "10:27:06.000000", {"id": 1}]對(duì)象:一個(gè)由零或者多個(gè)鍵值對(duì)組成的無序集合。其中鍵必須是字符串,值可以是對(duì)象、數(shù)組、數(shù)字、字符串或者三個(gè)字面值(false、null、true)中的一個(gè)。值中的字面值中的英文必須使用小寫。
對(duì)象使用花括號(hào){}括起來,鍵值對(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)系,甚至連誰是誰都不清楚。其實(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)單地說,JSON 可以將 JavaScript 對(duì)象中表示的一組數(shù)據(jù)轉(zhuǎn)換為字符串,然后就可以在網(wǎng)絡(luò)或者程序之間輕松地傳遞這個(gè)字符串,并在需要的時(shí)候?qū)⑺€原為各編程語言所支持的數(shù)據(jù)格式,例如在 Java中,可以將 JSON 還原為數(shù)組或者一個(gè)基本對(duì)象。
XML本質(zhì)上也可以作為跨語言的數(shù)據(jù)交換格式,JSON和XML的可讀性可謂不相上下,一邊是簡(jiǎn)易的語法,一邊是規(guī)范的標(biāo)簽形式,很難分出勝負(fù)。
三、Mysql當(dāng)中json函數(shù)
http://m.fzitv.net/database/2878197v4.htm
四、JSON值部分更新
4.1.使用 Partial Updates 的條件
在MySQL 8.0中,優(yōu)化器可以對(duì)JsoN列執(zhí)行局部就地更新,而不是刪除舊文檔并將整個(gè)新文檔寫入該列。此優(yōu)化可以在滿足以下條件的更新中執(zhí)行:
- 要更新的列被聲明為JSON。
- UPDATE語句使用JSON_SET()、JSON_REPLACE()或JSON_REMOVE()這三個(gè)函數(shù)中的任意一個(gè)來更新列。直接賦值列值(例如,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)這樣的語句不能作為部分更新執(zhí)行。
- 所有的更改都用新的值替換現(xiàn)有的數(shù)組或?qū)ο笾?,并且不向父?duì)象或數(shù)組添加任何新元素。
- 被替換的值必須至少與替換值一樣大。換句話說,新值不能大于舊值。當(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ǔ)空間。
- 語法:
JSON_STORAGE_FREE(json_val)
返回值:
- 如果參數(shù)是一個(gè)JSON列值,并按照前面的描述進(jìn)行了更新,則返回一個(gè)正的非零值,這樣它的二進(jìn)制表示比更新之前占用的空間更少。
- 假如其二進(jìn)制表示與以前相同或更大,或者如果更新無法利用部分更新,則返回0
- 假如沒有使用那三個(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 中開啟 Partial Updates
這種部分更新可以使用壓縮格式寫入二進(jìn)制日志,以節(jié)省空間;這可以通過將 binlog_row_value_options選項(xiàng)系統(tǒng)變量設(shè)置為PARTIAL_JSON來啟用。
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è)條件,無需考慮變更前后,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;接下來,測(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 只開啟存儲(chǔ)引擎層的 Partial Updates,查詢時(shí)間比 MySQL 5.7 快 1.94 倍。
- MySQL 8.0 同時(shí)開啟存儲(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。
但即使如此,只開啟存儲(chǔ)引擎層和 binlog 中的 Partial Updates,查詢時(shí)間也比 MySQL 5.7 快 4.87 倍,性能提升還是比較明顯的。
五、如何對(duì) JSON 字段創(chuàng)建索引
http://m.fzitv.net/database/287839lwd.htm
六、mybatis取json類型的數(shù)據(jù)
http://m.fzitv.net/program/2878405ct.htm
七、總結(jié)
- 使用longtext存儲(chǔ)json也可以使用函數(shù)等操作,既然mysql出了json數(shù)據(jù)類型,那我們盡量就使用json數(shù)據(jù)類型來存儲(chǔ)json,而且該數(shù)據(jù)類型還有數(shù)據(jù)校驗(yàn)。
- Mysql提供了大量的json相關(guān)函數(shù),基于此,我們針對(duì)于一些需求完全可以在sql層面使用函數(shù)解決問題,而不需要將數(shù)據(jù)拿到業(yè)務(wù)層,然后通過業(yè)務(wù)代碼來解決問題。
- 盡量使用8.0以上的mysql來使用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è)字段查詢,我們可以通過虛擬列進(jìn)行建立索引。同時(shí)在MySQL 8.0.17及以后的版本中,InnoDB存儲(chǔ)引擎支持JSON數(shù)組上的多值索引。
- 假如使用的是mybatis框架,如果圖省事可以直接使用JSONObject來接受前端的值,也可以作為返回值使用,當(dāng)然也可以自定義json的Java對(duì)象,但是都需要設(shè)置typeHandler,好處是中間不會(huì)出現(xiàn)轉(zhuǎn)義問題,而使用String不需要設(shè)置typeHandler,但是會(huì)存在轉(zhuǎ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ù)庫怎么正確查詢字符串長(zhǎng)度
MySQL中字符串長(zhǎng)度一般指數(shù)據(jù)庫表中一個(gè)字段或列中存儲(chǔ)的字符串的最大長(zhǎng)度,有時(shí)我們需要測(cè)量字符串長(zhǎng)度,來保證表結(jié)構(gòu)及數(shù)據(jù)庫性能穩(wěn)定,下面這篇文章主要給大家介紹了關(guān)于MySQL數(shù)據(jù)庫怎么正確查詢字符串長(zhǎng)度的相關(guān)資料,需要的朋友可以參考下2023-06-06
MySQL中Decimal類型和Float Double的區(qū)別(詳解)
下面小編就為大家?guī)硪黄狹ySQL中Decimal類型和Float Double的區(qū)別(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03
mysql unique key在查詢中的使用與相關(guān)問題
今天小編就為大家分享一篇關(guān)于mysql unique key在查詢中的使用與相關(guān)問題,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-04-04
MySQL8.0/8.x忘記密碼更改root密碼的實(shí)戰(zhàn)步驟(親測(cè)有效!)
忘記root密碼的場(chǎng)景還是比較常見的,特別是自己搭的測(cè)試環(huán)境經(jīng)過好久沒用過時(shí),很容易記不得當(dāng)時(shí)設(shè)置的密碼,下面這篇文章主要給大家介紹了關(guān)于MySQL8.0/8.x忘記密碼更改root密碼的實(shí)戰(zhàn)步驟,親測(cè)有效!需要的朋友可以參考下2023-04-04
mysql創(chuàng)建用戶以及給用戶授予權(quán)限實(shí)現(xiàn)方式
文章講解了MySQL用戶創(chuàng)建與授權(quán)方法,包括設(shè)置空密碼、分配特定數(shù)據(jù)庫表權(quán)限及全部權(quán)限,強(qiáng)調(diào)安全授權(quán)的重要性,并提及回收和修改權(quán)限的命令2025-07-07
mysql中ALTER COLLATION使用場(chǎng)景
ALTER COLLATION是SQL中用于修改字符集排序規(guī)則的操作,本文主要介紹了mysql中ALTER COLLATION使用場(chǎng)景,具有一定的參考價(jià)值,感興趣的可以了解一下2025-05-05

