07 October 2020

In-Memory Database Overview

In-Memory Database เป็นการจัดเก็บ data ทั้งหมดไว้ใน memory ทำให้การเข้าถึงข้อมูลรวดเร็วกว่าแบบเดิมที่ใช้ Disk ค่อนข้างมาก เพราะ memory มีการคำนวนซับซ้อนน้อยกว่า Disk และใช้งาน CPU ค่อนข้างต่ำ
ใน Oracle Database แบบเก่าจะเก็บข้อมูลในรูปแบบ row format ในระดับ physical level เพราะรูปแบบของการทำ transaction ของงาน OLTP อาจจะต้องการเข้าถึงข้อมูลคอลัมน์ทั้งหมดเพื่อบันทึกในเวลาเดียวกัน

Oracle In-Memory 12c
คือ feature ของ Oracle Database เหมาะกับงาน real-time analytics Data เพราะสามารถทำ mixed-workload enterprise OLTP ได้

โครงสร้างพื้นฐาน

In-Memory Dual-Format ของเวอร์ชั่น 12cR1
รูป In-Memory Dual-Format ของเวอร์ชั่น 12cR1
จาก https://www.doag.org/formes/pubfiles/8534096/2016-DB-Tirthankar_Lahiri-Database_In-Memory__A_Deep_Dive_and_a_Future_Preview-Manuskript.pdf


จากรูป
- ในเวอร์ชั่น 12cR1 ทำงานด้วยสถาปัตยกรรมแบบ Dual-Format คือ ข้อมูลของ Table ที่อยู่บน memory สามารถถูกเรียกใช้ในแบบ Row และ Column ได้พร้อมๆกัน โดยจะมี Oracle SQL Optimizer มาจัดการ Query
- การทำงานของ Oracle In-Memory จะ run ใน Oracle Database
- งาน OLTP จะ load Table มาเก็บบน memory ในรูปแบบ row format
- งาน Analytics จะ load Table มาเก็บบน memory ในรูปแบบ column format (รูปแบบ column format จะใช้ได้เฉพาะบน In-Memory เท่านั้น โดยไม่ต้องมีการ recovery เพราะมีการเก็บข้อมูลบน Disk ในแบบ row format อยู่แล้ว)

In-Memory Fast Start ของเวอร์ชั่น 12cR2
รูป In-Memory Fast Start ของเวอร์ชั่น 12cR2
จาก https://www.doag.org/formes/pubfiles/8534096/2016-DB-Tirthankar_Lahiri-Database_In-Memory__A_Deep_Dive_and_a_Future_Preview-Manuskript.pdf

จากรูป
- เวอร์ชั่น 12cR2 ปรับปรุงมาเป็นแบบใช้สถาปัตยกรรม Fast Start

อธิบายเพิ่มเติม

ข้อดีและข้อเสีย

ข้อดี
จาก http://www.dadbm.com/oracle-12c-in-memory-database-how-to-start/
- จำนวนข้อมูลที่เก็บบน memory สามารถเก็บได้มาก เพราะสามารถบีบอัดข้อมูลแบบ columnar compression ได้
- มีประสิทธิภาพที่เพิ่มขึ้นอย่างมากในงาน analytic และ reporting queries
- การทำงานของคำสั่ง DML ทำได้เร็วขึ้น เพราะมีการลบ index ที่ใช้สำหรับ analytical และสำหรับ performance ที่มีอยู่ออกให้มากที่สุด
- บน Disk เกิด I/O น้อยมาก
- มีความยืดหยุ่นและความละเอียดสูง โดยการโหลดเฉพาะ segments ของข้อมูลที่จะใช้งานเท่านั้นลงใน memory
- ไม่ต้องแก้ไขอะไรที่ Application
- การตั้งค่า setup In-Memory feature สามารถทำได้ง่าย
- ยังคงใช้ Oracle Database เหมือนเดิม แต่เพิ่มฟังชั่นการทำงานของ In-Memory เข้าไปเฉยๆ

ข้อเสีย
จาก http://www.dadbm.com/oracle-12c-in-memory-database-how-to-start/
- SYS objects ใน SYSTEM และ SYSAUX tablespace ไม่สามารถเก็บใน In-Memory
- ไม่สามารถใช้กับ Clustered table
- ไม่สามารถใช้กับ Column ที่ data type เป็น long

หลักในการเลือกใช้
จาก http://www.dadbm.com/oracle-12c-in-memory-database-how-to-start/
- ถ้าผู้ใช้มีปัญหาด้านประสิทธิภาพเกี่ยวกับงานด้านการค้นหาข้อมูล
- ถ้าไม่สามารถ tuning ในแบบอื่นๆได้
- ถ้าแอปพลิเคชันส่วนใหญ่ประกอบด้วยงาน analytics และ reporting queries ซึ่งส่วนใหญ่จะใช้คอลัมน์จำนวนน้อยแต่ส่งผลลัพธิ์กลับไปหลายแถว ซึ่งต่างจากงาน OLTP ที่ใช้แถวจำนวนน้อยแต่ส่งผลลัพธิ์กลับไปหลายคอลัมน์
- มีประโยชน์สำหรับระบบขนาดใหญ่ รวมถึงระบบที่เล็กกว่าก็สามารถแยกงานออกจาก I/O บน Disk มาทำที่ memory ได้
- ถ้าหากประสิทธิภาพของ In-Memory ทำงานได้ดีกว่าการต้องไปเพิ่ม Oracle license costs อื่น

06 October 2020

Multitenant Features Overview

ในสมัยก่อน Database แต่ละตัวจะแยกกันและจัดการเป็นอิสระด้วยตัวเองไม่เกี่ยวข้องกับ Database ตัวอื่นๆ แต่มีการพัฒนาระบบการจัดการ Database แบบรวมกันที่ส่วนเดียวได้ ด้วยโครงสร้างแบบ Multitenant Database

Oracle database 12c Multitenant คือ feature ใหม่ เปิดตัวครั้งแรกในเวอร์ชั่น 12cR1 ปี 2014 ทำให้สามารถสร้างและจัดการฐานข้อมูลได้หลายตัวมากขึ้นไว้ในฐานข้อมูลใหญ่ตัวเดียวที่เรียกว่า Container Database (CDB) ซึ่งฐานข้อมูลหลายตัวเหล่านั้นเราเรียกว่า Pluggable Databases (PDB) โดยทั้ง CDB และ PDB จะเรียกอีกชื่อเหมือนกันว่า Container
ฐานข้อมูลในเวอร์ชั่นก่อน 12c หรือ ฐานข้อมูลแบบเก่า จะเรียกว่า non-CDBs

โครงสร้างพื้นฐาน

เปรียบเทียบ แบบ non-CDBs และ CDBs
รูปจาก https://www.thegeekdiary.com/oracle-database-server-architecture-overview/
http://mkrishnadba.blogspot.com/2013/09/12c.html

non-CDB SALES and non-CDB HR


CDB (PDBs SALES and HR)


รูป ส่วนประกอบของ file ต่างๆ ใน CDB และ PDB
จาก https://avdeo.com/2015/01/16/creating-oracle-12c-multitenant-container-database/

อธิบายเพิ่มเติม

แต่ละ CDB ประกอบด้วย
- Root Container มีได้ 1 อัน ชื่อว่า CDB$ROOT เป็น Master set of data dictionary views ซึ่งมี metadata ที่เกี่ยวข้องกับ root container ตลอดจน pluggable databases ทุกตัวที่อยู่ใน CDB
- Static seed Container มีได้ 1 อัน ชื่อว่า PDB$SEED โดย container ตัวนี้จะอยู่เป็นอิสระมีหน้าที่เป็นเพียง template เพื่อสร้าง data files และ metadata สำหรับ pluggable databases ตัวใหม่ๆที่จะสร้างขึ้นใน CDB
- Pluggable Databases มีได้ 0 - 252 อัน แต่ละ PDB จะจัดการข้อมูลและฟังก์ชั่นต่างๆในตัวเองคล้ายกับ non-CDB database และมันมี data files และ application objects (users, tables, indexes, อื่นๆ) เป็นของตัวเอง เมื่อเราเชื่อมต่อเข้าสู่ PDB ตัวหนึ่งเราจะมองไม่เห็น root container หรือ PDB ตัวอื่นๆ
จาก http://orakitti.blogspot.com/2015/06/oracle-12c-multitenant-practice.html

