參考:https://blog.csdn.net/bugs4j/article/details/79932538
mysql原生並不支持json列中的屬性索引,但是我們可以通過mysql的虛擬列間接的為json中的某些屬性創建索引,原理就是為json中的屬性創建虛擬列,然后通過給虛擬列建立索引,從而間接的給屬性創建了索引。
在MySQL 5.7中,支持兩種Generated Column,即Virtual Generated Column和Stored Generated Column,前者只將Generated Column保存在數據字典中(表的元數據),並不會將這一列數據持久化到磁盤上;后者會將Generated Column持久化到磁盤上,而不是每次讀取的時候計算所得。很明顯,后者存放了可以通過已有數據計算而得的數據,需要更多的磁盤空間,與Virtual Column相比並沒有優勢,因此,MySQL 5.7中,不指定Generated Column的類型,默認是Virtual Column。
如果需要Stored Generated Golumn的話,可能在Virtual Generated Column上建立索引更加合適,一般情況下,都使用Virtual Generated Column,這也是MySQL默認的方式
許你寫的格式如下:
fieldname <type> [ GENERATED ALWAYS ] AS ( <expression> ) [ VIRTUAL|STORED ] [ UNIQUE [KEY] ] [ [PRIMARY] KEY ] [ NOT NULL ] [ COMMENT <text> ]
首先我們創建一張帶有虛擬列的表新表:
-
CREATE TABLE players (
-
id INT UNSIGNED NOT NULL primary key auto_increment,
-
player JSON NOT NULL,
-
vname VARCHAR( 50) GENERATED ALWAYS AS ( `player` ->> '$.name') NOT NULL -- name的虛擬列
-
);
利用操作符-» 來引用JSON字段中的KEY。在本例中字段names_virtual為虛擬字段,我把它定義成不可以為空。在實際的工作中,需要結合具體的情況來定。因為JSON本身是一種弱結構的數據對象。也就是說的它的結構不是固定不變的。
然后我們查一下這個表的列有哪些:
SHOW COLUMNS FROM `players`;
結果如下,會發現vname的附加信息里面顯示列類型為虛擬生成列:
然后,我們寫一個存儲過程,向表中插入八百萬條記錄,寫之前先執行確認開啟mysql存儲過程:
-
show variables like 'log_bin_trust_function_creators'; -- 查看是否開啟存儲函數
-
set global log_bin_trust_function_creators= 1; -- 開啟mysql存儲函數
存儲過程:
-
delimiter $$
-
create procedure insert_player( in max_num int( 10))
-
begin
-
declare i int default 0;
-
declare json_data varchar( 2000) default '1';
-
set autocommit= 0;
-
repeat
-
set i=i+ 1;
-
set json_data = concat( concat( '{"name":"yaoming-',i), '","age":34,"gender":"man","type":"basketBall"}');
-
insert into players ( id,player) values( null,json_data);
-
until i=max_num end repeat;
-
commit;
-
end $$
調用一下存儲過程,插入2000000條記錄:
call insert_player(2000000);
插入完畢,總共耗時:
在添加索引之前我們先通過vname直接查詢name為yaoming-990099的那條記錄,總共耗時3.107s:
我們看一下這條語句的查詢計划:
EXPLAIN SELECT * FROM `players` WHERE `vname` = "yaoming-990099"
結果如下:
然后我們為vname添加索引:
CREATE INDEX `name_idx` ON `players`(`vname`);
再次查詢這條語句的執行計划:
速度已經快到飛起了!!!
我們在看一下查詢計划詳情:
發現已經走了索引查詢!
但是需要注意的是,這種方法只能只能對json的某個單值屬性或者給數組中的某一個特定位置上的元素有效,如果你想給對象數組中的所有元素的某個屬性使用索引,那么最后你只能匹配數組中的所有對象的該屬性或者使用like,但是使用like的話就不能使用索引,也就是說對於這種情況是不可用的。