Ein paar - relativ ungeordnete - Beobachtungen zum Intra-Block Row Chaining. Zunächst: worum handelt es sich dabei überhaupt? Im Abschnitt Row Format and Size des Concept Guides findet sich der Hinweis: Oracle Database can only store 255 columns in a row piece. Thus, if you insert a row into a table that has 1000 columns, then the database creates 4 row pieces, typically chained over multiple blocks." Das Wort "typically" deutet dabei schon an, dass mehrere row pieces durchaus auch in einem einzigen Block gespeichert werden können. Dazu ein kleines (und gekürztes) Beispiel:
-- Anlage einer Test-Tabelle mit 1000 Spalten in einem MSSM-Tablespace create table test_chaining ( col_1 number , col_2 number , col_3 number ... , col_998 number , col_999 number , col_1000 number ) tablespace test_ts; -- Insert eines einzelnen Datensatzes insert into test_chaining values ( 1 , 2 , 3 ... , 998 , 999 , 1000 );
Also eine Tabelle mit 1000 Spalten - mehr sind nicht möglich: "ORA-01792: Höchstzahl für Spalten in einer Tabelle oder einer View ist 1000" - und einem einzigen Datensatz. Zu diesem Satz ermittle ich nun den zugehörigen Block, den ich anschließend per Dump ausgeben lasse:
select dbms_rowid.rowid_relative_fno(rowid) file_nr , dbms_rowid.rowid_block_number(rowid) block_nr from test_chaining; alter system dump datafile 7 block 1414;
Der erstellte Block-Dump enthält (unter anderem) folgende Informationen (der Beginn des Dumps und die col-Listen sind gekürzt):
Start dump data blocks tsn: 8 file#:7 minblk 1414 maxblk 1414 ... tsiz: 0x1f68 hsiz: 0x1a pbl: 0x0c408294 76543210 flag=-------- ntab=1 nrow=4 frre=-1 fsbo=0x1a fseo=0x1017 avsp=0xffd tosp=0xffd 0xe:pti[0] nrow=4 offs=0 0x12:pri[0] offs=0x1b6c 0x14:pri[1] offs=0x176a 0x16:pri[2] offs=0x1367 0x18:pri[3] offs=0x1017 block_row_dump: tab 0, row 0, @0x1b6c tl: 1020 fb: -----L-- lb: 0x1 cc: 255 col 0: [ 3] c2 08 2f col 1: [ 3] c2 08 30 col 2: [ 3] c2 08 31 col 3: [ 3] c2 08 32 ... col 252: [ 3] c2 0a 63 col 253: [ 3] c2 0a 64 col 254: [ 2] c2 0b tab 0, row 1, @0x176a tl: 1026 fb: -------- lb: 0x1 cc: 255 nrid: 0x01c00586.0 col 0: [ 3] c2 05 5c col 1: [ 3] c2 05 5d col 2: [ 3] c2 05 5e col 3: [ 3] c2 05 5f ... col 252: [ 3] c2 08 2c col 253: [ 3] c2 08 2d col 254: [ 3] c2 08 2e tab 0, row 2, @0x1367 tl: 1027 fb: -------- lb: 0x1 cc: 255 nrid: 0x01c00586.1 col 0: [ 3] c2 03 25 col 1: [ 3] c2 03 26 col 2: [ 3] c2 03 27 col 3: [ 3] c2 03 28 ... col 252: [ 3] c2 05 59 col 253: [ 3] c2 05 5a col 254: [ 3] c2 05 5b tab 0, row 3, @0x1017 tl: 848 fb: --H-F--- lb: 0x1 cc: 235 nrid: 0x01c00586.2 col 0: [ 2] c1 02 col 1: [ 2] c1 03 col 2: [ 2] c1 04 col 3: [ 2] c1 05 ... col 232: [ 3] c2 03 22 col 233: [ 3] c2 03 23 col 234: [ 3] c2 03 24 end_of_block_dump End dump data blocks tsn: 8 file#: 7 minblk 1414 maxblk 1414
Offensichtlich enthält der Block also 4 row pieces, von denen die ersten drei jeweils 255 Spalten umfassen, während das vierte nur 235 Spalten enthält. Interessant ist dabei auch, dass dieses vierte Stück offenbar die ersten Spalten ab col_1 enthält (was man am Inhalt c1 02 => 1 zu erkennen ist). Hemant Chitale hat vor einigen Jahren zwei Artikel zum Thema in seinem Blog veröffentlicht und dort auch ein paar Beobachtungen zu den zugehörigen Angaben in v$sesstat (bzw. v$mystat) vermerkt. Außerdem findet sich dort ein Verweis auf einen Oracle-L thread, in dem die Herren Poder und Antognini wichtige Ergänzungen liefern. Und wenn ich schon dabei bin hier noch ein paar Links:
- Jonathan Lewis: Analyze this! liefert Informationen zum CHAIN_CNT, der migrated und chained rows umfasst, aber intra-row-chaining nicht vermerkt; nach einem ANALYZE TABLE test_chaining COMPUTE STATISTICS; bleibt der CHAIN_CNT = 0, was insofern plausibel ist, da die Verkettung nicht block-übergreifend ist
- Tanel Poder: Detect chained and migrated rows in Oracle – Part 1; einen Part 2 habe ich nicht gefunden ...; darin wird die Semantik der Statistiken table fetch by rowid ("how many times Oracle took a ROWID (for example from an index) and went to a table to lookup the actual row") und table fetch continued row ("when we didn’t find all that we wanted from the original row piece and had to follow a pointer to the new location of the migrated row (or next row piece of a chained row)") erläutert.
Ausgehend von den Ausführungen des Herrn Poder noch ein kleiner Versuch:
select col_1 from TEST_CHAINING;select col_500 from TEST_CHAINING;
select col_1000 from TEST_CHAINING; -- v$sesstat: NAME COL_1 COL_500 COL_1000 -------------------------------------- ----- ------- -------- session logical reads 10 12 13 consistent gets from cache 10 12 13 consistent gets 10 12 13 consistent gets from cache (fastpath) 7 9 10 table scan blocks gotten 5 5 4 no work - consistent read gets 5 7 8 table scan rows gotten 4 4 4 buffer is not pinned count 2 4 5 table fetch by rowid 1 1 1 table scans (short tables) 1 1 1
Daraus ziehe ich im Moment nur zwei Schlüsse:
- Intra-Block Row Chaining wird nicht als table fetch continued row vermerkt (ist also auch in dieser Perspektive kein "echtes" Chaining)
- die erforderliche Arbeit unterscheidet sich für den Zugriff auf die erste, eine mittlere bzw. die letzte Spalte der Tabelle deutlich - und sie erhöht sich für weiter hinten liegende Spalten
Ich gebe zu: mal wieder mangelt es meinen Ausführungen an Struktur. Vielleicht sollte ich doch mal dazu übergehen meine Gedanken zu ordnen, ehe ich etwas schreibe...
Keine Kommentare:
Kommentar veröffentlichen