วิธีแยกประเภทของฐานข้อมูล
ดูค่า CON_ID จาก V$INSTANCE หรือ V$DATABASE
CON_ID = 0 คือ non-CDBs
CON_ID = 1 คือ CDB (CDB$ROOT)
CON_ID = 2 คือ PDB (PDB$SEED)
CON_ID > 2 คือ PDB

ข้อดี
- ส่วนของ memory และ process อยู่ที่ container ที่เดียว
- การโคลนฐานข้อมูลทำง่ายและรวดเร็วขึ้น
- งานพื้นฐานทั่วไป สามารถทำที่ container ที่เดียว เช่น งาน backup
- ทำ patch และ upgrade ฐานข้อมูลที่ container ที่เดียว

User และ Role ของ CDB และ PDB

รูป user ต่างๆที่ใช้งานระบบ
จาก https://docs.oracle.com/database/122/CNCPT/overview-of-the-multitenant-architecture.htm#CNCPT89257


 
Type ของ User

Common User 
- เป็น user ที่สามารถใช้ได้ใน containers ทุกอัน (root Container และ PDBs ทุกอัน)
- เมื่อสร้างเสร็จแล้วจะอยู่ใน root Container และทุก PDB รวมถึง PDB ที่จะถูกสร้างขึ้นในอนาคตด้วย
- สามารถมีสิทธิ์เหมือนกันหรือแตกต่างกันในแต่ละ PDB ได้ โดยต้องถูก grant จากใน PDB นั้นๆเท่านั้น
- Common user ที่เกิดจากระบบสร้างขึ้นเอง เรียกว่า Oracle-supplied คือ user SYS และ SYSTEM
- Common user ที่เกิดจากเราสร้างขึ้นเอง เรียกว่า User-created คือ user ที่ต้องสร้างโดยตั้งชื่อด้วย C## หรือ c## นำหน้า
- ตัวอย่าง จากรูป คือ user c##dba ใน root Container และทุก PDB ซึ่งเป็นคนคนเดียวกัน

Local User
- เป็น user ที่ใช้ได้เฉพาะ PDB นั้นๆเท่านั้น เป็น user ปกติที่ใช้ใน PDB โดยการจัดการจะเหมือนระบบ non-CDB ทั่วๆไป
- แต่ละ PDB สามารถตั้งชื่อ user เหมือนกันได้ แต่จะเป็นคนละคนกันตามแต่ละ PDB ไม่เกี่ยวข้องกัน
- ตัวอย่าง จากรูป คือ user hr ใน hrpdb, hr ใน salespdb, rep ใน salespdb และ rep ใน hrpdb ซึ่งเป็นคนละคนกันในแต่ละ PDB

Type ของ Role

Common Role
- เป็น role ที่สามารถใช้ได้ใน containers ทุกอัน (root และ PDBs ทุกอัน)
- จะคล้ายกับการสร้าง Common user ที่จะอยู่ในทุก PDB ที่อยู่ใน Root container นั้น ตลอดจน PDB ที่ถูกสร้างขึ้นในอนาคต
- Common role ต้องสร้างด้วย C## หรือ c## นำหน้า

Local Role 
- เป็น role ที่ใช้ได้เฉพาะ PDB นั้นๆเท่านั้น เป็น role ปกติที่ใช้ใน PDB โดยการจัดการจะเหมือนระบบ non-CDB ทั่วๆไป
- แต่ละ PDB สามารถตั้งชื่อ role เหมือนกันได้ แต่จะเป็นคนละอันกันตามแต่ละ PDB ไม่เกี่ยวข้องกัน

05 October 2020

Insert Large Data to test

สร้างการเพิ่มข้อมูลขนาดใหญ่บน Database แบบง่ายสำหรับทดสอบการทำงาน

อธิบาย

- เมื่อ run คำสั่ง จะเกิดการใช้ทรัพยากรต่างๆดังนี้
 - ที่ Database ใช้พื้นที่ของ temp Tablespace (เฉพาะใช้ /*+ append_values */ หรือ /*+ append */) - ที่ Database เกิด Logical I/O (มีการอ่านเขียนที่ buffer) - เกิด Disk I/O read และ write (write เกิดมากกว่า read ค่อนข้างแยอะ) - เกิดการขยายขนาด data file เมื่อ transaction เริ่มทำงาน (โดยยังไม่ commit หรือ rollback) - เกิดการเขียน redo log เมื่อ transaction เริ่มทำงาน (โดยยังไม่ commit หรือ rollback) - User มีการใช้พื้นที่ tablespace นั่นคือ ขนาด tablespace เพิ่มขึ้น เมื่อ transaction เริ่มทำงาน (โดยยังไม่ commit หรือ rollback) - Object มีการใช้พื้นที่ segment นั่นคือ ขนาด segment เพิ่มขึ้น เมื่อ transaction เริ่มทำงาน (โดยยังไม่ commit หรือ rollback) - เกิดการใช้ memory เล็กน้อย

- การเพิ่มข้อมูลสามารถเกิดปัญหาดังนี้
- อาจทำให้เกิด error ORA-00257: archiver error. และถ้ายังไม่แก้ปัญหา จะเกิดการ stop execute transactions
ที่ alert log แสดง error 
ORA-19809: limit exceeded for recovery files
ORA-19804: cannot reclaim ขนาดพื้นที่ที่ต้องการเพิ่ม bytes disk space from ขนาดพื้นที่ที่จำกัดไว้ limit
ORA-19815: WARNING: db_recovery_file_dest_size of ขนาดพื้นที่ที่จำกัดไว้ bytes is 100.00% used, and has 0 remaining bytes available.
วิธีแก้ไขไม่ให้สร้าง redolog ให้ปิด log ของ table และใช้คำสั่ง insert value ด้วย /*+ append_values */ หรือ insert as select ด้วย /*+ append */
ตัวอย่าง ปิด log ของ table
SQL> alter table u1.tb_test_large nologging;
- ถ้าต้องการ add primary key ทำภายหลัง insert data เพราะถ้าทำก่อน insert data จะทำให้การ insert data ทำงานช้ามาก
  ตัวอย่าง
SQL> alter table u1.tb_test_large add constraint
tb_test_large3_pk primary key(object_id);
SQL> begin
   dbms_stats.gather_table_stats
   ( ownname    => 'u1',
tabname    => 'tb_test_large',
cascade    => true );
end;
/
สร้าง insert large data โดยกำหนด data size ที่ต้องการ
จาก http://stackoverflow.com/questions/6674919/generate-and-insert-large-clob-1-mb-using-sqlplus

กำหนด
Create table
SQL> create table u1.test_tb_large1(id number
 , large_text clob);

No logging กำหนดให้ไม่เก็บ redolog
SQL> alter table u1.test_tb_large1 nologging;

Insert data
CLOB 32767 byte or 32 KB is the largest string we can use in PL/SQL
ตัวอย่าง 
    ถ้าต้องการ data ขนาด 1 MB = 0.03125 MB * 30 = ใช้ for i in 1..30
    ต้องการ data ขนาด 1024 MB = 1 GB = 0.03125 MB * 32768 = ใช้ for i in 1..32768
    ต้องการ data ขนาด 10240 MB = 10 GB = 0.03125 MB * 327680 = ใช้ for i in 1..327680

RPAD คือ  Store ใช้เพิ่ม characters ทางขวาของ string ในที่นี้คือ "X" โดย เพิ่ม "X" จำนวน 32767 ตัว มีค่าเท่ากับ 32767 byte หรือ 32 KB หรือ 0.03125 MB
   แล้วจึง insert ลง table
SQL> declare
c clob;
i number;
begin
    for i in 1..30
    loop
        -- 32767 byte or 32 KB or 0.03125 MB
        c := c||rpad('X', 32767, 'X');
    end loop;
    insert /*+ append */ into u1.test_tb_large1 values(1, c)
    ;
end;
/

Check
SQL> set long 5000
SQL> set linesize 1000
SQL> set pagesize 1000
SQL> select * from u1.test_tb_large1;
ID  LARGE_TEXT
--- ---------------------------------------------------------------------------------
1 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
...
.....
........

สร้าง insert large data โดยใช้  loop

อธิบาย
- ถ้าใช้ /*+ append */ หรือ /*+ append_values */ โดยไม่ commit จะเกิด error ORA-12838: cannot read/modify an object after modifying it in parallel
เพราะทุกครั้งที่ loop run คำสั่ง insert จะเปิด transaction ใหม่เสมอ จึงต้องจบ transaction เดิมก่อน

กำหนด
Create table
SQL> create table u1.tb_test_large2init(text varchar2(1000));
SQL> insert into u1.tb_test_large2init(text) values('XXX');
SQL> create table u1.tb_test_large2(id number,text varchar2(1000));
SQL> alter table u1.tb_test_large2 nologging;

Insert data
SQL> declare
i number;
begin
    for i in 1..1000
    loop
        --insert /*+ append */ into u1.tb_test_large2(id,text) select i,text from u1.tb_test_large2init;
insert /*+ append_values */ into u1.tb_test_large2(id,text) values(i,'XXX');
commit;
    end loop;
end;
/

ถ้าต้องการให้เป็นแบบ infinite
declare
i number:=1;
begin
  loop
--insert /*+ append */ into u1.tb_test_large2(id,text) select :i,text from u1.tb_test_large2init;
insert /*+ append_values */ into u1.tb_test_large2(id,text) values(i,'XXX');
commit;
i:=i+1;
  end loop;
end;
/

Slow query to test

สร้างการอ่านข้อมูลที่ทำงานเป็นระยะเวลานานบน Database แบบง่ายสำหรับทดสอบการทำงาน

อธิบาย
- การอ่านข้อมูลสามารถเกิดปัญหาดังนี้
- เกิดการใช้ CPU 1 ตัว ปริมาณ 90 - 100% หรือใช้ CPU คนเดียว 1 ตัว (ต่อ 1 session)

สร้าง query slow ด้วยการ loop infinite
อธิบาย
- เมื่อ run คำสั่ง จะเกิดการใช้ทรัพยากรต่างๆดังนี้
- เกิดการใช้ CPU 1 ตัว ปริมาณ 90 - 100%  (1 session ใช้ CPU 1 ตัว)

SQL> begin
  loop
null;
  end loop;
end;
/

สร้าง query slow ด้วยการ sleep
อธิบาย
- เมื่อ run คำสั่ง จะเกิดการใช้ทรัพยากรต่างๆดังนี้
- เกิด Parallel Sessions

Create Function ให้ sleep ระยะเวลา P_SECONDS sec
SQL> create or replace function test_func_slow(p_seconds in number)
return number
is
begin
  dbms_lock.sleep(p_seconds);
  return 1;
end;
/

ตัวอย่าง ใช้ table ใดๆ โดยแต่ละ row ใช้เวลา 300 sec
SQL> select test_func_slow(300)
from test_tb_slow
connect by level <= 20000;

ตัวอย่าง ใช้ table ใดๆ โดยแต่ละ row ใช้เวลา 600 sec = 10 min และใช้ run แบบ parallel 4 CPU
SQL> create table test_tb_slow(id number);
SQL> insert into test_tb_slow(id) values(1);
SQL> insert into test_tb_slow(id) values(2);
SQL> insert into test_tb_slow(id) values(3);
SQL> commit;
SQL> select /*+ parallel(test_tb_slow 4) */ test_func_slow(600)
from test_tb_slow;

ตัวอย่าง ใช้ dual กำหนดจำนวน row = 20000 โดยแต่ละ row ใช้เวลา 300 sec
SQL> select test_func_slow(300)
from dual
connect by level <= 20000;

สร้าง query slow ด้วยการ select ข้อมูลขนาดใหญ่
จากหัวข้อ Insert Large Data to test ให้ insert ข้อมูลขนาดใหญ่ก่อน แล้วจึงเอามา select
อธิบาย
- เมื่อ run คำสั่ง จะเกิดการใช้ทรัพยากรต่างๆดังนี้
- ที่ Database เกิด Logical I/O (มีการอ่านเขียนที่ buffer ปกติอ่านจะแยอะกว่าเขียนค่อนข้างมาก เพราะการเรียกใช้ข้อมูลครั้งต่อๆไป จะมีข้อมูลเดิมบางส่วนอยู่ที่ buffer)
- เกิด Disk I/O read ที่ data file ค่อนข้างมาก เมื่อเป็นการ run ครั้งแรก

- การ select ข้อมูลสามารถเกิดปัญหาดังนี้
- อาจทำให้เกิด error ORA-01555 Snapshot Too Old ถ้า select ข้อมูลจากตารางที่กำลังมีการแก้ไขจาก user คนอื่น
   ข้อมูลเพิ่มเติมจาก https://www.tekstream.com/resource-center/ora-01555-snapshot-too-old/