将MySQL中的数据存储为JSON


121

我认为这是一个n00b要做的事情。因此,我从未做到过。然后,我看到FriendFeed做到了这一点,实际上使他们的数据库扩展性更好,并减少了延迟。我很好奇我是否应该这样做。而且,如果是这样,正确的方法是什么?

基本上,什么地方是学习如何将MySQL中的所有内容存储为CouchDB类数据库的好地方?将所有内容存储为JSON似乎会变得更加轻松快捷(无需构建,延迟更短)。

另外,是否容易编辑,删除等以JSON形式存储在数据库中的内容?


作为参考,我相信这是FriendFeed的对MySQL中使用JSON的讨论:backchannel.org/blog/friendfeed-schemaless-mysql
dimo414

10
MySQL 5.7现在支持本机JSON数据存储。
eecue

Answers:


57

CouchDB和MySQL是两个非常不同的野兽。JSON是将内容存储在CouchDB中的本地方法。在MySQL中,最好的办法是将JSON数据作为文本存储在单个字段中。这将完全违背将其存储在RDBMS中的目的,并使每个数据库事务都大大复杂化。

别。

话虽这么说,FriendFeed似乎在MySQL之上使用了极其自定义的架构。这实际上取决于您要存储的内容,关于如何滥用数据库系统几乎没有一个明确的答案,因此这对您来说很有意义。鉴于这篇文章非常老,并且反对Mongo和Couch的主要原因是不成熟,如果MySQL不适合您,我将重新评估这两点。他们现在应该已经成长很多。


3
是的,我正在看Mongo,而php对其进行了扩展,并且数据库事务的实际语法似乎比MySQL容易,并且总体上来说,使用ouchDB似乎更容易。谢谢,我认为我将与MongoDB一起使用:)
Oscar Godson 2010年

67
在RDBMS中存储JSON Blob肯定是有效的。如果您只想存储和检索JSON数据的不透明blob,而无需查询该数据(在某些情况下经常发生),则可以这样做。
markus 2015年

9
@markus我实际上是在我的一个网站中执行此操作的,尤其是复杂形式的字段,这些字段从未在MySQL查询中直接搜索过,而是在查看表单(从表视图或直接通过链接)时使用。它可能不是理想的,但是它肯定会使其实现起来更快,并且不需要大量的表或表列。
尼克·贝德福德

1
如果要同时为应用程序同时存储RDBMS和文档类型存储,那么这是一个好方法,因此您不必管理多个数据库。
rjarmstrong '16

5
这是很短的建议,也许是有人在堆栈交换上花了太多时间吗?当我有一个要存储的包含100个字段的记录并且只需要搜索3个或4个字段时,创建一个包含100个字段的表是没有意义的。您可以将客户记录及其整个地址簿存储在JSON的1字段中,只需添加客户ID,姓氏,公司等其他字段即可用于搜索记录。它是。节省大量时间。
Danial '18

102

每个人的评论似乎都是从错误的角度出发的,可以通过PHP将JSON代码存储在关系数据库中,实际上这样加载和显示复杂数据会更快,但是您将需要考虑以下设计问题搜索,索引等

最好的方法是使用混合数据,例如,如果您需要基于日期时间进行搜索(性能进行了调整),MySQL将比PHP快很多,并且对于诸如搜索场所距离之类的事情,MySQL也会很多。更快(通知搜索无法访问)。然后,您无需搜索的数据就可以JSON,BLOB或您真正认为必要的任何其他格式存储。

您需要访问的数据可以很容易地存储为JSON,例如基本的按需开票系统。它们根本不能从RDBMS中获得太多好处,如果您具有正确的HTML表单结构,则可以仅通过json_encoding($ _ POST ['entires'])将其存储在JSON中。

我很高兴您对使用MongoDB感到满意,并希望它继续为您服务,但不要认为MySQL总是会被您忽略,因为您的应用程序复杂性增加,您可能最终需要RDBMS一些功能和特性(即使仅用于撤回已归档的数据或业务报告)


