Mysql 数据库内部:如何处理可变大小字段?

Mysql 数据库内部:如何处理可变大小字段?,mysql,database,postgresql,Mysql,Database,Postgresql,可变大小字段似乎可能会导致性能问题 为了具体起见,让我们假设我们使用的是关系数据库。假设一个关系有一个可变长度的文本字段。如果更新关系中的元组会增加可变长度字段的大小,会发生什么情况?在线记录编辑(即,在线编辑包含该记录的文件)需要在同一物理页面上的其他元组之间来回移动——可能会将一些元组踢出 我知道不同的DBMS处理这个问题的方式不同,但我很好奇一些常见的做法是什么。在我看来,最好的方法是简单地将现有的元组标记为已删除,然后创建一个全新的元组。对于PostgreSQL,下面提供了一些相关信息:

可变大小字段似乎可能会导致性能问题

为了具体起见,让我们假设我们使用的是关系数据库。假设一个关系有一个可变长度的文本字段。如果更新关系中的元组会增加可变长度字段的大小,会发生什么情况?在线记录编辑(即,在线编辑包含该记录的文件)需要在同一物理页面上的其他元组之间来回移动——可能会将一些元组踢出


我知道不同的DBMS处理这个问题的方式不同,但我很好奇一些常见的做法是什么。在我看来,最好的方法是简单地将现有的元组标记为已删除,然后创建一个全新的元组。

对于PostgreSQL,下面提供了一些相关信息:

提示:这三种类型[varchar(n)/字符变化(n)、char(n)/charachter(n)、text]之间没有性能差异,除了在使用空白填充类型时增加存储空间,以及在存储到长度受限列时检查长度的几个额外CPU周期之外。虽然字符(n)在其他一些数据库系统中有性能优势,但在PostgreSQL中没有这样的优势;事实上,字符(n)通常是三个字符中速度最慢的,因为它需要额外的存储成本。在大多数情况下,应改为使用文本或字符

由于数据完整性要求和事务处理(MVCC),我假设“在线记录编辑”永远不会发生

关于事务处理有一些(相当旧的)信息:

我们必须存储每行的多个版本。只有在元组被提交为已删除状态的时间足够长,以至于活动事务无法再看到它之后,才能删除元组

“视情况而定”。每个实现都是不同的,实际上都有自己的小册子。(我真的应该投票表决这个问题而不是回答它,但我想我会尽力帮忙,我不能把这个简短到足以发表评论的程度)


对于PostgreSQL,请阅读,以及。要了解更多信息,请开始阅读代码,许多关键标题和源文件都有很好的详细注释来解释低级操作

浓缩版,您可能需要阅读上述参考资料才能理解:

PostgreSQL在更新期间从不覆盖元组。它总是将其写入新位置。如果该位置位于同一物理页上,并且没有更改索引,则可以避免索引更新,但它始终会对新元组执行堆写入。它设置旧元组的xmax值和新元组的xmin,以便事务只能看到其中一个。有关详细信息,请参阅并发和mvcc文档

可变长度值可以存储为内联或外联(TOAST)。如果它以内联方式存储在堆上的元组中(这是小值的默认值),那么当您更新记录时(无论是更新该字段还是其他字段),数据将被复制到新元组,就像固定长度数据一样。如果它存储在TOAST边表中,那么如果它未被修改,则会复制指向它的指针,但其自身的值不会被复制。如果它被越行存储和修改,那么将向TOAST表中写入该值的新记录,并在新保存的堆元组中为新值存储指向它的新指针

后来,真空出现并标记过时的元组,释放空间并允许它们被覆盖

因为PostgreSQL必须保留旧数据,使其对旧事务可见,所以它永远不能进行就地修改

从理论上讲,可以将旧数据放在其他地方,然后覆盖它—Oracle就是这样做的,它有撤销和重做日志—但PostgreSQL不是这样做的。这样做会带来不同的复杂性和权衡,解决问题和创造其他问题

(无覆盖规则的唯一例外是pg_largeobject,它使用一种基于片的写上复制,允许对大文件(如数据块)进行事务性更新,而无需复制整个文件。哦,你可以说,
SEQUENCE
s也会被覆盖。还有一些全表锁操作。)



其他RDBMS以不同的方式工作。有些甚至支持多种模式。例如,MySQL使用MyISAM表(就地写入,AFAIK)和InnoDB(MVCC写入时拷贝)。Oracle具有撤消和重做日志—它将旧数据复制到离线存储,然后执行就地更新。其他DBMS无疑又不同了。

“它设置旧元组的xmin值和新元组的xmax”。事务将设置旧的xmax值和新的xmin值,对吗?@Jayadevan Er。。是 啊固定的。