Отправляет email-рассылки с помощью сервиса Sendsay

Открыто о СУБД Oracle на русском

  Все выпуски  

Открыто о СУБД Oracle на русском : Системная информация в индексах


Информационный Канал Subscribe.Ru

Выпуск 47

Уважаемые подписчики рассылки!

Этот выпуск посвящен обсуждению структуры записей в индексах (помните, я собирался часть выпусков посвящать индексам ;). По мотивам ответа Тома Кайта на вопрос, заданный 14 июня 2003 года.

Системная информация в индексах

Том,

После анализа индекса (analyze ... validate) я поделил LF_ROWS_LEN на LF_ROWS и обнаружил значение на 12 байтов больше, чем длина ключа. Я ожидал увеличения только на 6 байтов - размер rowid, а откуда взялись другие 6 байтов? Может, я неправильно считаю?

 
SQL> analyze index IDX_TBLACCOUNT validate structure; 
 
Index analyzed 
 
SQL> select * from index_stats; 
 
HEIGHT                 3              
BLOCKS                 18056          
NAME                   IDX_TBLACCOUNT 
PARTITION_NAME          
LF_ROWS                5796880        
LF_BLKS                16868          
LF_ROWS_LEN            75359440       
LF_BLK_LEN             7996           
BR_ROWS                16867          
BR_BLKS                57             
BR_ROWS_LEN            237396         
BR_BLK_LEN             8028           
DEL_LF_ROWS            7655           
DEL_LF_ROWS_LEN        99515          
DISTINCT_KEYS          20             
MOST_REPEATED_KEY      4228703        
BTREE_SPACE            135334124      
USED_SPACE             75596836       
PCT_USED               56             
ROWS_PER_KEY           289844         
BLKS_GETS_PER_ACCESS   144925.5       
PRE_ROWS               0              
PRE_ROWS_LEN           0              

LF_ROWS_LEN/LF_ROWS = 75359440/5796880 = 13

Длина столбца - 1 байт (varchar2(1)).

 
SQL> select * from user_ind_columns where index_name = 'IDX_TBLACCOUNT'; 
 
INDEX_NAME       IDX_TBLACCOUNT 
TABLE_NAME       TBLACCOUNT     
COLUMN_NAME      AC_STATUS      
COLUMN_POSITION  1              
COLUMN_LENGTH    1              
DESCEND          ASC            
 
SQL> select * from user_tab_columns where table_name='TBLACCOUNT' and column_name='AC_STATUS'; 
 
TABLE_NAME           TBLACCOUNT 
COLUMN_NAME          AC_STATUS  
DATA_TYPE            VARCHAR2   
DATA_TYPE_MOD                  
DATA_TYPE_OWNER                
DATA_LENGTH          1          
DATA_PRECISION                 
DATA_SCALE                     
NULLABLE             Y          
COLUMN_ID            67         
DEFAULT_LENGTH                 
DATA_DEFAULT                   
NUM_DISTINCT         17         
LOW_VALUE            30         
HIGH_VALUE           74         
DENSITY              0.05882352 
NUM_NULLS            0          
NUM_BUCKETS          1          
LAST_ANALYZED        6/14/2003  
SAMPLE_SIZE          235526         
CHARACTER_SET_NAME   CHAR_CS    
CHAR_COL_DECL_LENGTH 1          
GLOBAL_STATS         NO         
USER_STATS           NO         
AVG_COL_LEN          1          

Анализирую другой индекс, по той же таблице, и снова получаю 12 дополнительных байтов:

 
SQL> analyze index idx_tblaccount_stssch validate structure; 
 
Index analyzed 
 
SQL> select * from index_stats; 
 
HEIGHT               3                     
BLOCKS               18845                 
NAME                 IDX_TBLACCOUNT_STSSCH 
PARTITION_NAME                          
LF_ROWS              5794493               
LF_BLKS              17096                 
LF_ROWS_LEN          121620457             
LF_BLK_LEN           7996                  
BR_ROWS              17095                 
BR_BLKS              53                    
BR_ROWS_LEN          390029                
BR_BLK_LEN           8028                  
DEL_LF_ROWS          5268                  
DEL_LF_ROWS_LEN      110621                
DISTINCT_KEYS        22291                 
MOST_REPEATED_KEY    553444                
BTREE_SPACE          137125100             
USED_SPACE           122010486             
PCT_USED             89                    
ROWS_PER_KEY         259.94764703          
BLKS_GETS_PER_ACCESS 133.473823516217      
PRE_ROWS             0                     
PRE_ROWS_LEN         0                     

LF_ROWS_LEN/LF_ROWS = 121620457/5794493 = 20.99 (у нас 12880 строк со значениями NULL в первом столбце)

Получаем снова DATE (7 байтов) + разделитель? - прокомментируй, пожалуйста, (1 байт) + varchar2(1) (1 байт) = 9 байтов.

12 байтов использовано системой для своих целей.

 
SQL> select * from user_tab_columns where table_name='TBLACCOUNT' and column_name='AC_SCHEDULETIME'; 
 
TABLE_NAME           TBLACCOUNT      
COLUMN_NAME          AC_SCHEDULETIME 
DATA_TYPE            DATE            
DATA_TYPE_MOD                     
DATA_TYPE_OWNER                   
DATA_LENGTH          7               
DATA_PRECISION                    
DATA_SCALE                        
NULLABLE             Y               
COLUMN_ID            72              
DEFAULT_LENGTH                    
DATA_DEFAULT                      
NUM_DISTINCT         4941            
LOW_VALUE            78640714110101  
HIGH_VALUE           C7C70C1F0E0201  
DENSITY              0.00020238      
NUM_NULLS            12880           
NUM_BUCKETS          1               
LAST_ANALYZED        6/14/2003       
SAMPLE_SIZE          235526  
CHARACTER_SET_NAME                
CHAR_COL_DECL_LENGTH              
GLOBAL_STATS         NO              
USER_STATS           NO              
AVG_COL_LEN          7               

Ответ Тома Кайта

Системой используется 4/5 байтов.

Столбец типа varchar2 имеет начальный байт длины, так что varchar2(1) занимает, минимум, 2 байта. Надо также учесть индикатор null-значения. Значение rowid тоже хранится как "строка" (с начальными байтами длины).

Итак, для ключа типа varchar2(1) имеем:
1 байт "длины", 1 байт "данных", 1 байт "длины rowid", 6 байтов данных rowid = 9 байтов.

При тестировании я получаю 12 байтов на строку в версии 9203 для ОС RedHat Linux на платформе Intel. Это очень легко определить:

 
ops$tkyte@ORA920> create table t ( x varchar2(20) ); 
 
Table created. 
 
ops$tkyte@ORA920> create index t_idx on t(x); 
 
Index created. 
 
ops$tkyte@ORA920> begin 
  2      for i in 1 .. 20 
  3      loop 
  4          execute immediate 'truncate table t'; 
  5 
  6          insert into t 
  7          select rpad(chr(rownum),i,chr(rownum)) 
  8            from all_objects 
  9           where rownum <= 255; 
 10 
 11          execute immediate 'analyze index t_idx validate structure'; 
 12          for x in ( select lf_rows_len, lf_rows, lf_rows_len/lf_rows bytes_per_entry 
 13                       from index_stats ) 
 14          loop 
 15              dbms_output.put_line 
 16              ( 'Width = ' || i || ' lf_rows_len = ' || x.lf_rows_len || 
 17                ' bytes/entry = ' || x.bytes_per_entry ); 
 18          end loop; 
 19      end loop; 
 20  end; 
 21  / 
 
Width = 1 lf_rows_len = 3315 bytes/entry = 13 
Width = 2 lf_rows_len = 3570 bytes/entry = 14 
Width = 3 lf_rows_len = 3825 bytes/entry = 15 
Width = 4 lf_rows_len = 4080 bytes/entry = 16 
Width = 5 lf_rows_len = 4335 bytes/entry = 17 
Width = 6 lf_rows_len = 4590 bytes/entry = 18 
Width = 7 lf_rows_len = 4845 bytes/entry = 19 
Width = 8 lf_rows_len = 5100 bytes/entry = 20 
Width = 9 lf_rows_len = 5355 bytes/entry = 21 
Width = 10 lf_rows_len = 5610 bytes/entry = 22 
Width = 11 lf_rows_len = 5865 bytes/entry = 23 
Width = 12 lf_rows_len = 6120 bytes/entry = 24 
Width = 13 lf_rows_len = 6375 bytes/entry = 25 
Width = 14 lf_rows_len = 6630 bytes/entry = 26 
Width = 15 lf_rows_len = 6885 bytes/entry = 27 
Width = 16 lf_rows_len = 7140 bytes/entry = 28 
Width = 17 lf_rows_len = 7395 bytes/entry = 29 
Width = 18 lf_rows_len = 7650 bytes/entry = 30 
Width = 19 lf_rows_len = 7905 bytes/entry = 31 
Width = 20 lf_rows_len = 8160 bytes/entry = 32 
 
PL/SQL procedure successfully completed. 

Итак, при длине 20 мы имеем:

 
длина     1+ 
данные   20+ 
длина     1+ 
данные    6 
         -- 
      32-28 = 4 

В каждой строке - на 4 байта "больше", а если добавить еще один большой столбец:

 
ops$tkyte@ORA920> drop table t; 
 
Table dropped. 
 
ops$tkyte@ORA920> create table t ( x varchar2(20), y char(100) default 'x' ); 
 
Table created. 
 
ops$tkyte@ORA920> create index t_idx on t(x,y); 
 
Index created. 
 
ops$tkyte@ORA920> begin 
  2      for i in 1 .. 20 
  3      loop 
  4          execute immediate 'truncate table t'; 
  5 
  6          insert into t (x) 
  7          select rpad(chr(rownum),i,chr(rownum)) 
  8            from all_objects 
  9           where rownum <= 255; 
 10 
 11          execute immediate 'analyze index t_idx validate structure'; 
 12          for x in ( select lf_rows_len, lf_rows, lf_rows_len/lf_rows bytes_per_entry 
 13                       from index_stats ) 
 14          loop 
 15              dbms_output.put_line 
 16              ( 'Width = ' || i || ' lf_rows_len = ' || x.lf_rows_len || 
 17                ' bytes/entry = ' || x.bytes_per_entry ); 
 18          end loop; 
 19      end loop; 
 20  end; 
 21  / 
 
Width = 1 lf_rows_len = 29070 bytes/entry = 114 
Width = 2 lf_rows_len = 29325 bytes/entry = 115 
Width = 3 lf_rows_len = 29580 bytes/entry = 116 
Width = 4 lf_rows_len = 29835 bytes/entry = 117 
Width = 5 lf_rows_len = 30090 bytes/entry = 118 
Width = 6 lf_rows_len = 30345 bytes/entry = 119 
Width = 7 lf_rows_len = 30600 bytes/entry = 120 
Width = 8 lf_rows_len = 30855 bytes/entry = 121 
Width = 9 lf_rows_len = 31110 bytes/entry = 122 
Width = 10 lf_rows_len = 31365 bytes/entry = 123 
Width = 11 lf_rows_len = 31620 bytes/entry = 124 
Width = 12 lf_rows_len = 31875 bytes/entry = 125 
Width = 13 lf_rows_len = 32130 bytes/entry = 126 
Width = 14 lf_rows_len = 32385 bytes/entry = 127 
Width = 15 lf_rows_len = 32640 bytes/entry = 128 
Width = 16 lf_rows_len = 32895 bytes/entry = 129 
Width = 17 lf_rows_len = 33150 bytes/entry = 130 
Width = 18 lf_rows_len = 33405 bytes/entry = 131 
Width = 19 lf_rows_len = 33660 bytes/entry = 132 
Width = 20 lf_rows_len = 33915 bytes/entry = 133 
 
PL/SQL procedure successfully completed. 

Можно ожидать:

 
длина     1 
данные   20 
длина     1  (да, CHAR - всего лишь VARCHAR2, дополненный пробелами - длина тоже хранится) 
данные  100 
длина     1 
данные    6 
        --- 
  133 - 129 = 4 дополнительных байта 

Комментарий читателя от 15 июня 2003 года

Интересно, зачем для значения rowid длина, - что, идентификаторы строк могут быть переменной длины? И откуда берутся эти 4 байта? Зачем они нужны?

Ответ Тома Кайта

Они просто "есть" (то есть, нужны)

Комментарий читателя от 18 июня 2003 года

Вот дамп блока:

 
row#0[536] flag: -----, lock: 0 
col 0; len 20; (20):  01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 
col 1; len 6; (6):  02 40 03 8d 00 00 
row#1[566] flag: -----, lock: 0 
col 0; len 20; (20):  02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 
col 1; len 6; (6):  02 40 03 8d 00 01 
row#2[596] flag: -----, lock: 0 
col 0; len 20; (20):  03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 
col 1; len 6; (6):  02 40 03 8d 00 02 
row#3[626] flag: -----, lock: 0 
col 0; len 20; (20):  04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 
col 1; len 6; (6):  02 40 03 8d 00 03 

Судя по нему, каждая строка занимает 30 байтов: строки начинаются со смещений 536, 566, 596, 626... Не мог бы ты объяснить, откуда взялись 2 байта в твоих результатах?

Ответ Тома Кайта

Я не занимаюсь интерпретацией результатов дампов. Вы изучаете "отчет" о блоке, а не сам блок. См. поля flags, row#, и т.д. Считайте, что это "данные, которые необходимы серверу для управления данными пользователя"

Все просто - сколько надо, столько система и использует.

Оригинал обсуждения этого вопроса можно найти здесь.


Copyright © 2003 Oracle Corporation


Прежние выпуски

Все вышедшие выпуски рассылки можно найти на сайте рассылки. Там же реализована возможность поиска материалов по ключевым словам (с помощью Google...)

В следующем выпуске

О согласованности данных при изменениях. Еще одно лирическое отступление от опубликованной программы развития рассылки - очень уж важную тему затронули на сайте Тома...

С наилучшими пожеланиями,

  В.К.



http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное