UUID v4, kẻ phá hoại hiệu suất index

Huy NguyenHuy Nguyen
5 min read

Đọc qua cái title thì có vẻ cũng có nhiều anh em giật mình, nhưng mà đúng thật, dùng UUID v4 làm index thì ngon đấy, nhưng anh em đã quan tâm đến hiệu suất của nó chưa? Nếu chưa thì hãy cùng mình tìm hiểu nhé!

TLDR

Bài viết so sánh hiệu suất khi dùng UUID v4 và ULID làm index trong cơ sở dữ liệu. Qua thực nghiệm với 2 triệu bản ghi trên Oracle 19c, ULID cho tốc độ insert cao hơn từ 16.67% đến 61.44% nhờ tính thứ tự tự nhiên (do chứa timestamp), tối ưu buffer cache. Tuy nhiên, ULID có nhược điểm là lộ thông tin thời gian và dễ đoán, cùng với khó khăn khi chuyển đổi từ UUID v4 trong hệ thống đã có sẵn. Vì vậy, ULID là lựa chọn tốt cho các hệ thống mới hoặc có tải write cao nếu có thể chấp nhận các trade-off về bảo mật và chuyển đổi.

Thực nghiệm chứng minh

Câu lệnh tạo bảng: https://github.com/huyandres2001/uuid-vs-ulid/blob/main/SETUP.md

Chi tiết kết quả: https://github.com/huyandres2001/uuid-vs-ulid/blob/main/RESULT.md

Tốc độ insert với tổng số lượng bản ghi là 2 triệu, batch size là 100k bản ghi.

Thông số database: 8gb ram, 4v CPU. Version: Oracle 19c.

UUIDULIDPerformance increased
129038.11 records/sec33878.21 records/sec~16.67%
224674.30 records/sec31610.06 records/sec~28.11%
322757.53 records/sec36740.39 records/sec~61.44%
420412.95 records/sec28324.60 records/sec~38.76%

Lý thuyết UUID v4 và ULID

UUID v4 (Universally Unique Identifier)

UUID v4 là một id nhận dạng duy nhất dài 128bit, UUID v4 sử dụng 122 bit ngẫu nhiên trong tổng số 128 bit của nó. Túm lại là nó có độ ngẫu nhiên cao (khả năng sinh ra có độ trùng lặp cực kỳ thấp) nên thường được sử dụng làm primary key trong hệ thống phân tán. Ở dạng chuỗi, nó có độ dài 36 ký tự. Lý thuyết sơ sơ vậy, anh em nào thích tìm hiểu kỹ thì google cho lẹ.

VD: 374a676c-bed8-473b-b85f-198730afcd79

ULID (Universally Unique Lexicographically Sortable Identifier)

ULID cũng như UUID, nó cũng 128 bit nhưng có 48 bit đầu tiên là đại diện cho timestamp ở đơn vị millisecond, 80 bit còn lại thì ngẫu nhiên. Vậy nên, khả năng trùng lặp so với UUID v4 có thể cao hơn nhưng vẫn là một tỉ lệ cực kỳ thấp. Và do 48 bit đầu là đại diện cho timestamp, nên ULID có tính thứ tự một cách tự nhiên.

VD: 01JNYSP7KQK757X5A48VRTJ6EH

Tại sao dùng index trên ULID lại nhanh hơn UUID v4

Khi ta liên tục insert bản ghi UUID v4, do tính ngẫu nhiên nên mỗi lần insert là một lần truy cập ngẫu nhiên vào node lá của index, điều này làm cho việc sử dụng buffer cache không được tối ưu.

Ngược lại, do tính chất của ULID nên mỗi ULID được sinh ra đều sẽ được xếp sau ULID được sinh ra trước đó, khi liên tục insert bản ghi ULID vào, cây index sẽ luôn được duyệt về phía phải - giúp tối ưu sử dụng buffer cache, làm giảm tải cho việc đọc và duyệt các block dữ liệu trên cây index.

Nhược điểm của ULID

Bây giờ thì biết là ULID ngon hơn UUID v4 trong khoản đánh index rồi, nhưng giải pháp nào cũng có nhược điểm của nó, có chăng chỉ là cái giá trade off có thể chấp nhận được hay không thôi.

  • Lộ thời gian: có thể bị lộ lọt thông tin về khoảng thời gian mà bản ghi được sinh ra, điều này có thể gây rủi ro về bảo mật đối với một số hệ thống nhạy cảm (như theo dõi hành vi người dùng dựa trên thời gian, khối lượng giao dịch…). Để giảm thiểu vấn đề này thì có một số giải pháp như: masking dữ liệu khi đưa ra ngoài, hoặc là không cho ai thấy luôn, hoặc là tự triển khai logic sinh ULID để che giấu đi thời gian.

  • Dễ đoán: trong khi UUID có tính ngẫu nhiên cao và khó đoán, thì ULID lại có thành phần thời gian ở 48 bit đầu, có thể gây ra một số rủi ro bảo mật (chẳng hạn như có thể đoán được thứ tự hành vi của hệ thống)

  • Đối với các hệ thống legacy đang tích hợp sâu với UUID thì việc chuyển đổi dùng ULID là rất khó khăn, không đáng để đánh đổi.

Migration

  • Với các hệ thống có tải write thấp, độ tăng trưởng dữ liệu không nhiều thì việc migrate sang sử dụng ULID sẽ gặp một số khó khăn. Có thể là không đáng để đánh đổi. Điều này nên được chú ý khi thiết kế bảng mới.

  • Đối với các hệ thống có tải write lớn, lượng dữ liệu đổ vào là liên tục với số lượng lớn thì việc migrate sang sử dụng ULID sẽ là một tối ưu đáng kể cho performance, tuy nhiên sẽ gặp nhiều khó khăn hơn vì có thể phải dừng hoạt động của bảng.

  • Nếu id của bảng được sinh ra trong logic của ứng dụng, ta hoàn toàn có thể chỉnh sửa logic trên ứng dụng để áp dụng ULID cho vòng đời dữ liệu mới.

Dù sao thì việc migration cũng rất khó khăn, có lẽ chỉ nên áp dụng khi thiết kế bảng mới từ đầu 😁

Kết luận

  • Index trên UUID v4 cũng ngon đấy, nhưng ULID lại nhỏ gọn + hiệu suất tốt hơn.

  • The earlier, the better.

  • Nếu đã lỡ thiết kế bảng theo UUID v4, mà bảng lại có tải write cao thì có thể cân nhắc chơi liều một phen 😁

  • Đánh giá các yếu tố như tính tương thích, bảo mật… trước khi áp dụng.

0
Subscribe to my newsletter

Read articles from Huy Nguyen directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Huy Nguyen
Huy Nguyen