Sức mạnh của Index trong Database

Việc nghe nói về tác dụng của Index trong quá trình Turning Database nói chung và Oracle nói riêng từ rất lâu. Việc cảm nhận thấy được tốc độ truy vấn, các chỉ số liên quan tới quá trình Turning như I/O… trong quá trình thực thi thấy rất rõ ràng. Những việc này mình thấy được do những người khác chỉ, bản thân thấy được trước và sau quá trình người khác đánh Index để sử dụng. Về tầm quan trọng và sức mạnh của Index trong Database là không cần bàn cãi.

Vừa rồi mình có phải build một luồng nghiệp vụ hoàn toàn mới cho một quy trình nghiệp vụ mới. Cụ thể là mỗi một khách hàng mới cần phải có một mã OTP để nhắn tin xác thực số điện thoại. Mã OTP là ngẫu nhiên, có thể là chữ hoặc số.
Sau khi phân tích và họp, hệ thống sẽ sinh trước mã OTP. Mỗi mã OTP gồm 5 ký tự có thể có cả chữ hoặc số. Toàn bộ các mã OTP này được sinh ra và lưu vào 1 bảng, hàng ngày hệ thống sẽ quét các khách hàng mới và tiến hành cập nhật ngẫu nhiên vào bảng này. Bảng này sẽ có (10+26)(10+26)(10+26)(10+26)(10+26) = 60 triệu dòng.
Hàng ngày có dưới 1000 khách hàng mới nên hệ thống xử lý cũng hơi chậm nhưng không có gì đáng ngại khi lấy ngẫu nhiên các mã OTP để cập nhật khách hàng, lấy ngẫu nhiên mỗi mã OTP mất hơn 1s.

Nhưng hôm vừa rồi, hệ thống cần đồng bộ gần 100.000 khách hàng mới vào và cũng có yêu cầu xác thực nên phải lấy ngẫu nhiên từng đó mã OTP. Việc này khi chạy thì đúng là không ổn chút nào. Nếu để chạy bình thường thì phải vài ngày mới có thể chạy xong, như vậy không đảm bảo tiến độ. Tình thế này buộc mình phải xem lại phương thức lấy ngẫu nhiên mã OTP. Vì không muốn thay đổi quy tắc lấy ngẫu nhiên nên mình tập trung vào tối ưu câu truy vấn. Khi kiểm tra độ phức tạp và I/O của câu lệnh lấy thì mình thấy việc lấy mã OTP rất nặng. Phải thôi, lấy ngẫu nhiên 1 trong 60 triệu dòng mà phải loại đi những mã OTP đã qua sử dụng thì khá nặng rồi.
Mình kiểm tra thì đúng là mình chưa đánh Index cho bảng này, thế là việc đầu tiên mình tiến hành đánh Index và đo lại thì hiệu quả thật không ngờ. Tốc độ lấy mã OTP của một khác hàng giảm xuống còn ~0.0016s. Mình chạy cho 100.000 khách hàng mất chưa tới 3 phút là xong. Công việc hàng ngày khoảng 1000 khách hàng thì chưa tới 2s đã chạy xong.

Sử dụng Index còn cần phải quan tâm đến quá trình Insert, Update dữ liệu. Nhưng hệ thống mình thì trường đánh Index này chỉ sinh ra một lần và không có thao tác update vào trường Index nên không phải quan tâm đến nó. Sau việc này đúng là mình đã thực sự bị thuyết phục bởi sức mạnh của Index trong Database. Bài học sau này cần vận dụng nhiều hơn tới Index trong quá trình triển khai hệ thống.

Config send email from oracle database server

ORA-24247: network access denied by access control list (ACL)

Kiểm tra bằng cách chạy câu lệnh

select * from dba_network_acls;

Nếu có data dạng:

 HOST LOWER_PORT UPPER_PORT ACL  ACLID
 mail.vstv.vn    1   1024    /sys/acls/send_mail.xml D285E04B69B942688587AB8FE5B41C69 

Chạy câu lệnh này:

BEGIN
   DBMS_NETWORK_ACL_ADMIN.create_acl (
     acl          => 'send_mail.xml',
     description  => 'Purpose of the acl is to send mail',
     principal    => 'INTRANET',
     is_grant     => TRUE,
     privilege    => 'connect',
     start_date   => SYSTIMESTAMP,
     end_date     => NULL);
 DBMS_NETWORK_ACL_ADMIN.assign_acl (
     acl         => 'send_mail.xml',
     host        => 'mail.trituenhantao.info',
     lower_port  => 1,
     upper_port  => 1024);
 COMMIT;
 END;

Oracle chuyển hàng thành cột dữ liệu

Trong việc làm báo cáo ta thường xuyên gặp các tình huống thống kê từ raw data phải chuyển dữ liệu từ hàng sang cột. Ta có thể sử dụng Excel để xử lý nhưng từ Oracle 10i thì có hỗ trợ câu lệnh PIVOT để xử lý.
Ta có thể test thử hàm này với giả dữ liệu như sau:

+ Chuẩn bị bảng và data:

