JSON vs JSONB

Membandingkan penyimpanan teks json dan biner jsonb serta memilih waktunya.

Daftar isi

Topik ini membuka modul JSONB dengan membedah dua jenis data dokumen PostgreSQL, yaitu json yang menyimpan teks apa adanya dan jsonb yang menyimpan representasi binernya. Perbedaan penerimaan dan penyimpanannya terlihat langsung pada keluaran, dan keputusan memakai keduanya hampir selalu jatuh pada jsonb dengan satu pengecualian yang jelas.

Blok penyiapan dataset#

bash
# dataset profil pengguna aplikasi catatan untuk modul ini
PGOPTIONS='-c client_min_messages=warning' psql -q -U postgres -d perpustakaan <<'SQL'
DROP TABLE IF EXISTS profil, artikel CASCADE;
CREATE TABLE profil (
    id   SERIAL PRIMARY KEY,
    nama TEXT NOT NULL,
    data JSONB NOT NULL
);
INSERT INTO profil (nama, data) VALUES
    ('Ali Rahman', '{"nama_tampil": "Ali", "umur": 30, "kota": "Bandung", "minat": ["basis data", "membaca"]}'),
    ('Budi Santoso', '{"nama_tampil": "Budi", "umur": 24, "kota": "Yogyakarta", "minat": ["sejarah"]}'),
    ('Citra Kirana', '{"nama_tampil": "Citra", "umur": 28, "kota": "Bandung", "minat": ["botani", "memasak", "basis data"]}');
SQL

Tabel profil mewakili pola yang paling umum, yaitu kolom JSONB yang menyimpan sifat pengguna yang bentuknya tidak tetap di samping kolom terstruktur seperti nama.

json menyimpan teks, jsonb menyimpan struktur#

Jenis json menerima teks apa adanya dan tidak menguraikannya kecuali memastikan bentuknya sah, sehingga spasi berlebih, urutan kunci, dan kunci duplikat tetap tersimpan persis. Jenis jsonb menguraikannya menjadi struktur biner, dan proses itu menormalkan hasilnya, sebagaimana terlihat pada teks yang sama yang masuk ke dua kolom berbeda.

bash
# teks yang sama ke dua jenis kolom yang berbeda
psql -U postgres -d perpustakaan <<'SQL'
CREATE TABLE dua_json (
    versi_teks  JSON,
    versi_biner JSONB
);
INSERT INTO dua_json VALUES ('{"a": 1, "a": 2, "b":  "tiga"}', '{"a": 1, "a": 2, "b":  "tiga"}');
SELECT versi_teks, versi_biner FROM dua_json;
DROP TABLE dua_json;
SQL
text Keluaran
CREATE TABLE
INSERT 0 1
           versi_teks           |      versi_biner
--------------------------------+-----------------------
 {"a": 1, "a": 2, "b":  "tiga"} | {"a": 2, "b": "tiga"}
(1 row)

DROP TABLE

Kolom teks mengembalikan urutan kunci dan duplikatnya persis seperti masukan, sedangkan kolom biner mengurutkan kunci menurut panjang lalu abjadnya dan menyimpan nilai duplikat terakhir saja. Normalisasi inilah yang membuat dua dokumen jsonb dengan teks berbeda tetapi isi sama menjadi persis sama saat dibandingkan, sifat yang menjadi dasar indeks dan operatornya.

Keunggulan jsonb dan satu pengecualian json#

jsonb menang di hampir semua kebutuhan sehari-hari, yaitu operator penelusuran, perbandingan kesamaan, containment, dan pengindeksan GIN yang dibahas pada dua topik tersendiri. Kuncinya dideklarasikan sekali pada jenis kolomnya, sedangkan json hanya menerima pemeriksaan kesahan dan kesamaan teks, sehingga pencarian ke dalamnya menguraikan ulang teks setiap kali dibaca.

Pengecualian yang sah untuk json adalah kebutuhan menyalin teks dokumen persis, misalnya arsip log JSON yang isinya diperiksa kembali terhadap asalnya, karena normalisasi jsonb akan mengubah bentuknya. Di luar kebutuhan penyalinan persis itu, jsonb menjadi pilihan bawaan, sebagaimana dianjurkan dokumentasi resminya sendiri.

Pilihan desain di sekeliling jsonb#

Kolom JSONB paling tepat untuk sifat yang bentuknya beragam antar baris, sedangkan sifat yang bentuknya sama di seluruh baris sebaiknya disimpan sebagai kolom bertipe, karena tipe membawa aturan, lebih hemat ruang, dan dapat diindeks dengan btree biasa. Pola yang sehat mengangkat sifat yang sering disaring atau di-join ke kolom sungguhan, dan menyisakan sisa sifat yang jarang disentuh pada JSONB.

bash
# kesamaan dokumen biner dipakai untuk perbandingan langsung
psql -U postgres -d perpustakaan <<'SQL'
SELECT COUNT(*) AS profil_bandung FROM profil WHERE data = '{"kota": "Bandung"}';
DROP TABLE profil;
SQL
text Keluaran
 profil_bandung
----------------
              0
(1 row)

DROP TABLE

Keluaran nol menegaskan bahwa kesamaan penuh menuntut seluruh isi dokumen yang cocok, bukan satu kuncinya, dan penyaringan parsial itulah pekerjaan operator containment pada topik tersendiri. Tabel profil dihapus karena setiap topik modul ini membangun ulang datasetnya sendiri.

Kesalahan umum#

Kesalahan pertama adalah memakai JSONB untuk data yang bentuknya tetap, yang melepaskan tipe, aturan, dan index btree untuk memperoleh fleksibilitas yang tidak dibutuhkan. Kesalahan kedua adalah menyimpan array nilai sederhana sebagai JSONB padahal jenis array PostgreSQL melayani kebutuhan itu dengan operator dan GIN-nya sendiri, dan pilihannya mengikuti sifat data yang di dalamnya.

Kesalahan ketiga adalah mengira jsonb menghemat ruang, padahal representasi biner dan TOAST-nya biasanya lebih besar daripada teks yang rapat, biaya yang dibayar untuk kecepatan baca struktur. Kesalahan keempat adalah mengandalkan urutan kunci pada jsonb, karena urutannya dinormalkan dan bukan bagian dari makna dokumen.

Ringkasan#

  • json menyimpan teks dokumen apa adanya, sedangkan jsonb menyimpan struktur biner yang dinormalkan.
  • Normalisasi jsonb mengurutkan kunci dan memilih nilai duplikat terakhir, sehingga perbandingan dokumen menjadi eksak.
  • jsonb menang untuk semua kebutuhan query dan indeks, dan json hanya untuk penyalinan teks yang persis.
  • Sifat yang bentuknya tetap tetap menjadi kolom bertipe, dan JSONB untuk sifat yang beragam per baris.

Referensi#