mysql 5.7 新特性之 json 类型 创建索引

参考: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> ]
 

首先我们创建一张带有虚拟列的表新表:

  1.  
    CREATE TABLE players (
  2.  
    id INT UNSIGNED NOT NULL primary key auto_increment,
  3.  
    player JSON NOT NULL,
  4.  
    vname VARCHAR(50) GENERATED ALWAYS AS (`player` ->> '$.name') NOT NULL -- name的虚拟列
  5.  
    );

利用操作符-» 来引用JSON字段中的KEY。在本例中字段names_virtual为虚拟字段,我把它定义成不可以为空。在实际的工作中,需要结合具体的情况来定。因为JSON本身是一种弱结构的数据对象。也就是说的它的结构不是固定不变的。

然后我们查一下这个表的列有哪些:

SHOW COLUMNS FROM `players`;
 

结果如下,会发现vname的附加信息里面显示列类型为虚拟生成列:

然后,我们写一个存储过程,向表中插入八百万条记录,写之前先执行确认开启mysql存储过程:

  1.  
    show variables like 'log_bin_trust_function_creators'; -- 查看是否开启存储函数
  2.  
    set global log_bin_trust_function_creators=1; -- 开启mysql存储函数

存储过程:

  1.  
    delimiter $$
  2.  
    create procedure insert_player(in max_num int(10))
  3.  
    begin
  4.  
    declare i int default 0;
  5.  
    declare json_data varchar(2000) default '1';
  6.  
    set autocommit= 0;
  7.  
    repeat
  8.  
    set i=i+1;
  9.  
    set json_data = concat(concat('{"name":"yaoming-',i),'","age":34,"gender":"man","type":"basketBall"}');
  10.  
    insert into players (id,player) values(null,json_data);
  11.  
    until i=max_num end repeat;
  12.  
    commit;
  13.  
    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的话就不能使用索引,也就是说对于这种情况是不可用的。

 
原文地址:https://www.cnblogs.com/kuangke/p/14702254.html