Tìm hiểu về các loại Khoá (Explicit Locking) trong PostgreSQL

Tìm hiểu về các loại Khoá (Explicit Locking) trong PostgreSQL

Tin ngắn hàng ngày dành cho bạn
  • Từ lâu rồi suy nghĩ làm thế nào để tăng sự hiện diện thương hiệu, cũng như người dùng cho blog. Nghĩ đi nghĩ lại thì chỉ có cách chia sẻ lên mạng xã hội hoặc trông chờ họ tìm kiếm, cho đến khi...

    In cái áo này được cái tắc đường khỏi phải lăn tăn, càng đông càng vui vì hàng trăm con mắt nhìn thấy cơ mà 🤓

    (Có tác dụng thật nha 🤭)

    » Xem thêm
  • Một vòng của sự phát triển nhiều dự án khá là thú vị. Tóm tắt lại trong 3 bước: Thấy một cái gì đó phức tạp -> Làm cho nó đơn giản đi -> Thêm thắt tính năng cho đến khi nó phức tạp... -> Quay trở lại vòng lặp mới.

    Tại sao lại như vậy? Để mình lấy 2 ví dụ cho các bạn thấy.

    Markdown ra đời với mục tiêu tạo ra một định dạng văn bản thô "dễ viết, dễ đọc, dễ dàng chuyển thành một dạng gì đó như HTML". Vì thời đó chẳng ai đủ kiên nhẫn mà vừa ngồi viết vừa thêm định dạng cho văn bản hiển thị ở trên web như thế nào. Ấy vậy mà giờ đây người ta đang "nhồi nhét" hoặc tạo ra các biến thể dựa trên markdown để bổ sung thêm nhiều định dạng mới đến mức... chẳng nhớ nổi hết cú pháp.

    React cũng là một ví dụ. Từ thời PHP, việc khát khao tạo ra một cái gì đó tách biệt hẳn giao diện người dùng và phần xử lý logic chính của ứng dụng thành 2 phần riêng biệt cho dễ đọc, dễ viết. Kết quả là các thư viện UI/UX phát triển rất mạnh mẽ, mang lại khả năng tương tác với người dùng rất tốt, còn phần logic ứng dụng thì nằm ở một máy chủ riêng biệt. Bộ đôi Front-end, Back-end cũng từ đấy mà thịnh hành, không thể thiếu anh bồi bàn REST API. Ấy vậy mà giờ đây React trông cũng không khác biệt gì so với PHP là mấy, kéo theo là cả Vue, Svelte... lại cùng quy tất cả về một mối.

    Cơ mà không phải vòng lặp là xấu, ngược lại vòng lặp này mang tính tiến hoá nhiều hơn là "cải lùi". Nhiều khi lại tạo ra được cái hay hơi cái cũ thế là người ta lại dựa trên cái hay đó để tiếp tục lặp. Nói cách khác là chắc lọc tinh hoa từng tí một tí một á 😁

    » Xem thêm
  • Song song với các dự án chính thức thì thi thoảng mình vẫn thấy các dự án "bên lề" nhằm tối ưu hoặc cải tiến ngôn ngữ theo khía cạnh nào đó. Ví dụ nature-lang/nature là một dự án hướng tới cải tiến Go, mang lại một số thay đổi nhằm giúp cho việc sử dụng Go trở nên thân thiện hơn.

    Nhìn lại mới thấy hao hao JavaScript 😆

    » Xem thêm

Vấn đề

Làm việc với SQL hay PostgreSQL đủ lâu nhưng có bao giờ bạn đặt câu hỏi sẽ thế nào nếu như thực hiện một lệnh SELECT và DROP trên cùng một table cùng lúc? Lệnh nào sẽ chạy trước lệnh nào sẽ chạy sau, độ ưu tiên của các câu lệnh như thế nào hay là PostgreSQL dựa vào đâu để phân mức ưu tiên? Bài viết này hôm nay tôi xin phép trình bày cách mà PostgreSQL sử dụng Khoá (Locks) để giải quyết xung đột dữ liệu như thế nào nhé.

Tại sao lại cần khoá?

PostgreSQL cung cấp nhiều chế độ khóa khác nhau để kiểm soát quyền truy cập đồng thời vào dữ liệu trong bảng. Các khoá này có thể được sử dụng để can thiệp vào các tình huống của MVCC (Multi Version Concurrency Control).

Ngoài ra, hầu hết các lệnh PostgreSQL có được các khóa của các chế độ thích hợp để đảm bảo rằng các bảng được tham chiếu không bị xóa hoặc sửa đổi theo cách không mong muốn trong khi các lệnh đang được thực thi. Ví dụ như lệnh TRUNCATE không thể thực hiện đồng thời với các lệnh thao tác khác trên cùng một bảng như SELECT..., để làm được điều TRUNCATE được cấp phát cho một khoá gọi là ACCESS EXCLUSIVE.

Các cấp khoá

Postgres có các cấp khoá sau đây:

  • Table-Level Locks
  • Row-Level Locks
  • Page-Level Locks
  • Advisory Locks

Các cấp khoá mô tả miền xung đột của các lệnh PostgreSQL ở các cấp độ bảng (table), dòng (row) hay là trang (page)...

Tuy nhiên trong bài viết này tôi xin phép trình bày hai cấp khoá là Table và Row. Bạn đọc có thể tìm hiểu thêm các loại khoá tại trang tài liệu của PostgreSQL.

Khoá cấp bảng (Table-Level Locks)

Là các khoá ở quy mô bảng, các khoá này được cấp phát tự động hoặc thông qua lệnh LOCK.

