Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.
01Apa perbedaan utama antara database SQL dan NoSQL?
Database SQL bersifat relasional: data dinormalisasi dan bisa di-query secara fleksibel dengan join, termasuk pertanyaan yang tidak direncanakan. Database NoSQL menata data di sekitar pencarian tertentu, sehingga query yang direncanakan cepat sedangkan yang tidak direncanakan bisa mahal dan lambat. Pilihan sebenarnya adalah fleksibilitas untuk pertanyaan masa depan melawan kecepatan untuk pertanyaan yang sudah diketahui.
02Apa saja empat jenis database NoSQL?
Empat keluarga utamanya adalah key-value (misalnya Redis atau DynamoDB), document (MongoDB atau Firestore), wide-column (Cassandra atau Bigtable), dan graph (Neo4j). Masing-masing dibentuk di sekitar satu jenis pencarian: lewat key, lewat dokumen, lewat baris yang dipartisi, atau lewat relasi.
03Bisakah PostgreSQL menggantikan MongoDB untuk proyek baru?
Sering kali bisa. Tipe jsonb di PostgreSQL menyimpan JSON dalam format biner, mendukung query containment dengan operator @>, dan bisa di-index dengan GIN. Anda tetap mendapat transaksi dan join untuk bagian data yang stabil, sementara bagian yang bervariasi disimpan di kolom jsonb.
04Kapan sebaiknya memilih NoSQL dibanding SQL?
Pilih NoSQL ketika ada access pattern spesifik yang terukur dan tidak bisa dilayani tabel relasional ber-index, misalnya volume tulis di atas kemampuan satu primary node atau traversal relasi yang dalam. Schema yang sering berubah saja belum cukup, karena jsonb menanganinya di dalam Postgres.
05Apa arti pemodelan data yang dimulai dari access pattern?
Artinya mendaftarkan query yang harus dijawab sistem, lengkap dengan frekuensi dan ukuran datanya, sebelum mendesain schema apa pun. Panduan DynamoDB menyarankan tidak mulai mendesain sebelum tahu pertanyaannya, dan MongoDB menyatakan data yang diakses bersama sebaiknya disimpan bersama. Schema mengikuti query, bukan sebaliknya.
Pilih database dari daftar query yang harus dijawab sistem, bukan dari label SQL atau NoSQL. Jadikan PostgreSQL sebagai default, karena tipe jsonb-nya mencakup sebagian besar kebutuhan schema fleksibel. Pindah ke key-value, document, wide-column, atau graph hanya jika satu access pattern, skala, atau bentuk data tidak bisa dilayani tabel ber-index.
Setiap kali saya memulai modul ERP atau POS baru, pertanyaan yang sama muncul di diskusi desain pertama: apakah yang satu ini butuh database lain? Pertanyaan itu biasanya dirumuskan sebagai SQL versus NoSQL, dan rumusan itu sudah kesalahan pertama, karena kita diminta memilih label sebelum tahu apa yang harus dilakukan sistemnya.
Tulisan ini menjawabnya seperti dokumentasi vendor: mulai dari query. Rujukannya adalah dokumentasi PostgreSQL, DynamoDB, MongoDB, dan Redis, ditambah Wikipedia untuk pembagian keluarga, serta satu contoh kerja kecil dengan hitungan yang ditunjukkan. Tidak ada benchmark di sini, karena tidak ada yang dijalankan untuk tulisan ini.
Apa perbedaan sebenarnya antara SQL dan NoSQL?
Perbedaannya ada pada di mana upaya desain dikeluarkan. Di database relasional Anda melakukan normalisasi dulu dan bertanya belakangan. AWS menjelaskan kontrasnya langsung: di RDBMS Anda mendesain demi fleksibilitas tanpa memikirkan detail implementasi, sedangkan di DynamoDB Anda mendesain schema khusus agar query yang paling umum dan penting menjadi cepat dan murah.
Jadi pertukarannya adalah fleksibilitas untuk pertanyaan di masa depan melawan kecepatan untuk pertanyaan yang sudah diketahui. Model relasional menjawab pertanyaan yang belum terpikirkan, dengan ongkos join. Store NoSQL menjawab pertanyaan yang sudah direncanakan dengan sangat efisien, dan panduan DynamoDB menyatakan terang bahwa query di luar cara yang direncanakan bisa mahal dan lambat.
Apa saja empat keluarga NoSQL dan untuk apa masing-masing?
Wikipedia mengelompokkan sistem NoSQL ke dalam empat model data utama, dan masing-masing dibentuk di sekitar satu jenis pencarian.
Key-value: Anda mengambil value lewat key-nya. Contoh yang disebut antara lain Redis, Memcached, dan Amazon DynamoDB. Redis melangkah lebih jauh dari string biasa dan menyediakan list, set, sorted set, hash, dan stream.
Document: dokumen mirip JSON yang dialamatkan lewat key dan bisa di-query berdasarkan field-nya. Contohnya MongoDB, CouchDB, dan Firestore.
Wide-column: baris dengan kumpulan kolom yang fleksibel dan tersebar di banyak node. Contohnya Cassandra, HBase, dan Bigtable.
Graph: data yang nilainya ada pada hubungan antar record. Contohnya Neo4j dan ArangoDB.
Model
Biasanya di-query lewat
Cocok untuk
Yang dikorbankan
Relasional SQL (PostgreSQL, MySQL)
Kolom mana pun, join antar tabel
Transaksi, reporting, pertanyaan yang belum diketahui
Perubahan schema butuh migration
Key-value (Redis, DynamoDB)
Key, atau kombinasi key dan sort key yang dirancang
Session, cache, counter, pencarian yang sudah diketahui dengan volume tinggi
Query ad hoc dan join
Document (MongoDB, Firestore)
ID dokumen dan field di dalam dokumen
Data yang dibaca dan ditulis sebagai satu unit utuh
Konsistensi antar dokumen, data terduplikasi saat di-embed
Wide-column (Cassandra, Bigtable)
Partition key yang dipilih sesuai query
Data sangat besar dan berat tulis di banyak node
Query yang fleksibel; tabel dibuat per query
Graph (Neo4j)
Traversal sepanjang relasi
Rekomendasi, jaringan fraud, jaringan dependensi
Engine kedua yang harus dijalankan untuk beban kerja tabular
Baca kolom terakhir dulu. Setiap keluarga membeli kekuatannya dengan menghilangkan sesuatu yang diberikan database relasional secara gratis, karena itu pertanyaan termurah untuk setiap keluarga adalah apa yang Anda korbankan.
Bagaimana memodelkan data dari access pattern lebih dulu?
Tulis access pattern sebelum menggambar schema apa pun. DynamoDB menjadikannya aturan: jangan mulai mendesain sebelum tahu pertanyaan yang harus dijawab, dan identifikasi dulu ukuran, bentuk, dan kecepatan data. MongoDB menyatakan prinsip yang sama dengan kata-katanya sendiri: data yang diakses bersama sebaiknya disimpan bersama. Berikut latihannya untuk sistem order, dengan carwash sebagai contoh.
// Worked example: a carwash order system. Five access patterns, written down FIRST.
// AP1 one order with its line items by orderId
// AP2 today's orders for one branch by branchId + date
// AP3 every order for one plate number by plateNo
// AP4 revenue per branch per day aggregate
// AP5 "which orders had a wax add-on?" ad hoc, not known in advance
// Key design in a DynamoDB-style single table (PK = partition key, SK = sort key):
// AP1 PK = ORDER#1042 SK = META | ITEM#1 | ITEM#2 | ITEM#3
// -> one Query returns 4 items (1 header + 3 line items) in one round trip
// AP2 GSI1PK = BRANCH#7#2026-10-10 GSI1SK = ORDER#1042
// -> one Query on a global secondary index
// AP3 GSI2PK = PLATE#B1234XYZ GSI2SK = ORDER#1042
// -> one Query on a second index
// AP4 no key serves it: needs a stream plus a counter item, or an export
// AP5 no key serves it at all
// Score: 3 of 5 patterns served by keys, 1 needs extra machinery, 1 is unserved.
// The same five in Postgres: 1 primary key + 2 indexes cover AP1-AP3,
// AP4 is a GROUP BY, AP5 is a WHERE clause. 5 of 5, no redesign.
Hitungannya adalah intinya. Lima pattern, tiga dilayani langsung oleh key, satu butuh stream dan counter, satu tidak terlayani sama sekali. Di Postgres kelimanya cukup dengan satu primary key dan dua index, ditambah GROUP BY dan klausa WHERE. Ini bukan vonis terhadap DynamoDB, melainkan ongkos dari kesepakatan query terencana yang dibuat terlihat.
Latihan yang sama juga menunjukkan kapan NoSQL menang. Jika AP1 dan AP2 adalah satu-satunya pattern dan berjalan pada volume yang tidak sanggup dilayani satu server Postgres, desain key di atas tepat, dan AP4 serta AP5 dijalankan di salinan data untuk analitik.
Beri angka pada setiap access pattern sebelum memilih: seberapa sering dijalankan, seberapa banyak data yang disentuh, seberapa cepat harus kembali. Pattern tanpa angka hanyalah harapan, dan harapan membuat semua database tampak sama bagusnya.
Mengapa Postgres JSONB sering sudah cukup?
Karena jsonb memberi bagian dari document database yang sebenarnya diinginkan kebanyakan tim, yaitu bentuk yang fleksibel untuk sebagian field, tanpa keluar dari engine relasional. Dokumentasi PostgreSQL menyebut bahwa kebanyakan aplikasi sebaiknya memilih jsonb daripada json: disimpan dalam format biner yang terurai, sedikit lebih lambat saat input tetapi jauh lebih cepat diproses karena tidak perlu parsing ulang. jsonb juga mendukung containment dengan operator @> dan index GIN.
CREATE TABLE orders (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
branch_id integer NOT NULL,
plate_no text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
total_idr integer NOT NULL, -- money and joins stay relational
attrs jsonb NOT NULL DEFAULT '{}' -- the flexible, per-service part
);
CREATE INDEX orders_branch_day ON orders (branch_id, created_at);
CREATE INDEX orders_plate ON orders (plate_no);
-- jsonb_path_ops: smaller and more specific than the default jsonb_ops,
-- but it only supports containment style operators (@>, @?, @@), not key-exists (?).
CREATE INDEX orders_attrs_gin ON orders USING GIN (attrs jsonb_path_ops);
-- Right: containment, which the GIN index can answer.
SELECT id FROM orders WHERE attrs @> '{"addons": ["wax"]}';
-- Wrong: extracting text with ->> bypasses the GIN index on the whole column.
-- You would need a separate btree index on that exact expression.
SELECT id FROM orders WHERE attrs ->> 'tier' = 'premium';
Pola di atas menjaga uang, cabang, dan timestamp sebagai kolom sungguhan, dan hanya bagian yang bervariasi yang masuk ke attrs. Dokumentasi mencantumkan dua operator class GIN: jsonb_ops bawaan meng-index setiap key dan value, sedangkan jsonb_path_ops biasanya jauh lebih kecil dan lebih spesifik tetapi hanya mendukung operator bergaya containment. Pilih berdasarkan operator yang benar-benar dipakai query Anda.
Kapan JSONB tidak cukup?
JSONB berhenti menjadi jawaban yang baik ketika masalahnya bukan bentuk data, melainkan skala atau model beban kerjanya. Tiga situasi biasanya memenuhi syarat.
Data melampaui satu primary node dan Anda perlu mempartisi penulisan ke banyak mesin sejak desain, wilayah yang memang dibangun untuk store wide-column dan key-value.
Pertanyaan intinya tentang relasi dengan banyak hop, yang di-traverse graph engine secara native dan diekspresikan SQL dengan recursive join.
Anda butuh primitif yang tidak dimiliki Postgres secara native, seperti sorted set, stream, atau struktur probabilistik dalam daftar tipe data Redis.
Perhatikan bahwa tidak satu pun alasannya sekadar schema yang sering berubah. Schema yang fleksibel saja bukan alasan untuk meninggalkan Postgres.
Dokumentasi PostgreSQL memperingatkan bahwa setiap update pada nilai jsonb mengambil row-level lock pada seluruh baris, dan menyarankan dokumen dijaga tetap kecil, idealnya setiap dokumen adalah satu datum atomik. Dua transaksi yang mengedit key berbeda di dokumen besar yang sama tetap saling antre.
Seperti apa checklist praktis untuk memilih?
Jalankan berurutan dan berhenti di langkah pertama yang memaksa sebuah keputusan.
Daftarkan setiap access pattern beserta frekuensi, ukuran data, dan target latency-nya. Jika tidak bisa mendaftarkannya, pilih PostgreSQL, karena ia menjawab pertanyaan yang belum terpikirkan.
Tandai tiap pattern sebagai pencarian lewat key, query lintas field, agregasi, atau traversal relasi.
Periksa apakah satu node Postgres dengan index yang tepat melayani semuanya pada volume yang disebutkan. Jika ya, berhenti.
Untuk pattern yang tidak terlayani, sebut satu keluarga yang dibangun untuk pencarian itu dan taruh hanya data tersebut di sana.
Hitung apa yang sekarang Anda operasikan: setiap datastore adalah hal lain yang harus di-backup, di-patch, dipantau, dan diamankan. Jaga jumlahnya serendah yang diizinkan pattern.
Langkah kelima paling sering dilewati. Polyglot persistence adalah desain yang sah, tetapi dibayar dengan operasional, bukan dengan schema.
Apa yang saya pilih untuk sistem ERP atau POS di satu VPS?
Default saya untuk ERP dan POS adalah PostgreSQL sebagai system of record, dengan jsonb untuk atribut yang benar-benar bervariasi, dan Redis di sampingnya sebagai cache untuk hot key. Order, invoice, dan pergerakan stok butuh transaksi dan reporting lintas tabel, persis yang diberikan kolom relasional pada tabel di atas.
Itu default yang saya pilih untuk deployment Docker di satu VPS, di mana setiap datastore tambahan adalah container lain yang harus di-backup dan dipantau, bukan aturan untuk semua sistem. Saya akan meninjaunya begitu ada access pattern terukur yang gagal di pemeriksaan langkah tiga, dan tidak sebelumnya.
Jangan memilih antara SQL dan NoSQL; pilihlah terhadap daftar access pattern. Mulai dari PostgreSQL dan jsonb, tambahkan store khusus hanya untuk satu pattern yang gagal di sana, dan hitung ongkos operasional setiap datastore yang Anda tambah.