69

为动态逻辑数据库模式提供存储的推荐架构是什么?

澄清一下:如果系统需要为其模式在生产中可能由其用户扩展或更改的模型提供存储,有哪些好的技术、数据库模型或存储引擎可以实现这一点?

几个可能性来说明:

  • 通过动态生成的 DML 创建/更改数据库对象
  • 创建具有大量稀疏物理列的表并仅使用“覆盖”逻辑模式所需的表
  • 创建一个“长而窄”的表,将动态列值存储为行,然后需要对其进行透视以创建一个包含特定实体的所有值的“短而宽”行集
  • 使用 BigTable/SimpleDB PropertyBag 类型系统

任何基于现实世界经验的答案将不胜感激

4

16 回答 16

38

你的提议并不新鲜。很多人都尝试过……大多数人都发现他们追求“无限”的灵活性,结果却远比这要少得多。它是数据库设计的“蟑螂汽车旅馆”——数据进入,但几乎不可能将其取出。尝试并概念化为任何类型的约束编写代码,您就会明白我的意思。

最终结果通常是一个更难调试、维护和充满数据一致性问题的系统。情况并非总是如此,但通常情况下,事情就是这样结束的。主要是因为程序员没有看到火车残骸的到来,也没有针对它进行防御性编码。此外,通常会得出这样一种情况,即“无限”的灵活性确实没有那么必要。当开发团队收到一份规格说明“天哪,我不知道他们要放什么类型的数据,所以让他们放什么”时,这是一种非常糟糕的“气味”......最终用户很好具有他们可以使用的预定义属性类型(编写通用电话#,

如果你有一个非常优秀的开发团队,并且非常清楚在这个设计中你必须克服的问题,你就可以成功地编写出一个设计良好、漏洞不严重的系统。大多数时候。

但是,为什么要从对您不利的赔率开始呢?

不相信我?谷歌“一个真正的查找表”或“单表设计”。一些好的结果: http ://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:10678084117056

http://thedailywtf.com/Comments/Tom_Kyte_on_The_Ultimate_Extensibility.aspx?pg=3

http://www.dbazine.com/ofinterest/oi-articles/celko22

http://thedailywtf.com/Comments/The_Inner-Platform_Effect.aspx?pg=2

于 2008-09-16T00:11:10.353 回答
20

MSSQL 中的强类型 xml 字段对我们有用。

于 2008-09-15T20:05:56.463 回答
17

就像其他人所说的那样,除非您别无选择,否则不要这样做。需要这样做的一种情况是,如果您销售的现成产品必须允许用户记录自定义数据。我公司的产品属于这一类。

如果您确实需要允许您的客户这样做,这里有一些提示:
- 创建一个强大的管理工具来执行架构更改,并且不允许以任何其他方式进行这些更改。
- 使其成为管理功能;不允许普通用户访问。
- 记录有关每个架构更改的每个细节。这将帮助您调试问题,并且如果客户做了一些愚蠢的事情,它还会为您提供 CYA 数据。

如果你能成功地做这些事情(尤其是第一个),那么你提到的任何架构都可以工作。我的偏好是动态更改数据库对象,因为这允许您在访问存储在自定义字段中的数据时利用 DBMS 的查询功能。其他三个选项需要您加载大量数据,然后在代码中进行大部分数据处理。

于 2009-11-19T17:12:58.737 回答
9

我有类似的要求,并决定使用无模式MongoDB

MongoDB(来自“humongous”)是一个开源、可扩展、高性能、无模式、面向文档的数据库,用 C++ 编程语言编写。(维基百科)

强调:

  • 具有丰富的查询功能(可能最接近 SQL DB)
  • 生产就绪(foursquare,sourceforge 使用它)

Lowdarks(您需要了解的东西,以便您可以正确使用 mongo):

于 2010-09-26T18:33:43.090 回答
7

我在一个真实的项目中做到了:

该数据库由一个表组成,其中一个字段是 50 个数组。它上面设置了一个“单词”索引。所有数据都是无类型的,因此“单词索引”按预期工作。数字字段表示为字符,实际排序已在客户端完成。(如果需要,每个数据类型仍然可以有多个数组字段)。

逻辑表的逻辑数据模式保存在具有不同表行“类型”(第一个数组元素)的同一数据库中。它还支持使用相同“类型”字段的写时复制样式的简单版本控制。

优点:

  1. 您可以动态重新排列和添加/删除列,无需转储/重新加载数据库。任何新的列数据都可以在零时间内(实际上)设置为初始值。
  2. 碎片最少,因为所有记录和表的大小都相同,有时它会提供更好的性能。
  3. 所有表模式都是虚拟的。任何逻辑模式结构都是可能的(甚至是递归的或面向对象的)。
  4. 它适用于“一次写入、主要读取、不删除/标记为已删除”的数据(大多数 Web 应用程序实际上都是这样)。

缺点:

  1. 仅按全词索引,无缩写,
  2. 复杂的查询是可能的,但性能会略有下降。
  3. 取决于您首选的数据库系统是否支持数组和单词索引(它已在 PROGRESS RDBMS 中实现)。
  4. 关系模型只存在于程序员的脑海中(即只存在于运行时)。

现在我想下一步可​​能是——在文件系统级别实现这样的数据库。这可能相对容易。

于 2008-09-15T21:30:12.237 回答
7

拥有关系数据库的全部意义在于保持您的数据安全和一致。在您允许用户更改架构的那一刻,您的数据完整性就...

如果您需要存储异构数据,例如 CMS 场景,我建议将通过 XSD 验证的 XML 存储在一行中。当然,您会失去性能和轻松的搜索功能,但恕我直言,这是一个很好的权衡。

由于是 2016 年,忘记 XML!使用 JSON 存储非关系数据包,并使用适当类型的列作为后端。您通常不需要在 bag中按值进行查询,即使许多当代 SQL 数据库本机理解 JSON,这也会很慢。

于 2008-09-15T20:09:12.650 回答
3

创建 2 个数据库

  • DB1 包含静态表,并代表数据的“真实”状态。
  • DB2 对用户来说是免费的——他们(或您)将不得不编写代码来从 DB1 填充他们奇形怪状的表。
于 2008-09-16T15:25:59.343 回答
3

在我看来,您真正想要的是某种“元模式”,一种能够描述用于存储实际数据的灵活模式的数据库模式。动态模式更改是敏感的,不是您想要搞砸的,尤其是在允许用户进行更改的情况下。

您不会找到比其他任何数据库都更适合此任务的数据库,因此您最好的选择就是根据其他标准选择一个。例如,您使用什么平台来托管数据库?应用程序是用什么语言编写的?ETC

为了澄清我所说的“元模式”的意思:

CREATE TABLE data (
    id INTEGER NOT NULL AUTO_INCREMENT,
    key VARCHAR(255),
    data TEXT,

    PRIMARY KEY (id)
);

这是一个非常简单的示例,您可能会有更具体的需求(并且希望更容易使用),但它确实可以说明我的观点。您应该认为数据库模式本身在应用程序级别是不可变的;任何结构更改都应反映在数据中(即,该模式的实例化)。

于 2008-09-15T20:11:07.920 回答
3

我知道问题中指出的模型已在整个生产系统中使用。我工作的一所大型大学/教学机构正在使用相当大的一个。他们专门使用长窄表方法来映射由许多不同的数据采集系统收集的数据。

此外,谷歌最近通过他们的代码站点发布了他们的内部数据共享协议协议缓冲区,作为开源。以这种方法为模型的数据库系统将非常有趣。

检查以下内容:

实体-属性-价值模型

谷歌协议缓冲区

于 2008-09-15T20:14:06.587 回答
2

我知道这是一个古老的话题,但我想它永远不会失去现实。我现在正在开发类似的东西。这是我的方法。我使用 MySQL、Apache、PHP 和 Zend Framework 2 作为应用程序框架的服务器设置,但它应该与任何其他设置一样工作。

这是一个简单的实现指南,您可以从这里进一步发展它。

您需要实现自己的查询语言解释器,因为有效的 SQL 太复杂了。

例子:

select id, password from user where email_address = "xyz@xyz.com"

物理数据库布局:

表“规格”:(应缓存在您的数据访问层中)

  • 编号:整数
  • parent_id:整数
  • 名称:varchar(255)

表“项目”:

  • 编号:整数
  • parent_id:整数
  • spec_id:整数
  • 数据:varchar(20000)

表“规格”的内容:

  • 1, 0, '用户'
  • 2, 1, 'email_address'
  • 3, 1, '密码'

表“项目”的内容:

  • 1, 0, 1, ''
  • 2, 1, 2, 'xyz@xyz.com'
  • 3, 1, 3, '我的密码'

用我们自己的查询语言翻译的例子:

select id, password from user where email_address = "xyz@xyz.com"

标准 SQL 看起来像这样:

select 
    parent_id, -- user id
    data -- password
from 
    items 
where 
    spec_id = 3 -- make sure this is a 'password' item
    and 
    parent_id in 
    ( -- get the 'user' item to which this 'password' item belongs
        select 
            id 
        from 
            items 
        where 
            spec_id = 1 -- make sure this is a 'user' item
            and 
            id in 
            ( -- fetch all item id's with the desired 'email_address' child item
                select 
                    parent_id -- id of the parent item of the 'email_address' item
                from 
                    items 
                where 
                    spec_id = 2 -- make sure this is a 'email_address' item
                    and
                    data = "xyz@xyz.com" -- with the desired data value
            )
    )

您需要将规格表缓存在关联数组或哈希表或类似的东西中,以便从规格名称中获取 spec_id。否则,您将需要插入更多的 SQL 开销来从名称中获取 spec_id,就像在这个片段中一样:

不好的例子,不要使用这个,避免这个,而是缓存规格表!

select 
    parent_id, 
    data 
from 
    items 
where 
    spec_id = (select id from specs where name = "password") 
    and 
    parent_id in (
        select 
            id 
        from 
            items 
        where 
            spec_id = (select id from specs where name = "user") 
            and 
            id in (
                select 
                    parent_id 
                from 
                    items 
                where 
                    spec_id = (select id from specs where name = "email_address") 
                    and 
                    data = "xyz@xyz.com"
            )
    )

我希望您能理解并自己确定该方法对您是否可行。

享受!:-)

于 2014-03-05T19:53:23.437 回答
2

我认为 EAV 方法是最好的方法,但成本很高

于 2011-09-16T13:55:48.933 回答
1

在 c2.com wiki 上,探索了“动态关系”的概念。您不需要 DBA:列和表是 Create-On-Write,除非您开始添加约束以使其更像传统的 RDBMS:随着项目的成熟,您可以逐步“锁定”。

从概念上讲,您可以将每一行视为一个 XML 语句。例如,员工记录可以表示为:

<employee lastname="Li" firstname="Joe" salary="120000" id="318"/>

这并不意味着它必须被实现为 XML,它只是一个方便的概念化。如果您要求一个不存在的列,例如“SELECT madeUpColumn ...”,它将被视为空白或 null(除非添加的约束禁止这样做)。并且可以使用SQL,尽管由于隐含的类型模型而必须小心比较。但除了类型处理之外,动态关系系统的用户会感到宾至如归,因为他们可以利用大部分现有的 RDBMS 知识。现在,如果有人愿意建造它...

于 2017-09-13T16:39:54.660 回答
0

在过去,我选择了选项 C——创建一个“长而窄”的表,将动态列值存储为行,然后需要对其进行透视以创建一个包含特定实体的所有值的“短而宽”行集。. 但是,我使用的是 ORM,这真的让事情变得很痛苦。我想不出你会如何在 LinqToSql 中做到这一点。我想我必须创建一个哈希表来引用这些字段。