Tự động có nghĩa là phụ thuộc vào lệnh SQL nào khi gọi thì bạn ngay lập tức sẽ được cấp pháp khoá đó. Dưới đây là một số câu lệnh quen thuộc và các khoá được cấp tự động tương ứng khi bạn gọi chúng.

  • SELECT: ACCESS SHARE
  • UPDATE, DELETE, INSERT: ROW EXCLUSIVE
  • CREATE INDEX: SHARE
  • CREATE TRIGGER: SHARE ROW EXCLUSIVE
  • DROP TABLE, TRUNCATE: ACCESS EXCLUSIVE

Nhiều khoá và chi tiết hơn xem lại trang tài liệu của postgres.

Dưới đây là bảng thể hiện sự xung đột của các khoá với nhau.

table locking

Tôi có thể giải thích như sau: nhìn vào bảng chúng ta thấy khoá ACCESS SHARE sẽ xung đột với ACCESS EXCLUSIVE vì thế trong các transaction nếu lệnh nào đang được thực hiện trước (nắm khoá ACCESS SHARE) thì lệnh sau (nắm khoá ACCESS EXCLUSIVE) phải chờ cho đến khi transaction được hoàn thành mới có thể tiếp tục và ngược lại.

-- (1)
BEGIN; 
-- (2)
SELECT * FROM users; -- ACCESS SHARE LOCKS
-- (4)
COMMIT;
-- (3)
TRUNCATE users; -- ACCESS EXCLUSIVE LOCKS

Hãy thử chạy các lệnh trên trên hai tiến trình khác nhau theo các bước (1) (2) (3) (4) để xem điều gì xảy ra nhé.

Khoá cấp hàng (Row-Level Locks)

Các khoá cấp hàng được cấp phát bằng cách xác định tên của khoá trong câu truy vấn. Có 4 khoá là:

  • FOR UPDATE
  • FOR NO KEY UPDATE
  • FOR SHARE
  • FOR KEY SHARE

row locking

FOR UPDATE khiến các hàng được truy xuất bởi SELECT bị khóa như thể để cập nhật. Điều này giúp chúng không bị khóa, sửa đổi hoặc xóa bởi các transaction khác cho đến khi transaction hiện tại kết thúc. Nghĩa là, các transaction khác như DELETE hoặc UPDATE một trong số các hàng này sẽ bị chặn cho đến khi transaction hiện tại kết thúc.

Tương tự như Table-Level Locks, bảng trên mô tả xung đột giữa các khoá. Các khoá xung đột với nhau sẽ phải đợi cho đến khi khoá cấp phát trước được thực hiện xong thì mới đến lượt mình.

Ví dụ bạn phát triển một tính năng đổi quà, có một bảng quà chứa số lượng còn lại của quà muốn đổi và bảng lưu số điểm hợp lệ của bạn dùng để đổi quà. Khi đổi quà bạn cần lấy ra số lượng quà còn lại và số điểm của bạn, kiểm tra qua nhiều điều kiện khắc nghiệt và tốn rất nhiều thời gian nữa mới có thể đổi. Giả sử trong lúc yêu cầu thứ nhất đã lấy ra số lượng quà còn lại là 1 và có yêu cầu thứ hai ngay lập tức cũng lấy ra số lượng quà còn lại là 1 thì điều gì sẽ xảy ra nếu như cả hai tiếp tục đổi quà? Để ngăn chặn điều đó bạn có thể sử dụng Row-Level Locks, tức là khi một transaction đang lấy số lượng quà còn lại để bắt đầu validate thì transaction thứ hai cần chờ đợi cho đến khi transaction đầu kết thúc.

Deadlock

Deadlock xảy ra khi hai hoặc nhiều transaction giữ các khoá mà bên kia mong muốn. Ví dụ transaction thứ nhất có được khoá ACCESS EXCLUSIVE trên bảng A và sau đó muốn có tiếp khoá đó trên bảng B trong khi transaction thứ hai đã có khoá ACCESS EXCLUSIVE trên bảng B và bây giờ lại muốn có khoá đó trên bảng A thì lúc đó không có transaction nào có thể tiếp tục. PostgreSQL tự động phát hiện tình huống này và giải quyết bằng cách huỷ bỏ một trong các transaction có liên quan, transaction bị huỷ bỏ không thể đoán trước.

Deadlock cũng có thể xảy ra ở trong các khoá cấp hàng khi các transaction cũng giữ khoá của nhau.

Biện pháp bảo vệ tốt nhất chống lại các deadlock nói chung là tránh để cho các lệnh có được các khóa trên nhiều đối tượng (table/row) theo một thứ tự xác định.

Nếu transaction được thực hiện trên nhiều đối tượng liên quan, cách tốt nhất là làm sao cho thời gian thực hiện transaction càng ngắn càng tốt. Tránh giữ các transaction trong thời gian dài. Ví dụ cho một thực hành tồi là transaction sẽ được thực thi tiếp cho đến khi chờ đợi thông tin nhập của người dùng xong.

Tổng kết

Bài viết trên đây tôi đã trình bày về cách mà PostgreSQL sử dụng khoá để quản lý quyền truy cập đồng thời vào dữ liệu trong bảng. Mặc dù là dành riêng cho PostgreSQL nhưng tư tưởng sử dụng khoá vẫn tồn lại trong các loại cơ sở dữ liệu SQL khác như MySQL, SQL Server... Nắm được cách hoạt động của khoá sẽ giúp bạn ứng biến với những tình huống trong việc phát triển ứng dụng cùng SQL.

Cao cấp
Hello

Bí mật ngăn xếp của Blog

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.

Bình luận (1)

Nội dung bình luận...
Avatar
Trần Ngọc Hải2 năm trước

Bài viết quá hay quá chi tiết cảm ơn tác giả

Trả lời