Apa itu Database?
Jika Anda baru mengenal database, pikirkan sebagai lemari arsip terorganisir untuk data aplikasi Anda. Tanpa database, data hilang setiap kali server restart.
Database SQL (PostgreSQL, MySQL) = Spreadsheet terstruktur dengan aturan ketat
| id | nama | email | status |
|-----|---------|-----------------|--------|
| 1 | Ahmad | [email protected] | active |
| 2 | Budi | [email protected] | idle |
Database NoSQL (Redis, MongoDB) = Penyimpanan key-value yang fleksibel
"driver:1" → { nama: "Ahmad", status: "active", lokasiTerakhir: {...} }
Mengapa Kita Pakai Tiga Database Berbeda
Kebanyakan tutorial pakai satu database untuk semuanya. Di dunia nyata, data berbeda punya kebutuhan berbeda. Pendekatan ini disebut polyglot persistence.
| Database | Apa yang Kita Simpan | Mengapa Database Ini |
|---|---|---|
| PostgreSQL | Koordinat GPS, data sensor BBM | PostGIS untuk query geospasial, excellent untuk time-series |
| MySQL | Driver, kendaraan, invoice, jadwal | Dukungan transaksional matang, familiar tim |
| Redis | Cache, sesi, posisi kendaraan real-time | Pembacaan mikro-detik, sempurna untuk "Di mana truk X?" |
Analoginya seperti rumah sakit:
- Rekam medis (MySQL) — terstruktur, harus akurat, jarang berubah
- Data monitor jantung (PostgreSQL) — volume tinggi, time-stamped, butuh query khusus
- Display ruang saat ini (Redis) — harus update instan, data sementara
PostgreSQL: Data Telemetri
-- Telemetri GPS — menerima 1000+ baris per menit
CREATE TABLE telemetry_gps (
id BIGSERIAL PRIMARY KEY,
truck_id UUID NOT NULL,
latitude DECIMAL(10, 7) NOT NULL,
longitude DECIMAL(10, 7) NOT NULL,
speed SMALLINT DEFAULT 0,
recorded_at TIMESTAMPTZ NOT NULL
);
-- Partisi per bulan untuk performa
CREATE TABLE telemetry_gps_2026_05 PARTITION OF telemetry_gps
FOR VALUES FROM ('2026-05-01') TO ('2026-06-01');
-- Cari semua truk dalam radius 5km dari gudang
SELECT truck_id, latitude, longitude
FROM telemetry_gps
WHERE ST_DWithin(
ST_MakePoint(longitude, latitude)::geography,
ST_MakePoint(106.8456, -6.2088)::geography,
5000
) AND recorded_at > NOW() - INTERVAL '5 minutes';
MySQL: Data Bisnis
// Tabel deliveries — transaksi bisnis inti
Schema::create('deliveries', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->foreignUuid('vehicle_id')->constrained();
$table->foreignUuid('driver_id')->constrained();
$table->decimal('fuel_volume_liters', 10, 2);
$table->enum('status', ['scheduled', 'in_transit', 'delivered', 'cancelled']);
$table->timestamp('scheduled_at');
$table->timestamps();
$table->softDeletes(); // Jangan pernah hard-delete data bisnis
$table->index(['status', 'scheduled_at']);
});
Redis: Cache & Real-Time
// Pattern 1: Cache query database yang mahal
async function getFleetStats(): Promise<FleetStats> {
const cached = await redis.get('fleet:stats');
if (cached) return JSON.parse(cached);
const stats = await calculateFleetStats();
await redis.setex('fleet:stats', 60, JSON.stringify(stats));
return stats;
}
// Pattern 2: Simpan posisi kendaraan real-time
async function updateVehiclePosition(truckId: string, lat: number, lng: number) {
await redis.hset(`truck:${truckId}:position`, { lat, lng, updatedAt: Date.now() });
await redis.publish('fleet:position-update', JSON.stringify({ truckId, lat, lng }));
}
// Pattern 3: Rate limiting
async function checkRateLimit(clientIp: string): Promise<boolean> {
const current = await redis.incr(`ratelimit:${clientIp}`);
if (current === 1) await redis.expire(`ratelimit:${clientIp}`, 60);
return current <= 100;
}
Kesalahan Database Umum
Kesalahan 1: Masalah N+1 Query
// ❌ Buruk — 1 query untuk driver + N query untuk kendaraan
$drivers = Driver::all();
foreach ($drivers as $driver) {
echo $driver->vehicles->count(); // Setiap iterasi = 1 query lagi!
}
// ✅ Baik — 2 query total dengan eager loading
$drivers = Driver::with('vehicles')->get();
Kesalahan 2: Index yang Hilang
-- Tanpa index, query ini scan SETIAP baris
SELECT * FROM deliveries WHERE status = 'in_transit';
-- Tambah index dan hanya scan baris yang cocok
CREATE INDEX idx_deliveries_status ON deliveries (status, scheduled_at);
Kesalahan 3: Simpan Semua di Satu Database
Jangan simpan 10 juta koordinat GPS di instance MySQL yang sama yang menangani login user.
Selanjutnya
Di Part 6, kita akan menerapkan prinsip SOLID dan design pattern untuk me-refactor kode kita.
Ini adalah Part 5 dari seri Fleet Management System. Memahami kapan menggunakan database mana adalah skill yang membedakan developer senior.
