Xử Lý Database Change Notification Oracle Như Thế Nào?

Xử Lý Database Change Notification Oracle Như Thế Nào?

Nếu chúng ta có một bảng dữ liệu trong database oracle lưu log giao dịch tại cửa hàng, chúng ta sẽ cần đọc bảng dữ liệu đó để cộng tiền công cho từng nhân viên theo các giao dịch. Giả sử với mỗi giao dịch nhân viên sẽ được hưởng hoa hồng và nhân viên sẽ mong muốn được cộng tiền ngay sau khi giao dịch thành công. Vậy chúng ta sẻ xử lý thế nào?

Trong bài viết này mình sẽ giới thiệu với các bạn 2 cách để giải quyết bài toán trên.

Hướng giải quyết vấn đề

Đầu tiên chúng ta sẽ tạo ra các bảng dữ liệu trên database oracle để phục vụ cho ví dụ này:

-- Bảng lưu trữ thông tin nhân viên 
CREATE TABLE staff(
	staff_id number(10,0) PRIMARY KEY,    -- mã nhân viên
	total_amount number(10,2) DEFAULT 0   -- tổng số tiền của nhân viên
);

-- Bảng lưu trữ thông tin giao dịch
CREATE TABLE transaction_log(
	id number(10,0) PRIMARY KEY,          -- id giao dịch
	staff_id number(10,0) NOT NULL,       -- id nhân viên thực hiện giao dịch
	amount number(10,2) DEFAULT 0,        -- số tiền nhân viên được hưởng
    status number(1,0) DEFAULT 0          -- trạng thái giao dịch đã được xử lý chưa
);

Cách 1: Quét dữ liệu của bảng bảng liên tục để lấy các bản ghi mới nhất để xử lý.

Với cách này chúng ta sẽ thực hiện như sau:

B1: Thực hiện query bảng transaction_log để lấy bản ghi chưa được xử lý ở đây mỗi lần mình lấy 100 bản ghi:

select rowid, a.* from transaction_log a where a.status = 0 and rownum < 100;

B2: Thực hiện cộng tiền cho nhân viên theo thông tin log sau đó cập nhận lại thông tin database:

update staff set total_amount = ? where staff_id = ?
update transaction_log set status = 1 where rowid = ?

B3: Thực hiện sleep sau đó quay lại B1

Các bước thực hiện của các này khá đơn giản nên mình sẽ không implement bằng code. 

Cách 2: Sử dụng Database change notification của oracle để lấy các bản ghi mới được insert vào để xử lý. 

Trước hết mình xin nói về cách mà ứng dụng hoạt động như sau:

  • B1: Đăng kí nhận thông báo từ database 
  • B2: Gán listener để xử lý khi có sự kiện
  • B3: Khi có sự kiện thay đổi database thì nó sẽ được notify về listener và ở đó chúng ta sẽ thực hiện xử lý logic

Bắt tay vào thực hiện như sau:

Đầu tiên để database oracle có thể notify được thì chúng ta cần cấp quyền cho user bằng cách thực thi câu lệnh sau:

grant change notification to PHAMHUNG;

Tiếp theo chúng ta sẽ đăng kí nhận notification từ database:

// Mở kết nối database
OracleConnection conn = connect();

Properties prop = new Properties();

// Trả về ROWID của bản ghi thay đổi
prop.setProperty(OracleConnection.DCN_NOTIFY_ROWIDS, "true");
        
// Bỏ qua thông báo về update và delete do chúng ta chỉ quan tâm tới insert mới
prop.setProperty(OracleConnection.DCN_IGNORE_DELETEOP, "false");
prop.setProperty(OracleConnection.DCN_IGNORE_UPDATEOP, "false");
        
        
prop.setProperty(OracleConnection.DCN_QUERY_CHANGE_NOTIFICATION, "true");

// Thực hiện đăng kí nhận notification theo properties
DatabaseChangeRegistration dcr = conn.registerDatabaseChangeNotification(prop);

Tiếp đó chúng ta cần add một listener để xử lý các sự kiện thay đổi.Ở đây chúng ta tạo thêm một class DCNListener để xử lý

DCNListener listener = new DCNListener();
dcr.addListener(listener);

Tiếp tục chúng ta có thể thực hiện truy vấn để lọc các bản ghi thay đổi theo điều kiện mà chúng ta mong muốn

Statement stmt = conn.createStatement();
((OracleStatement) stmt).setDatabaseChangeRegistration(dcr);
// Mọi thay đổi của database phù hợp với câu truy vấn sẽ được thông báo
ResultSet rs = stmt.executeQuery("select * from transaction_log where status = 0");
while (rs.next()) {
}

Cuối cùng là class DCNListener để thực hiện xử lý các sự kiện thay đổi dữ liệu trong database:

public class DCNListener implements DatabaseChangeListener{

    @Override
    public void onDatabaseChangeNotification(DatabaseChangeEvent dce) {
        QueryChangeDescription[] qcds = dce.getQueryChangeDescription();
        for(QueryChangeDescription qcd : qcds){
            // In ra rowid của bản ghi thay đổi
            System.out.println(qcd.getTableChangeDescription()[0].getRowChangeDescription()[0].getRowid().stringValue());
        }
        // Tiếp tục xử lý logic ở đây
    }
}

Lưu ý:

  • Các sự kiện khi được gửi về listener sẽ được sử lý tuần tự tức là phải chờ sự kiện trước xử lý xong thì sự kiện sau mới được xử lý.
  • Trong thông báo chỉ bao bao gồm rowid cuả bản ghi thay đổi mà không bao gồm dữ liệu nên chúng ta cần query một lần nữa để lấy được dữ liệu.

Như vậy có thể thấy đối với bài toán này thì cách làm 1 sẽ mang lại hiệu quả hơn bởi vì:

  • Dữ liệu log giao dịch thường sẽ rất nhiều nên không phù hợp cho việc xử lý tuần tự từng bản ghi.
  • Việc phải query database mỗi lần cho mỗi giao dịch sẽ làm tăng đáng kể thời gian xử lý

Vậy khi nào nên dùng Database Change Notification?

Theo quan điểm cá nhân mình thì Database Change Notification chỉ nên được sử dụng khi dữ liệu trong database thay đổi không nhiều ví dụ như dùng để cập nhật giá trị cho cache của ứng dụng.

Nếu là bạn thì bạn sẽ sử dụng Database Change Notification cho bài toán như thế nào ???