суббота, 31 января 2009 г.

[Oracle] Построчное хранение данных

Минимальная единица информации в хранилище Oracle называется блок.

Таблицы хранятся в блоках построчно. Чем меньше суммарный объём полей в записях, тем больше записей помещается в одном блоке.

При чтении какой-либо записи Oracle берёт с диска (или из кеша) весь блок, в котором находится эта запись.

При необходимости перебрать, например, все значения одного поля таблицы, надо прочесть все соответствующие блоки. Предположим, что таблица большая и не помещается в кеш. Тогда, при наличии в таблице «посторонних» неиспользуемых в этом запросе полей, Oracle будет считывать большее количество блоков для получения того же результата.

Пример на Oracle 11:
create table a (n number, s varchar2(4000));
create table b (n number);
begin
for i in 1..1000000 loop
insert into a values(i, lpad('a', 4000, 'a'));
insert into b values(i);
end loop;
end;

set timing on;

select sum(n) from a;

SUM(N)
----------
5.0000E+11

Elapsed: 00:02:04.51

select sum(n) from b;

SUM(N)
----------
5.0000E+11

Elapsed: 00:00:00.11

Хранения «по колонкам» в Oracle не предусмотрено. Для лучшего быстродействия следует избегать объединения в одной таблице полей, которые могут быть востребованы по отдельности.

4 комментария:

Анонимный комментирует...

create index i on a(n)

Dmitry комментирует...

Да, это годится для данного примера, но не меняет дела в принципе. И не поможет в случаях, когда записи выбираются не подряд, а по списку значений другого индексированного поля, или по списку ROWID, который вернул внешний поисковый движок.

Анонимный комментирует...

Есть возможность индексного hash join на одной таблице, не уверен что это сработает с доменным индексом.

drop table a;
create table a (n1 number not null, n2 number not null, s varchar2(4000));
insert into a select level, level, lpad('x',4000,'x') from dual connect by level < 100000;
commit;
create index i1 on a(n1);
create index i2 on a(n2);
begin
dbms_stats.gather_table_stats(user, 'A', estimate_percent => null, cascade => true);
end;
/
explain plan for
select n1, n2 from a;
select * from table(dbms_xplan.display);

| 0 | SELECT STATEMENT |
| 1 | VIEW | index$_join$_001
|* 2 | HASH JOIN |
| 3 | INDEX FAST FULL SCAN| I1
| 4 | INDEX FAST FULL SCAN| I2

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access(ROWID=ROWID)

Dmitry комментирует...

Да, это годится для вашего примера (хотя FULLSCAN всей таблицы, если в ней нет лишнего поля "s", всё равно быстрее) но не меняет дела в принципе :) и не будет работать для случаев, когда интересующее нас поле является BLOB-ом или CLOB-ом. Для доменного индекса тоже не сработает, видимо.