B-tree thường không giúp gì cho việc tìm bên trong document. jsonb cần GIN index, và chỉ một số toán tử mới dùng được nó.
sql
create index on events using gin (payload);
-- uses the GIN index (containment)
select * from events where payload @> '{"type": "order_paid"}';
-- does NOT use it: the ->> operator extracts text, GIN cannot help
select * from events where payload ->> 'type' = 'order_paid';@>(containment),?,?|,?&(tồn tại key) → dùng được GIN mặc định (jsonb_ops).jsonb_path_opsnhỏ và nhanh hơn nhưng chỉ hỗ trợ@>.- Nếu chỉ lọc theo một field cố định, một expression index trên
(payload ->> 'type')rẻ hơn nhiều so với GIN toàn bộ document.
Khi nào không nên dùng jsonb:
- Field được truy vấn và lọc thường xuyên → nên là cột thật, có kiểu và constraint. jsonb không có NOT NULL, FOREIGN KEY hay kiểm tra kiểu cho field bên trong.
- Document lớn và bị update thường xuyên → mỗi lần cập nhật ghi lại toàn bộ giá trị (đã TOAST), tạo bloat.
- Cần JOIN theo giá trị bên trong → phải ép kiểu ở mọi truy vấn, planner ước lượng số dòng kém.
jsonb phù hợp nhất cho phần thực sự không có schema cố định: payload webhook thô, metadata tuỳ biến theo khách hàng, snapshot audit.