如何将站点范围的设置存储在数据库中?
Posted
技术标签:
【中文标题】如何将站点范围的设置存储在数据库中?【英文标题】:How to store site wide settings in a database? 【发布时间】:2011-10-17 03:38:06 【问题描述】:我正在讨论为 Web 应用程序存储站点范围设置的三种不同方法。
一个键/值对查找表,每个键代表一个设置。
优点易于实施 缺点对个别设置没有限制单行设置表。
优点 每个设置的默认值和约束 缺点 - 大量设置意味着大量列。不确定 Postgres 是否会有问题只需硬编码,因为设置不会经常更改。
优点易于设置和添加更多设置。 缺点更难改变想好要走哪条路了?
【问题讨论】:
【参考方案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】:我已经按照您描述的方式使用了键/值对查找表,结果很好。
作为一个额外的好处,该表有一个“配置名称”列,它提供了一种简单的方法来选择/激活特定的组配置设置。这意味着prod
、dev
和test
都可以存在于同一个表中,尽管由应用程序来选择使用哪个集合。在我们的例子中,JVM 参数是有意义的。在同一个数据库表中存储不同的“组”配置设置可能是有意义的;再说一次,它可能不会。
如果您正在考虑基于文件的配置,我喜欢INI 或YAML。您仍然可以将其存储在数据库中,尽管您可能找不到 INI 或 YAML 列类型(就像 XML 一样)。
【讨论】:
+1 键/值对我来说是我第一次使用它时感觉最好的方式。而且重量也很轻。 查看 hstore contrib 模块,它使存储键值对变得非常简单。或者,如果您在访问数据库时不需要访问它的位,您也可以序列化整个对象。 IE。如果您要提取整个用户会话对象,只需序列化整个对象,然后将其存储为一个大文本块。【参考方案7】:如果我必须选择,我会选择第一个选项。可以根据需要轻松添加/删除行。而单行可能最终会成为一场噩梦,并且可能扩展性要差得多。对于选项 3:将来您可能会后悔对设置进行硬编码,因此您绝对不想陷入困境。
虽然您没有列出作为选项,但 XML 是否可用?它易于设置,并为您提供更多选择,因为您可以在设置中嵌套设置。
【讨论】:
MEH @XML。键值对是可行的方法,而 XML 是一种愚蠢的方法。对于基于文件的配置,INI 或 YAML 更有意义。 @Matt:“傻”可能有点多。如果您的值开始包含内部结构(并且很有可能最终会包含),那么 XML 是一个合理的选择;如果您碰巧有一堆基于 XML 的其他东西,那就更是如此。我也不是 XML 的忠实粉丝,但这并不会使 XML 变得愚蠢。【参考方案8】:我会选择第一个选项——键/值对查找表。在我看来,这是最灵活和可扩展的解决方案。如果您担心在这里和那里运行许多查询以检索各种配置值的成本,您总是可以实现某种缓存,例如一次将整个表加载到内存中。除了键和值之外,您还可以添加“描述”和“默认值”等列,并构建一个通用的配置编辑器,在屏幕上显示描述等,以帮助用户编辑配置价值观。
我见过一些带有单行配置表的商业应用程序,虽然我没有直接的开发经验,但它的可扩展性和阅读难度都大大降低。
【讨论】:
【参考方案9】:选择#1。如果您想要基于简单类型的约束,那么除了将简单的字符串作为值之外,还可以添加一个日期和数字字段。各个属性将“知道”他们想要什么值。没有理由了解它的所有元数据。
【讨论】:
以上是关于如何将站点范围的设置存储在数据库中?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 url 重写将位置数据存储在我的 asp.net 站点上所有页面的 url 中?
如何将CACTI导出的CSV数据变成5分钟取次值(时间范围是1个月)
如何更改 Google Compute Engine 服务帐户的范围以将数据写入 Google Cloud Storage 存储桶?