- vừa được xem lúc

Case Study tối ưu sử dụng Partition và Index để tối ưu câu lệnh từ 30 phút xuống còn 0.01s như thế nào?

0 0 23

Người đăng: Trần Quốc Huy

Theo Viblo Asia

Đây là một bài toán tối ưu thực tế , tôi đã áp dụng kỹ thuật tối ưu này cho rất nhiều doanh nghiệp lớn: hệ thống Core chứng khoán, cơ sở dữ liệu core billing của đơn vị viễn thông lớn, hệ thống của ngân hàng... Bạn có thể xem danh sách các dự án tôi thực hiện tại đây:https://wecommit.com.vn/du-an/

Với bộ dữ liệu giả lập trong bài viết, bạn có thể thấy hiệu năng cải thiện hàng trăm nghìn lần. Trong những bài toán thực thế khi dữ liệu rất lớn (ví dụ hàng trăm triệu bản ghi, hàng tỷ bản ghi...), bạn sẽ thấy hiệu năng còn cải thiện KHỦNG KHIẾP hơn RẤT NHIỀU lần.

1. Mô tả bài toán

Giả sử chúng ta có 1 bảng tên là HIST để lưu trữ lịch sử giao dịch của khách hàng. Bảng HIST có 90 cột, tổng số bản ghi hiện tại của bảng là hơn 24 triệu bản ghi.

SQL> select count(*) from hist; COUNT(*) 24665111

Người dùng thường xuyên tìm kiếm trên bảng HIST theo yêu cầu như sau:

  • Tìm kiếm giao dịch vào một ngày hoặc một khoảng ngày (từ ngày đến ngày).
  • Khoảng thời gian tìm kiếm thường diễn ra trong 1 tháng (ví dụ: tối đa từ ngày đầu tháng đến ngày cuối tháng của một tháng nào đó).
  • Tìm kiếm có thể kết hợp giữa ngày giao dịch (cột TXDATE) và một cột liên quan đến giá trị của giao dịch (AMOUNT). Câu lệnh tìm kiếm như sau select * from hist where txdate=to_date('01-05-2022','dd-mm-yyyy') and amount = 4000; Tại đây tôi sẽ chia sẻ 1 phương án thiết kế để đảm bảo hiệu năng của ứng dụng cực kỳ nhanh và ổn định.

2. Phân tích hiệu năng khi chưa thiết kế PARTITION và INDEX

Để đánh giá hiệu năng của câu lệnh, chúng ta sẽ xem chiến lược thực thi của câu lệnh như thế nào.

select * from hist where txdate=to_date('01-05-2022','dd-mm-yyyy') and amount = 4000; | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 83 | 152K (1)| 00:30:29 |
|* 1 | TABLE ACCESS FULL| HIST | 1 | 83 | 152K (1)| 00:30:29 |

Hiện tại, khi chưa có bất kỳ kỹ thuật tối ưu nào; để tìm được dữ liệu trả ra cho người dùng, câu lệnh cần phải thực hiện quét toàn bộ các block dữ liệu của bảng HIST (TABLE ACCESS FULL). Thời gian để thực hiện câu lệnh trên là 30 phút 29s, chi phí thực hiện của câu lệnh là 152K.

3. Thực hiện thiết kế bảng theo chiến lược Partition

Do người dùng thường xuyên tìm kiếm theo cột TXDATE, nên tôi sẽ lựa chọn tiêu chí PARTITON là TXDATE. Giá trị tìm kiếm thường diễn ra trong 1 tháng, do đó tra sẽ chia bảng theo các PARTITION từng tháng. Để làm việc này tôi tạo một bảng mới tên là HIST_PARTITION với cấu trúc (các cột, định nghĩa cột trong bảng) giống hệt với bảng HIST ban đầu. Điều khác biệt là bảng HIST_PARTITION tôi sẽ lựa chọn tham số cấu hình dạng PARTITION BY RANGE (chia những dữ liệu theo từng khoảng thời gian).

Thực hiện đổ dữ liệu từ bảng HIST sang bảng HIST_PARTITION, để đảm bảo dữ liệu 2 bảng giống hệt nhau. Sau khi chuyển dữ liệu, trên bảng HIST_PARTITION đã có cùng số lượng bản ghi với bảng HIST

SQL> select count(*) from hist_partition; COUNT(*) 24665111

Để mô phỏng lài bài toán đánh giá hiệu năng khi kết hợp giữa kỹ thuật PARTITION và INDEX, chúng ta thực hiện tạo dữ liệu như sau Bảng HIST và HIST_PARTITION có cùng số lượng bản ghi, cùng thiết kế các cột. Cả 2 bảng đều có 90 column Bảng HIST và HIST_PARTITION đều có 24.665.111 bản ghi

SQL> select count(*) from hist_partition; COUNT(*)
24665111
SQL> select count(*) from hist; COUNT(*) 24665111

4. Đánh giá hiệu năng sau khi đã thực hiện PARTITION

Tôi sẽ thực hiện câu lệnh với cùng điều kiện và giá trị tìm kiếm trên bảng HIST_PARTITION. Thông số lúc này chỉ ra như sau

select * from hist_partition where txdate=to_date('01-05-2022','dd-mm-yyyy') and amount = 4000; | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
| 0 | SELECT STATEMENT | | 1 | 152 | 3022 (1)| 00:00:37 | | |
| 1 | PARTITION RANGE SINGLE| | 1 | 152 | 3022 (1)| 00:00:37 | 18 | 18 |
|* 2 | TABLE ACCESS FULL | HIST_PARTITION | 1 | 152 | 3022 (1)| 00:00:37 | 18 | 18 |

Chỉ với động tác thực hiện Partition theo cột TXDATE, câu lệnh đã cải thiệt** TỤT HUYẾT ÁP (WOW)** Thời gian thực thi của câu lệnh lúc này chỉ còn 37s (lúc đầu là 30 phút 29s!!!) Chi phí để hệ thống thực hiện xong câu lệnh là 3022 (lúc đầu là 152K) Bây giờ tuy câu lệnh đã được cải thiện rất nhiều về hiệu năng, nhưng tôi vẫn chưa bằng lòng lắm. Thời gian thực thi 37s chưa phải là điều tôi mong muốn. Do đó tôi tiếp tục thực hiện thêm 1 phương án tối ưu nữa: sử dụng Index trên bảng đã partition.

5. Thực hiện kết hợp Partition và Index và xem kết quả cải thiện hàng nghìn lần

5.1. Sử dụng Local Index trên bảng Partition

Tôi thực hiện tạo index trên Amount (do điều kiện tìm kiếm của câu lệnh). Thực hiện tại Index như sau:

create index idx_amount_partition hist_partition(amount) nologging local tablespace DATA;

Có 1 lưu ý tại đây: tôi sử dụng option LOCAL Câu lệnh thực hiện lúc này sẽ thế nào, chúng ta cùng kiểm tra nhé


select * from hist_partition where txdate=to_date('01-05-2022','dd-mm-yyyy') and amount = 4000; | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
| 0 | SELECT STATEMENT | | 1 | 152 | 10 (0)| 00:00:01 | | |
| 1 | PARTITION RANGE SINGLE | | 1 | 152 | 10 (0)| 00:00:01 | 18 | 18 |
|* 2 | TABLE ACCESS BY LOCAL INDEX ROWID| HIST_PARTITION | 1 | 152 | 10 (0)| 00:00:01 | 18 | 18 |
|* 3 | INDEX RANGE SCAN | IDX_AMOUNT_P_PARTITION | 7 | | 3 (0)| 00:00:01 | 18 | 18 |

Thời gian thực thi của câu lệnh đã chỉ còn ~1s Chi phí mà hệ thống phải thực hiện là 10. Nếu đo lường một cách chính xác khi thực thi câu lệnh, kết quả chi tiết sẽ như sau