8
-1表示“可以通过PHP将JSON代码存储在关系数据库中” –在单个字段中存储JSON(可以将整个实体表示为非原子数据)违反了关系模型并防止了1NF。此外,不要在没有指标支持的情况下对性能进行大肆宣传。
Sage Gerard 2014年

80
如前所述,这取决于您要存储的内容,即,对于发票,您真的需要单独存储每个条目吗?不,您的评论像您所了解的那样广为人知,但是1NF并非适用于每个字段,也不会有BLOB和文本类型...对于生产系统而言这完全是胡说八道,您只需要优化从中搜索所需的内容即日期,键和在某些数据上设置索引。我并不是说将所有内容都存储为JSON,而是说如果可以帮助您解决问题,则可以将某些数据存储为JSON。
刘易斯·理查德·菲利普·考尔斯

2
您所说的是可能且方便的,但是背离格式正确的关系意味着要付出更多的工作来适应和维护上述偏差。混混关系模型需要比您提供的更好的理由。有关与答案相关的复杂性的更多信息,请参见Kroenke和Auer的数据库处理,因为它们涉及关系中属性的滥用。
Sage Gerard'3

29
您假设我尚未就此问题咨询DBA,并且不理解您的意思。无论是对于小型系统还是在更深层次的系统上,我都没有确切地了解这意味着什么,但是我的意思是,您错了,并且您指向的研究是古老的,没有使用我们的应用程序战略。这完全是错误的,问题在于该过程的不良实现。例如,我并不是说只有一种模型或不使用RDBMS,而是要对在哪里使用RDBMS以及在哪里不需要使用RDBMS保持聪明。
刘易斯·理查德·菲利普·考尔斯,2014年

6
根据我的经验,这是最好的答案。如果您知道自己在做什么,则可以使用RDBMS,但仅在特定情况下存储JSON。实际上,我在阵列数据的临时缓存存储中使用了很多方法,在某些其他情况下,您可以获得更快的结果和更少的代码。实际上,许多项目都有一些混杂的特征。
Heroselohim 2015年

72

MySQL 5.7现在支持类似于MongoDB和其他无模式文档数据存储的本机JSON数据类型:

JSON支持

从MySQL 5.7.8开始,MySQL支持本机JSON类型。JSON值不存储为字符串,而是使用内部二进制格式存储,该格式允许快速读取文档元素。每当插入或更新JSON列中存储的JSON文档时,都会自动对其进行验证,无效文档会产生错误。JSON文档在创建时已标准化,可以使用大多数比较运算符进行比较,例如=,<,<=,>,> =,<>,!=和<=>;有关在比较JSON值时支持的运算符以及MySQL遵循的优先级和其他规则的信息,请参见JSON值的比较和排序。

MySQL 5.7.8还引入了许多用于处理JSON值的函数。这些功能包括此处列出的功能:

  1. 创建JSON值的函数:JSON_ARRAY(),JSON_MERGE()和JSON_OBJECT()。请参见第12.16.2节“创建JSON值的函数”。
  2. 搜索JSON值的函数:JSON_CONTAINS(),JSON_CONTAINS_PATH(),JSON_EXTRACT(),JSON_KEYS()和JSON_SEARCH()。请参见第12.16.3节“搜索JSON值的函数”。
  3. 修改JSON值的函数:JSON_APPEND(),JSON_ARRAY_APPEND(),JSON_ARRAY_INSERT(),JSON_INSERT(),JSON_QUOTE(),JSON_REMOVE(),JSON_REPLACE(),JSON_SET()和JSON_UNQUOTE()。请参见第12.16.4节“修改JSON值的函数”。
  4. 提供有关JSON值的信息的函数:JSON_DEPTH(),JSON_LENGTH(),JSON_TYPE()和JSON_VALID()。请参见第12.16.5节“返回JSON值属性的函数”。

