<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用MySQL,請(qǐng)用好 JSON 這張王牌!

          共 8254字,需瀏覽 17分鐘

           ·

          2022-01-14 01:53


          來(lái)源:https://blog.csdn.net/java_pfx/article/details/116594654

          關(guān)系型的結(jié)構(gòu)化存儲(chǔ)存在一定的弊端,因?yàn)樗枰A(yù)先定義好所有的列以及列對(duì)應(yīng)的類(lèi)型。但是業(yè)務(wù)在發(fā)展過(guò)程中,或許需要擴(kuò)展單個(gè)列的描述功能,這時(shí),如果能用好 JSON 數(shù)據(jù)類(lèi)型,那就能打通關(guān)系型和非關(guān)系型數(shù)據(jù)的存儲(chǔ)之間的界限,為業(yè)務(wù)提供更好的架構(gòu)選擇。

          當(dāng)然,很多同學(xué)在用 JSON 數(shù)據(jù)類(lèi)型時(shí)會(huì)遇到各種各樣的問(wèn)題,其中最容易犯的誤區(qū)就是將類(lèi)型 JSON 簡(jiǎn)單理解成字符串類(lèi)型。但當(dāng)你看完這篇文章后,會(huì)真正認(rèn)識(shí)到 JSON 數(shù)據(jù)類(lèi)型的威力,從而在實(shí)際工作中更好地存儲(chǔ)非結(jié)構(gòu)化的數(shù)據(jù)。

          JSON 數(shù)據(jù)類(lèi)型


          JSON(JavaScript Object Notation)主要用于互聯(lián)網(wǎng)應(yīng)用服務(wù)之間的數(shù)據(jù)交換。MySQL 支持RFC 7159定義的 JSON 規(guī)范,主要有?JSON 對(duì)象?和?JSON 數(shù)組?兩種類(lèi)型。下面就是 JSON 對(duì)象,主要用來(lái)存儲(chǔ)圖片的相關(guān)信息:

          {
          ?"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 類(lèi)型可以很好地描述數(shù)據(jù)的相關(guān)內(nèi)容,比如這張圖片的寬度、高度、標(biāo)題等(這里使用到的類(lèi)型有整型、字符串類(lèi)型)。

          JSON對(duì)象除了支持字符串、整型、日期類(lèi)型,JSON 內(nèi)嵌的字段也支持?jǐn)?shù)組類(lèi)型,如上代碼中的 IDs 字段。

          另一種 JSON 數(shù)據(jù)類(lèi)型是數(shù)組類(lèi)型,如:

          [
          ???{
          ?????"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"
          ???}
          ?]

          上面的示例演示的是一個(gè) JSON 數(shù)組,其中有 2 個(gè) JSON 對(duì)象。

          到目前為止,可能很多同學(xué)會(huì)把 JSON 當(dāng)作一個(gè)很大的字段串類(lèi)型,從表面上來(lái)看,沒(méi)有錯(cuò)。但本質(zhì)上,JSON 是一種新的類(lèi)型,有自己的存儲(chǔ)格式,還能在每個(gè)對(duì)應(yīng)的字段上創(chuàng)建索引,做特定的優(yōu)化,這是傳統(tǒng)字段串無(wú)法實(shí)現(xiàn)的。JSON 類(lèi)型的另一個(gè)好處是無(wú)須預(yù)定義字段,字段可以無(wú)限擴(kuò)展。而傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)的列都需預(yù)先定義,想要擴(kuò)展需要執(zhí)行 ALTER TABLE ... ADD COLUMN ... 這樣比較重的操作。

          需要注意是,JSON 類(lèi)型是從 MySQL 5.7 版本開(kāi)始支持的功能,而 8.0 版本解決了更新 JSON 的日志性能瓶頸。如果要在生產(chǎn)環(huán)境中使用 JSON 數(shù)據(jù)類(lèi)型,強(qiáng)烈推薦使用 MySQL 8.0 版本。

          講到這兒,你已經(jīng)對(duì) JSON 類(lèi)型的基本概念有所了解了,接下來(lái),我們進(jìn)入實(shí)戰(zhàn)環(huán)節(jié):如何在業(yè)務(wù)中用好JSON類(lèi)型?

          業(yè)務(wù)表結(jié)構(gòu)設(shè)計(jì)實(shí)戰(zhàn)


          用戶(hù)登錄設(shè)計(jì)

          在數(shù)據(jù)庫(kù)中,JSON 類(lèi)型比較適合存儲(chǔ)一些修改較少、相對(duì)靜態(tài)的數(shù)據(jù),比如用戶(hù)登錄信息的存儲(chǔ)如下:

          DROP?TABLE?IF?EXISTS?UserLogin;

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

          由于當(dāng)前業(yè)務(wù)的登錄方式越來(lái)越多樣化,如同一賬戶(hù)支持手機(jī)、微信、QQ 賬號(hào)登錄,所以這里可以用 JSON 類(lèi)型存儲(chǔ)登錄的信息。

          接著,插入下面的數(shù)據(jù):

          SET?@a?=?'
          {
          ???"cellphone"?:?"13918888888",
          ???"wxchat"?:?"破產(chǎn)碼農(nóng)",
          ???"QQ"?:?"82946772"
          }
          '
          ;

          INSERT?INTO?UserLogin?VALUES?(1,@a);

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

          INSERT?INTO?UserLogin?VALUES?(2,@b);

          從上面的例子中可以看到,用戶(hù) 1 登錄有三種方式:手機(jī)驗(yàn)證碼登錄、微信登錄、QQ 登錄,而用戶(hù) 2 只有手機(jī)驗(yàn)證碼登錄。

          而如果不采用 JSON 數(shù)據(jù)類(lèi)型,就要用下面的方式建表:

          SELECT
          ????userId,
          ????JSON_UNQUOTE(JSON_EXTRACT(loginInfo,"$.cellphone"))?cellphone,
          ????JSON_UNQUOTE(JSON_EXTRACT(loginInfo,"$.wxchat"))?wxchat
          FROM?UserLogin;
          +--------+-------------+--------------+
          |?userId?|?cellphone???|?wxchat???????|
          +--------+-------------+--------------+
          |??????1?|?13918888888?|?破產(chǎn)碼農(nóng)?????|
          |??????2?|?15026888888?|?NULL?????????|
          +--------+-------------+--------------+
          2?rows?in?set?(0.01?sec)

          當(dāng)然了,每次寫(xiě) JSON_EXTRACT、JSON_UNQUOTE 非常麻煩,MySQL 還提供了 ->> 表達(dá)式,和上述 SQL 效果完全一樣:

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

          當(dāng) JSON 數(shù)據(jù)量非常大,用戶(hù)希望對(duì) JSON 數(shù)據(jù)進(jìn)行有效檢索時(shí),可以利用 MySQL 的?函數(shù)索引?功能對(duì) JSON 中的某個(gè)字段進(jìn)行索引。

          比如在上面的用戶(hù)登錄示例中,假設(shè)用戶(hù)必須綁定唯一手機(jī)號(hào),且希望未來(lái)能用手機(jī)號(hào)碼進(jìn)行用戶(hù)檢索時(shí),可以創(chuàng)建下面的索引:

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

          ALTER?TABLE?UserLogin?ADD?UNIQUE?INDEX?idx_cellphone(cellphone);

          上述 SQL 首先創(chuàng)建了一個(gè)虛擬列 cellphone,這個(gè)列是由函數(shù) loginInfo->>"$.cellphone" 計(jì)算得到的。然后在這個(gè)虛擬列上創(chuàng)建一個(gè)唯一索引 idx_cellphone。這時(shí)再通過(guò)虛擬列 cellphone 進(jìn)行查詢(xún),就可以看到優(yōu)化器會(huì)使用到新創(chuàng)建的 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)

          當(dāng)然,我們可以在一開(kāi)始創(chuàng)建表的時(shí)候,就完成虛擬列及函數(shù)索引的創(chuàng)建。如下表創(chuàng)建的列 cellphone 對(duì)應(yīng)的就是 JSON 中的內(nèi)容,是個(gè)虛擬列;uk_idx_cellphone 就是在虛擬列 cellphone 上所創(chuàng)建的索引。

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

          用戶(hù)畫(huà)像設(shè)計(jì)

          某些業(yè)務(wù)需要做用戶(hù)畫(huà)像(也就是對(duì)用戶(hù)打標(biāo)簽),然后根據(jù)用戶(hù)的標(biāo)簽,通過(guò)數(shù)據(jù)挖掘技術(shù),進(jìn)行相應(yīng)的產(chǎn)品推薦。比如:

          • 在電商行業(yè)中,根據(jù)用戶(hù)的穿搭喜好,推薦相應(yīng)的商品;
          • 在音樂(lè)行業(yè)中,根據(jù)用戶(hù)喜歡的音樂(lè)風(fēng)格和常聽(tīng)的歌手,推薦相應(yīng)的歌曲;
          • 在金融行業(yè),根據(jù)用戶(hù)的風(fēng)險(xiǎn)喜好和投資經(jīng)驗(yàn),推薦相應(yīng)的理財(cái)產(chǎn)品。

          在這,我強(qiáng)烈推薦你用 JSON 類(lèi)型在數(shù)據(jù)庫(kù)中存儲(chǔ)用戶(hù)畫(huà)像信息,并結(jié)合 JSON 數(shù)組類(lèi)型和多值索引的特點(diǎn)進(jìn)行高效查詢(xún)。假設(shè)有張畫(huà)像定義表:

          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?|?愛(ài)運(yùn)動(dòng)???????|
          |?????6?|?高學(xué)歷???????|
          |?????7?|?小資?????????|
          |?????8?|?有房?????????|
          |?????9?|?有車(chē)?????????|
          |????10?|?常看電影?????|
          |????11?|?愛(ài)網(wǎng)購(gòu)???????|
          |????12?|?愛(ài)外賣(mài)???????|
          +-------+--------------+

          可以看到,表 Tags 是一張畫(huà)像定義表,用于描述當(dāng)前定義有多少個(gè)標(biāo)簽,接著給每個(gè)用戶(hù)打標(biāo)簽,比如用戶(hù) David,他的標(biāo)簽是 80 后、高學(xué)歷、小資、有房、常看電影;用戶(hù) Tom,90 后、常看電影、愛(ài)外賣(mài)。

          若不用 JSON 數(shù)據(jù)類(lèi)型進(jìn)行標(biāo)簽存儲(chǔ),通常會(huì)將用戶(hù)標(biāo)簽通過(guò)字符串,加上分割符的方式,在一個(gè)字段中存取用戶(hù)所有的標(biāo)簽:

          +-------+---------------------------------------+
          |用戶(hù)????|標(biāo)簽???????????????????????????????????|
          +-------+---------------------------------------+
          |David ?|80后?;?高學(xué)歷?;?小資?;?有房?;常看電影???|
          |Tom ???|90后?;常看電影?;?愛(ài)外賣(mài)?????????????????|
          +-------+---------------------------------------

          這樣做的缺點(diǎn)是:不好搜索特定畫(huà)像的用戶(hù),另外分隔符也是一種自我約定,在數(shù)據(jù)庫(kù)中其實(shí)可以任意存儲(chǔ)其他數(shù)據(jù),最終產(chǎn)生臟數(shù)據(jù)。

          用 JSON 數(shù)據(jù)類(lèi)型就能很好解決這個(gè)問(wèn)題:

          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 存儲(chǔ)的標(biāo)簽就是表 Tags 已定義的那些標(biāo)簽值,只是使用 JSON 數(shù)組類(lèi)型進(jìn)行存儲(chǔ)。

          MySQL 8.0.17 版本開(kāi)始支持 Multi-Valued Indexes,用于在 JSON 數(shù)組上創(chuàng)建索引,并通過(guò)函數(shù) member of、json_contains、json_overlaps 來(lái)快速檢索索引數(shù)據(jù)。所以你可以在表 UserTag 上創(chuàng)建 Multi-Valued Indexes:

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

          如果想要查詢(xún)用戶(hù)畫(huà)像為常看電影的用戶(hù),可以使用函數(shù) 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)

          如果想要查詢(xún)畫(huà)像為 80 后,且常看電影的用戶(hù),可以使用函數(shù) 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)

          如果想要查詢(xún)畫(huà)像為 80 后、90 后,且常看電影的用戶(hù),則可以使用函數(shù) 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)

          總結(jié)


          JSON 類(lèi)型是 MySQL 5.7 版本新增的數(shù)據(jù)類(lèi)型,用好 JSON 數(shù)據(jù)類(lèi)型可以有效解決很多業(yè)務(wù)中實(shí)際問(wèn)題。最后,我總結(jié)下今天的重點(diǎn)內(nèi)容:

          • 使用 JSON 數(shù)據(jù)類(lèi)型,推薦用 MySQL 8.0.17 以上的版本,性能更好,同時(shí)也支持 Multi-Valued Indexes;
          • JSON 數(shù)據(jù)類(lèi)型的好處是無(wú)須預(yù)先定義列,數(shù)據(jù)本身就具有很好的描述性;
          • 不要將有明顯關(guān)系型的數(shù)據(jù)用 JSON 存儲(chǔ),如用戶(hù)余額、用戶(hù)姓名、用戶(hù)身份證等,這些都是每個(gè)用戶(hù)必須包含的數(shù)據(jù);
          • JSON 數(shù)據(jù)類(lèi)型推薦使用在不經(jīng)常更新的靜態(tài)數(shù)據(jù)存儲(chǔ)。

          推薦閱讀

          ??程序員床上二三事 …竟還有如此沙雕的代碼注釋?zhuān)∥倚娏?/a>

          --- EOF ---

          剛剛整理好了的第五版《Java大廠面試題》,而且已經(jīng)分類(lèi)?25?PDF累計(jì) 2098頁(yè)!

          整理的面試題,內(nèi)容列表


          互聯(lián)網(wǎng)大廠面試題,怎么領(lǐng)取?

          ?注意,不要亂回復(fù)?
          (一定要回復(fù)?面試題?)否則獲取不了


          覺(jué)得不錯(cuò),請(qǐng)點(diǎn)個(gè)在看

          瀏覽 52
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚州乱伦网 | 二区三区在线视频观看网站 | 国产精品成人毛片 | 操逼视频免费观看 | 曰夲卖婬片免费看9.1 |