CodeGym /Các khóa học /SQL SELF /Đánh chỉ mục dữ liệu kiểu JSONB: sử dụng <...

Đánh chỉ mục dữ liệu kiểu JSONB: sử dụng GINBTREE index

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

Câu hỏi đầu tiên: tại sao tụi mình lại dùng JSONB? JSONB cho phép lưu trữ dữ liệu theo dạng JSON, giúp cấu trúc dữ liệu linh hoạt. Cái này cực kỳ tiện khi dữ liệu có quan hệ phức tạp và lồng nhau (ví dụ như profile người dùng với danh sách địa chỉ hoặc cài đặt). Khác với JSON thường, JSONB lưu dữ liệu ở dạng nhị phân, nên thao tác tìm kiếm và lọc sẽ nhanh hơn nhiều.

Nhưng mà nếu không có index thì việc tìm kiếm trên JSONB sẽ khá là chậm, nhất là khi bảng có hàng ngàn hoặc hàng triệu dòng. Ví dụ, tưởng tượng tụi mình có một bảng lưu thông tin người dùng, trong đó cột settings lưu cài đặt của từng user dưới dạng JSONB. Nếu thử tìm tất cả user có một giá trị nhất định trong cài đặt mà không có index — thì đúng là tốn tài nguyên cực kỳ. Và đây là lúc index cứu cánh tụi mình!

Đánh chỉ mục JSONB: những điểm cần nhớ

Để làm việc với JSONB, PostgreSQL hỗ trợ đánh chỉ mục theo hai cách chính:

  1. GIN (Generalized Inverted Index) — để tìm kiếm theo key và value bên trong JSONB.
  2. BTREE — cho việc tìm kiếm và sắp xếp đơn giản.

Mỗi loại có đặc điểm riêng. Cùng phân tích kỹ hơn nhé.

Index GIN cho JSONB

GIN là một loại index mạnh mẽ, hoạt động tốt với array, text, và cả dữ liệu JSONB. Nó sẽ "bẻ nhỏ" nội dung object JSONB thành từng key và value riêng biệt, tạo ra một cấu trúc đặc biệt để tìm kiếm nhanh hơn.

Ưu điểm của GIN cho JSONB:

  • Cho phép tìm kiếm cả theo key lẫn value.
  • Hỗ trợ tốt cho cấu trúc lồng nhau.
  • Tăng tốc các thao tác với các toán tử @>, ?, ?|, ?& (lọc key và value).

Giả sử tụi mình có bảng users, trong đó cột settings lưu cài đặt user dưới dạng JSONB. Ví dụ dữ liệu:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name TEXT,
    settings JSONB
);

INSERT INTO users (name, settings) VALUES
('Alice', '{"theme": "dark", "notifications": {"email": true, "sms": false}}'),
('Bob', '{"theme": "light", "notifications": {"email": false, "sms": true}}'),
('Charlie', '{"theme": "dark", "notifications": {"email": true, "sms": true}}');

Bây giờ tụi mình muốn tìm nhanh tất cả user có theme tối (theme: dark). Đầu tiên tạo index:

CREATE INDEX idx_users_settings_gin ON users USING GIN (settings);

Tiếp theo chạy truy vấn với toán tử @> (tìm theo value):

SELECT name
FROM users
WHERE settings @> '{"theme": "dark"}';

Bây giờ PostgreSQL sẽ dùng index GIN để tìm kiếm, và truy vấn sẽ chạy nhanh hơn hẳn.

Nó hoạt động thế nào? Khi bạn tạo GIN-index trên cột JSONB, PostgreSQL sẽ xây dựng một index "ngược", tức là tạo bản ghi riêng cho từng key và value trong JSON. Ví dụ, từ object:

{"theme": "dark", "notifications": {"email": true, "sms": false}}

nó sẽ tạo index cho các key theme, notifications.email, notifications.sms và các value tương ứng. Nhờ vậy, tìm kiếm từng phần tử sẽ nhanh hơn nhiều.

Index BTREE cho JSONB

BTREE là loại index kinh điển. Nó dùng khi bạn cần so sánh toàn bộ object JSONB hoặc sắp xếp. Nhưng khác với GIN, BTREE không phân tích nội dung object JSON.

Ưu điểm của BTREE cho JSONB:

  • Rất hợp cho thao tác sắp xếp và so sánh object.
  • Nhanh hơn nếu JSONB dùng như một "khối nguyên" (ví dụ bạn so sánh nó với object khác hoặc tìm dòng mà JSONB bằng một giá trị cụ thể).

Lấy ví dụ dùng index BTREE. Giả sử trong bảng users tụi mình thường xuyên so sánh cột settings với một object cụ thể:

{"theme": "dark", "notifications": {"email": true, "sms": false}}

Đầu tiên tạo index:

CREATE INDEX idx_users_settings_btree ON users USING BTREE (settings);

Bây giờ có thể chạy truy vấn so sánh object:

SELECT name
FROM users
WHERE settings = '{"theme": "dark", "notifications": {"email": true, "sms": false}}';

Truy vấn này sẽ dùng index BTREE để tăng tốc.

So sánh GINBTREE

Đặc điểm GIN BTREE
Phân tích object JSONB Có, phân tích thành key và value Không, so sánh toàn bộ
Tìm kiếm trong cấu trúc lồng nhau Không
Sắp xếp Không
Kích thước index Lớn hơn Nhỏ hơn
Toán tử hỗ trợ @>, ?, `? ,?&`

Vậy nên, GIN hợp cho truy vấn phức tạp, còn BTREE thì tốt khi cần so sánh nguyên object hoặc sắp xếp.

Nên chọn index nào?

  • Nếu bạn muốn tìm kiếm theo từng key hoặc value trong JSONB, hãy dùng GIN.
  • Nếu cần so sánh hoặc sắp xếp toàn bộ object JSONB, BTREE sẽ hợp hơn.

Nhưng nhớ là không ai cấm bạn kết hợp cả hai loại index! Ví dụ, bạn có thể tạo cả GIN lẫn BTREE trên cùng một trường nếu bảng cần cả hai loại truy vấn.

Những lỗi phổ biến khi đánh chỉ mục JSONB

Tạo index không cần thiết: không phải lúc nào cũng nên index mọi trường JSONB. Index chiếm dung lượng và có thể làm chậm thao tác insert/update.

Index cho toán tử ít dùng: đừng index chỉ vì "cảm thấy đúng". Hãy phân tích truy vấn và chỉ index khi thực sự cần tăng tốc thao tác.

Bỏ qua đặc điểm của GIN: GIN có thể mất nhiều thời gian tạo index hơn BTREE. Nên nhớ điều này khi index bảng lớn.

Ứng dụng thực tế

Làm việc với JSONB rất hữu ích trong các dự án thực tế, nơi dữ liệu linh hoạt và thay đổi liên tục. Ví dụ:

  • Web app với cài đặt người dùng.
  • Lưu log có nhiều trường khác nhau cho từng sự kiện.
  • Cache dữ liệu dạng JSON.

Đánh chỉ mục các dữ liệu này bằng GINBTREE giúp tăng tốc truy vấn đáng kể. Ví dụ, khi đi phỏng vấn bạn có thể show cách mình đã tăng tốc hệ thống bằng cách thêm index cho cấu trúc dữ liệu phức tạp.

Tài liệu chính thức về index JSON của PostgreSQL có ở đây. Đừng quên ghé qua đó để xem chi tiết và ví dụ nhé.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION