Oracle Data Access Methods
- Ekrem Oğuz ŞAHİN
- 9 Şub 2022
- 4 dakikada okunur
Merhabalar, bu yazımda bir Oracle Veritabanında veriye erişim yöntemlerini ele alacağız. Bu yöntemler verilere tablolardan veya indexlerden nasıl erişildiğini gösterir. Bu erişim yöntemlerini SQL sorgularımızın 'explain plan'larında görebiliriz. Oracle aşağıdaki erişim yöntemlerini destekler.
Full Table SCAN (FTS)
Table Access by ROW-ID
Index Unique Scan
Index Range Scan
Index Skip Scan
Full Index Scan
Fast Full Index Scans
Index Joins
Bit Map Index
1.FULL TABLE SCAN (FTS):
Full Table Scan, Optimizer tarafından kullanılan erişim yöntemlerinden biridir. Tablodaki tüm bloklar (HWM'ye kadar) taranır ve 'where' filtre koşulları uygulanır ve filtre koşulunu sağlayan satırlar döndürülür.

DB_FILE_MULTIBLOCK_READ_COUNT parametresi ile tek bir I/O'da daha fazla blok okunabilir.
Full Table Scan'ler explain plan'da aşağıdaki gibi gözükür.

FTS Yapmaktan Nasıl Kaçınılır?
Çoğu durumda FTS yapmak performans katili olarak tanımlanabilir ama sanılanın aksine FTS her zaman çok kötü bir seçenek değildir. Optimizer tabloya index ile erişmek yerine Full Table Scan yapmayı seçebilir. Çünkü FTS ile büyük I/O'lar yapılabilir, az sayıda büyük I/O yapmak çok sayıda küçük I/O yapmaktan iyidir. Yani planda FTS gördüğümüz zaman hemen aksiyon almak yerine iyi bir analiz yapılmalıdır.
• Tablo analizlerin güncel olup olmadığı kontrol edilmelidir.
• Doğru indexlerin olup olmadığı kontrol edilmelidir.
• Doğru index olsa bile sorguda parallel hint'i varsa FTS optimizer için bir opsiyon olabilir.
2.TABLE ACCES BY ROW-ID
RowID bir satırın fiziksel yeridir. Veriye erişmenin en hızlı yoludur. Rowid ile bir tabloya erişmek için where koşulunda ‘rowid = **‘ şeklinde yada bir indexten rowid alındıktan sonra tabloya rowid ile erişilir.
SQL> select * from hr.employees where rowid ='AAAR0RAADAAAHovABH'

Sorgunun planında ‘Table Access by Rowid’ görülüyorsa bu genelde indexten sonra gelen ikinci bir adımdır. Ama indexten sonra her zaman rowid ile tabloya erişmek gerekmeyebilir, eğer index gerekli tüm kayıtlara sahipse sonuç direk indexten döndürülür.
SQL> select * from hr.employees where employee_id > 160;

ROWID ve ROWID BATCHED Arasındaki Fark
Table Access by Rowid batched 12c ile gelmiş bir özelliktir ve normal Table Access rowid’den biraz daha performanslı çalışması hedeflenmiştir.

Indexten dönen sonuca göre yukarıdaki bloklara erişilmesi gerekmektedir. Table access by rowid ile sıra ile:
•15 nolu bloktan satır 015-000123 getirir.
•34 nolu bloktan satır 034-000527 getirir.
•88 nolu bloktan satır 088-000285 getirir.
•15 nolu bloktan satır 015-000889 getirir.
•88 nolu bloktan satır 088-000632 getirir.
Table Access by RowID Batched ile indexten dönen sonucu önce blok sırasına göre sıralar ve o sıraya göre bloklara erişim yapar. Böylece erişim şu şekilde olur:
•15 nolu bloktan satır 015-000123 ve 015-000889 getirir.
•34 nolu bloktan satır 034-000527 getirir.
•88 nolu bloktan satır 088-000285 ve 088-000632 getirir.
Örnekte de görüldüğü gibi rowid Batched ile tabloya 5 kere blok okumak yerine 3 kere okuma yapılmıştır.
3.INDEX UNIQUE SCAN:

Indexten en fazla 1 rowid döndürür. Index Unique Scan yapılması için eşitlik (=) kullanılması gerekir. Genel olarak unique veya primary key olan alanlarda index unique scan yaptığı söylenir fakat bu tek başına yeterli değildir. Non-unique index atılmış bir kolonda sonradan primary veya unique constraint atılsa bile Oracle buraya gelen sorgularda index unique scan yerine index range scan kullanmayı tercih eder. Çünkü o kolonda halihazırda bir index olduğu için yeni bir unique index oluşturmayacaktır.
SQL> CREATE TABLE example(col Varchar2(10));
SQL> CREATE INDEX example_idx ON example(col);
SQL> ALTER TABLE example ADD CONSTRAINT example_pk PRIMARY KEY(col);
SQL> SELECT * FROM example WHERE col='Example';

SQL> CREATE TABLE example2 (col varchar2(10), CONSTRAINT example2_pk PRIMARY KEY(col));
SQL> SELECT * FROM example2 WHERE col='Example';

4.INDEX RANGE SCAN:

Index range scan en yaygın erişim yöntemlerindendir. Oracle index kayıtlarına erişir ve rowid karşılıklarını bulup tabloya erişir. Aşağıdaki koşullarda index range scan kullanılır:
• col1 = :b1
• col1 < :b1
• col1 > :b1
Indexler sortlu bir şekilde oluşturulurlar, bundan dolayı like'lı gelen sorgularda;
• col1 like 'ASD%' şeklinde gelen sorguda index range scan yapılır.
• col1 like '%ASD' şeklinde gelen sorgularda ise index range scan kullanılmaz.
SQL> CREATE TABLE test (NAME VARCHAR2(100));
SQL> INSERT INTO test VALUES ('Ekrem');
SQL> INSERT INTO test VALUES ('Oguz');
SQL> INSERT INTO test VALUES ('Alperen');
SQL> COMMIT;
SQL> CREATE INDEX I#test#NAME ON test(NAME);
SQL> SELECT * FROM test WHERE NAME LIKE 'E%';

SQL> SELECT * FROM test WHERE NAME LIKE '%E';

Yukarıdaki örneklerde görüldüğü gibi like '%ASD' gibi gelen sorgularda index range scan kullanılmıyor. Örnek tablomuz tek kolonlu olduğu için optimizer index full scan yapmayı tercih etti çünkü döndürülecek tüm sonuç zaten indexte mevcut; bu yüzden tabloya hiç erişmeden index üzerinden sonucu döndürdü. Eğer tabloda 2 veya daha fazla kolon olsaydı full table scan tercih edilecekti.
5.INDEX SKIP SCAN:

Kompozit index atılmış kolonlarda tercih edilir. Index range scanden yavaş ama FTS'den hızlıdır.
SQL> CREATE TABLE example3 (NAME VARCHAR2(20), SURNAME VARCHAR2(20), AGE NUMBER);
SQL> INSERT INTO example3 VALUES('EKREM OGUZ','SAHIN',24);
SQL> INSERT INTO example3 VALUES('ALPEREN','ULKU',25);
SQL> INSERT INTO example3 VALUES('MURAT CAN','COBAN',25);
SQL> COMMIT;
SQL> CREATE INDEX I#EXAMPLE3#NAME_AGE ON example3(NAME,AGE);
SQL> SELECT * FROM example3 WHERE NAME='EKREM';

SQL> SELECT /*+ index_ss(example3 I#EXAMPLE3#NAME_AGE) */ * FROM example3 WHERE AGE>22;

6.FULL INDEX SCAN:

Tüm Index segmenti okunur. Eğer döndürülmesi istenen sonuç index'te mevcutsa tablo okunmaz ve sonuç direkt olarak index üzerinden döndürülür. Index olan kolonlar için sort işlemi yapılacaksa yine index full scan ile sonuç döndürülür. Sorguda gruplama varsa ve gruplanacak kolonlar index'li ise index full scan yapılır.
SQL> SELECT employee_id FROM hr.employees;

Bu tablomuzda employee_id kolonu primary key olarak tanımlanmış. Dolayısıyla sonuç olarak sadece employee_id kolonu istendiğinde tabloya erişilmesine gerek yoktur. Direkt olarak index üzerinden sonuç döndürülmüştür.
SQL> SELECT employee_id FROM hr.employees order by 1 desc;

Yine aynı index üzerinden sonuç döndürülmüştür. Zaten indexte kayıtlar sortlu bir şekilde tutulduğu için order by bir maliyet oluşturmamıştır.
SQL> CREATE INDEX I#HR#GROUP ON hr.employees(employee_id, salary);
SQL> SELECT employee_id,sum(salary) FROM hr.employees group by employee_id;

SQL> DROP INDEX I#HR#GROUP;
SQL> SELECT employee_id,sum(salary) FROM hr.employees group by employee_id;

7.FAST FULL INDEX SCAN:
Full index scanden farklı olarak verileri sıralı bir şekilde okumaz ve paralel okuyabilir. Oracle, Full Table Scanlere bir alternatif olması açısından böyle bir erişim metodu ortaya koymuştur. Full index scanin aksine FTS gibi multi block read yapabilir. Genel olarak count(*) bulunan sorgularda tercih edilir; SELECT ve WHERE şartında konulan tüm kolonların indexte bulunması gerekir.

SQL> select /*+ index_ffs(a EMP_EMP_ID_PK) */ count(*) from hr.employees a;

8.INDEX JOINS:
Sorguda döndürülmesi istenen kolonların 2 veya daha fazla index'te olması sonucu indexlerin hash join ile birleştirilmesidir. Çok fazla kolona sahip tablolarda oldukça efektif bir erişim yöntemidir.

9.BIT MAP INDEX:

Bit map indexler genel olarak Veri Ambarı ortamlarında kullanılırlar. Sorgu süresini azaltsa da DML işlemlerini çok yavaşlatırlar. Bundan dolayı, OLTP ortamlarda çok fazla tercih edilmez. B*Tree indexlerden farklı olarak null değerleri içerirler.
Gelecek yazılarımızda görüşmek üzere, sağlıcakla kalın...
Commenti