SQL> select * from hist_partition where txdate=to_date('01-05-2022','dd-mm-yyyy') and amount = 4000;
Elapsed: 00:00:00.36 Statistics
1 recursive calls
0 db block gets
31 consistent gets
0 physical reads
0 redo size
6871 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2 rows processed

Thời gian thực tế khi thưc thi là 0.36s Để thực hiện được câu lệnh này, cơ sở dữ liệu sẽ cần lấy dữ liệu từ 31 block

5.2. Nếu tôi vẫn muốn TỐI ƯU hơn nữa thì sao?

Phương án chi tiết này dành riêng cho những anh em tham gia nhóm Zalo Tư Duy - Tối Ưu - Đặc Biệt. Trong nhóm anh em sẽ được nhận mật khẩu để đọc toàn bộ bài viết (nhóm đặc quyền). Bài viết đặc quyền: https://wecommit.com.vn/tuning-with-partition-and-index/

Sau khi áp dụng giải pháp, thông số như sau

---------------------------------------------------------
0 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
6871 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2 rows processed

Thời gian thực thi lúc này chỉ còn 0.01s Số block dữ liệu cần thực hiện bây giờ là 6 block

6. Tổng kết.

Khi kết hợp chuẩn xác giữa kỹ thuật PARTITION và INDEX, câu lệnh đã được tối ưu cực kỳ SHOCK

  • Thời gian thực thi ban đầu 30 phút 27s xuống chỉ còn 0.01s (NHANH NHƯ ĐIỆN)
  • Chi phí để hệ thống thực hiện câu lệnh giảm hơn 30K lần (từ 152K xuống còn 5).

8. Thông tin tác giả

Tác giả: Trần Quốc Huy - Founder & CEO Wecommit Facebook: https://www.facebook.com/tran.q.huy.71 Email: huy.tranquoc@wecommit.com.vn Youtube: Trần Quốc Huy Số điện thoại: 0888549190

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Stored Procedure trong SQL Server

Stored Procedure là 1 phần không thể thiếu của SQL Server. Chúng có thể hỗ trợ rất nhiều cho lập trình và cấu hình cơ sở dữ liệu.

0 0 134

- vừa được xem lúc

sử dụng index trong sql query

Index là một trong những yếu tố quan trọng nhất góp phần vào việc nâng cao hiệu suất của cơ sở dữ liệu. Index trong SQL tăng tốc độ của quá trình truy vấn dữ liệu bằng cách cung cấp phương pháp truy xuất nhanh chóng tới các dòng trong các bảng, tương tự như cách mà mục lục của một cuốn sách giúp bạn

0 0 178

- vừa được xem lúc

Hướng dẫn sửa lỗi không cài được SQL Server

Hôm qua do yêu cầu môn học, mình có cài lại Microsoft SQL Server. Trước đó mình có cài rồi, nhưng rồi lâu không dùng nên gỡ ra cho nhẹ máy. Bây giờ có dịp cần nên mình mới cài lại. Chi tiết lỗi mình gặp phải.

0 0 119

- vừa được xem lúc

Bạn nên tránh sử dụng Soft Delete khi có thể, và đây là lý do tại sao

Con người luôn luôn mắc sai lầm. Vì vậy, việc "lo xa" trước mọi tình huống xấu nhất chưa bao giờ là thừa.

0 0 126

- vừa được xem lúc

Sử dụng trigger trong SQL qua ví dụ cơ bản.

Trigger là gì . Cú pháp của Trigger. CREATE TRIGGER tên_trigger ON tên_bảng. FOR {DELETE, INSERT, UPDATE}.

0 0 144

- vừa được xem lúc

Khác biệt giữa khóa chính và khóa ngoại trong SQL

Các khoá chính và khóa ngoại là hai loại ràng buộc có thể được sử dụng để thực thi toàn vẹn dữ liệu trong các bảng SQL Server và đây là những đối tượng cơ sở dữ liệu quan trọng. Trong bài này, tôi muố

0 0 131