---
# System prepended metadata

title: '010: Exclusive lock và Shared lock'
tags: [Series PostgreSQL]

---

---
tags: Series PostgreSQL
---

# 010: Exclusive lock và Shared lock

Bài viết nằm trong series [Performance optimization với PostgreSQL](https://hackmd.io/@datbv/rk74QmqFd).

Phía client, hay nói cách khác là application, chúng ta tương tác với database thông qua các câu lệnh DML (INSERT/UPDATE/DELETE)... và bản chất tất cả đều được thực thi như một transaction nếu không explicit khai báo và không auto commit.

Nếu execute single query:

```sql=
UPDATE TABLE X SET COLUMN = 'Y' WHERE ID = 1;
```
Nó được thực thi giống như:

```sql=
START TRANSACTION;
UPDATE TABLE X SET COLUMN = 'Y' WHERE ID = 1;
COMMIT;
```

Phía dưới database level, nó nhận các transaction và thực hiện xử lý với multi-thread. Ta biết rằng nếu xử lý multi-thread không tốt sẽ dẫn đến nhiều rủi ro không lường trước. Cách đơn giản để handle issue này là sync/lock.

Lock cũng có nhiều loại, với bài trước ta biết một vài level locking là **row-level lock**, **page-level lock** và **table-level lock**. Cụ thể hơn với các concurrent read/write còn sử dụng 2 lock mode là **exclusive lock** và **shared lock**.

Cùng đi tìm hiểu về **exclusive lock** và **shared lock** trong bài viết này nhé. Lưu ý rằng 2 loại lock này chỉ đảm bảo **data integriry** - tức là tính toàn vẹn dữ liệu.

Let's begin.

## 1) Shared lock

**Shared lock** hay còn gọi là **read lock**, điểm qua một vài tính chất của nó đã:
> - Được sử dụng khi một transaction muốn read data.
> - Vì chỉ cho phép read nên mọi transaction khác muốn write trong khoảng thời gian read đều không được phép.
> - Ngoài ra, **shared lock** cho phép nhiều transaction cùng read data.

Nhờ các tính chất trên, nó đảm bảo tính toàn vẹn dữ liệu - **data integrity**. Quá trình đọc diễn ra trọn vẹn, không bị cắt ngang bởi quá trình write.

## 2) Exclusive lock

Với **exclusive lock**, hay còn gọi là **read-write lock**, chúng có những tính chất sau:
> - Được sử dụng khi transaction muốn read và write data.
> - Chỉ duy nhất một transaction có quyền chiếm **exclusive lock** trên data đó.
> - Ngoài ra không có transaction nào khác có thể apply **shared lock**.
> - Do đó, nó ngăn chặn các transaction khác chiếm quyền trong khoảng thời gian thực thi.

Có thể nhìn vào matrix dưới đây để biết khả năng tương thích giữa các **lock** trên data:

![](https://i.imgur.com/XX6mlvj.png)

Chỉ duy nhất **shared lock** có khả năng share chung với multi-transaction. Ngoài ra tất cả các kết hợp còn lại đều không thể. Đã có **shared lock** thì không có **exclusive lock** và ngược lại.

## 3) Example

Chắc hẳn với mớ lý thuyết trên, ta vẫn chưa nắm được cách thức hoạt động và trường hợp sử dụng của **shared lock**/**exclusive lock**. Phải có ví dụ mới dễ hình dung.

Table **Account** bao gồm rất nhiều columns, nhưng chỉ cần quan tâm đến:
> - Tên tài khoản
> - Số dư

|Name|Balance|...|...|...|...|...|...|...|...|...|
|-|-|-|-|-|-|-|-|-|-|-|
|Gấu|$1000|...|...|...|...|...|...|...|...|...|
|Thỏ|$200|...|...|...|...|...|...|...|...|...|
|Rùa|$500|...|...|...|...|...|...|...|...|...|

Vẫn 3 nhân vật chính trong câu truyện lần này là Gấu, Thỏ và Rùa. Lấy ví dụ về $$$ cho có hứng khởi. Let's start. 

![](https://i.imgur.com/Fx1d8wG.png)

- Thời điểm T1, Gấu muốn nạp thêm $100 vào tài khoản. Để làm được điều đó, ta cần thực hiện query UPDATE - write data. Do vậy cần acquire **exclusive lock**. Việc acquire thành công vì chưa có lock nào trên data của Gấu. Nếu một transaction khác muốn acquire **shared lock** để read data thì sao? Data đó có thể không chính xác vì lý do [**half-written**](https://hackmd.io/@datbv/ryceolmCu#After-credit). Muốn read không được, write thì càng không được. Vậy nên không cho phép bất kì **exclusive lock** hay **shared lock** nào khác được apply trên data đã có **exclusive lock**.
- Đến thời điểm T2, transaction xử lý thành công. Lúc này tài khoản của Gấu tổng cộng là $1100. Không có vấn đề gì xảy ra.
- T3, Gấu tiếp tục thực hiện một longgg transaction để... read data thôi. Hơi lâu, chắc smell code, nhưng kệ nó đi. Do chỉ read data nên acquire **shared lock**, chưa có lock nào nên acquire thành công. Với **shared lock**, Gấu ngầm nói rằng đứa nào muốn đọc thì đọc, nhưng đừng có write data nhé. Tao đang đọc $1100, đứa nào modify làm tao đọc lỗi (**half written**) thì cứ cẩn thận. Đó là lý do đã **shared lock** thì không thể **exclusive lock**.
- Đến T4, Thỏ kiểm tra số dư tài khoản để tối nay đi chơi với Sói - đôi bạn thân mến thân. Lúc này Thỏ acquire **shared lock** trên account của nó và acquire thành công vì chưa có lock nào, mà cũng không liên quan gì đến account của Gấu.
- T5, bạn Rùa hay tin tối nay Thỏ đi chơi với Sói nên hào hiệp chuyển ít tiền cho Thỏ... Chắc chắn là phải thực hiện query UPDATE - write data, acquire **exclusive lock** trên account của Thỏ nhưng tất nhiên sẽ faillllll... vì đã tồn tại **shared lock** zoài. Muốn làm người tốt cũng không dễ :joy:. Thỏ đang read data và không muốn ai modify data đó cả, đọc nhầm thì.. bỏ m.
- Đúng là Rùa, luôn kiên trì và nỗ lực. Rùa không bỏ cuộc, chờ đến thời điểm T6 khi Thỏ đã check xong account, Rùa acquire **exclusive lock**, chuyển cho Thỏ $200. Lần này chắc chắn là thành công rồi.

Clear hơn về cách sử dụng của **shared lock** và **exclusive lock** rồi đúng không. Cơ bản, các loại **lock** đều hướng đến mục đích đảm bảo tính **data integrity**, các dữ liệu cần được nguyên vẹn.
> - Với **shared lock**: khi read data, ta muốn data đó nguyên vẹn không bị thay đổi từ lúc bắt đầu đọc cho đến khi kết thúc. 
> - Với **exclusive lock**: khi write data, ta không muốn bất kì một ai có thể xen vào giữa quá trình này kể cả là read data.

Lưu ý lại lần nữa, 2 loại lock trên chỉ đảm bảo **data integrity**, không đảm bảo **data consistency**. Vậy làm thế nào để đảm bảo **data consistency**, đón chờ phần sau nhé.

## 3) Anything better than lock?

Việc concurrent read/write data tưởng chừng đơn giản nhưng với programming thì không ez tí nào. Thực ra cũng không quá phức tạp khi chúng ta sử dụng các cơ chế **sync/lock**, tuy nhiên nó dẫn tới vấn đề liên quan đến performance. 

Vậy có cách nào không cần **lock** mà vẫn concurrent read/write được không, hoặc chỉ cần giảm thiểu việc **lock** để tăng performance? Hồi sau sẽ rõ.

### Reference
- https://hackmd.io/@datbv/rk74QmqFd#Reference

### After credit

Sao không gọi **shared lock**  và **exclusive lock** là **read lock** và **read-write lock** cho đơn giản?
> - Theo mình nghĩ chắc là các engineer thích những thứ phức tạp, cho nó có vẻ nguy hiểm :joy:, ý kiến của bạn thế nào?

Ngoài 2 loại **lock** cơ bản trên, còn... 4 loại lock nữa là:
> - Update lock
> - Intent lock
> - Schema lock
> - Bulk update lock

Phần này hơi sâu, chúng ta (trong đó có mình) không cần lặn ngụp trong đó làm gì. Chỉ cần hiểu cơ bản về 2 loại **lock** phổ biến trên là được, cùng lắm là hiểu thêm **update lock**. Còn đâu nhường đất diễn lại cho các DBA.

© [Dat Bui](https://www.linkedin.com/in/datbv/)