Đây là những bài viết về các dự án & kinh nghiệm tối ưu cơ sở dữ liệu của Wecommit. Những giá trị mà bạn sẽ nhận được
- Tư duy về tối ưu cơ sở dữ liệu
- Kinh nghiệm thực tế
- Ví dụ đời sống cực dễ hiểu
- Phân tích chi tiết dưới góc độ của chuyên gia
1. Một hiểu lầm kinh điển của rất nhiều anh em lập trình
- Rất nhiều lập trình viên coi rằng: nếu một Table có số lượng bản ghi ít thì chắc chắn việc tương tác sẽ nhanh.
- Trên thực tế tôi đã trực tiếp xử lý rất nhiều bài toán hệ thống cơ sở dữ liệu có số lượng bản ghi rất ít, tuy nhiên khi làm việc vẫn gặp vấn đề trầm trọng liên quan đến hiệu năng.
- Trong bài viết này tôi sẽ chia sẻ 1 trường hợp thực tế để anh em có thể hiểu rõ hơn về sai lầm này.
- Bài viết cũng lấy 1 demo: Table có số lượng bản ghi dưới 10 nhưng thực hiện câu lệnh vẫn bị treo.
2. Một trường hợp tối ưu cơ sở dữ liệu thực tế mà chúng tôi đã triển khai
Cơ sở dữ liệu tại một bệnh viện gặp vấn đề treo cứng, các chức năng gần như tê liệt. Các bệnh nhân phản ánh dữ dội, và chúng tôi (Wecommit) được mời vào kiểm tra tình hình.
Bước đầu tiên trong việc ứng cứu các sự cố về hiệu năng không phải là kiểm tra các câu lệnh TOP SQL, kiểm tra CPU, RAM, theo kinh nghiệm của mình, tôi kiểm tra ngay các WAIT trong Cơ sở dữ liệu.
Lưu ý:
- Hoạt động của Cơ sở dữ liệu cũng giống như hoạt động giao thông trên một đường phố.
- Các tài nguyên mà bạn cấp cho cơ sở dữ liệu giống như cơ sở hạ tầng của con phố đó
- Các tiến trình làm việc với cơ sở dữ liệu giống như những chiếc xe đang di chuyển trên con đường. Chúng ta có thể có xe tải, xe buýt, xe đạp…., việc này cúng giống với trong cơ sở dữ liệu có thể có các tiến trình khác nhau, có tiến trình thực hiện các câu lệnh phức tạp, có tiến trình thực hiện các câu lệnh đơn giản vậy.
- Khi một cơ sở dữ liệu gặp sự cố về hiệu năng, bạn có thể thấy rất giống với hiện trạng một con đường bị tắc nghẽn giao thông, các xe bị kẹt cứng.
- Phương pháp xem xét WAIT của tôi giống với việc: bạn đứng ở trên một tòa nhà cao tầng và nhìn xuống toàn cảnh của con đường đang kẹt xe, bạn sẽ tìm ra chính xác thì các xe trên con đường đó đang CHỜ cái gì mà không đi tiếp (đó chính là WAIT).
- Trong thực tế có rất nhiều loại WAIT khác nhau, cũng giống như trong thực tế các xe có thể bị “CHỜ” rất nhiều tình huống khác nhau (ví dụ: có xe bị tai nạn, hoặc chờ vì lòng đường quá nhỏ…)
Tại thời điểm kiểm tra, tôi phát hiện cơ sở dữ liệu có một số lượng Wait cực lớn với với tên là : enq: TM – contention.
Đây wait liên quan đến việc thiết kế Cơ sở dữ liệu đã bỏ quên việc đánh Index trên các cột đang là Foreign Key của bảng.
Ngay sau khi phát hiện được gốc của vấn đề và tinh chỉnh lại thiết kế, hệ thống đã hoạt động trơn tru.
3. Vậy tại sao nếu không đánh Index trên các cột đang là Foreign Key thì sẽ gây ra hiện tượng treo bên trên?
3.1. Demo sự cố hiệu năng
Tạo bảng hai bảng có mối quan hệ CHA – CON như sau:
Bước 1. Tạo một bảng vô cùng đơn giản tên là PARENT, bảng này chỉ có một cột PID và cột này chính là khóa chính của bảng
CREATE TABLE PARENT
(
PID INTEGER
);
ALTER TABLE PARENT
ADD (PRIMARY KEY (PID));
Bước 2. Tạo bảng con tên là CHILD. Bảng này cũng chỉ có một cột duy nhất tên là CID. Cột này chính là Foreign key, tham chiếu tới cột PID của bảng PARENT vừa tạo bên trên.
CREATE TABLE CHILD
(
CID INTEGER
);
ALTER TABLE CHILD ADD (
FOREIGN KEY (CID)
REFERENCES PARENT (PID);
Bước 3: Thêm dữ liệu phục vụ cho demo
INSERT INTO PARENT VALUES(1) ; INSERT INTO PARENT VALUES(2) ; INSERT INTO PARENT VALUES(3) ; COMMIT; INSERT INTO CHILD VALUES (3); COMMIT;
Bước 4: Kiểm tra dữ liệu trên 2 bảng
SQL> select * from parent; PID ---------- 1 2 3
SQL> select * from child; CID ---------- 3
SQL> insert into child values(1); 1 row created.
SQL> delete parent where pid=2;
SELECT EVENT, COUNT (*) FROM GV$SESSION WHERE BLOCKING_SESSION IS NOT NULL GROUP BY EVENT ORDER BY 2 DESC
Bạn đang thấy chính xác sự kiện WAIT mà tôi đã gặp trong dự án thực tế đã nói bên trên.
Bạn thấy một điều thú vị ở đây là:
- Session thứ 2 thực hiện DELETE dữ liệu từ bảng PARENT ở bản ghi có giá trị bằng 2 (hoàn toàn không liên quan tới các bản ghi đang được làm việc – có giá trị bằng 1 tại bảng CHILD), tuy nhiên session vẫn bị LOCK.
- Session số 2 này sẽ treo VÔ TẬN, cho đến thời điểm Session số 1 kết thúc transaction (khi COMMIT hoặc ROLLBACK)
Bây giờ tôi sẽ thực hiện tạo Index trên cột đang là Foreign Key (cột CID của bảng CHILD) và thực hiện lại quá trình trên, chúng ta sẽ cùng chờ đợt kết quả sau khi sửa đổi nhé.
Thực hiện tạo index
SQL> CREATE INDEX IDX_CID ON CHILD(CID); Index created.
SQL> insert into child values(1); 1 row created.
SQL> delete parent where pid=2; 1 row deleted.
3.2. Nghiên cứu sâu hơn về cách Cơ sở dữ liệu hoạt động khi chúng ta không đánh Index trên cột Foreign Key
SQL> begin for i in 4..1000000 loop insert into parent values(i); end loop; commit; end; / PL/SQL procedure successfully completed.
SQL> begin for i in 4..1000000 loop insert into child values(i); end loop; commit; end; / PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats(ownname=>'HUYTQ', tabname=>'PARENT'); PL/SQL procedure successfully completed. SQL> exec dbms_stats.gather_table_stats(ownname=>'HUYTQ', tabname=>'CHILD'); PL/SQL procedure successfully completed.
SQL> delete parent where pid=2; 0 rows deleted. Elapsed: 00:00:00.00
----------------------------------------------------------
Plan hash value: 1460050403
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time|
| 0 | DELETE STATEMENT | | 1 | 5 | 2 (0)| 00:00:01 |
| 1 | DELETE | PARENT | | | ||
|* 2 | INDEX UNIQUE SCAN| SYS_C0011149 | 1 | 5 | 2 (0)| 00:00:01 |
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("PID"=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
842 bytes sent via SQL*Net to client
781 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
0 rows processed
SQL> drop index IDX_CID; Index dropped.
SQL> delete parent where pid=2; 0 rows deleted. Elapsed: 00:00:00.01 Execution Plan ---------------------------------------------------------- Plan hash value: 1460050403 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time| | 0 | DELETE STATEMENT | | 1 | 5 | 2 (0)| 00:00:01 | | 1 | DELETE | PARENT | | | || |* 2 | INDEX UNIQUE SCAN| SYS_C0011149 | 1 | 5 | 2 (0)| 00:00:01 | Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("PID"=2) Statistics ---------------------------------------------------------- 28 recursive calls 0 db block gets 23 consistent gets 0 physical reads 0 redo size 843 bytes sent via SQL*Net to client 781 bytes received via SQL*Net from client 3 SQL*Net roundtrips to/from client 4 sorts (memory) 0 sorts (disk) 0 rows processed
4. Làm thế nào để phát hiện toàn bộ những Foreign Key chưa được đánh Index trong Cơ sở dữ liệu.
Scripts sau sẽ giúp các bạn nhanh chóng rà soát toàn bộ các Tables có FK và chỉ ra chi tiết thông tin các tables chứa FK chưa được đánh Index
WITH ref_int_constraints AS ( SELECT col.owner, col.table_name, col.constraint_name, con.status, con.r_owner, con.r_constraint_name, COUNT(*) col_cnt, MAX(CASE col.position WHEN 01 THEN col.column_name END) col_01, MAX(CASE col.position WHEN 02 THEN col.column_name END) col_02, MAX(CASE col.position WHEN 03 THEN col.column_name END) col_03, MAX(CASE col.position WHEN 04 THEN col.column_name END) col_04, MAX(CASE col.position WHEN 05 THEN col.column_name END) col_05, MAX(CASE col.position WHEN 06 THEN col.column_name END) col_06, MAX(CASE col.position WHEN 07 THEN col.column_name END) col_07, MAX(CASE col.position WHEN 08 THEN col.column_name END) col_08, MAX(CASE col.position WHEN 09 THEN col.column_name END) col_09, MAX(CASE col.position WHEN 10 THEN col.column_name END) col_10, MAX(CASE col.position WHEN 11 THEN col.column_name END) col_11, MAX(CASE col.position WHEN 12 THEN col.column_name END) col_12, MAX(CASE col.position WHEN 13 THEN col.column_name END) col_13, MAX(CASE col.position WHEN 14 THEN col.column_name END) col_14, MAX(CASE col.position WHEN 15 THEN col.column_name END) col_15, MAX(CASE col.position WHEN 16 THEN col.column_name END) col_16, par.owner parent_owner, par.table_name parent_table_name, par.constraint_name parent_constraint_name FROM dba_constraints con, dba_cons_columns col, dba_constraints par WHERE con.constraint_type = 'R' AND con.owner NOT IN ('ANONYMOUS','APEX_030200','APEX_040000','APEX_SSO','APPQOSSYS','CTXSYS','DBSNMP','DIP','EXFSYS','FLOWS_FILES','MDSYS','OLAPSYS','ORACLE_OCM','ORDDATA','ORDPLUGINS','ORDSYS','OUTLN','OWBSYS') AND con.owner NOT IN ('SI_INFORMTN_SCHEMA','SQLTXADMIN','SQLTXPLAIN','SYS','SYSMAN','SYSTEM','TRCANLZR','WMSYS','XDB','XS$NULL','PERFSTAT','STDBYPERF','MGDSYS','OJVMSYS') AND col.owner = con.owner AND col.constraint_name = con.constraint_name AND col.table_name = con.table_name AND par.owner(+) = con.r_owner AND par.constraint_name(+) = con.r_constraint_name GROUP BY col.owner, col.constraint_name, col.table_name, con.status, con.r_owner, con.r_constraint_name, par.owner, par.constraint_name, par.table_name ), ref_int_indexes AS ( SELECT /*+ MATERIALIZE NO_MERGE */ r.owner, r.constraint_name, c.table_owner, c.table_name, c.index_owner, c.index_name, r.col_cnt FROM ref_int_constraints r, dba_ind_columns c, dba_indexes i WHERE c.table_owner = r.owner AND c.table_name = r.table_name AND c.column_position <= r.col_cnt AND c.column_name IN (r.col_01, r.col_02, r.col_03, r.col_04, r.col_05, r.col_06, r.col_07, r.col_08, r.col_09, r.col_10, r.col_11, r.col_12, r.col_13, r.col_14, r.col_15, r.col_16) AND i.owner = c.index_owner AND i.index_name = c.index_name AND i.table_owner = c.table_owner AND i.table_name = c.table_name AND i.index_type != 'BITMAP' GROUP BY r.owner, r.constraint_name, c.table_owner, c.table_name, c.index_owner, c.index_name, r.col_cnt HAVING COUNT(*) = r.col_cnt ) SELECT * FROM ref_int_constraints c WHERE NOT EXISTS ( SELECT NULL FROM ref_int_indexes i WHERE i.owner = c.owner AND i.constraint_name = c.constraint_name ) ORDER BY 1, 2, 3;
Kết quả như sau
5. Nếu bạn muốn nhận scripts đầy đủ và các scripts kiểm tra hiệu năng thực chiến khác
- Bạn hãy điền thông tin vào form liên hệ của Wecommit.
6. Một số kiến thức khác về tối ưu SQL mà bạn cần biết
- 6 bước thực thi của một câu lệnh SQL là gì và áp dụng trong tối ưu thế nào: Đọc bài viết tại đây
- Chiến lược thực thi của câu lệnh SQL là gì: Đọc bài viết tại đây.
- Trường hợp câu lệnh SQL nhất quyết không nhận Index vì sao: Đọc bài viết tại đây
7. Một số bài viết có ích cho bạn
- Chia sẻ đầy đủ bộ đề thi chứng chỉ (tôi đã mua các bộ đề này và chia sẻ cho bạn toàn bộ nội dung của bộ đề, bạn được nhận mà không cần tốn bất kỳ chi phí nào)
- Đề thi chứng chỉ OCA:
- Môn thi SQL: https://wecommit.com.vn/bo-de-thi-quoc-te-mien-phi-oracle-oca-1z0-071/
- Môn thi quản trị Oracle: https://wecommit.com.vn/bo-de-thi-quoc-te-mien-phi-oracle-oca-1z0-062/
- Đề thi nâng cấp lên chứng chỉ Oracle OCP: https://wecommit.com.vn/bo-de-thi-quoc-te-mien-phi-oracle-ocp-12c-1z0-060-oracle-database-12c-administrator-certified-professional-upgrade-from-7-3-8-8i-9i-10g-11g-dba-ocp-certification-overview/
- Đề thi chứng chỉ chuyên gia Oracle: https://wecommit.com.vn/bo-de-thi-quoc-te-mien-phi-oracle-oce-1z0-066oracle-certified-expert-oracle-database-12c-data-guard-administrator/
- Đề thi chứng chỉ bảo mật Oracle: https://wecommit.com.vn/bo-de-thi-quoc-te-mien-phi-oracle-1z0-528oracle-database-11g-security-essentials/
- Đề thi Oracle Golden Gate: https://wecommit.com.vn/bo-de-thi-quoc-te-mien-phi-oracle-1z0-481oracle-goldengate-11g-certified-implementation-exam-essentials/
- Đề thi chứng chỉ quốc tế Microsoft Power BI: https://wecommit.com.vn/bo-de-thi-quoc-te-mien-phi-microsoftpowerbi-da-100/
- Đề thi chứng chỉ OCA:
8. Nếu bạn muốn xem các giải pháp tối ưu được áp dụng trong những hệ thống Production giao dịch 24×7
Nếu bạn chưa thuộc nhóm học viên đặc quyền của tôi nhưng vẫn muốn xem một số giải pháp tối ưu thực tế (giải pháp chi tiết, phân tích cụ thể), bạn có thể nhận mật khẩu để đọc giải pháp thông qua nhóm Zalo sau: Nhóm Tư Duy – Tối Ưu – Khác Biệt
9. Nếu bạn muốn liên hệ với tôi
Youtube: https://www.youtube.com/channel/UCtsYzL7iN7rBCPnkjYp4XYw
Zalo: 0888549190
Facebook: https://www.facebook.com/tran.q.huy.71
Fanpage: https://www.facebook.com/ora.huytran/
Email: huy.tranquoc@wecommit.com.vn