Oracle - 3 - 【学习笔记】for ocp 12c、行链接和行迁移、表、表分区、数据泵、DBlink、日志信息

文章目录


行链接(Row Chaining)和行迁移(Row Migration)

原文:The Secrets of Oracle Row Chaining and Migration - https://www.akadia.com/services/ora_chained_rows.html
翻译:行链接和行迁移的秘密 - https://blog.csdn.net/leshami/article/details/7266719

其他:

概述(Overview)

如果你的Oracle数据库性能低下(poor performance),行链接(row chaining)和行迁移(row migration)可能是其中的原因之一(one of several reasons)。我们能够通过合理的设计(properly designing)或调整(diagnosing)数据库来阻止(prevent)这个现象。

行链接(row chaining)和行迁移(row migration)是能够被避免的两个潜在性问题(two potential problems)。我们可以通过合理的调整(suitably diagnosing)来提高(improve)数据库性能(database performance)。本文主要描述(the main considerations)的是:

⭐️ 什么是行迁移与行链接?(what is Row Migration & Row Chaining?)
⭐️ 如何判断行迁移与行链接?(How to identify Row Migration & Row Chaining?)
⭐️ 如何避免行迁移与行链接?(How to avoid Row Migration & Row Chaining?)

  • 行迁移(Row Migration) 影响(affect)OLTP系统1,当使用索引读取单行时。
    最糟糕的情形是(In the worst case),对所有读取操作而言,增加了额外的I/O(extra I/O)。
  • 行链接(Row Chaining) 则影响索引读(index reads)和全表扫描(full table scans)。

注:在翻译行(row)时使用记录来描述(便于理解),如第一行,使用第一条记录。

Oralce 块(Oracle Block)

操作系统块的大小(The Operating System Block size)是操作系统读写的最小操作单元(the minimum unit of operation (read/write) by the OS),也是操作系统文件的属性之一(a property of the OS file system)。

当创建一个数据库时(while creating an Oracle database),选择一个基于操作系统块的整数倍大小作为Oracle数据库块的大小the 《Data Base Block Size》 as a multiple of the Operating System Block Size)。Oracle块(《Data Base Block Size》)是Oracle数据库读写操作的最小单位(The minimum unit of operation (read/write) by the Oracle database),而非操作系统块。

一旦设置了(once set)Oracle数据块的大小(the 《Data Base Block Size》),则在整个数据库生命期间不能被更改cannot be changed during the life of the database)(除 Oracle 9i之外(except in case of Oracle 9i))。为了Oracle数据库定制(to decide on)合理的Oralce块大小(a suitable block size for the database),像预期数据库总大小(the size of the database)以及并发用户数(the concurrent number of transactions expected)这些因素(factors)应当予以考虑(take into consideration)。

数据库块逻辑结构(the structure of the database blocks)

数据库块(the database block)由下列逻辑结构(following structure)(在整个数据库结构下(while the whole database structure))

在这里插入图片描述

在这里插入图片描述

头部(header)

头部(header)包含(contains)一些常用信息(the general information about the data),象(i.e.2块地址(block address),段的类型(the type of segments)(表段(table)、索引段(index)等(etc3))。也包含(it also contains)一些表、实际数据行的地址信息等(the information about table and the actual row(address) which that holds the data)。

空闲空间(free space)

用于保留给后续(allocated for future)DML(update/insert)操作(update/insert operations)的空间(Space)。通常(Generally)受(affected by)pctfree和pctused参数的影响(the values of PCTFEE and PCTUSED parameters)。

数据(Data)

实际的数据内容(Actual row data.)

FREELIST, PCTFREE, PCTUSED

当创建或修改任意表,索引时(while creating/altering any table/index)。Oracle使用两个存储参数来控制空间的分配(Oracle used two storage parameters for space control)

  • PCTFREE - 为已存在数据将来 更新(update) 需要保留空闲空间的百分比。该值决定块何时可以到 freelist 结构中
    (the precentage of space reserved for future update of existing data)

  • PCTUSED - 新 插入(insert) 数据可使用空间的最小百分比,该值决定块何时回收到 freelist 结构中
    (The percentage of minimum space used for insertion of new row data. This value determines when the block gets back into the FREELISTS structure.)

    数据使用空间小于PCTUSED,则回到FREELIST

    这个值决定了块的可用状态。可用的块时可以执行插入的块,不可用状态的块只能执行删除和修改,可用状态的块被放在freelist中。

  • FREELIST - Oracle通过维护该列表来记录或更新所有可用的数据块
    (Structure where Oracle maintains a list of all free avaliable blocks.)

三个值的使用过程
  • 当插入一条记录的时候,Oracle 首先在 freelist 列表上搜索可用的空闲数据块(Oracle will first search for a free block in the FREELIST),搜索成功之后将数据插入到那个空闲块(and then the data is inserted into that block)。

  • 块在 free list 列表中的可用性由 pctfree 参数值来决定(the availability of the block in the FREELIST is decided by the PCTFREE value)。起初一个空块在freelist列表上列出(Initially an empty block will be listed int the FREELIST),并且会一直保留(and it will continue to remain there),直到到空闲空间达到pctfree设定的值。(until the free space reaches the PCTFREE value)

    假设插入第一条记录的时候占用一个block的10%的空间(除去block头占去的大小),剩余的自由空间90%大于pctfree20%,因此这个block还将继续为下次的插入操作提供空间。
    在这里插入图片描述在这里插入图片描述

  • 当一个块被使用且达到 pctfree 设定的值之后则该块从 freelist 列表被移除(when the free space reach the PCTFREE value the block is removed from the FREELIST)

    再连续插入七条记录,使block的剩余自由空间剩下20%,此时,这个block将要从free list中移走,如果再插入记录,Oracle将再free list中寻找下一个空余的block去存放后来插入的数据。
    在这里插入图片描述

  • 而当数据块的可用空间低于 PCTUSED 值的时候(when the volume of data in the block comes below the PCTUSED value),该块又会回收,即重新回到freelist列表。(it is re-listed in the FREELIST table )

Oracle使用freelist方式以提高数据库性能(Oracle use FREELIST to increase the performance)。因此,每一个insert 操作(so for every insert operation),Oracle 仅仅需要搜索freelist结构(Oracle needs to search for free blocks only),而不是搜索所有数据块(instead of searching all blocks)。

行迁移(Row Migration)

当一个行上的更新操作(原来的数据存在且没有减少)导致当前的数据不能在容纳在当前块,我们需要进行行迁移。(we will migrate a row when an update to that row would cause it to not fit on the block anymore(with all of the other data that exists there currently)) 一个行迁移意味着整行数据将会移动(the migration meas that the entire row will move),仅仅保留的是一个转移地址(and we will leave behind 《forwarding address》)。因此整行数据都被移动(the entire row is moved),原始的数据块上仅仅保留的是指向新块的一个地址信息(the original row just has the rowid of the new block)。
在这里插入图片描述

迁移行不影响全表扫描(Full Table scans are not affected by migrated rows)

当使用全表扫描时,转移地址被忽略(the forwarding address are ignored)。因为我们最终能够获得所有的数据,所以能够忽略其转移地址(we know that as we continue the full scan,we’ll eventually get to that row so we can ignore the forwarding address and just process the row when we get there)。因此行迁移并不会对全表扫描产生额外的操作。(Hence,in the full scan migrated rows don’t cause to really do any extra work – they are meaningless )

迁移行对索引读产生额外的I/O(Index Read will cause additional IO’s on migrated rows)

当使用索引读取数据时将产生额外的I/O。(when we Index Read into a table,then a migrated row will cause additional IO’s)这是由于索引告诉我们通过文件X,块Y,slot槽Z可以获得该行数据(that is because the index will tell us 《goto file X,block Y,slot Z to find this row》)。但是当我们根据地址信息达到所在位置时(but when we get there we find a message),从其转移地址得知,其真实的数据是存储在文件A,块B,slot槽C上(a message that says 《well,really goto file A,block B,slot C to find this row》)。因此为了寻找该行数据不得不产生额外的逻辑或物理I/O。(we have to do another IO(logical or physical)to find the row)

行链接(row chaining)

当一行数据太大而不能在一个单数据块容纳时(a row is too large to fit into a single database block),行链接由此产生。举例来说,当你使用了4kb的Oracle 数据块大小(for example,if you use a 4kb blocksize for your database),而你需要插入一行数据是8k(and you need to insert a row of 8bk into it),Oracle则需要使用3个数据块分成片来存储(Oracle will use 3 blocks and store the row in pieces)。因此,引起行链接的情形通常是表上行记录的大小超出了数据库Oracle块的大小(some condition that will cause row chaining are Tables whose rowsize exceeds the blocksize)。

  • 表上使用了LONG 或 LONG RAW数据类型的时候容易产生行链接(Tables with LONG and LONG RAW column are pone to having chained rows)。
  • 其次表上多于255列时Oracle会将这些过宽的表分片而产生行链接(Table with more then 255 columns will have chained rows as Oracle break wide tables up into pieces)

因此,与行迁移所不同的是,行迁移是将数据存放到另外一个块,而行链接是数据同时位于多个数据块。(So,instead of just having forwarding address on one block and the data on another we have data on two or more blocks)
在这里插入图片描述

行链接有着不同于行迁移的影响,取决于我们所需的数据。如果我们有一行数据有两个列且跨越两个数据块。假定使用下面的查询:

SELECT column1 FROM table

列1位于第一块,它将不会引起任何table fetch continued row 。由于不需要获得第2列的信息,因此行链接没有任何影响。然而,如果我们使用下面的查询:

SELECT column2 FROM table

由于 column2 在块2且是一个链接行,因此将产生table fetch continued row

示例(Example)

1. 行迁移(row migration)

--下面的引用Tom Kyte的例子来展示行迁移和行链接. 我们使用了一个4k 的 blocksize.                                                                 
                                                                                                                                             
SELECT name,value                                                                                                                          
  FROM v$parameter                                                                                                                         
 WHERE name = 'db_block_size';                                                                                                             
	                                                                                                                                           
NAME                 VALUE                                                                                                                 
--------------      ------                                                                                                                 
db_block_size         4096                                                                                                                 
                                                                                                                                              
                                                                                                                                              
--使用固定列宽的char类型来创建下面的表                                                                                                         
                                                                                                                                             
CREATE TABLE row_mig_chain_demo (                                                                                                            
  x int PRIMARY KEY,                                                                                                                         
  a CHAR(1000),                                                                                                                              
  b CHAR(1000),                                                                                                                              
  c CHAR(1000),                                                                                                                              
  d CHAR(1000),                                                                                                                              
  e CHAR(1000)                                                                                                                               
);                                                                                                                                           
                                                                                                                                             
--上面的这个表很容易产生行链接和行迁移。我们使用了5个列 a,b,c,d,e,以便于整个行的长度增长达到或超过5k以确保产生真实的行链接。                  
                                                                                                                                             
INSERT INTO row_mig_chain_demo (x) VALUES (1);                                                                                               
INSERT INTO row_mig_chain_demo (x) VALUES (2);                                                                                               
INSERT INTO row_mig_chain_demo (x) VALUES (3);                                                                                               
COMMIT;                                                                                                                                      
                                                                                                                                             
--a,b,c,d,e列我们并没有兴趣查看,而仅仅是提取他们。由于他们的确太宽,我们在查询结果中没有显示这些列。                                          
                                                                                                                                             
column a noprint                                                                                                                             
column b noprint                                                                                                                             
column c noprint                                                                                                                             
column d noprint                                                                                                                             
column e noprint                                                                                                                             
                                                                                                                                             
SELECT * FROM row_mig_chain_demo;                                                                                                            
                                                                                                                                             
         X                                                                                                                                   
----------                                                                                                                                   
         1                                                                                                                                   
         2                                                                                                                                   
         3                                                                                                                                   
                                                                                                                                             
--检查行链接                                                                                                                                   
                                                                                                                                             
SELECT a.name, b.value                                                                                                                       
  FROM v$statname a, v$mystat b                                                                                                              
 WHERE a.statistic# = b.statistic#                                                                                                           
   AND lower(a.name) = 'table fetch continued row';                                                                                          
NAME                                                                  VALUE                                                                  
---------------------------------------------------------------- ----------                                                                  
table fetch continued row                                                 0                                                                  
                                                                                                                                              
--现在正如我们所期待的那样,按插入的顺序返回的结果(对这个查询Oracle使用了全表扫描)。也如我们所预料的那样table fetch continued row 为零值。     
--这个数据是现在是如此的小,我们知道三行数据填充到一个数据块,没有行链接。    

在这里插入图片描述

--接下来,我们基于指定的方式做一些更新来演示行迁移的问题以及他如何影响全扫描。                                              
                                                                                                                          
UPDATE row_mig_chain_demo SET a = 'z1', b = 'z2', c = 'z3' WHERE x = 3;                                                   
COMMIT;                                                                                                                   
UPDATE row_mig_chain_demo SET a = 'y1', b = 'y2', c = 'y3' WHERE x = 2;                                                   
COMMIT;                                                                                                                   
UPDATE row_mig_chain_demo SET a = 'w1', b = 'w2', c = 'w3' WHERE x = 1;                                                   
COMMIT;                                                                                                                   
                                                                                                                          
--注意更新的顺序,最后一行我们首先更新,而第一行则最后更新它。                                                              
                                                                                                                          
SELECT * FROM row_mig_chain_demo;                                                                                         
                                                                                                                          
         X                                                                                                                
----------                                                                                                                
         3                                                                                                                
         2                                                                                                                
         1                                                                                                                
                                                                                                                          
SELECT a.name, b.value                                                                                                    
  FROM v$statname a, v$mystat b                                                                                           
 WHERE a.statistic# = b.statistic#                                                                                        
   AND lower(a.name) = 'table fetch continued row';                                                                       
                                                                                                                          
NAME                                                                  VALUE                                               
---------------------------------------------------------------- ----------                                               
table fetch continued row                                                 0                                               
                                                                                                                           
--有趣的是,返回的结果是倒序的。是由于我们首先更新了第三行。此时并没有行迁行,但是它填充了整个数据块1。随着第二行的更新,它 
--不得不迁移到第2块,由于第3行几乎占用了整个块。当我们更新第一行的时候,它迁移到块3。最终使第三行数据保留在第一块。         
                                                                                                                          
--因此,当oralce使用全表扫描时,它首先发现第3条记录在数据块1,第2条记录在数据块2,第1条记录在数据块3。当进行表扫描时,它忽略
--第一块的头部信息是记录1,记录2的rowid信息。这就是为什么table fetch continued row依旧是零值的原因。此时没有行链接。        

在这里插入图片描述

2. 行链接(row chaining)

--让我们来看一看对table fetch continued row的影响                                                                          
                                                                                                                         
SELECT * FROM row_mig_chain_demo WHERE x = 3;                                                                            
                                                                                                                         
         X                                                                                                               
----------                                                                                                               
         3                                                                                                               
                                                                                                                         
SELECT a.name, b.value                                                                                                   
  FROM v$statname a, v$mystat b                                                                                          
 WHERE a.statistic# = b.statistic#                                                                                       
   AND lower(a.name) = 'table fetch continued row';                                                                      
                                                                                                                         
NAME                                                                  VALUE                                              
---------------------------------------------------------------- ----------                                              
table fetch continued row                                                 0                                              
                                                                                                                          
--这是一个索引范围扫描方式,然后根据主键获得rowid。此时并没有增加table fetch continued row的值,因此记录3并没有发生行迁移。
UPDATE row_mig_chain_demo SET d = 'z4', e = 'z5' WHERE x = 3;                                
COMMIT;                                                                                      
                                                                                             
--update之后记录3新增的内容不在填充到数据块1,因为列d和e之和是5k,因此行链接产生了。            
                                                                                             
SELECT x,a FROM row_mig_chain_demo WHERE x = 3;                                              
                                                                                             
         X                                                                                   
----------                                                                                   
         3                                                                                   
                                                                                             
SELECT a.name, b.value                                                                       
  FROM v$statname a, v$mystat b                                                              
 WHERE a.statistic# = b.statistic#                                                           
   AND lower(a.name) = 'table fetch continued row';                                          
                                                                                             
NAME                                                                  VALUE                  
---------------------------------------------------------------- ----------                  
table fetch continued row                                                 1                  
                                                                                             
                                                                                             
--当从记录3的头部提取列x和列a的时候,将不会产生table fetch continued row,也没有额外的I/O产生。

在这里插入图片描述

行链接触发 table fetch continued row 条件

查到行链接的列

SELECT x,d,e FROM row_mig_chain_demo WHERE x = 3;
		
SELECT a.name, b.value
FROM v$statname a, v$mystat b
WHERE a.statistic# = b.statistic#
AND lower(a.name) = 'table fetch continued row';
	
NAME                                                                  VALUE
---------------------------------------------------------------- ----------
table fetch continued row                                                 2
 
 
--现在当我们通过主键索引扫描从记录3的尾部提取数据时,这将增加table fetch continued row的值。因为需要从行的头部和尾部获取数据来组合。
		
--现在来看看全表扫描是否也有相同的影响。
 
SELECT * FROM row_mig_chain_demo;
	
         X
----------
         3
         2
         1
		
SELECT a.name, b.value
  FROM v$statname a, v$mystat b
 WHERE a.statistic# = b.statistic#
   AND lower(a.name) = 'table fetch continued row';
 
NAME                                                                  VALUE
---------------------------------------------------------------- ----------
table fetch continued row                                                 3
 
--此时table fetch continued row的值被增加,因为不得不对记录3的尾部进行融合。而记录1和2即便是存在迁移现象,但由于是全表扫描,
--因此不会增加table fetch continued row的值。
		
SELECT x,a FROM row_mig_chain_demo;
	
         X
----------
         3
         2
         1
		
SELECT a.name, b.value
  FROM v$statname a, v$mystat b
 WHERE a.statistic# = b.statistic#
   AND lower(a.name) = 'table fetch continued row';
	
NAME                                                                  VALUE
---------------------------------------------------------------- ----------
table fetch continued row                                                 3
 
--当需要提取的数据是整个表上的头两列的时候,此时table fetch continued row也不会增加。因为不需要对记录3进行数据融合。
		
SELECT x,e FROM row_mig_chain_demo;
	
         X
----------
         3
         2
         1
		
SELECT a.name, b.value
  FROM v$statname a, v$mystat b
 WHERE a.statistic# = b.statistic#
   AND lower(a.name) = 'table fetch continued row';
		
NAME                                                                  VALUE
---------------------------------------------------------------- ----------
table fetch continued row                                                 4
 
--但是当提取列d和e的时候,table fetch continued row的值被增加。通常查询时容易产生行迁移即使是真正存在行链接,因为我们的查询 
--所需的列通常位于表的前几列(所以通常不会查到产生行链接的列(才怪。。。))。
--聚合统计所创建的表,这也将使得重构整行而发生table fetch continued row                                                             
                                                                                                                                  
SELECT count(e) FROM row_mig_chain_demo;                                                                                          
                                                                                                                                  
  COUNT(E)                                                                                                                        
----------                                                                                                                        
         1                                                                                                                        
                                                                                                                                  
SELECT a.name, b.value                                                                                                            
  FROM v$statname a, v$mystat b                                                                                                   
 WHERE a.statistic# = b.statistic#                                                                                                
   AND lower(a.name) 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

骆言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值