+2

Xác định kích thước tài nguyên giao dịch phù hợp với nhu cầu kinh doanh

1. Đặt vấn đề

Hãy lấy ví dụ về một giao dịch ngân hàng đơn giản. Giả sử một khách hàng giàu có muốn chuyển $1,000,000 từ tài khoản tiết kiệm sang tài khoản thanh toán của mình. Để thực hiện yêu cầu này, cơ sở dữ liệu có thể thực hiện các bước sau (theo yêu cầu của ứng dụng ngân hàng):

  1. Giảm số dư tài khoản tiết kiệm xuống $1,000,000.
  2. Tăng số dư tài khoản thanh toán lên $1,000,000.
  3. Thực hiện các hành động liên quan đến giao dịch tài chính của ứng dụng ngân hàng.

Các bước trên có thể được thực hiện qua một số lệnh SQL, tùy thuộc vào cách ứng dụng ngân hàng được triển khai. Tuy nhiên, tính chất nguyên tử (atomicity) của giao dịch đòi hỏi rằng cả ba bước trên hoặc phải thành công hoàn toàn hoặc thất bại hoàn toàn. Ví dụ, nếu khách hàng nhập sai số tài khoản thanh toán, bước thứ hai sẽ thất bại. Nếu chỉ bước đầu tiên thành công và dữ liệu được commit vào cơ sở dữ liệu ở thời điểm đó, thì xét về kỹ thuật, tính toàn vẹn của dữ liệu trong cơ sở dữ liệu ngân hàng đã bị tổn hại (và trong thực tế, chúng ta sẽ có một khách hàng cực kỳ không hài lòng!). Ngược lại, một ứng dụng được thiết kế tốt sẽ hiển thị thông báo lỗi hợp lý sau khi hủy bỏ toàn bộ giao dịch. Khách hàng sau đó sẽ có cơ hội nhập lại thông tin và tiếp tục quá trình giao dịch.

2. Giải quyết vấn đề

Khi tạo một giao dịch thay đổi cơ sở dữ liệu, Oracle sẽ tự động tạo dữ liệu undo để giữ lại trạng thái trước khi giao dịch diễn ra. Dữ liệu này cần được lưu trong các phân đoạn undo cho đến khi bạn thực hiện commit (lúc đó các thay đổi sẽ được lưu vĩnh viễn) hoặc rollback (lúc đó các thay đổi sẽ bị hủy). Sau khi thực hiện commit hoặc rollback cho giao dịch T1, dữ liệu undo của T1 có thể bị ghi đè bởi giao dịch khác. Tuy nhiên, nếu không còn đủ không gian undo trước khi giao dịch kết thúc (commit hoặc rollback), bạn sẽ gặp lỗi (ORA-30036: unable to extend segment...) và thường phải rollback giao dịch.

Giả sử bạn có một giao dịch lớn, T1, thực hiện 1.000 lệnh SQL và tạo ra rất nhiều dữ liệu undo. Nếu cơ sở dữ liệu hết không gian undo khi đang thực thi lệnh SQL thứ 500, bạn thường sẽ phải rollback, và tất cả công việc đã làm cho đến lúc đó sẽ bị hủy bỏ.

Để giải quyết vấn đề này, từ góc nhìn của developer, có các giải pháp sau:

  1. Tối ưu hóa các câu lệnh SQL để giảm thiểu tài nguyên tiêu thụ (bao gồm cả không gian undo cần thiết). Đây là giải pháp tốt nhất và có thể thực hiện được trong hầu hết các trường hợp, là điều mà chúng ta nên xem xét đầu tiên với vai trò là developer.
  2. Yêu cầu DBA (quản trị cơ sở dữ liệu) cấp thêm không gian undo để tránh bị thiếu hụt giữa chừng giao dịch. Đây là một giải pháp hoàn toàn chấp nhận được nếu chúng ta đã thực hiện bước 1 mà vẫn gặp vấn đề. Hãy nhớ rằng, hiện nay ổ đĩa cứng rẻ, nhưng thời gian của developer thì không!
  3. Chia giao dịch thành các giao dịch nhỏ hơn để thực hiện commit gián đoạn (intermittent commits) nhằm giảm lượng không gian undo mà mỗi giao dịch tiêu thụ. Đừng làm điều này! Đây là giải pháp tồi nhất cho vấn đề này, vì các lý do sau. Nó làm tổn hại đến tính toàn vẹn của dữ liệu. Bạn còn nhớ giao dịch ngân hàng không? Một ví dụ tương tự cho giải pháp này là thực hiện commit gián đoạn sau các bước 1, 2 và 3 như sau:
  • Giảm số dư tài khoản tiết kiệm xuống $1,000,000.
  • Thực hiện commit.
  • Tăng số dư tài khoản thanh toán thêm $1,000,000.
  • Thực hiện commit khác.
  • Thực hiện các hành động liên quan đến giao dịch tài chính của ứng dụng ngân hàng.
  • Thực hiện commit cuối cùng.