@Skliwz:我猜他对允许用户创建用户定义的字段更感兴趣。

于 2008-09-15T20:12:29.333 回答
0

弹性搜索。如果您正在处理可以按日期分区的数据集,您应该考虑它,您可以将 JSON 用于您的数据,并且不固定使用 SQL 来检索数据。

ES 为您发送的任何新 JSON 字段推断您的架构,无论是自动、带有提示,还是手动您可以通过一个 HTTP 命令(“映射”)定义/更改。虽然它不支持 SQL,但它有一些很棒的查找功能甚至聚合。

于 2017-07-01T20:45:45.667 回答
0

我知道这是一篇非常古老的帖子,在过去的 11 年中发生了很大变化,但我想我会添加这个,因为它可能对未来的读者有所帮助。我和我的联合创始人创建 HarperDB 的原因之一是在提供完整索引功能的同时,在单个、不重复的数据集中本地完成动态模式。您可以在此处阅读更多信息:
https ://harperdb.io/blog/dynamic-schema-the-harperdb-way/

于 2020-09-02T17:23:01.517 回答
-5

sql 已经提供了一种更改架构的方法:ALTER 命令。

只需有一个表格,列出不允许用户更改的字段,并为 ALTER 编写一个漂亮的界面。

于 2008-09-15T20:16:13.100 回答