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

这如何不使varchar2效率低下?

  •  3
  • Moeb  · 技术社区  · 16 年前

    假设我有一张有柱子的桌子 name varchar(20) ,并将一行存储为name=“abcdef”。

    INSERT INTO tab(id, name) values(12, 'abcdef');
    

    内存分配如何 name 在这种情况下完成了吗?

    我可以想到两种方法:

    a)

    分配了20个字节,但只使用了6个。在这种情况下 varchar2 没有任何明显的优势 char ,在内存分配方面。

    b)

    只分配了6个字节。如果是这样的话,我在这之后又加了几行,

    INSERT INTO tab(id, name) values(13, 'yyyy');
    INSERT INTO tab(id, name) values(14, 'zzzz');
    

    然后我做一个更新,

    UPDATE tab SET name = 'abcdefghijkl' WHERE id = 12;
    

    DBMS从何处获取所需的额外6个字节? 可能存在下6个字节不可用的情况(如果最初只分配了6个字节,那么下一个字节可能已分配给其他一些字节)。

    有没有别的办法可以把这一排换成一个新的地方?在索引组织表的情况下,甚至移位也是一个问题(对于堆组织表,这可能是可以的)。

    5 回复  |  直到 16 年前
        1
  •  1
  •   Guffa    16 年前

    根据您使用的RDBMS,可能会有一些变化,但一般来说:

    只有存储在 varchar 字段已分配。大小只是允许的最大值,而不是分配的大小。

    我想应该是这样的 char 在某些系统上也有字段。可变大小的数据类型被有效地处理,以至于在分配最大值时不再有任何收益。

    如果更新一条记录以使其需要更多空间,则会向下移动同一分配块中的记录,如果记录不再适合该块,则会分配另一个块,并在这些块之间分布记录。这意味着记录在分配块内是连续的,但这些块不必在磁盘上是连续的。

        2
  •  1
  •   DevinEllingson    16 年前

    它当然不会分配比需要更多的空间,这将击败使用可变长度类型的点。

    在您提到的情况下,我认为下面的行必须在页面上向下移动,也许这是以某种方式优化的。我真的不知道确切的细节,也许其他人可以进一步评论。

        3
  •  1
  •   szbalint    16 年前

    这可能严重依赖于数据库。

    但有几点: MVCC 观察数据库实际上并不更新磁盘或内存缓存中的数据。它们插入一个包含更新数据的新行,并将旧行标记为已从上的某个事务中删除。一段时间后,删除的行对任何事务都不可见,并且被回收。

    对于空间存储问题,它通常是 1-4 bytes of header + data (+ padding)

    在chars的情况下,数据被填充以达到足够的长度。对于varchar或text,头部存储后面的数据长度。

        4
  •  1
  •   Martin Smith    16 年前

    编辑 出于某种原因,我认为它被标记为Microsoft SQL Server。不过,我认为答案还是有意义的

    这就是为什么 official recommendation

    • 当列数据项的大小一致时,使用char。
    • 当列数据项的大小变化很大时,请使用varchar。
    • 当列数据项的大小不同时,使用varchar(max) 相当大,尺寸可能 超过8000字节。

    在设计表结构时,需要考虑这种权衡。在这个计算中,您可能也需要考虑更新和读取的频率。

    值得注意的是 char NULL 值仍然使用所有存储空间。管理工作室有一个加载项,名为 SQL Internals Viewer 这样您就可以轻松查看行的存储方式。

        5
  •  1
  •   Adam Musch    16 年前

    考虑到问题标题中的varchar2,我认为您的问题集中在Oracle上。在Oracle中,可以使用pctfree子句为数据块内的行扩展保留空间。这有助于减轻更新使行变长的影响。

    但是,如果Oracle在块中没有足够的可用空间来写回行,那么它所做的操作称为行迁移;它将原始地址单独留在磁盘上(因此不必更新索引),但它不会将数据存储在原始位置,而是存储指向该行新地址的指针。

    在索引大量访问表(如果迁移了大量行)的情况下,这可能会导致性能问题,因为它添加了额外的I/O来满足查询。