代码之家  ›  专栏  ›  技术社区  ›  mario

可扩展数据库设计:自动更改表或序列化()字段blob?

  •  1
  • mario  · 技术社区  · 15 年前

    我想要一个适应性强的数据库方案。但是在我的应用程序中仍然使用一个简单的表数据网关,在那里我只传递一个$data[]数组来存储。
    基本列在初始表方案中结算。但是稍后会出现一些元字段(ca 10-20)。我希望在那里有一些灵活性,而不是每次都手动调整数据库,或者更糟的是,仅仅因为新字段而更改应用程序逻辑。
    所以现在有两种选择似乎可行,但又不过分杀伤力。但我不确定可伸缩性或数据库缺陷。

    (1)自动更改表。 每当要保存$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)将()字段序列化为blob。 任何新的/无关的元字段对数据库都可能是不透明的。从实际数据库列中简单地排序虚拟字段很简单。元字段可以序列化为blob/text字段,然后:

    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”列的序列化和解包是一个很小的开销。优点是数据库中不会有任何稀疏填充的列,所以我猜它比自动更改表方法更紧凑,速度更快。
    当然,这种方法可以防止在WHERE或GROUPBY子句中使用任何一个新字段。但我认为,任何可能的元字段(用户代理、作者IP、作者IMG、投票、点击、最后修改等)都不会/不应该在这里使用。

    所以我现在更喜欢“ext”blob方法,即使它是单向的。
    通常如何调用这些列?(查找示例/doc)
    是否将XML序列化用于( 非常 理论上)在数据库查询中?

    调整表方案似乎是一个“更干净”的接口,即使大多数列可能仍然是空的。这对速度有什么影响?mysql/innodb胃可以有多少这样的稀疏varchar字段?
    但最重要的是:是否有任何标准实现?带有 自动的 改变桌子技巧?存储一个简单的列列表似乎是可行的,但是像pdo::getcolumnmeta这样的东西会更健壮。

    1 回复  |  直到 15 年前
        1
  •  4
  •   Dal Hundal    15 年前

    在你提出的两个想法中,我会选择第二个。第一个让我想哭,别跟它走。

    如果您确定不需要基于元字段进行查询,那么序列化是存储它们的一种完全有效的方法。

    还有第三种更可取的解决方案,您似乎还没有确定——即使用透视表。使用原始表,然后使用类似以下模式的第二个表:

    metaid   metaname   metavalue
    1        colour     red
    2        texture    rough
    

    然后,第三个“枢轴”表将两者联系起来。

    tbl1_id    metaid
    1          1
    2          2
    

    这样,就没有稀疏填充的列,您可以根据元数据进行查询。