假设我有一个包含列name varchar(20)
的表,并且我存储了一行名为=“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个字节,则可能已分配了下一个字节别的东西)。
除了将行转移到新地方之外,还有其他方法吗?在索引组织表的情况下,即使转换也是一个问题(堆组织表可能没问题)。
答案 0 :(得分:1)
当然不需要分配更多的空间,这样就无法使用变长类型。
在你提到的情况下,我认为下面的行必须在页面上向下移动,也许这会以某种方式进行优化。我真的不知道确切的细节,也许其他人可以进一步评论。
答案 1 :(得分:1)
编辑 出于某种原因,我认为这是标记为Microsoft SQL Server的。我认为答案仍然是相关的,但
的原因
- 当列数据条目的大小一致时使用char。
- 当列数据条目的大小变化很大时使用varchar。
- 当列数据条目的大小变化时,使用varchar(max) 相当大,可能的大小 超过8,000字节。
在设计表结构时需要考虑的是权衡。在这个计算中你可能还需要考虑更新频率和读数
值得注意的是,对于char
,NULL
值仍然使用所有存储空间。 Management Studio中有一个名为SQL Internals Viewer的插件,可让您轻松查看行的存储方式。
答案 2 :(得分:1)
根据您使用的rdbms,可能会有不同的变化,但通常是:
仅分配存储在varchar
字段中的实际数据。大小只是允许的最大值,而不是分配的大小。
我认为在某些系统上也适用于char
字段。可变大小的数据类型处理得足够高,以至于分配最大值不再有任何好处。
如果更新记录以使其需要更多空间,则同一分配块内的记录将向下移动,如果记录不再适合该块,则会分配另一个块并在块之间分配记录。这意味着记录在分配块中是连续的,但块不必在磁盘上连续。
答案 3 :(得分:1)
这可能与数据库有很大关系。
但有几点:MVCC观察数据库实际上并不更新磁盘或内存缓存中的数据。他们插入带有更新数据的新行,并将旧行标记为从某个事务中删除。一段时间后,删除的行对任何交易都不可见,并且它已被回收。
对于空间存储问题,它通常采用1-4 bytes of header + data (+ padding)
在字符的情况下,数据被填充以达到足够的长度。对于varchar或text,标头存储后续数据的长度。
答案 4 :(得分:1)
鉴于问题标题中的VARCHAR2,我认为您的问题主要围绕Oracle。在Oracle中,您可以使用PCTFREE子句为数据块中的行扩展保留空间。这可以帮助减轻更新使行更长的影响。
但是,如果Oracle在块中没有足够的可用空间来写回行,那么它的行为称为行迁移;它将原始地址留在磁盘上(因此它不一定需要更新索引),但它不是将数据存储在原始位置,而是存储指向该行的新地址的指针。
如果已经迁移了大量行,则索引会严重访问表时会导致性能问题,因为它会添加额外的I / O以满足查询。