269

我正在实现以下模型来在我的表中存储用户相关数据 - 我有 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 中(是不同的行)?


更新

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

4

10 回答 10

242

2017 年 6 月 4 日更新

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

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

但是,我仍然坚持我最初的说法,即在使用关系数据库时,您的默认首选项仍应为 column-per-value。关系数据库仍然建立在假设它们中的数据将被很好地标准化的假设之上。查询计划器在查看列时比查看 JSON 文档中的键时具有更好的优化信息。可以在列之间创建外键(但不能在 JSON 文档中的键之间)。重要的是:如果您的架构的大部分是易变的,足以证明使用 JSON 是合理的,那么您可能至少要考虑关系数据库是否是正确的选择。

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

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

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

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

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

原始答案

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

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

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

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

于 2013-03-12T17:15:17.710 回答
90

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

其他人已经很好地回答了技术权衡是什么。

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

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

我的团队在 postgres 的传统列旁边使用 JSON,起初它是自切片面包以来最好的东西。JSON 具有吸引力且功能强大,直到有一天我们意识到灵活性是有代价的,它突然成为一个真正的痛点。有时,这一点会迅速上升,然后变得很难改变,因为我们在这个设计决策之上构建了很多其他东西。

随着时间的推移,添加新功能、使用 JSON 格式的数据会导致看起来比我们坚持使用传统列时可能添加的查询更复杂。因此,我们开始将某些键值重新放入列中,以便我们可以进行连接并在值之间进行比较。馊主意。现在我们有了重复。一个新的开发人员会加入并感到困惑?我应该存回哪个值?JSON 之一还是列?

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

回想起来,JSON 让我们能够非常快速地迭代并得到一些东西。太好了。然而,当我们达到一定的团队规模后,它的灵活性也让我们背上了一长串的技术债务,从而减缓了后续的功能发展进程。谨慎使用。

仔细思考数据的性质是什么。它是您的应用程序的基础。随着时间的推移,这些数据将如何被使用。它会如何改变?

于 2016-02-10T16:08:10.253 回答
31

只是把它扔在那里,但 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
于 2013-03-12T17:49:05.937 回答
18

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

它使查找内容变得非常缓慢,因为每次您都需要对其执行文本搜索。

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

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

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

于 2013-03-12T17:14:36.520 回答
12

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

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

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

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

于 2013-03-12T17:26:07.167 回答
5

看来您主要是在犹豫是否使用关系模型。

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

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

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

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

事实上,PostgreSQL 可以在(不可变的)函数上构建索引(据我所知 MySQL 不能),并且在最近的版本中,您可以直接在 JSON 数据上使用 PLV8来为感兴趣的特定 JSON 元素构建索引,这将改进搜索该数据时查询的速度。

编辑:

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

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

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

于 2013-03-12T17:30:15.930 回答
1

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

于 2014-09-16T05:34:51.407 回答
1

简短的回答你必须在它们之间混合,使用 json 来处理你不会与它们建立关系的数据,比如联系数据、地址、产品变量

于 2018-10-18T22:54:35.293 回答
0

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

db.mycollection.find(
    {
      name: 'sann'
    }
)
于 2013-03-12T17:21:20.950 回答
0

正如其他人指出的那样,查询会更慢。我建议至少添加一个“_ID”列来查询。

于 2017-04-04T16:52:08.417 回答