在MySQL 5.7.9及更高版本中,您可以使用column-> path作为JSON_EXTRACT(column,path)的简写。只要列标识符可以在SQL语句中出现的任何地方(包括WHERE,ORDER BY和GROUP BY子句),它都用作列的别名。这包括SELECT,UPDATE,DELETE,CREATE TABLE和其他SQL语句。左侧必须是JSON列标识符(而不是别名)。右侧是带引号的JSON路径表达式,该表达式将针对作为列值返回的JSON文档进行评估。

有关->和JSON_EXTRACT()的更多信息,请参见第12.16.3节“搜索JSON值的函数”。有关MySQL 5.7中JSON路径支持的信息,请参见搜索和修改JSON值。另请参见二级索引和虚拟生成的列。

更多信息:

https://dev.mysql.com/doc/refman/5.7/zh-CN/json.html


26

json字符在存储时没有什么特别的,例如chars

{}[]'a-z0-9......真的没什么特别的,并且可以存储为文本。

您要面对的第一个问题是

{profile_id:22,用户名:'Robert',密码:'skhgeeht893htgn34ythg9er'}

除非您有自己的操作程序并为mysql开发了jsondecode,否则存储在数据库中的文件并不是那么容易更新

UPDATE users SET JSON(user_data,'username') = 'New User';

因此,您不能做的是,必须首先选择json,对其进行解码,更改,更新,因此从理论上讲,您不妨花更多的时间来构建合适的数据库结构!

我确实使用json来存储数据,但仅存储元数据,即不经常更新的数据,与用户特定条件无关。例如,如果用户添加了帖子,并且在该帖子中他添加了图像,则无法解析图像并创建拇指和然后使用json格式的thumb URL。


当我根本不更新json字符串时,将其存储在数据库中是否足够好?我只想使用LIKE对json数据执行常规搜索。我看到甚至Wordpress都将插件元数据作为json字符串存储在数据库中。
shasi kanth 2015年

@shasikanth,如果您要在JSON数据中搜索值,那么我会寻找一种更好的方法
Kirby

15

为了说明使用查询获取JSON数据有多么困难,我将分享为处理此问题而进行的查询。

它不考虑数组或其他对象,仅考虑基本数据类型。您应该将column的4个实例更改为存储JSON的列名,并将myfield的4个实例更改为要访问的JSON字段。

SELECT
    SUBSTRING(
        REPLACE(REPLACE(REPLACE(column, '{', ''), '}', ','), '"', ''),
        LOCATE(
            CONCAT('myfield', ':'),
            REPLACE(REPLACE(REPLACE(column, '{', ''), '}', ','), '"', '')
        ) + CHAR_LENGTH(CONCAT('myfield', ':')),
        LOCATE(
            ',',
            SUBSTRING(
                REPLACE(REPLACE(REPLACE(column, '{', ''), '}', ','), '"', ''),
                LOCATE(
                    CONCAT('myfield', ':'),
                    REPLACE(REPLACE(REPLACE(column, '{', ''), '}', ','), '"', '')
                ) + CHAR_LENGTH(CONCAT('myfield', ':'))
            )
        ) - 1
    )
    AS myfield
FROM mytable WHERE id = '3435'

5
您将不会查询此服务器端。这将是存储blob并将其返回客户端。然后,您只需使用JS对其进行查询。这是很久以前的了:)从那以后我就搬到了MongoDB上来了:)支持这个漂亮的查询。
奥斯卡·戈德森

我认为这是该人员是否要定期访问JSON数据的问题。在示例中,我将非必需的标头移到数组中,解析为JSON,然后存储。当我检索JSON(针对罕见的额外标头AJAX请求)时,我将简单地从MySQL中提取,将JSON读入数组并回显标头。对于任何数据密集型项目,可能不应将其存储为JSON。
约翰,

10

这实际上取决于您的用例。如果您存储的信息在报表中绝对没有价值,并且不会通过JOIN与其他表进行查询,那么将数据存储在一个编码为JSON的单个文本字段中可能是有意义的。

这可以大大简化您的数据模型。但是,正如罗伯特·皮特(RobertPitt)所提到的那样,不要指望能够将此数据与已标准化的其他数据结合起来。