—-create table

CREATE TABLE vn_temp_test_pivote (
C_MONTH VARCHAR2(15) NULL,
C_CORIG VARCHAR2(15) NULL,
C_VOLUME NUMBER(5) NULL);

—-test data

insert into vn_temp_test_pivote
values('201801','A',123);

insert into vn_temp_test_pivote
values('201801','B',121);

insert into vn_temp_test_pivote
values('201801','C',122);

insert into vn_temp_test_pivote
values('201802','A',1);

insert into vn_temp_test_pivote
values('201802','B',2);

insert into vn_temp_test_pivote
values('201802','C',3);

insert into vn_temp_test_pivote
values('201803','A',10);

insert into vn_temp_test_pivote
values('201803','B',20);

insert into vn_temp_test_pivote
values('201803','C',30);

Đây ví dụ ta có số lượng view(C_VOLUME) của từng C_CORIG trong từng tháng(C_MONTH).
Dữ liệu được trình bày:

C_MONTH C_CORIG C_VOLUME
201801 A 123
201801 B 121
201801 C 122
201802 A 1
201802 B 2
201802 C 3
201803 A 10
201803 B 20
201803 C 30

+ Ta cần nhìn xem số lượng view của các tháng theo từng loại C_CORIG, ta sử dụng hàm PIVOTE như sau:

SELECT *
FROM vn_temp_test_pivote PIVOT (SUM(C_volume)
FOR C_MONTH
IN ('201801' AS p_201801,
'201802' AS p_201802,
'201803' AS p_201803));

Kết quả:

C_CORIG p_201801 p_201802 p_201803
A 123 1 10
B 121 2 20
C 122 3 30

+ Như ta thấy ở ví dụ trên, nhược điểm của PIVOTE là ta phải liệt kê từng dữ liệu của cột cần chuyển sang hàng, nhưng ta có thể lấy tự động các tháng phát sinh không?
Oracle có hỗ trợ việc này nhưng PIVOTE chỉ có thể hiển thị data nằm trong XML:

SELECT *
FROM vn_temp_test_pivote PIVOT XML (SUM (C_VOLUME)
FOR C_MONTH
IN (SELECT DISTINCT C_MONTH FROM vn_temp_test_pivote));

Kết quả:

C_CORIG XML
A <XML>
B <XML>
C <XML>

Để lấy dữ liệu ra để trình bày ta phải đọc chuỗi XML theo khuôn dạng để trình bày ra.

SELECT C_CORIG,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[1]/column[2]') p1,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[2]/column[2]') p2,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[3]/column[2]') p3,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[4]/column[2]') p4,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[5]/column[2]') p5,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[6]/column[2]') p6,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[7]/column[2]') p7,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[8]/column[2]') p8,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[9]/column[2]') p9,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[10]/column[2]') p10,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[11]/column[2]') p11,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[12]/column[2]') p12,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[13]/column[2]') p13,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[14]/column[2]') p14,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[15]/column[2]') p15,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[16]/column[2]') p16,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[17]/column[2]') p17,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[18]/column[2]') p18,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[19]/column[2]') p19,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[20]/column[2]') p20,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[21]/column[2]') p21,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[22]/column[2]') p22,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[23]/column[2]') p23,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[24]/column[2]') p24,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[25]/column[2]') p25,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[26]/column[2]') p26,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[27]/column[2]') p27,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[28]/column[2]') p28,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[29]/column[2]') p29,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[30]/column[2]') p30
FROM (
SELECT *
FROM vn_temp_test_pivote PIVOT XML (SUM (C_VOLUME)
FOR C_MONTH
IN (SELECT DISTINCT C_MONTH FROM vn_temp_test_pivote))

) ;

Kết quả:

C_CORIG p1 p2 p3 p4 p5 p6...
A 123 1 10 null null
B 121 2 20 null null
C 122 3 30 null null

Phần này ta có thể liệt kê dài ra hoặc có thể check trước sau đó gen câu lệnh rồi ghép vào với nhau nếu muốn đẹp.
Được như thế này rồi nhưng ta vẫn chỉ là định nghĩa cột khi viết thôi, để map lại cột tự động(p1,p2…) với data(201801,201802…) ta sử dụng lấy dữ liệu ở cột 1 và chỉ lấy 1 dòng (vì tất cả các dòng là như nhau) sau đó UNION ALL lại là được:

