【问题标题】:How to store site wide settings in a database?如何将站点范围的设置存储在数据库中?
【发布时间】:2011-10-17 03:38:06
【问题描述】:

我正在讨论为 Web 应用程序存储站点范围设置的三种不同方法。

一个键/值对查找表,每个键代表一个设置。

  • 优点易于实施
  • 缺点对个别设置没有限制

单行设置表。

  • 优点 每个设置的默认值和约束
  • 缺点 - 大量设置意味着大量列。不确定 Postgres 是否会有问题

只需硬编码,因为设置不会经常更改。

  • 优点易于设置和添加更多设置。
  • 缺点更难改变

想好要走哪条路了?

【问题讨论】:

    标签: database postgresql web-applications


    【解决方案1】:

    按照 Mike 的想法,这里有一个脚本来创建一个表来保存键/值对。这集成了一种机制(约束)来检查最小值/最大值/非空值是否正常,并且它还自动创建一个函数 fn_setting_XXXX() 以快速获取相应设置的值(正确转换)。

    CREATE TABLE settings
    (
       id serial,
       name varchar(30),
       type regtype,
       value text,
       v_min double precision,
       v_max double precision,
       v_not_null boolean default true,
       description text,
       constraint settings_pkey primary key(id),
       constraint setting_unique unique(name),
       constraint setting_type check (type in ('boolean'::regtype, 'integer'::regtype, 'double precision'::regtype, 'text'::regtype))
    );
    
    /* constraint to check value */
    
    ALTER TABLE settings
      ADD CONSTRAINT check_value
      CHECK (
            case when type in ('integer'::regtype,'double precision'::regtype) then
                case when v_max is not null and v_min is not null then
                    value::double precision <= v_max and value::double precision >= v_min
                when v_max is not null then
                    value::double precision <= v_max 
                when v_min is not null then
                    value::double precision >= v_min
                else
                    true
                end
            else
                true
            end
        and
            case when v_not_null then
                value is not null
            else
                true
            end
        );
    
    /* trigger to create get function for quick access to the setting */
    
    CREATE OR REPLACE FUNCTION ft_setting_create_fn_get() RETURNS TRIGGER AS
        $BODY$
        BEGIN
            IF TG_OP <> 'INSERT' THEN
                EXECUTE format($$DROP FUNCTION IF EXISTS fn_setting_%1$I();$$, OLD.name);
            END IF;
            IF TG_OP <> 'DELETE' THEN
                EXECUTE format($$
                    CREATE FUNCTION fn_setting_%1$I() RETURNS %2$s AS
                        'SELECT value::%2$s from settings where name = ''%1$I''' language sql
                $$, NEW.name, NEW.type::regtype );
            END IF;
            RETURN NEW;
        END;
        $BODY$
        LANGUAGE plpgsql;
    
    
    CREATE TRIGGER tr_setting_create_fn_get_insert
        BEFORE INSERT OR DELETE ON settings
        FOR EACH ROW
        EXECUTE PROCEDURE ft_setting_create_fn_get();
    COMMENT ON TRIGGER tr_setting_create_fn_get_insert ON settings IS 'Trigger: automatically create get function for inserted settings';
    
    CREATE TRIGGER tr_setting_create_fn_get_update
        BEFORE UPDATE OF type, name ON settings
        FOR EACH ROW
        WHEN ( NEW.type <> OLD.type OR  OLD.name <> NEW.name)
        EXECUTE PROCEDURE ft_setting_create_fn_get();
    COMMENT ON TRIGGER tr_setting_create_fn_get_update ON settings IS 'Trigger: automatically create get function for inserted settings';
    

    【讨论】:

      【解决方案2】:

      由于您的问题被标记为 database/sql,我认为您可以毫无问题地访问 sql 表以查找和管理设置...在这里猜测,但我将从如下表开始:

      settingName  value   can_be_null   minvalue maxvalue  description
      TheAnswer      42       no            1       100     this setting does...
      ...
      

      如果您考虑管理大量设置,您需要关于每一项设置的更多信息,而不仅仅是它们的当前值。

      【讨论】:

        【解决方案3】:

        我之前使用了一种混合方法,其中我将所有设置(不太可能更改)放入一个单独的 PHP 文件中。单个设置(可能会更改)作为键/值对。这样我可以减少数据库中的条目,从而减少我的整体查询时间,这也有助于我保持较小的密钥大小。

        【讨论】:

          【解决方案4】:

          混合方法是最好的。您必须考虑哪种设置最适合每种设置 - 这在很大程度上归结为谁将更改每个站点范围的设置。

          如果您有一个开发服务器和一个实时服务器,那么如果它们仅在数据库中,那么添加新的应用程序设置可能会很尴尬。您要么需要在更新代码之前更新数据库,要么让所有代码处理设置不可用的情况。显然,一个常见的站点范围设置是数据库名称,它不能存储在数据库中!

          您可以轻松地在测试和实际环境中使用不同的设置。在此之前,我已将设置从数据库中移到文本文件中。

          我建议在“硬编码”文件中设置默认值,然后可能会被键/值对查找表覆盖。

          因此,您无需更改存储在数据库中的设置即可推送新代码。

          如果有不同数量的值,或者总是同时更改的值,我会将这些值存储为 JSON 或其他序列化形式。

          【讨论】:

            【解决方案5】:

            我包括使用一个单独的 PHP 脚本,只包含设置:

            $datatables_path = "../lib/dataTables-1.9.4/media";
            $gmaps_utils_dir =  "../lib/gmaps-utils";
            $plupload_dir = "../lib/plupload-1.5.2/js";
            $drag_drop_folder_tree_path = "../lib/dhtmlgoodies/drag-drop-folder-tree2";
            
            $lib_dir = "../lib";
            $dbs_dir = "../.no_backup/db";
            
            $amapy_api_registration_id = "47e5efdb-d13b-4487-87fc-da7920eb6618";
            $google_maps_api_key = "ABQIABBDp7qCIMXsNBbZABySLejWiBSmGz7YWLno";
            

            所以这是您的第三个变体。

            我实际上并没有发现更改这些值有什么困难;事实上,这是管理这些设置的最简单的方法。这不是您希望您的用户(具有不同角色)通过 Web 界面更改的那种数据。 PHPMyAdmin 和 Joomla 等产品很乐意使用这种方法。

            【讨论】:

              【解决方案6】:

              我已经按照您描述的方式使用了键/值对查找表,结果很好。


              作为一个额外的好处,该表有一个“配置名称”列,它提供了一种简单的方法来选择/激活特定的配置设置。这意味着proddevtest 都可以存在于同一个表中,尽管由应用程序来选择使用哪个集合。在我们的例子中,JVM 参数是有意义的。在同一个数据库表中存储不同的“组”配置设置可能是有意义的;再说一次,它可能不会。


              如果您正在考虑基于文件的配置,我喜欢INIYAML。您仍然可以将其存储在数据库中,尽管您可能找不到 INI 或 YAML 列类型(就像 XML 一样)。

              【讨论】:

              • +1 键/值对我来说是我第一次使用它时感觉最好的方式。而且重量也很轻。
              • 查看 hstore contrib 模块,它使存储键值对变得非常简单。或者,如果您在访问数据库时不需要访问它的位,您也可以序列化整个对象。 IE。如果您要提取整个用户会话对象,只需序列化整个对象,然后将其存储为一个大文本块。
              【解决方案7】:

              如果我必须选择,我会选择第一个选项。可以根据需要轻松添加/删除行。而单行可能最终会成为一场噩梦,并且可能扩展性要差得多。对于选项 3:将来您可能会后悔对设置进行硬编码,因此您绝对不想陷入困境。

              虽然您没有列出作为选项,但 XML 是否可用?它易于设置,并为您提供更多选择,因为您可以在设置中嵌套设置。

              【讨论】:

              • MEH @XML。键值对是可行的方法,而 XML 是一种愚蠢的方法。对于基于文件的配置,INI 或 YAML 更有意义。
              • @Matt:“傻”可能有点多。如果您的值开始包含内部结构(并且很有可能最终会包含),那么 XML 是一个合理的选择;如果您碰巧有一堆基于 XML 的其他东西,那就更是如此。我也不是 XML 的忠实粉丝,但这并不会使 XML 变得愚蠢。
              【解决方案8】:

              我会选择第一个选项——键/值对查找表。在我看来,这是最灵活和可扩展的解决方案。如果您担心在这里和那里运行许多查询以检索各种配置值的成本,您总是可以实现某种缓存,例如一次将整个表加载到内存中。除了键和值之外,您还可以添加“描述”和“默认值”等列,并构建一个通用的配置编辑器,在屏幕上显示描述等,以帮助用户编辑配置价值观。

              我见过一些带有单行配置表的商业应用程序,虽然我没有直接的开发经验,但它的可扩展性和阅读难度都大大降低。

              【讨论】:

                【解决方案9】:

                选择#1。如果您想要基于简单类型的约束,那么除了将简单的字符串作为值之外,还可以添加一个日期和数字字段。各个属性将“知道”他们想要什么值。没有理由了解它的所有元数据。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2020-10-18
                  • 1970-01-01
                  • 2012-02-19
                  • 2012-09-24
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多