2
我的想法正好。如果其数据从未加入/搜索过,甚至很少更新,那么为什么不在TEXT字段中使用json。一个很好的例子是一个fooditem表,其中每个食品都需要存储营养信息。份量,蛋白质,碳水化合物,总脂肪,饱和脂肪等。但不仅如此,您还需要存储值(0.2)及其度量单位(克,盎司,液量盎司,毫升)。考虑到不需要搜索数据(取决于我在做什么),我会说1 TEXT与16 int / varchar / enum列是一个很好的权衡。
布拉德·摩尔

究竟!!!当您必须存储根本不打算使用SQL进行过滤的变量和/或未知数据结构时,这很有用。数据只是按原样存储,其他人(应用程序代码)可能知道其结构以及如何处理。
2015年

9

这是一个老问题,但是我仍然可以在Google搜索结果的顶部看到该问题,因此我认为在提出问题4年后添加一个新答案将是有意义的。

首先,在RDBMS中存储JSON有更好的支持。您可以考虑切换到PostgreSQL(尽管MySQL自v5.7.7起已支持JSON)。PostgreSQL使用与MySQL非常相似的SQL命令,但它们支持更多功能。他们添加的功能之一是它们提供JSON数据类型,您现在可以查询存储的JSON。(对此有一些参考)如果您不是直接在程序中构成查询,例如,在php中使用PDO或在Laravel中使用雄辩的话,那么您所要做的只是在服务器上安装PostgreSQL并更改数据库连接设置。您甚至不需要更改代码。

在大多数情况下,正如其他答案所建议的那样,将数据直接以JSON形式直接存储在RDBMS中并不是一个好主意。虽然有一些例外。我可以想到的一种情况是一个具有可变数量的链接条目的字段。

例如,要存储博客文章的标签,通常需要一个博客文章表,一个标签表和一个匹配表。因此,当用户想要编辑帖子时,您需要显示与该帖子相关的标签,则需要查询3个表。如果您的匹配表/标签表很长,这会严重损害性能。

通过将标签以JSON格式存储在博客文章表中,相同的操作只需要搜索一个表即可。然后,用户将能够看到博客帖子,以便更快地进行编辑,但是,如果您要报告将哪些帖子链接到标签,或者可能要按标签搜索,这将损害性能。

您也可以尝试对数据库进行非规范化。通过以两种方式复制数据并存储数据,您可以从两种方法中受益。您只需要多一点时间来存储数据和更多存储空间(与更多计算能力的成本相比,这是便宜的)


8

我想说的只有两个原因:

  • 使用标准化方法的性能还不够好
  • 您无法轻松地为您特别灵活/灵活/变化的数据建模

我在这里写了一些关于自己的方法的文章:

使用NoSQL数据存储遇到了哪些可伸缩性问题?

(请参见最佳答案)

甚至JSON的速度也不够快,因此我们使用了自定义文本格式方法。工作/继续为我们工作良好。

您没有使用像MongoDB这样的东西是有原因的吗?(可能是“必需的” MySQL;只是出于好奇)


6

在我看来,每个回答此问题的人都有点错过了一个关键问题,除了@deceze以外,请使用正确的工具进行工作。您可以强制关系数据库存储几乎任何类型的数据,并且可以强制Mongo处理关系数据,但是要花多少钱?您最终在从模式设计到应用程序代码的所有开发和维护级别上都引入了复杂性。更不用说性能打击了。

在2014年,我们可以访问许多处理特殊类型数据的数据库服务器。

  • Mongo(文档存储)
  • Redis(键值数据存储)
  • MySQL / Maria / PostgreSQL / Oracle / etc(关系数据)
  • CouchDB(JSON)

我确定我想念其他一些人,例如RabbirMQ和Cassandra。我的观点是,为需要存储的数据使用正确的工具。

如果您的应用程序需要真正,非常快地(并且不需要)存储和检索各种数据,则不要回避为应用程序使用多个数据源。大多数流行的Web框架都支持多种数据源(Rails,Django,Grails,Cake,Zend等)。这种策略将复杂性限制在应用程序,ORM或应用程序的数据源接口的一个特定区域。


