MySQL到现如今已经是原生支持Json类型(5.7.8版本之后),毕竟JSON存储一直都是NoSql玩的溜,触不及防,MySQL里头也能支持了,且能对JSON数据进行相对的操作。

NoSql中,对JSON数据天生友好,都算是不固定(弱结构)的数据存储。而MySQL中,字段都是先天所定(强结构),后续更改会相对别扭一些(相对于NoSql型数据库)。

Gorm

Go语言中,我们一般使用Gorm和原生SQL对数据库进行操作,虽然中大型项目中,大多前辈以及一些DBA都会推荐使用原生SQL,这样的好处是防止中间层出现意外的错误,以及排查问题方便性。特别的,如果遇到性能问题,使用orm会造成排查问题的一些复杂性。

当然,我要是为了开发的便利和速度,一般都会选择Gorm

由于Gorm原生并不支持JSON数据结构,需要添加一些方法才能正常操作JSON字段。

JSON字段的支持

Json字段的创建和普通字段创建没有什么区别:

1
2
3
CREATE TABLE `users` (
`info` json NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

更详细的代码请直接参考我的GitHub代码

Gorm需要加入两个函数:ValueScan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type Demo struct {
id string
obj DemoObj `sql:"TYPE:json"`
}

struct DemoObj struct {
c1 string
c2 int
c3 bool
}

func (c DemoObj) Value() (driver.Value, error) {
b, err := json.Marshal(c)
return string(b), err
}

func (c *DemoObj) Scan(input interface{}) error {
return json.Unmarshal(input.([]byte), c)
}

以下是对数组的支持。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/****************使gorm支持[]string结构*******************/
type Strings []string

func (c Strings) Value() (driver.Value, error) {
b, err := json.Marshal(c)
return string(b), err
}

func (c *Strings) Scan(input interface{}) error {
return json.Unmarshal(input.([]byte), c)
}

/****************使gorm支持[]string结构*******************/

/****************使gorm支持[]int64结构*******************/
type Int64s []int64

func (c Int64s) Value() (driver.Value, error) {
b, err := json.Marshal(c)
return string(b), err
}

func (c *Int64s) Scan(input interface{}) error {
return json.Unmarshal(input.([]byte), c)
}

/****************使gorm支持[]int64结构*******************/

JSON字段的操作

在上面我的GitHub代码中,有对json字段查找的操作,但其实JSON字段还有很多其他操作,已经有前人总结了这些操作,在此我就直接引用了,给出链接。

MySQL JSON 类型数据操作

JSON字段的索引

那知道了如何操作JSON字段,这里我有个疑问,JSON字段能否加入索引呢?来试试。

1
2
mysql> ALTER TABLE users ADD INDEX (info);
ERROR 3152 (42000): JSON column 'info' cannot be used in key specification.

显然是有问题的,对于索引 JSON 类型列问题,MySQL 文档有如下阐述 [doc ]:

JSON columns, like columns of other binary types, are not indexed directly; instead, you can create an index on a generated column that extracts a scalar value from the JSON column. See Indexing a Generated Column to Provide a JSON Column Index, for a detailed example.

就是说,不能直接在 JSON 列上创建索引;替代方式是,先创建提取 JSON 纯量的生成列(generated column),然后在这个生成列上创建索引。

生成列也被称为虚拟列,其详细作用参考:(看一眼博客里头举的例子,非常好理解虚拟列的用处)

Mysql 5.7 的‘虚拟列’是做什么?

虚拟列不存储在数据行中,但虚拟列的元数据信息会存在于相关系统表中,对虚拟列的添加或者删除只会涉及这些系统表,不会导致数据表的重建,所以效率很高

需要注意,不能建立虚拟列和真实列的联合索引。

具体MySQL中创建JSON虚拟列的操作,参考:MySQL · 最佳实践 · 如何索引JSON字段