提问人:mario 提问时间:5/25/2010 更新时间:5/25/2010 访问量:618
可扩展的数据库设计:自动 ALTER TABLE 还是 serialize() 字段 BLOB?
extensible database design: automatic ALTER TABLE or serialize() field BLOB?
问:
我想要一个适应性强的数据库方案。但仍然在我的应用程序中使用一个简单的表数据网关,我只是传递一个 $data[] 数组进行存储。
基本列在初始表方案中结算。然而,稍后会出现几个元场(大约 10-20 年)。我想要一些灵活性,而不是每次都手动调整数据库,或者更糟糕的是,仅仅因为新字段而更改应用程序逻辑。
因此,现在有两种选择似乎可行,但又不过分。但我不确定可扩展性或数据库的缺点。
(1) 自动 ALTER TABLE。每当要保存 $data 数组时,都会将键与当前数据库列进行比较。在将$data插入到表中之前定义新列。实际上在测试代码中看起来很简单:
function save($data, $table="forum") {
// columns
if ($new_fields = array_diff(array_keys($data), known_fields($table))) {
extend_schema($table, $new_fields, $data);
}
// save
$columns = implode("`, `", array_keys($data));
$qm = str_repeat(",?", count(array_keys($data)) - 1);
echo ("INSERT INTO `$table` (`$columns`) VALUES (?$qm);");
function known_fields($table) {
return unserialize(@file_get_contents("db:$table")) ?: array("id");
function extend_schema($table, $new_fields, $data) {
foreach ($new_fields as $field) {
echo("ALTER TABLE `$table` ADD COLUMN `$field` VARCHAR;");
由于它主要是元信息字段,因此像 VARCHAR 一样添加它们似乎就足够了。反正没人会问他们。因此,数据库实际上只是用作此处的存储。
但是,虽然我可能想在旅途中添加许多新的$data字段,但它们并不总是被填充。
(2) serialize() 字段转换为 BLOB。任何新的/无关的元字段对数据库来说都是不透明的。简单地从真实数据库列中整理出虚拟字段很简单。元字段可以简单地序列化()到一个 blob/文本字段,然后:
function ext_save($data, $table="forum") {
$db_fields = array("id", "content", "flags", "ext");
// disjoin
foreach (array_diff(array_keys($data),$db_fields) as $key) {
$data["ext"][$key] = $data[$key];
unset($data[$key]);
}
$data["ext"] = serialize($data["ext"]);
在读取查询上反序列化和解压缩此“ext”列是一个很小的开销。优点是数据库中不会有任何稀疏填充的列,所以我想它比 AUTO ALTER TABLE 方法更紧凑、更快。
当然,此方法可防止在 WHERE 或 GROUP BY 子句中使用新字段之一。但我认为任何可能的元字段(user_agent、author_ip、author_img、投票、命中率、last_modified等)都不会/应该在那里使用。
因此,我目前更喜欢“ext”blob 方法,即使它是单程票。
通常如何调用此类列?(寻找示例/文档)
您会将 XML 序列化用于(非常理论化的)数据库内查询吗?
适应表方案似乎是一个“更干净”的界面,即使大多数列可能保持为空。这对速度有何影响?MySQL/innodb 可以容纳多少个这样的稀疏 VARCHAR 字段?
但最重要的是:是否有任何标准实现?具有自动 ALTER TABLE 技巧的伪 ORM?存储一个简单的列列表似乎是可行的,但像 pdo::getColumnMeta 这样的东西会更健壮。
答:
在你提出的两个想法中,我会选择第二个。第一个让我想哭,不要随波逐流。
如果您确定不需要基于元字段进行查询,那么序列化是存储它们的一种非常有效的方式。
还有第三种更可取的解决方案,您似乎还没有确定 - 那就是使用数据透视表。拥有原始表,然后是具有如下架构的第二个表:
metaid metaname metavalue
1 colour red
2 texture rough
然后,第三个“数据透视表”将两者链接起来
tbl1_id metaid
1 1
2 2
这样,就没有稀疏填充的列,并且您可以保留基于元数据进行查询的能力。
评论