【问题标题】:Dynamic Database Schema [closed]动态数据库模式 [关闭]
【发布时间】:2010-09-09 03:29:31
【问题描述】:

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

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

说明的几种可能性:

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

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

【问题讨论】:

    标签: sql database-design architecture dynamic-data


    【解决方案1】:

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

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

    如果您有一个非常优秀的开发团队,并且非常了解您必须通过这种设计克服的问题,那么您就可以成功地编写出一个设计良好、漏洞不严重的系统。大多数时候。

    为什么一开始就对你不利呢?

    不相信我?谷歌“一个真正的查找表”或“单表设计”。一些好的结果: 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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 这是一种强大的技术,在使用它之前您应该始终犹豫。绝对更喜欢数据库标准化。也就是说,它确实有效,你可以查询数据(虽然它很丑),而且性能还不错。
      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

        我有类似的要求,并决定使用无架构的MongoDB

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

        亮点:

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

        Lowdarks(你需要了解的东西,这样你才能正确使用 mongo):

        【讨论】:

        • 还有符合 ACID 的 RavenDB。
        【解决方案5】:

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

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

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

        优点:

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

        缺点:

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

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

        【讨论】:

          【解决方案6】:

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

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

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

          【讨论】:

            【解决方案7】:

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

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

            澄清我所说的“元模式”是什么意思:

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

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

            【讨论】:

              【解决方案8】:

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

              此外,Google 最近通过其代码站点将其内部数据共享协议 Protocol Buffer 作为开源发布。以这种方法为模型的数据库系统会非常有趣。

              检查以下内容:

              Entity-attribute-value model

              Google Protocol Buffer

              【讨论】:

                【解决方案9】:

                创建 2 个数据库

                • DB1 包含静态表,代表数据的“真实”状态。
                • DB2 可供用户随意使用 - 他们(或您)必须编写代码才能从 DB1 填充其奇形怪状的表。

                【讨论】:

                  【解决方案10】:

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

                  【讨论】:

                  • 这是在 Magento 中完成的。查询繁重且缓慢,但非常灵活。
                  【解决方案11】:

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

                  这是一个简单的实施指南,您可以在此基础上进一步发展。

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

                  例子:

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

                  物理数据库布局:

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

                  • id: int
                  • parent_id: int
                  • 名称:varchar(255)

                  表“项目”:

                  • id: int
                  • parent_id: int
                  • spec_id: int
                  • 数据: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,就像在这个 sn-p 中一样:

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

                  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"
                              )
                      )
                  

                  我希望您能明白这一点,并自己确定该方法对您是否可行。

                  享受吧! :-)

                  【讨论】:

                    【解决方案12】:

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

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

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

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

                    【讨论】:

                      【解决方案13】:

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

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

                      【讨论】:

                        【解决方案14】:

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

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

                        【讨论】:

                          【解决方案15】:

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

                          【讨论】:

                            【解决方案16】:

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

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

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 2011-02-24
                              • 2010-09-12
                              • 1970-01-01
                              • 2010-09-13
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              相关资源
                              最近更新 更多