CodeGym /Các khóa học /SQL SELF /Phân tích các lỗi phổ biến khi phát triển trigger

Phân tích các lỗi phổ biến khi phát triển trigger

SQL SELF
Mức độ , Bài học
Có sẵn

Ok các bạn sinh viên, giờ các bạn đã có kiến thức về trigger, các loại trigger, nguyên lý hoạt động và thậm chí đã biết tạo trigger để làm nhiều việc khác nhau rồi. Nhưng như thường thấy trong lập trình, biết cái gì có thể làm thì quan trọng, nhưng biết cái gì không nên làm còn quan trọng hơn. Hôm nay tụi mình sẽ cùng mổ xẻ những lỗi điển hình mà dev hay mắc phải khi làm việc với trigger, để các bạn tránh được và tiết kiệm cho mình vài tiếng, thậm chí vài ngày debug nhé.

Đệ quy trigger: trigger tự gọi chính nó

Đây chắc chắn là lỗi phổ biến nhất của newbie. Hãy tưởng tượng bạn tạo một trigger để cập nhật giá trị ở một cột nào đó trong bảng, ví dụ last_modified. Nhưng ngay khi thay đổi này xảy ra, chính thao tác update lại gọi trigger đó lần nữa. Vậy là vòng lặp vô tận, và kết quả là server của bạn sập với lỗi tràn stack.

Ví dụ:

CREATE OR REPLACE FUNCTION update_last_modified()
RETURNS TRIGGER AS $$
BEGIN
    -- Cập nhật trường last_modified
    UPDATE my_table
    SET last_modified = NOW()
    WHERE id = NEW.id;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER after_update
AFTER UPDATE ON my_table
FOR EACH ROW
EXECUTE FUNCTION update_last_modified();

Chuyện gì sai ở đây? Lệnh UPDATE trong function lại gọi đúng trigger đã tạo ra nó. Vậy là vòng lặp vô tận xuất hiện.

Cách tránh:

Dùng biến OLD và so sánh giá trị trước khi thực hiện thay đổi:

CREATE OR REPLACE FUNCTION update_last_modified_safe()
RETURNS TRIGGER AS $$
BEGIN
    -- Kiểm tra giá trị có thay đổi không
    IF NEW.last_modified IS DISTINCT FROM OLD.last_modified THEN
        NEW.last_modified = NOW();
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Hãy chắc chắn là bạn không gọi những thao tác không cần thiết trong trigger.

Sử dụng sai OLDNEW

Hai biến này là bạn thân khi làm việc với trigger, nhưng nếu không quen tay thì cũng dễ gây đau đầu lắm. OLD lưu dữ liệu trước khi dòng bị thay đổi, còn NEW là dữ liệu sẽ được lưu sau khi thay đổi.

Lỗi thường xảy ra khi hiểu nhầm hoặc cố dùng biến ở nơi không có. Ví dụ, nếu bạn dùng trigger BEFORE INSERT, thì OLD sẽ không tồn tại — vì dòng mới chỉ vừa được tạo thôi mà.

Ví dụ lỗi:

-- Cái này sẽ báo lỗi vì OLD không tồn tại khi insert
CREATE OR REPLACE FUNCTION log_inserts()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO audit_log (old_data, new_data)
    VALUES (OLD.my_column, NEW.my_column);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Cách tránh:

Chọn kỹ chỗ dùng OLDNEW nhé:

  • OLD dùng được cho thao tác UPDATEDELETE.
  • NEW dùng được cho thao tác INSERTUPDATE.

Nhiều trigger cho một thao tác

Trong PostgreSQL bạn có thể tạo nhiều trigger cho cùng một thao tác và bảng. Nghe thì tiện đấy, nhưng thực tế có thể thành mớ hỗn độn nếu các trigger xung đột hoặc cùng thay đổi một dữ liệu.

Ví dụ:

-- Trigger 1
CREATE OR REPLACE FUNCTION trigger_one()
RETURNS TRIGGER AS $$
BEGIN
    -- Logic trigger 1
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Trigger 2
CREATE OR REPLACE FUNCTION trigger_two()
RETURNS TRIGGER AS $$
BEGIN
    -- Logic trigger 2
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Tạo hai trigger
CREATE TRIGGER trigger_one AFTER INSERT ON my_table EXECUTE FUNCTION trigger_one();
CREATE TRIGGER trigger_two AFTER INSERT ON my_table EXECUTE FUNCTION trigger_two();

Cả hai trigger sẽ chạy khi chèn dòng vào bảng my_table. Nếu logic của chúng không đồng bộ, có thể dẫn đến kết quả khó lường.

Cách tránh:

  • Lên kế hoạch kiến trúc trigger từ trước.
  • Nếu các trigger liên quan đến cùng một logic, hãy gộp chúng thành một trigger.

Vấn đề hiệu năng

Trigger sẽ thêm thao tác tính toán cho mỗi thao tác liên quan. Nếu bạn dùng trigger trên bảng có nhiều thao tác hoặc nhiều dòng, hiệu năng có thể giảm đáng kể.

Ví dụ lỗi:

CREATE OR REPLACE FUNCTION heavy_trigger_function()
RETURNS TRIGGER AS $$
BEGIN
    -- Thao tác nặng, chạy mỗi lần update dòng
    PERFORM some_heavy_query();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER performance_killer AFTER UPDATE ON huge_table EXECUTE FUNCTION heavy_trigger_function();

Cách tránh:

  • Giảm tối đa logic trong trigger. Nếu cần thao tác nặng, hãy cân nhắc chuyển sang background job.
  • Dùng điều kiện WHEN để giới hạn trigger:
CREATE TRIGGER optimized_trigger
AFTER UPDATE ON my_table
WHEN (OLD.column_name IS DISTINCT FROM NEW.column_name)
EXECUTE FUNCTION light_function();

Trigger và giao dịch

Trigger chạy trong phạm vi transaction do truy vấn của bạn khởi tạo. Nếu có lỗi trong trigger, toàn bộ transaction sẽ bị rollback. Điều này đôi khi hữu ích, nhưng cũng có thể gây rắc rối nếu bạn không xử lý lỗi cẩn thận.

Ví dụ lỗi:

CREATE OR REPLACE FUNCTION error_prone_trigger()
RETURNS TRIGGER AS $$
BEGIN
    -- Tạo lỗi
    RAISE EXCEPTION 'Có gì đó sai rồi!';
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Nếu trigger này chạy, transaction của truy vấn chính sẽ bị rollback.

Cách tránh:

Thêm xử lý lỗi vào trigger để giảm ảnh hưởng đến transaction chính:

CREATE OR REPLACE FUNCTION safe_trigger()
RETURNS TRIGGER AS $$
BEGIN
    BEGIN
        -- Code có thể gây lỗi
        INSERT INTO another_table VALUES (NEW.data);
    EXCEPTION
        WHEN OTHERS THEN
            RAISE NOTICE 'Có lỗi xảy ra, nhưng đã xử lý nhẹ nhàng.';
    END;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Lời khuyên thực tế

  1. Giữ trigger càng đơn giản càng tốt. Nếu bạn thấy trigger quá dài hoặc phức tạp, có lẽ nên tách ra thành function riêng hoặc nghĩ lại logic.

  2. Luôn test trigger trên dữ liệu nhỏ. Trước khi gắn trigger vào bảng quan trọng, hãy thử trên môi trường test trước.

  3. Ghi chú/document trigger. Vài tháng sau bạn hoặc đồng nghiệp sẽ quên lý do tạo trigger đó. Ghi chú rõ ràng sẽ giúp bạn đỡ đau đầu.

  4. Tránh dùng trigger cho việc có thể xử lý ở tầng ứng dụng. Trigger rất hợp cho tự động hóa thao tác cần thực hiện ngay, nhưng dùng cho business logic phức tạp thì sau này dễ gặp vấn đề.

  5. Theo dõi hiệu năng. Luôn monitor ảnh hưởng của trigger lên hiệu năng database, nhất là khi dữ liệu hoặc tải tăng lên.

Với những lời khuyên và kiến thức bạn đã học hôm nay, bạn đã sẵn sàng không chỉ tạo trigger mà còn viết trigger chạy đúng, hiệu quả và không gây bất ngờ khó chịu nào rồi nhé.

1
Khảo sát/đố vui
, cấp độ , bài học
Không có sẵn
Trigger cấp dòng và cấp bảng
Trigger cấp dòng và cấp bảng
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION