MYSQL 5.7中的本机JSON支持:MYSQL中JSON数据类型的优缺点是什么?


113

在MySQL 5.7中,添加了用于在JSON表中存储JSON数据的新数据类型。显然,这将是MySQL的巨大变化。他们列出了一些好处

文档验证 -JSON列中只能存储有效的JSON文档,因此您可以自动验证数据。

有效访问 -更重要的是,当您将JSON文档存储在JSON列中时,它不会存储为纯文本值。相反,它以优化的二进制格式存储,从而可以更快地访问对象成员和数组元素。

性能 -通过在JSON列中的值上创建索引来提高查询性能。这可以通过虚拟列上的“功能索引”来实现。

便利性 -JSON列的附加内联语法使在SQL中集成文档查询变得非常自然。例如(features.feature是一个JSON列):SELECT feature->"$.properties.STREET" AS property_street FROM features WHERE id = 121254;

哇 !它们包括一些很棒的功能。现在,更容易操作数据。现在可以在列中存储更复杂的数据。因此,MySQL现在具有NoSQL的味道。

现在我可以想象对JSON数据的查询类似

SELECT * FROM t1
WHERE JSON_EXTRACT(data,"$.series") IN 
( 
SELECT JSON_EXTRACT(data,"$.inverted") 
FROM t1 | {"series": 3, "inverted": 8} 
WHERE JSON_EXTRACT(data,"$.inverted")<4 );

那我可以在几个json列中存储巨大的小关系吗?好吗?它破坏规范化了吗?如果可能的话,我想它将像MySQL列中的NoSQL一样。我真的很想了解更多有关此功能的信息。MySQL JSON数据类型的优缺点。


哦,请不要说我想你在说什么。在这里,阅读此。您的又一个坏主意。
画了

@德鲁你给了一个很大的答案。但这不是我的问题。我只想知道,如果我们编写一个json数据查询,那么我们可能会跳过sql规则。因为我们不需要很多桌子
Imran

1
你说Now it is possible to store more complex data in column。小心
Drew 2015年

2
Json数据类型支持索引,并且具有智能大小:64K和4G。那么,如果我想存储2000个数据并添加5个嵌套标签而不是5个具有关系的表,会出现什么问题?
伊兰(Imran)

5
“我真的很想了解更多有关此功能的信息。” 和“ MySQL JSON数据类型的优缺点”。不是问题,如果措辞过于笼统,请改写。“因此,我从没想到MySQL中有复杂的架构结构和外键。我仅使用几个表存储复杂的关系。” 是自相矛盾的,因为JSON不是关系和FK。对“这是一件好事”的解释只是对关系模型的介绍,因此,这又太广泛了。通过一些示例工作,列出自己的优缺点和参考文献,并询问出错的地方。
philipxy

Answers:


57
SELECT * FROM t1
WHERE JSON_EXTRACT(data,"$.series") IN ...

在这样的表达式或函数中使用列会浪费使用索引来帮助优化查询的任何机会。上面显示的查询被强制执行表扫描。

关于“有效访问”的说法具有误导性。这意味着在查询检查了带有JSON文档的行之后,它可以提取一个字段,而不必解析JSON语法的文本。但是仍然需要进行表格扫描来搜索行。换句话说,查询必须检查每一行。

以此类推,如果我正在搜索电话簿中名字为“ Bill”的人,即使我的名字被突出显示以使其更快地发现它们,我仍然必须阅读电话簿中的每一页。

MySQL 5.7允许您在表中定义虚拟列,然后在虚拟列上创建索引。

ALTER TABLE t1
  ADD COLUMN series AS (JSON_EXTRACT(data, '$.series')),
  ADD INDEX (series);

然后,如果您查询虚拟列,它可以使用索引并避免进行表扫描。

SELECT * FROM t1
WHERE series IN ...

很好,但是有点遗漏了使用JSON的意义。使用JSON的吸引力在于,它允许您添加新属性,而不必执行ALTER TABLE。但是事实证明,如果要在索引的帮助下搜索JSON字段,则无论如何都必须定义一个额外的(虚拟)列。

但是,您不必为JSON文档中的每个字段定义虚拟列和索引,而只需定义要搜索或排序的列和索引。只需在选择列表中提取,JSON中可能还有其他属性,如下所示:

SELECT JSON_EXTRACT(data, '$.series') AS series FROM t1
WHERE <other conditions>

我通常会说这是在MySQL中使用JSON的最佳方法。仅在选择列表中。

当您在其他子句(JOIN,WHERE,GROUP BY,HAVING,ORDER BY)中引用列时,使用常规列而不是JSON文档中的字段会更有效。

我在2018年4月的Percona Live会议上发表了名为“ 如何在MySQL错误MySQL中使用JSON”的演讲。我将在秋季在Oracle Code One上更新并重复该演讲。

JSON还有其他问题。例如,在我的测试中,JSON文档所需的存储空间是存储相同数据的常规列的2-3倍。

MySQL正在积极地推广其新的JSON功能,主要目的是劝说人们不要迁移到MongoDB。但是像MongoDB这样的面向文档的数据存储从根本上说是一种非关系式的数据组织方式。与关系型不同。我并不是说一个比另一个更好,这只是一种不同的技术,适用于不同类型的查询。

当JSON使查询更高效时,您应该选择使用JSON。

不要仅仅因为一项新技术或为了时尚而选择一项技术。


编辑:如果您的WHERE子句使用与虚拟列的定义完全相同的表达式,则MySQL中的虚拟列实现应该使用索引。也就是说,以下内容使用虚拟列上的索引,因为虚拟列已定义AS (JSON_EXTRACT(data,"$.series"))

SELECT * FROM t1
WHERE JSON_EXTRACT(data,"$.series") IN ...

除了通过测试此功能发现,如果表达式是JSON提取函数,由于某种原因它将无法工作。它适用于其他类型的表达式,但不适用于JSON函数。


7
值得跟随幻灯片的链接
Paul Campbell,

好的一点是,这两种技术各有优点,我们可以决定哪种技术可以满足我们的需求,以及在安全性和性能方面给我们带来更多优势。
Christopher Pelayo

1
问题的症结在于,仍需要ALTER TABLE来为JSON中的每个新键利用生成的列上的索引。很高兴看到有人指出。
user1454926

仅当您需要添加虚拟列和/或索引时。如果您将JSON数据视为“黑匣子”,并且不尝试对JSON内的子字段进行搜索或排序的任何查询,则无需这样做。这就是为什么我建议,以避免引用JSON的JOINWHERE或其他条款。只需获取选择列表中的JSON列即可。
比尔·卡温

幻灯片的链接已损坏,@ BillKarwin。
湖区

43

MySQL 5.7中的以下内容使JSON听起来很性感,对我来说很不错:

与在文本字段中存储JSON字符串相比,在MySQL中使用JSON数据类型具有两个优点:

数据验证。JSON文档将被自动验证,无效的文档将产生错误。改进的内部存储格式。JSON数据被转换为允许以结构化格式快速读取数据的格式。服务器能够通过键或索引查找子对象或嵌套值,从而增加了灵活性和性能。

...

NoSQL存储(文档数据库,键值存储和图形数据库)的特殊形式可能是针对其特定用例的更好选择,但是添加此数据类型可能使您降低技术堆栈的复杂性。价格正在耦合到MySQL(或兼容)数据库。但这对许多用户来说不是问题。

请注意有关文档验证的语言,因为它是一个重要因素。我猜想需要对这两种方法进行一系列测试。那两个是:

  1. 具有JSON数据类型的MySQL
  2. MySQL没有

到目前为止,从我所看到的内容来看,关于mysql / json / performance的话题,网络的幻灯片共享还很薄。

也许您的帖子可以成为它的中心。也许性能是经过深思熟虑的(不确定),您很高兴不创建一堆表。