1
您认为RabbitMQ是数据库服务器还是某种服务器?我会说这是一个面向消息的中间件,具有很好的持久性功能,不会丢失任何消息,但没有什么可保存数据的。只是我的两分钱。
Osiriz

@Osiriz:你是对的。我可能不应该在讨论中包括它。
CheddarMonkey

5

这是一个将在一个列中保存/更新JSON数组的键的函数,以及另一个检索JSON值的函数。假设存储JSON数组的列名称为json,则创建此函数。它正在使用PDO

保存/更新功能

function save($uid, $key, $val){
 global $dbh; // The PDO object
 $sql = $dbh->prepare("SELECT `json` FROM users WHERE `id`=?");
 $sql->execute(array($uid));
 $data      = $sql->fetch();
 $arr       = json_decode($data['json'],true);
 $arr[$key] = $val; // Update the value
 $sql=$dbh->prepare("UPDATE `users` SET `json`=? WHERE `id`=?");
 $sql->execute(array(
   json_encode($arr), 
   $uid
 ));
}

其中$ uid是用户的ID,$ key-要更新的JSON密钥,其值称为$ val

获取价值功能

function get($uid, $key){
 global $dbh;
 $sql = $dbh->prepare("SELECT `json` FROM `users` WHERE `id`=?");
 $sql->execute(array($uid));
 $data = $sql->fetch();
 $arr  = json_decode($data['json'], true);
 return $arr[$key];
}

其中$ key是我们需要该值的JSON数组的键。


1
这在冲突情况下失败,如果您刚刚读取的json被另一个进程更新,然后将json保存在当前线程中怎么办呢?您可能需要SELECT FOR UPDATE在json数据中添加类似或版本控制的锁。
DhruvPathak 2014年

@DhruvPathak能否请使用更新您的答案SELECT FOR UPDATE,这样会更好。我不知道怎么用。
Subin 2014年

3

MySQL 5.7.7 JSON实验版本Linux二进制文件源代码)中已添加了对在JSON中存储JSON的早期支持!该版本似乎是从2013年公开的一系列与JSON相关的用户定义函数发展而来的。

这种新生的原生JSON支持似乎朝着非常积极的方向发展,包括在INSERT上进行JSON验证,这是一种优化的二进制存储格式,包括在序言中的查找表,该表允许JSN_EXTRACT函数执行二进制查找,而不是对每次访问进行解析。还有大量用于处理和查询特定JSON数据类型的新功能:

CREATE TABLE users (id INT, preferences JSON);

INSERT INTO users VALUES (1, JSN_OBJECT('showSideBar', true, 'fontSize', 12));

SELECT JSN_EXTRACT(preferences, '$.showSideBar') from users;

+--------------------------------------------------+
| id   | JSN_EXTRACT(preferences, '$.showSideBar') |
+--------------------------------------------------+
| 1    | true                                      |
+--------------------------------------------------+

恕我直言,以上是此新功能的绝佳用例;许多SQL数据库已经具有一个用户表,而不是进行无休止的模式更改以适应不断发展的用户首选项集,而只需一个JSON列JOIN就可以了。特别是因为不太可能需要针对单个项目进行查询。

尽管还很早,但是MySQL服务器团队博客 交流所做的工作非常出色。


2

我相信,将JSON存储在mysql数据库中确实会破坏使用RDBMS的目的,因为它打算被使用。我不会在任何时候进行处理或报告的数据中使用它,因为它不仅增加了复杂性,而且根据其使用方式可能很容易影响性能。

但是,我很好奇是否有人想到了这样做的可能原因。我当时正在考虑出于记录目的而设置例外。就我而言,我想记录具有可变数量的参数和错误的请求。在这种情况下,我想将表用于请求的类型,并将请求本身使用获得的具有不同值的JSON字符串。

