在数据库中存储JSON与每个密钥都有一个新的列

我正在实施下面的模型来存储用户相关的数据在我的表 – 我有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']} -------------------------------------------------- 

这是一种更好的方法(性能明智,devise明智)比每列属性模型,其中表中将有许多列像uidnameemailid

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

另外,我想知道,现在我已经实现了第一个模型。 我如何对其执行查询,比如,我想获取所有像“foo”这样的名字的用户?

问题 – 在数据库中使用JSON或者按字段存储用户相关数据的更好方法(记住字段数量不固定)? 另外,如果实现了第一个模型,如何查询数据库如上所述? 我是否应该使用这两个模型,将所有可能通过查询search到的数据存储在单独的行中,并将其他数据存储在JSON中(是不同的行)?


更新

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

2017年6月4日更新

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

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

但是,我仍然坚持我原来的声明,即在使用关系数据库时,您的默认首选项应该仍然是按列的值。 关系数据库仍然build立在它们内部的数据将被很好地标准化的假设之上。 在查看列时,查询规划器比在JSON文档中查看键时具有更好的优化信息。 可以在列之间创build外键(但不能在JSON文档中的键之间)。 重要的是:如果大部分模式足够易于使用JSON,那么您可能至less要考虑关系数据库是否是正确的select。

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

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

  • 保存任意键/值用户首选项(值可以是布尔值,文本值或数字值,并且不希望为不同的数据types分开列)

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

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

原始答复

如果您希望能够根据需要添加尽可能多的字段(除了任意的文档大小限制之外),请考虑使用NoSQL解决scheme(例如MongoDB)。

对于关系数据库:每个值使用一列。 把一个JSON blob放在一列中几乎不可能查询(而且当你真正发现一个可用的查询的时候,速度会很慢)。

关系数据库在索引时利用数据types,并且打算用规范化的结构来实现。

作为一个方面说明:这并不是说你永远不应该将JSON存储在关系数据库中。 如果您要添加真正的元数据,或者如果您的JSON描述的信息不需要查询 ,只用于显示,那么为所有数据点创build一个单独的列可能是矫枉过正的。

像大多数事情“取决于”。 将数据存储在列或JSON中是不对还是错,好或坏。 这取决于你以后需要做什么。 你预测访问这些数据的方式是什么? 你需要交叉引用其他数据吗?

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

没有多less人讨论过您的应用程序和function会随着时间的推移而变化,以及数据存储决策如何影响您的团队。

因为使用JSON的诱惑之一就是为了避免迁移模式,所以如果团队没有遵守规则,那么将另一个键/值对插入JSON字段是非常容易的。 没有迁移,没有人记得它是什么。 没有validation。

我的团队在postgres旁边的传统栏目中使用了JSON,起初它是自切片面包以来最好的。 JSON是有吸引力和强大的,直到有一天,我们意识到灵活性是有代价的,突然间是一个真正的痛点。 有时候,这一点很快就会升起,然后变得很难改变,因为我们在这个devise决策的基础上build立了很多其他的东西。

加class,增加新function,使用JSON数据导致查询查询比查看传统列时可能添加的查询更复杂。 于是我们开始将某些关键值捕获到列中,以便我们可以进行连接并对值进行比较。 馊主意。 现在我们有重复。 一个新的开发者会登上来混淆? 我应该挽回的价值是什么? JSON之一或列?

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

回顾一下,JSON使我们能够快速迭代并获得一些东西。 太好了。 然而,当我们达到一定的团队规模后,它的灵活性也让我们挂上一大串技术债务,从而减缓了随后的特征演变进程。 谨慎使用。

仔细考虑你的数据的性质。 这是你的应用程序的基础。 数据如何随着时间的推移而被使用。 怎么可能改变?

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

它允许使用无限的密钥,search速度比使用JSON blob快,但不像一些NoSQL解决scheme那么快。

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

编辑

用于存储历史logging/多个密钥

 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 

该方法的缺点正是你所提到的:

它使得查找的速度非常慢,因为每次你都需要对它进行文本search。

而是每列的值匹配整个string。

您的方法(基于JSON的数据)适用于不需要search的数据,只需要显示您的正常数据。

编辑:只是为了澄清,上述去经典的关系型数据库。 NoSQL在内部使用JSON,如果这是所需的行为,可能是更好的select。

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

第二种模式是stream行的关系数据库结构。

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

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

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

就目前来看,你的例子可以很好的适应关系模型,但是当你需要使这个模型发展的时候,问题可能就会出现。

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

如果您预计会得到较less的结构化值,那么您将需要使用应用程序进行search,但MySQL可能不是这里的最佳select。

如果你使用的是PostgreSQL,你可能会得到两全其美的好处。 (这真的取决于这里的数据的实际结构… MySQL不一定是错误的select,NoSQL选项可能是有趣的,我只是build议替代品。)

事实上,PostgreSQL可以build立(不可变的)函数的索引(MySQL不能),在最近的版本中,你可以直接在JSON数据上使用PLV8来构build特定的感兴趣的JSON元素的索引,search该数据时查询的速度。

编辑:

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

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

实现这一点的一个好方法是让触发器执行自动更新,只要进行更新或插入,就在数据库服务器中运行一个存储过程。 据我所知,MySQL存储过程语言可能不支持任何types的JSON处理。 再一次,支持PLV8的PostgreSQL(可能还有其他更灵活的存储过程语言的RDBMS)应该更有用(使用触发器自动更新关系列与更新索引非常相似)。

你正试图将一个非关系模型放到一个关系数据库中,我想你会更好地使用一个NoSQL数据库,比如MongoDB 。 没有预定义的模式适合于您对字段数量没有限制的要求(请参阅典型的MongoDB收集示例)。 查看MongoDB 文档 ,了解如何查询文档,例如

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

有些时候join桌子将是一个开销。 可以说对于OLAP。 如果我有两个表,一个是ORDERS表,另一个是ORDER_DETAILS。 为了获得所有的订单细节,我们必须join两个表,这将使查询变慢,当表中没有任何行增加可以说在数百万左右..左/右连接是比内部连接慢。 我想如果我们添加JSONstring/对象在各自的ORDERS条目JOIN将被避免。 添加报告生成将会更快…

正如其他人所指出的那样,查询会比较慢。 我build议至less添加一个“_ID”列来查询。