7
一弊 Mysql内存表不支持JSON数据类型,例如数据类型TEXT和BLOB。这意味着如果需要一个临时表,它将创建一个基于磁盘的表而不是内存。当使用临时表在这里列出了一些情况: dev.mysql.com/doc/refman/5.7/en/internal-temporary-tables.html
raiz媒体

1
@raizmedia您能否详细说明为什么基于磁盘的表与内存(我猜是基于表)有问题?
lapin

@lapin可能是由于速度限制。
小助手

@LittleHelper如果使用PCI 4x 40 Gb / s M.2插槽并插入支持的40 Gb / s驱动器,则可以避免使用它。这和记忆一样快。您可以将特殊格式应用于用于格式化内存的驱动器。
谢尔盖·罗曼诺夫

@SergeyRomanov,[citation required]您是否已将该驱动器与RAM进行了基准测试?
Bill Karwin

11

我最近遇到了这个问题,并总结了以下经验:

1,没有办法解决所有问题。2,您应该正确使用JSON。

一种情况:

我有一个表命名为:CustomField,必须两列:namefieldsname是本地化的字符串,其内容应为:

{
  "en":"this is English name",
  "zh":"this is Chinese name"
   ...(other languages)
}

并且fields应该是这样的:

[
  {
    "filed1":"value",
    "filed2":"value"
    ...
  },
  {
    "filed1":"value",
    "filed2":"value"
    ...
  }
  ...
]

如您所见,namefields都可以另存为JSON,并且可以使用!

但是,如果我name经常使用来搜索该表,该怎么办?使用JSON_CONTAINSJSON_EXTRACT...?显然,将其另存为JSON并不是一个好主意,我们应该将其保存到一个独立的表:中CustomFieldName

从上述情况来看,我认为您应该牢记以下想法:

  1. 为什么MYSQL支持JSON?
  2. 为什么要使用JSON?您的业​​务逻辑只需要这个吗?还是还有别的?
  3. 永远不要偷懒

谢谢


2
您可能对使用VIRTUAL列感兴趣。percona.com/blog/2016/03/07/…–
贝尔

10

根据我的经验,至少在MySql 5.7中,JSON实现由于性能不佳而不太有用。好吧,对于读取数据和验证来说还不错。但是,使用MySql进行JSON修改的速度比使用Python或PHP慢10到20倍。让我们想象一下非常简单的JSON:

{ "name": "value" }

假设我们必须将其转换为类似的内容:

{ "name": "value", "newName": "value" }

您可以使用Python或PHP创建简单的脚本,该脚本将选择所有行并逐一更新它们。您没有被迫为此进行一项巨大的交易,因此其他应用程序可以并行使用该表。当然,如果需要,您也可以进行一个巨大的事务,因此可以保证MySql将执行“全部或不执行”,但是其他应用程序很可能在事务执行期间无法使用数据库。

我有4000万行表,Python脚本会在3-4小时内更新它。

现在我们有了MySql JSON,因此我们不再需要Python或PHP,我们可以执行以下操作:

UPDATE `JsonTable` SET `JsonColumn` = JSON_SET(`JsonColumn`, "newName", JSON_EXTRACT(`JsonColumn`, "name"))

它看起来简单而出色。但是,它的速度比Python版本慢10到20倍,并且它是单个事务,因此其他应用程序无法并行修改表数据。

因此,如果我们只想在4000万行表中复制JSON键,则无需在30-40个小时内完全使用表。它没有感觉。

关于读取数据,根据我的经验,通过JSON_EXTRACTin 直接访问JSON字段WHERE也非常慢(TEXTLIKE未索引列相比,访问速度慢得多)。虚拟生成的列的执行速度要快得多,但是,如果我们事先知道我们的数据结构,则不需要JSON,而可以使用传统列。当我们在真正有用的地方使用JSON时,即当数据结构未知或经常更改(例如,自定义插件设置)时,定期为任何可能的新列创建虚拟列似乎不是一个好主意。

Python和PHP像使JSON验证一样具有吸引力,因此是否需要在MySql端进行JSON验证是一个问题。为什么不同时验证XML,Microsoft Office文档或检查拼写?;)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.