在数据库中存储JSON与为每个密钥添加一个新列


213

我正在实现以下用于在表中存储与用户相关的数据的模型-我有2列- uid(主键),该meta列以JSON格式存储有关用户的其他数据。

 uid   | meta
--------------------------------------------------
 1     | {name:['foo'], 
       |  emailid:['foo@bar.com','bar@foo.com']}
--------------------------------------------------
 2     | {name:['sann'], 
       |  emailid:['sann@bar.com','sann@foo.com']}
--------------------------------------------------

这是一个更好的方式(性能明智的,设计明智)比一列,每个属性模型,其中表将有很多像列uidnameemailid

我喜欢第一个模型,您可以添加尽可能多的字段,没有限制。

另外,我想知道,既然我已经实现了第一个模型。我该如何对其执行查询,例如,我要获取所有具有“ foo”之类名称的用户?

问题 -使用JSON或每字段列数,哪种更好的方式将用户相关数据存储(请记住字段数是固定的)?另外,如果实现了第一个模型,如何如上所述查询数据库?我是否应该通过将查询可能搜索到的所有数据存储在单独的行中以及将其他数据存储在JSON(是不同的行)中来使用这两种模型?


更新资料

由于不会有太多列需要执行搜索,因此同时使用两个模型是否明智?我需要搜索的数据的每列关键字以及其他数据(在同一MySQL数据库中)的JSON?


40
好问题!但是你为什么不接受答案?会对其他使用者(例如我)有所帮助
Sahar Ch。

Answers:


198

2017年6月4日更新

鉴于这个问题/答案已经很流行,我认为值得进行更新。

最初发布此问题时,MySQL不支持JSON数据类型,而PostgreSQL中的支持才刚刚起步。从5.7开始,MySQL 现在支持JSON数据类型(采用二进制存储格式),而PostgreSQL JSONB已经非常成熟。两种产品均提供可存储任意文档的高性能JSON类型,包括支持为JSON对象的特定键编制索引。

但是,我仍然支持我的原始声明,即在使用关系数据库时,您的默认首选项仍应为按值列。关系数据库仍基于以下假设:它们中的数据将被很好地标准化。与查看JSON文档中的键相比,查询计划器在查看列时具有更好的优化信息。可以在列之间创建外键(但不能在JSON文档中的键之间创建外键)。重要的是:如果您的大多数架构易变,足以使用JSON进行验证,那么您可能至少要考虑一下关系数据库是否是正确的选择。

也就是说,很少有应用程序是完全关系的或面向文档的。大多数应用程序将两者混合使用。以下是一些我个人发现JSON在关系数据库中有用的示例:

  • 当存储联系人的电子邮件地址和电话号码时,将它们存储为JSON数组中的值比管理多个单独的表要容易得多

  • 保存任意的键/值用户首选项(值可以是布尔值,文本值或数字,并且您不想为不同的数据类型使用单独的列)

  • 存储没有定义架构的配置数据(如果您正在构建Zapier或IFTTT,并且需要为每个集成存储配置数据)

我敢肯定还有其他例子,但这只是几个简单的例子。

原始答案

如果您确实希望能够不受限制地添加任意多个字段(除了任意文档大小限制),请考虑使用NoSQL解决方案,例如MongoDB。

对于关系数据库:每个值使用一列。将JSON Blob放在一列中实际上使查询变得不可能(并且当您实际找到有效的查询时会非常缓慢)。

关系数据库在建立索引时会利用数据类型的优势,并打算通过规范化的结构来实现。

附带说明:这并不是说您永远不要将JSON存储在关系数据库中。如果您要添加真实的元数据,或者您的JSON描述的信息不需要查询而仅用于显示,那么为所有数据点创建单独的列可能会过头了。


1
由于不会有太多列需要执行搜索,因此同时使用两个模型是否明智?我需要搜索的数据的每列关键字和其他人的JSON(在同一MySQL数据库中)?
ShuklaSannidhya

3
@Sann对于每个您想经常读取查询的数据,应该对每个值使用一列。在JSON把别人的名字没有任何意义,因为即使你基于它是不太可能的查询,你可能需要它非常频繁。在您的应用程序端,这是很多浪费的解码。除非您真的觉得您的数据可以更好地用JSON表示(并且相信我,可能不是),否则您不应求助于此。
Colin M

5
virtually impossible to query”-今天的psql允许您搜索和索引它的jsonb
ted

1
@ted是。但是,在撰写此答案时,还没有真正可用的答案。另外,此问题引用的是其中不存在功能的MySQL。
Colin M

3
@ColinM,是的,我知道我的评论比您的帖子还年轻3年。我之所以离开,是因为它可能对其他人有所帮助,并且可以改变决策。至于对MySQL的引用:可能是正确的,但"For relational databases"答案中有= P
ted

69

像大多数事情一样,“取决于”。将数据存储在列或JSON中本身并没有错是非非。这取决于您以后需要做什么。您访问该数据的预期方式是什么?您是否需要交叉引用其他数据?

其他人已经很好地回答了技术上的权衡。

没有多少人讨论过您的应用程序和功能会随着时间的推移发展,以及这种数据存储决策如何影响您的团队。

因为使用JSON的一种诱惑是避免迁移架构,所以如果团队没有纪律,那么很容易将另一个键/值对粘贴到JSON字段中。没有它的迁移,没有人记得它的用途。没有验证。

我的团队在PostgreSQL的传统列中使用了JSON,一开始这是自切面包以来最好的方法。JSON具有强大的吸引力,直到有一天,我们才意识到灵活性是有代价的,这突然成为一个真正的痛点。有时,这一点会迅速上升,然后变得很难更改,因为我们在此设计决策基础上建立了很多其他东西。

随着时间的推移,添加新功能以及将数据存储在JSON中会导致比我们如果坚持传统列可能要添加的内容看起来更复杂的查询。因此,我们开始将某些关键值返回到列中,以便我们可以进行联接并在值之间进行比较。馊主意。现在我们有了重复。新的开发人员会加入并感到困惑吗?我应该存回的值是多少?是JSON还是列?

JSON字段变成了这些东西的一小部分。没有数据库级别的数据验证,文档之间没有一致性或完整性。这将所有责任推到了应用程序中,而不是从传统列中进行困难的类型和约束检查。

回顾过去,JSON使我们能够非常快速地进行迭代并获得成功。太好了。但是,在达到一定的团队规模之后,灵活性又使我们陷入了沉重的技术负担之中,从而减慢了后续功能开发的进度。请谨慎使用。

认真思考一下数据的本质。这是您应用程序的基础。随着时间的推移将如何使用数据。并有可能改变吗?


7
“它的灵活性还使我们能够背负一长串技术债务”,这是一个很好的比喻!
Antoine Gallix

经过多年的发展并与不同的人合作,如果我要写这个主题,我会写同样的东西。现在有那么多开发人员,其中许多人即使有多年的经验也没有真正升级。我们必须使一切保持简单,对我而言,我们必须始终考虑的两件事可以“框架化”成功的原因是代码的可伸缩性和可维护性。
JohnnyJaxs

27

只是把它扔在那里,但是WordPress具有这种东西的结构(至少WordPress是我观察到的第一个地方,它可能起源于其他地方)。

它允许无限制的键,并且比使用JSON blob更快地进行搜索,但不如某些NoSQL解决方案快。

uid   |   meta_key    |   meta_val
----------------------------------
1         name            Frank
1         age             12
2         name            Jeremiah
3         fav_food        pizza
.................

编辑

用于存储历史记录/多个键

uid   | meta_id    |   meta_key    |   meta_val
----------------------------------------------------
1        1             name            Frank
1        2             name            John
1        3             age             12
2        4             name            Jeremiah
3        5             fav_food        pizza
.................

并通过类似这样的查询:

select meta_val from `table` where meta_key = 'name' and uid = 1 order by meta_id desc

1
我很好奇,看看NoSQL解决方案是否确实比在正确索引键上的关系查询的性能更好。我怀疑在这样的1级示例中它应该大致相同。
布鲁诺

+1。我也注意到了!但这给您提供了一个巨大的表(按行)。另外,您不能存储多个值,例如,如果用户更改了他/她的姓名,但是我也想保留旧名称,在这种情况下,我将需要JSON类型的数据模型。
ShuklaSannidhya

@Sann,如果您想将旧值保留在JSON中,则还必须重命名密钥:您可以使用EAV(此示例就是此示例)或JSON进行操作。没有什么特别的不同。
布鲁诺

它确实为您提供了一个巨大的表,但是对于重复值,您会遇到JSON的相同问题-您不能在同一级别使用重复键(例如,两个“名称”键)并且期望可预测的行为。
亚当

当然,您不能有重复的键,但是可以有一个与该键关联的数组。查看emailid我在问题中给出的示例中的键。
ShuklaSannidhya 2013年

13

该方法的缺点正是您提到的:

由于每次您都需要对其进行文本搜索,因此查找内容非常缓慢。

每列的值将匹配整个字符串。

您的方法(基于JSON的数据)适合不需要搜索的数据,只需要与常规数据一起显示即可。

编辑:只是为了澄清,以上适用于经典的关系数据库。NoSQL在内部使用JSON,如果这是所需的行为,可能是一个更好的选择。


1
所以你的意思是,我应该同时使用两者。我需要搜索的数据的每列关键字,其他数据则用JSON,对吗?
ShuklaSannidhya 2013年

4
是。这样,您可以通过搜索“每列数据”字段来获得所需的性能,并在需要时获取JSON blob以在代码中使用。
Nick Andriopoulos

9

基本上,您使用的第一个模型称为基于文档的存储。您应该看看流行的基于NoSQL文档的数据库,例如MongoDB和CouchDB。基本上,在基于文档的数据库中,您将数据存储在json文件中,然后可以查询这些json文件。

第二种模型是流行的关系数据库结构。

如果您想使用像MySql这样的关系数据库,那么我建议您只使用第二个模型。与第一个模型一样,使用MySql和存储数据毫无意义

要回答第二个问题,如果使用第一个模型则无法查询“ foo”之类的名称


使用两个模型是否明智?我需要搜索的数据的每列关键字以及其他人(在同一数据库中)的JSON?
ShuklaSannidhya

@Sann-哈哈 那就是数据重复。您将必须确保两条数据始终相同。即使其中一个数据在任何时间都不同,您的数据也不干净,可能会导致严重的问题。因此,我的回答是:NO
Girish

但是,当冗余数据很小时,冗余并不昂贵,例如,我只需要在两个字段上执行搜索,因此我为它们创建了两个新列,[也许]从JSON数据中将它们删除[/也许] 。那不是昂贵的复制吗?
ShuklaSannidhya 2013年

如果您正在查看性能,则MongoDB和CouchDB提供的读写操作要比MySql快,因为它们没有提供关系数据库中的许多功能,而在大多数用例中这些功能并不需要。
Girish

好处不是从API存储JSON对象/回调吗?例如,您可以仅在本地数据库(mysql,lite等)中查询JSON对象,而不用调用YouTube的URL,拇指等API。我不知道,这对我来说很有意义,尤其是当您尝试缓存或使应用程序运行得更快时。但我不是专业人士:/
markbratanov 2015年

4

似乎您主要是在犹豫是否使用关系模型。

就目前而言,您的示例将很好地适合关系模型,但是当您需要使该模型发展时,问题当然就会出现。

如果您只有一个(或几个预定级别)主要实体(用户)的属性级别,则仍可以在关系数据库中使用实体属性值(EAV)模型。(这也有其优点和缺点。)

如果您希望得到的结构化值较少,而您希望使用应用程序进行搜索,那么MySQL可能不是最佳选择。

如果您使用的是PostgreSQL,则有可能两全其美。(这实际上取决于此处数据的实际结构... MySQL不一定也不是错误的选择,NoSQL选项可能会引起人们的兴趣,我只是在建议替代方法。)

确实,PostgreSQL可以在(不可变的)函数上建立索引(据我所知MySQL无法做到),而在最新版本中,您可以直接在JSON数据上使用PLV8在感兴趣的特定JSON元素上建立索引,这将改善搜索该数据时查询的速度。

编辑:

由于不会有太多列需要执行搜索,因此同时使用两个模型是否明智?我需要搜索的数据的每列关键字以及其他数据(在同一MySQL数据库中)的JSON?

混合两个模型不一定是错误的(假设多余的空间可以忽略不计),但是如果您不能确保两个数据集保持同步,则可能会引起问题:您的应用程序一定不能更改一个数据集而不更新另一个数据集。

实现此目的的一种好方法是,只要执行更新或插入操作,就可以通过在数据库服务器内运行存储过程来使触发器执行自动更新。据我所知,MySQL存储过程语言可能缺少对任何JSON处理的支持。同样,支持PLV8的PostgreSQL(以及可能具有更灵活的存储过程语言的其他RDBMS)应该更有用(使用触发器自动更新关系列与以相同方式更新索引非常相似)。


除了我上面说的以外,值得看看PostgreSQL 9.4及更高版本中JSONB数据类型的运算符。
布鲁诺

1

在表上加入一些时间将是开销。可以说是OLAP。如果我有两个表,一个是ORDERS表,另一个是ORDER_DETAILS。为了获得所有订单的详细信息,我们必须将两个表连接起来,当表中的行数都没有增加时,这会使查询变慢。例如,以百万计左右。.左/右连接比内部连接慢。我认为,如果我们在相应的ORDERS条目JOIN中添加JSON字符串/对象,将可以避免。添加报告生成将更快...


1

简短的答案,您必须将它们混合在一起,使用json表示您不会与它们建立联系的数据,例如联系方式,地址,产品可变性


0

您正在尝试将非关系模型拟合到关系数据库中,我认为使用NoSQL数据库(例如MongoDB)会更好。没有预定义的架构可以满足您对字段数没有限制的要求(请参阅典型的MongoDB集合示例)。查看MongoDB 文档,以了解如何查询文档,例如

db.mycollection.find(
    {
      name: 'sann'
    }
)

2
出于好奇,是什么使您认为他的模型是非关系的。他上面提供的信息似乎与我有关。
Colin M

0

正如其他人指出的那样,查询速度会变慢。我建议至少添加一个“ _ID”列以代替查询。

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.