在数据库中存储JSON与为每个键创建一个新列

时间:2013-03-12 17:11:49

标签: mysql sql sql-server database optimization

我正在实现以下用于在我的表中存储用户相关数据的模型 - 我有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']}
--------------------------------------------------

这是一种更好的方式(性能方面,设计方面),而不是单列每属性模型,其中表将包含许多列,如uidname,{{ 1}}。

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

另外,我想知道,现在我已经实现了第一个模型。如何对其执行查询,例如,我想获取所有名称为'foo'的用户?

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


更新

由于我不需要执行搜索的列数太多,使用这两个模型是否明智?我需要搜索的数据的每列密钥和其他人的JSON(在同一个MySQL数据库中)?

10 个答案:

答案 0 :(得分:163)

2017年6月4日更新

鉴于这个问题/答案已经获得了一些人气,我认为值得更新。

当这个问题最初发布时,MySQL不支持JSON数据类型,并且PostgreSQL中的支持还处于起步阶段。从5.7开始,MySQL now supports a JSON data type(二进制存储格式)和PostgreSQL JSONB已经成熟。这两种产品都提供了可以存储任意文档的高性能JSON类型,包括支持索引JSON对象的特定键。

但是,我仍然支持我的原始声明,即在使用关系数据库时,您的默认首选项应该仍然是每列值列。关系数据库仍然建立在假设其中的数据将被很好地规范化的基础上。查看计划程序在查看列时比在查看JSON文档中的键时具有更好的优化信息。可以在列之间创建外键(但不能在JSON文档中的键之间创建)。重要的是:如果您的架构的大部分都足够易变以证明使用JSON,那么您可能至少需要考虑关系数据库是否是正确的选择。

尽管如此,很少有应用程序是完全关系型或面向文档的。大多数应用程序都有两种组合。以下是我个人发现JSON在关系数据库中有用的一些示例:

  • 存储联系人的电子邮件地址和电话号码时,将其作为值存储在JSON数组中比单独的表更容易管理

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

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

我确信还有其他一些,但这些只是一些简单的例子。

原始答案

如果您真的希望能够无限制地添加任意数量的字段(除了任意文档大小限制),请考虑使用NoSQL解决方案,例如MongoDB。

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

关系数据库在索引时利用数据类型,并且旨在使用规范化结构实现。

作为旁注:这并不是说你永远不应该将JSON存储在关系数据库中。如果您要添加真正的元数据,或者您的JSON正在描述不需要查询并且仅用于显示的信息,那么为所有数据创建单独的列可能会有点过分。分。

答案 1 :(得分:51)

像大多数事情一样,它取决于"。在列或JSON中存储数据本身并不是对错,好坏。这取决于您以后需要做什么。您预测的访问此数据的方式是什么?你需要交叉引用其他数据吗?

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

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

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

我的团队在postgres的传统专栏中使用了JSON,起初它是切片面包以来最好的东西。 JSON很有吸引力,也很强大,直到有一天我们意识到灵活性是有代价的,而且它突然变成了一个真正的痛点。有时候这一点很快就会迅速发展,然后变得很难改变,因为我们已经在这个设计决策之上构建了许多其他东西。

加班,添加新功能,使用JSON中的数据导致查询比查看传统列时可能添加的查询更复杂。因此,我们开始将某些键值捕获回列中,以便我们可以进行连接并在值之间进行比较。馊主意。现在我们有重复。一个新的开发人员会加入并混淆?我应该挽回的价值是多少? JSON one或列?

JSON字段成为垃圾抽屉,用于处理这个和那个小部分。没有数据库级别的数据验证,文档之间没有一致性或完整性。这将所有责任推到了应用程序中,而不是从传统列中获取硬类型和约束检查。

回顾过去,JSON允许我们快速迭代并获得一些东西。太棒了。然而,在我们达到某个团队规模之后,它的灵活性也使我们能够忍受长长的技术债务,从而减缓后续功能演变的进程。请谨慎使用。

长期而艰难地思考数据的性质。它是您应用的基础。如何随着时间的推移使用数据。它怎么可能改变?

答案 2 :(得分: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

答案 3 :(得分:13)

这种方法的缺点就是你提到的:

它使得查找事物非常慢,因为每次你需要对它进行文本搜索。

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

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

编辑:为了澄清,上面介绍了经典的关系数据库。 NoSQL在内部使用JSON,如果这是期望的行为,则可能是更好的选择。

答案 4 :(得分:8)

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

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

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

要回答您的第二个问题,如果您使用第一个模型,则无法像'foo'那样查询名称。

答案 5 :(得分:4)

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

按照目前的情况,你的例子很适合关系模型,但当你需要让这个模型发展时,问题可能会出现。

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

如果您预计您将使用您的应用程序搜索较少的结构化值,那么MySQL可能不是最佳选择。

如果您使用的是PostgreSQL,您可能会获得两全其美的效果。 (这个真的取决于这里的数据的实际结构...... MySQL也不一定是错误的选择,并且NoSQL选项可能是有意义的,我只是建议替代方案。)< / p>

事实上,PostgreSQL可以在(不可变的)函数上构建索引(MySQL不能据我所知),在最近的版本中,你可以use PLV8 on the JSON data directly在感兴趣的特定JSON元素上构建索引,在搜索该数据时提高查询速度。

修改

  

因为我不需要执行太多列   搜索,使用这两个模型是明智的吗?数据的每列按键   我需要为其他人搜索和JSON(在同一个MySQL数据库中)?

混合这两个模型并不一定是错误的(假设额外的空间可以忽略不计),但是如果你不确保两个数据集保持同步,它可能会导致问题:你的应用程序必须永远不会改变一个更新另一个。

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

答案 6 :(得分:1)

一些时候加入桌面将是一个开销。让我们说OLAP。如果我有两个表,一个是ORDERS表,另一个是ORDER_DETAILS。为了获取所有订单详细信息,我们必须连接两个表,这将使查询更慢,因为表中的行数没有增加让我们说数百万左右。左/右连接比内连接慢得多。 我想如果我们在相应的ORDERS条目中添加JSON字符串/ Object将避免JOIN。添加报告生成会更快......

答案 7 :(得分:1)

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

答案 8 :(得分:0)

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

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

答案 9 :(得分:0)

正如其他人指出的那样,查询会更慢。我建议至少添加一个&#39; _ID&#39;要由此进行查询的列。