Thomas Kejser hat vor ein paar Tagen ein paar Bemerkungen zu den Wirkungen von Page-Compression auf die Performance von Selects und Updates aufgeschrieben. Hier noch mal eine kurze Definition der Page-Compression, basierend auf den Aussagen der Dokumentation - demnach besteht Page Compression aus drei Teilen:
- row compression: verwendet den kleinsten geeigneten Datentyp zur Speicherung der Daten
- prefix compression: wiederholte prefix Werte (einzelner Spalten) werden in einer compress information (CI) Struktur direkt hinter dem page header eingefügt und an den passenden Stellen referenziert
- dictionary compression: wiederholte Werte der page werden in die CI area eingefügt und an den passenden Stellen referenziert (wobei sich diese Ersetzung Spalten-übergreifend erfolgt)
Das Verfahren weist demnach recht massive Ähnlichkeiten zu Oracles compression auf und stellt ebenfalls eine Deduplication durch Ersetzung wiederholter Token dar (wobei Oracle im Fall von Tabellen keine prefix compression durchführt).
Beim Herrn Kejser wird eine recht große Tabelle ("the TPC-H LINEITEM table at scale factor 10. That is about 6.5GB of data.") gelesen bzw. per update geändert und im Ergebnis sind die Operationen auf dem komprimierten Objekt deutlich langsamer als die auf einer entsprechenden Tabelle ohne Komprimierung. Das Fazit lautet:
Ein ähnlicher Versuch mit einer Oracle 11.1.0.7-Test-Datenbank liefert mir ein verwandtes Ergebnis:Quoting a few of my tests from my presentation, I have shown you that PAGE compression carries a very heavy CPU cost for each page access. Of course, not every workload is dominated by accessing data in pages – some are more compute heavy on the returned data. However, in these days when I/O bottlenecks can easily be removed, it is worth considering if the extra CPU cycles to save space are worth it.It turns out that it is possible to also show another expected results: that locks are held longer when updating compressed pages (Thereby limiting scalability if the workload contains heavily contended pages).
drop table t1; create table t1 as select round(rownum/1000) col1 , mod(rownum, 1000) col2 , round(rownum/100) col3 , mod(rownum, 100) col4 , round(rownum/10) col5 , mod(rownum, 10) col6 from dual connect by level <= 1000000; insert into t1 select * from t1; insert into t1 select * from t1; insert into t1 select * from t1; insert into t1 select * from t1; drop table t2; create table t2 compress as select * from t1; exec dbms_stats.gather_table_stats(user, 'T1') exec dbms_stats.gather_table_stats(user, 'T2') select segment_name , blocks , round(bytes/1024/1024) mbyte from dba_segments where segment_name in ('T1', 'T2'); SEGMENT_NAME BLOCKS MBYTE -------------------- ---------- ---------- T1 30720 480 T2 17920 280
In diesem Fall ist der Größenunterschied zwischen der komprimierten und der nicht-komprimierten Version nicht gewaltig (Reduzierung auf 58,33% der Basisgröße), aber beim lesenden Zugriff ergibt sich eine (stabil) messbare Erhöhung von CPU_TIME und Gesamtlaufzeit für den Fall der komprimierten Tabelle T2:
select /* test t1 */ sum(col1) , sum(col2) , sum(col3) , sum(col4) , sum(col5) , sum(col6) from t1; select /* test t2 */ sum(col1) , sum(col2) , sum(col3) , sum(col4) , sum(col5) , sum(col6) from t2; select substr(sql_text, 1, 20) sql_text , disk_reads , buffer_gets , elapsed_time , cpu_time , user_io_wait_time from v$sql where sql_id in ('1whhzus1w5j57', '123rusnujf8dp'); SQL_TEXT DISK_READS BUFFER_GETS ELAPSED_TIME CPU_TIME USER_IO_WAIT_TIME -------------------- ---------- ----------- ------------ ---------- ----------------- select /* test t1 */ 30293 30302 6180032 6176061 568945 select /* test t2 */ 17400 17405 7694043 7668834 328810
Zwar sinkt die USER_IO_WAIT_TIME, aber gegenüber der CPU_TIME fällt sie kaum ins Gewicht. Anscheinend also wieder mal ein Fall, der für SQL Server und Oracle ein ähnliches Verhalten zeigt - wobei ich (ohne länger darüber nachzudenken) annehme, dass die Locking Probleme des SQL Servers bei Oracle keine Rolle spielen.