开发提出需求,要向一张已经包含100多个字段的表再新增字段,技术上可行,但是这种操作,究竟有何副作用?


这里就引出了行迁移和行链接这两个容易混淆的概念,整理一下,算是让自己重新认识下。


《Concept》对这两个概念的解释:

当第一次向表中插入行,由于行太长,不能存储在一个数据块中时,就会发生行链接,此时,数据会被拆成2个或者多个部分,存储在多个数据块中,这些数据块会构成链式结构,因此叫做行链接。下图就是行链接,左边的数据块,存储了插入数据的第一部分,以及行片段指针,右边的数据块存储了行数据的第二个部分,两个数据块,通过链条关联。例如2K的数据块,行中包含LONG、LONG RAW、VARCHAR2(4000)这种大字段,或者一张表有很多的字段,在这些场景下,行链接是不可避免的。另外,11g下一个行片段中只能包含255个列,插入一个1000个字段的行,就会被分为4个片段存储在不同的数据块中,通过链条关联,

混淆的行迁移和行链接_数据


每张表都会有pctfree这个参数,指定了数据块中为更新操作预留的空间百分比,默认是10,当数据块的可用空间低于10%的时候,就不能插入,只能更新了。更新一条已存在的行,当所在的数据块没有足够的空间容纳,就会发生行迁移。此时,该数据块只会存储一个新块的rowid,这个新块则包含了原始行的数据,为了避免rowid改变导致查询出错,因此原始行rowid不变,该行原始空间的剩余空间不再被数据库使用,可以说这是表产生碎片的主要原因,一定意义上,表产生碎片是不可避免的,

混淆的行迁移和行链接_字段_02


当更新的记录导致记录大于一个数据块时,就会同时发生行迁移和行链接,因此行迁移是一种特殊的行链接。


行迁移对于全表扫描,没影响,因为第一个数据块只有rowid,没有数据,所以会被跳过,只会扫描第二个数据块,但是使用rowid的扫描,需要读取迁移前(迁移后数据块的rowid)和迁移后(行数据)的两个数据块,如果表中有大量的行迁移,就需要消耗更多IO完成数据的检索。同时,行锁开销会增加,因为每个行片段,都需要持有锁的。


从现象上看,发生行链接和行迁移,可能导致INSERT、UPDATE和通过索引执行的SELECT操作缓慢,原因就是需要消耗更多的IO,读取更多数据块。对于行迁移,可以考虑表的pctfree是否需要调整,数据块大小是否需要调整。对于行链接,可能需要考虑表的设计,是真的合理?如果业务上是,这种情况,就不可避免了。


其实无论如何设计表,行链接和行迁移,或许都可能发生,此时就看多消耗的这些IO,以及锁的开销,能不能成为性能问题的主要矛盾了。