Sự đáng sợ của câu lệnh SELECT

lock-table

Mình đã có dịp làm sập cả hệ thống chỉ bởi 1 câu lệnh SELECT mà lúc đầu mình tưởng là vô thưởng vô phạt hay không gây hại gì!

Mình đã làm gì?

Một hôm có chút việc mình cần truy cập vào DB của hệ thống đang chạy. (Vì là hệ thống không có thông tin cá nhân nhạy cảm nên mình được sếp cho chọc thẳng vào DB để xem dữ liệu thử)

Bình thường thì tụi mình thường đợi tới đợt có bảo trì hệ thống thì mới access vào DB nhưng dạo đấy không có lịch bảo trì. Do vậy mình access trực tiếp tới DB đang chạy ở môi trường product luôn!

Mình định dùng 1 vài câu lệnh SELECT đơn giản để truy cập dữ liệu, cũng vì nghĩ câu lệnh đơn giản nên sẽ không ảnh hưởng gì đến hệ thống.

Mặc dù nghĩ không ảnh hưởng hệ thống nhưng để phòng trừ bất cẩn copy hay cho chạy nhầm câu lệnh có INSERT hay DELETE dữ liệu thì để đảm bảo tính an toàn cho data của hệ thống nên mình đã cài transaction trước khi chạy lệnh SELECT.

Cụ thể mình đã kết nối với hệ thống product DB bằng psql (terminal của PostgreSQL) và sau đấy chạy câu lệnh bên dưới:

BEGIN; 
SELECT * FROM user_setting WHERE xxx = 1; 

Vì là câu lệnh đơn giản nên kết quả trả về rất nhanh. Cứ thế mình cho chạy câu lệnh tiếp theo, đúng lúc đấy thì đồng nghiệp của mình lại chỗ mình để nhờ mình giải thích về source-code mà mình đưa hôm qua. Vì công việc bên này của mình cũng không gấp nên mình đã để nguyên terminal như vậy và giải thích về đoạn source-code hôm qua đã đưa cho bạn mình.

Thì tầm 10 phút sau hệ thống đã sập hoàn toàn. Các bạn đoán thử chuyện gì đã xảy ra? Hôm đấy công nhận là khá xui xẻo đang yên đang lành lại táy máy làm treo hệ thống.

Chuyện gì đã xảy ra với hệ thống?

Việc này là lỗi của mình hay là của hệ thống. Đi từ kết luận thì trường hợp này là 100% lỗi của mình, mình đã cài transaction và cho chạy lệnh SELECT.

Sau khi xem lại log thì nguyên nhân là có một thread (luồng xử lý dữ liệu) đã cố lock table “user_setting”. Cụ thể là lock bằng câu lệnh như bên dưới:

LOCK TABLE user_setting;

Câu lệnh này thì lại không chỉ định mode khi lock nên PostgreSQL đã tự động chỉ định “ACCESS EXCLUSIVE” mode để thực thi câu lệnh trên. Thì “ACCESS EXCLUSIVE” mode này lại là mode mạnh nhất khi lock table. Và bản thân nó cũng gây tranh chấp quyền với mode “ACCESS SHARE” LOCK khi chạy lệnh SELECT thông thường. Nên đã dẫn tới sập hệ thống.

Nói cụ thể hơn là …

Hệ thống đã cố gắng thiết lập mode ACCESS EXCLUSIVE LOCK thông qua việc thực thi lệnh LOCK TABLE lên talbe “user_setting”, nhưng do trước đấy mình đã cho chạy lệnh SELECT, thông qua lệnh SELECT mình đã cố gắng thiết lập mode ACCESS SHARE LOCK. Nên hệ thống rơi vào trạng thái chờ lock được cancel khiến xử lý bị stop hoàn toàn.

Ngoài ra, sau đấy cũng có thêm một vài thread nữa đã cố gắng SELECT đến talbe “user_setting” nhưng lúc này table “user_setting” đã bị khóa nên lệnh này cũng bị treo.

Kết quả là do một thread thực thi LOCK TABLE và vô số thread thực thi SELECT bị treo nên dẫn đến connect đến DB bị full hoàn toàn khiến không thể access tới hệ thống. Dẫn đến hệ thống bị down hoàn toàn!!!

Tóm lại là làm sao để tránh lỗi này

Để tránh lỗi này thì nên:

◎Một là không được chủ quan khi dùng lệnh SELECT hay bất cứ lệnh đơn giản khác
◎Hai là sau khi start transaction thì phải đóng lại không để mở lâu vậy (nên chạy lệnh COMMIT hoặc ROLLBACK càng sớm càng tốt)

Ngoài ra khi thiết kế hệ thống mình còn chú ý thêm là:

◎Một là chú ý đến mưc độ LOCK
◎Hai là hạn chế sử dụng LOCK TABLE
◎Ba là nếu phải sử dụng LOCK TABLE thì nên sử dụng ở LOCK MODE yếu một chút
◎Bốn là nếu có sử dụng LOCK TABLE thì nên nói lại cho mọi người trong team infra về các case có thể dẫn đến tranh chấp về LOCK TABLE

コメントを残す

メールアドレスが公開されることはありません。