SELECT '1_Code Origin' Code_Origin,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[1]/column[1]') A1,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[2]/column[1]') A2,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[3]/column[1]') A3,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[4]/column[1]') A4,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[5]/column[1]') A5,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[6]/column[1]') A6,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[7]/column[1]') A7,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[8]/column[1]') A8,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[9]/column[1]') A9,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[10]/column[1]') A10,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[11]/column[1]') A11,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[12]/column[1]') A12,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[13]/column[1]') A13,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[14]/column[1]') A14,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[15]/column[1]') A15,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[16]/column[1]') A16,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[17]/column[1]') A17,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[18]/column[1]') A18,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[19]/column[1]') A19,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[20]/column[1]') A20,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[21]/column[1]') A21,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[22]/column[1]') A22,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[23]/column[1]') A23,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[24]/column[1]') A24,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[25]/column[1]') A25,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[26]/column[1]') A26,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[27]/column[1]') A27,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[28]/column[1]') A28,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[29]/column[1]') A29,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[30]/column[1]') A30
FROM (
SELECT *
FROM vn_temp_test_pivote PIVOT XML (SUM (C_VOLUME)
FOR C_MONTH
IN (SELECT DISTINCT C_MONTH FROM vn_temp_test_pivote))

) where rownum=1
UNION
SELECT C_CORIG,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[1]/column[2]') p1,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[2]/column[2]') p2,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[3]/column[2]') p3,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[4]/column[2]') p4,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[5]/column[2]') p5,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[6]/column[2]') p6,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[7]/column[2]') p7,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[8]/column[2]') p8,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[9]/column[2]') p9,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[10]/column[2]') p10,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[11]/column[2]') p11,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[12]/column[2]') p12,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[13]/column[2]') p13,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[14]/column[2]') p14,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[15]/column[2]') p15,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[16]/column[2]') p16,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[17]/column[2]') p17,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[18]/column[2]') p18,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[19]/column[2]') p19,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[20]/column[2]') p20,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[21]/column[2]') p21,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[22]/column[2]') p22,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[23]/column[2]') p23,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[24]/column[2]') p24,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[25]/column[2]') p25,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[26]/column[2]') p26,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[27]/column[2]') p27,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[28]/column[2]') p28,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[29]/column[2]') p29,
EXTRACTVALUE (C_MONTH_XML, '/PivotSet/item[30]/column[2]') p30
FROM (
SELECT *
FROM vn_temp_test_pivote PIVOT XML (SUM (C_VOLUME)
FOR C_MONTH
IN (SELECT DISTINCT C_MONTH FROM vn_temp_test_pivote))

);

Kết quả:

C_CORIG p1 p2 p3 p4 p5 p6...
C_CORIG 201801 201802 201803...
A 123 1 10 null null
B 121 2 20 null null
C 122 3 30 null null

Oracle thông báo về trạng thái các TABLESPACE

Mình cũng chưa nắm được rõ về cấu trúc đặt dữ liệu của Oracle lắm, chỉ biết rằng data sẽ được đặt trong TABLESPACE. Trong 1 DATABASE có nhiều TABLESPACE, mỗi TABLESPACE có thể chứa 1 hoặc nhiều TABLE, nhưng 1 TABLE chỉ được nằm trong 1 TABLESPACE duy nhất.

TABLESPACE được khai báo khi tạo, có địa chỉ đặt ở ổ cứng xác định và được khai báo dung lượng trước. Nếu lượng data vượt quá dung lượng TABLESPACE thì hệ thống sẽ không ghi vào được nữa. Do vậy phải thường xuyên kiểm tra TABLESPACE xem có cái nào gần đầy thì phải nới ra, có thể nới rộng nhiều nhưng thường người ta nới dần dần, dùng đến đâu thì nới tới đó.

Trong thực tế triển khai, nhiều trường hợp không để ý đến TABLESPACE đầy hệ thống sẽ gặp lỗi không thể ghi dữ liệu nên việc DBA phải thường xuyên kiểm tra và nới TABLESPACE nếu cần.

Để kiểm tra thông tin về TABLESPACE ta có thể sử dụng câu lệnh SQL sau để lấy thông tin:

 

select b.tablespace_name,
b.MEGS,
(b.MEGS - a.MEGS_FREE) as ALLOCATED_MEGS,
round(((b.MEGS - a.MEGS_FREE) / b.MEGS) * 100) PCT_USED,
a.MEGS_FREE, EXTENSIBLE_MEGS
from (select tablespace_name, round(sum(bytes / 1024 / 1024)) MEGS_FREE
from dba_free_space
group by tablespace_name) a,
(select tablespace_name, round(sum((bytes / 1024 / 1024)),0) MEGS,
round(
sum(
case when autoextensible = 'YES' then (maxbytes - bytes)/1024/1024
else 0
end
),0) EXTENSIBLE_MEGS
from dba_data_files
group by tablespace_name
) b
where a.tablespace_name(+) = b.tablespace_name
order by 4 desc;

 

Với các thông tin này ta sẽ biết tình trạng của từng TABLESPACE và nới ra nếu cần.

Ta có thể tự động hóa hàng ngày thông báo qua email tình trạng của TABLESPACE bằng cách đặt crontab run câu lệnh này và gửi email.
Cao cấp hơn, ta cũng có thể làm cho hệ thống tự động kiểm tra TABLESPACE nào đầy thì tiến hành nới ra 1 khoảng. Ví dụ câu lệnh SQL trên ta check xem TABLESPACE nào có dung lượng còn dưới 10% thì tiến hành tự động nới TABLESPACE đó thêm 10% dung lượng hiện có và thông báo tới DBA.

Một bài viết chi tiết về TABLESPACE, rảnh đọc sau: http://www.oravn.com/viewtopic.php?t=48