在上述情况下,将记录请求,并且永远不会在JSON字符串字段中对请求进行操作或建立索引。但是,在更复杂的环境中,我可能会尝试使用更倾向于这种类型的数据的东西,并将其存储在该系统中。正如其他人所说,这实际上取决于您要完成的工作,但是遵循标准始终可以延长使用寿命和可靠性!


2

JSON在PostgreSQL数据库中也是有效的数据类型。但是,MySQL数据库尚未正式支持JSON。但这很糟糕:http : //mysqlserverteam.com/json-labs-release-native-json-data-type-and-binary-format/

我也同意,在许多有效的情况下,最好将某些数据序列化为数据库中的字符串。主要原因可能是不定期查询它的时间,并且它自己的模式可能会更改-您不想更改与此相对应的数据库模式。第二个原因是,当序列化的字符串直接来自外部来源时,您可能不希望解析所有字符串并不惜一切代价将其输入数据库,直到您使用任何形式。因此,我将等待新的MySQL版本支持JSON,因为在不同数据库之间进行切换会更容易。


1

我使用json记录项目的任何内容,实际上我使用了三个表!一个用于json中的数据,一个用于json结构的每个元数据的索引(每个元数据均由唯一的ID编码),另一个用于会话用户,仅此而已。在早期的代码状态下无法对基准进行量化,但是例如,我是用户视图(与索引进行内部连接)以获取类别(或其他任何形式的用户,...),而且非常慢(非常慢) ,在mysql中使用视图不是一个好方法)。在这种结构中,搜索模块可以执行我想要的任何操作,但是,我认为mongodb在完整JSON数据记录的概念上将更加高效。例如,我使用用户视图来创建类别树,面包屑,我的天!要做这么多查询!Apache本身不见了!实际上,对于这个小型网站,我使用的是一个会生成树和面包屑的php,数据的提取由搜索模块(仅使用索引)完成,数据表仅用于更新。如果我愿意,我可以销毁所有索引,并使用每个数据重新生成它,然后执行相反的工作,例如销毁所有数据(json)并仅使用索引表重新生成它。我的项目还很年轻,可以在php和mysql下运行,但是有时候我使用node js和mongodb可以使该项目更有效。

如果您认为可以做到,请使用json,因为可以做到!并且,如果它是一个错误,请忘记它;尝试做出好还是坏的选择,但是尝试!

法国用户


1
不明白 我不会说英语,但是我建议您使用句点(。),逗号(,)和段落(Enter键)来组织您的想法。然后,只有这样,才能尝试组织数据库;-)
Diego Jancic

您是对的,实际上是令人困惑的答案,必须通过显示示例来更加明确。但是,如果可以用mongoDB代替mysql,则使用json(对mongodb而言是本机)会更有效,如果mysql是强制性的,那么让我们在几天后再试一次!

1

我知道这确实很晚,但是我确实遇到过类似的情况,我使用一种混合方法来维护RDBMS标准,以将表规范化到一个点,然后将数据作为文本值存储在JSON之外。因此,例如,我按照RDBMS规范化规则将数据存储在4个表中。但是,在第4张表中,为了适应动态模式,我以JSON格式存储数据。每次我想检索数据时,我都会检索JSON数据,将其解析并以Java显示。到目前为止,这对我一直有效,并确保我仍然能够使用ETL以规范化的方式索引表中转换为json数据的字段。这样可以确保在用户处理应用程序时,他面临的延迟最小,并且字段已转换为RDBMS友好格式以进行数据分析等。


0

您可以使用以下要点:https//gist.github.com/AminaG/33d90cb99c26298c48f670b8ffac39c3

将其安装到服务器后(只需要root特权而不是super),您可以执行以下操作:

select extract_json_value('{"a":["a","2"]}','(/a)')

它将返回 a 2 。您可以使用此方法在JSON中返回任何内容。好的部分是它支持MySQL 5.1、5.2、5.6。而且您不需要在服务器上安装任何二进制文件。

基于旧项目common-schema,但今天仍然可以使用 https://code.google.com/archive/p/common-schema/


要点现在是404
neoDev
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.