=> Bây giờ hãy tưởng tượng nếu khách hàng nhập sai số tài khoản thanh toán. Bước 2 sẽ thất bại, nhưng lần này toàn bộ giao dịch ban đầu sẽ không bị hoàn tác vì bước 1 đã được commit vào cơ sở dữ liệu, do đó làm ảnh hưởng đến tính toàn vẹn của dữ liệu.

  1. Lý do thứ hai khiến chúng ta không nên commit giữa chừng giao dịch là việc này sẽ làm tăng đáng kể độ phức tạp của mã nguồn (code). Vì chúng ta đã thực hiện commit giữa giao dịch, chúng ta cần đảm bảo rằng code của mình có thể khởi động lại trong trường hợp xảy ra lỗi từ bất kỳ điểm nào trong các commit trung gian này. Tùy thuộc vào số lượng và vị trí commit, việc này có thể dẫn đến mã nguồn phức tạp với nhiều trạng thái trung gian mà chúng ta phải đảm bảo có thể tiếp tục tới trạng thái cuối cùng (sau commit cuối cùng) để giữ đúng logic. Điều này đồng nghĩa với việc chúng ta sẽ phải phát triển, debug và duy trì nhiều mã nguồn hơn.
  2. Lý do cuối cùng chúng ta không nên thực hiện commit gián đoạn trong Oracle là nó làm chậm toàn bộ hệ thống. Điều này xảy ra vì mỗi lần commit tạo ra các bản ghi redo-log bổ sung và redo là điểm gây tranh chấp trên toàn hệ thống.

Ví dụ, sau khi giảm số dư tài khoản tiết kiệm, tăng số dư tài khoản thanh toán thêm $1,000,000 và thực hiện các hành động tài chính liên quan, chúng ta thực hiện commit cuối cùng. Bây giờ hãy tưởng tượng điều gì xảy ra nếu khách hàng nhập sai số tài khoản thanh toán. Bước 2 sẽ thất bại, nhưng lần này toàn bộ giao dịch ban đầu sẽ không bị hoàn tác vì bước 1 đã được commit vào cơ sở dữ liệu, do đó làm tổn hại tính toàn vẹn của dữ liệu.

Mình cũng đang thực hiện một số phép thử so sánh, anh em có thể tự kiểm chứng. Cụ thể mình thực hiện insert 10,000 bản ghi.

Kịch bản 1: Thực hiện commit từng bản ghi một trong vòng lặp lặp

Kịch bản 2: Thực hiện commit một lần cho tất cả các bản ghi

Kết quả test cho thấy, kịch bản 1 mất khoảng 360% thời gian so với kịch bản 2. Lượng dữ liệu càng tăng thì thời gian càng tăng

3. Kết luận

Từ những thảo luận trên, chúng ta có thể kết luận rằng trong Oracle, commit nên dựa trên nhu cầu giao dịch, chứ không phải dựa trên lượng tài nguyên (như dung lượng đĩa) có thể bị tiêu thụ. Bởi vì commit tạm thời nhằm tiết kiệm tài nguyên một cách sai lầm có thể làm giảm tính toàn vẹn của dữ liệu, tăng tiêu thụ tài nguyên, và làm cho mã nguồn phức tạp, nhiều lỗi, chạy chậm và không thể mở rộng. Do đó, chúng ta nên điều chỉnh tài nguyên Oracle được sử dụng trong giao dịch dựa trên yêu cầu kinh doanh, chứ không phải ngược lại. Tuy nhiên, ta vẫn cần cố gắng sử dụng tài nguyên Oracle một cách hiệu quả.

4. Thông tin kết nối

Nếu anh em muốn trao đổi thêm về bài viết, hãy kết nối với mình qua LinkedIn và Facebook:

Rất mong được kết nối và cùng thảo luận!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí