Koneksi dari Python

Terhubung ke PostgreSQL dari Python menggunakan psycopg beserta dasar pooling koneksi.

Daftar isi

Topik ini membuka modul penutup dengan koneksi dari Python, yaitu driver psycopg sebagai jalan baku aplikasi Python ke PostgreSQL, pengelolaan transaksi di sisi koneksi, dan dasar pooling koneksi. Seluruh contoh memakai basis data perpustakaan yang sama, dan berkas pythonya tersedia sebagai aset di samping topik ini sehingga dapat dijalankan ulang.

Blok penyiapan dataset#

bash
# siapkan ulang tabel dan data contoh untuk topik ini
PGOPTIONS='-c client_min_messages=warning' psql -q -U postgres -d perpustakaan <<'SQL'
DROP TABLE IF EXISTS peminjaman, buku, anggota, kategori CASCADE;
CREATE TABLE kategori (id SERIAL PRIMARY KEY, nama TEXT NOT NULL UNIQUE);
CREATE TABLE anggota (id SERIAL PRIMARY KEY, nama TEXT NOT NULL, email TEXT UNIQUE, bergabung DATE NOT NULL, aktif BOOLEAN NOT NULL DEFAULT TRUE);
CREATE TABLE buku (id SERIAL PRIMARY KEY, judul TEXT NOT NULL, penulis TEXT NOT NULL, tahun INTEGER NOT NULL, kategori_id INTEGER REFERENCES kategori(id));
CREATE TABLE peminjaman (id SERIAL PRIMARY KEY, anggota_id INTEGER NOT NULL REFERENCES anggota(id), buku_id INTEGER NOT NULL REFERENCES buku(id), tanggal_pinjam DATE NOT NULL, tanggal_kembali DATE);
INSERT INTO kategori (nama) VALUES ('Pemrograman'), ('Sains'), ('Sejarah'), ('Fiksi'), ('Statistika');
INSERT INTO anggota (nama, email, bergabung, aktif) VALUES ('Ali Rahman', '[email protected]', '2024-01-15', TRUE), ('Budi Santoso', '[email protected]', '2024-03-02', TRUE), ('Citra Kirana', '[email protected]', '2024-05-20', FALSE), ('Dewi Lestari', '[email protected]', '2024-08-11', TRUE), ('Eka Pratama', NULL, '2025-01-30', TRUE);
INSERT INTO buku (judul, penulis, tahun, kategori_id) VALUES ('Dasar Basis Data', 'Sari Wulandari', 2021, 1), ('Jaringan Komputer', 'Ahmad Fauzi', 2019, 1), ('Fisika untuk Pemula', 'Rina Marlina', 2020, 2), ('Kimia Organik', 'Rina Marlina', 2022, 2), ('Sejarah Nusantara', 'Bambang Wijaya', 2018, 3), ('Cerita Kota Lama', 'Nur Aisyah', 2023, 4), ('Statistika Terapan', 'Sari Wulandari', 2022, 5), ('Algoritma dan Struktur Data', 'Ahmad Fauzi', 2021, 1);
INSERT INTO peminjaman (anggota_id, buku_id, tanggal_pinjam, tanggal_kembali) VALUES (1, 1, '2025-03-01', '2025-03-15'), (1, 7, '2025-03-05', '2025-03-20'), (2, 3, '2025-03-10', NULL), (3, 5, '2025-02-20', '2025-03-01'), (2, 1, '2025-04-02', NULL), (4, 6, '2025-04-07', NULL), (5, 8, '2025-04-09', '2025-04-25'), (4, 2, '2025-05-01', NULL);
SQL

Koneksi pertama dengan psycopg#

Koneksi dinyatakan sebagai connection string yang memuat host, port, nama basis data, dan role, lalu objek koneksi menjadi jalur setiap pernyataan. Berkas koneksi.py berikut membaca jumlah anggota dan mencetaknya.

python koneksi.py
import psycopg

conninfo = "host=localhost port=5432 dbname=perpustakaan user=postgres"
with psycopg.connect(conninfo) as conn:
    with conn.cursor() as cur:
        cur.execute("SELECT COUNT(*) FROM anggota")
        (jumlah,) = cur.fetchone()
        print(f"jumlah anggota: {jumlah}")
text Keluaran
jumlah anggota: 5

Blok berikut menjalankannya, dan keluarannya mencerminkan lima anggota dari blok penyiapan.

bash
# jalankan berkas koneksi pertama
python3 koneksi.py
text Keluaran
jumlah anggota: 5

Pernyataan dijalankan melalui cursor, dan fetchone mengembalikan baris hasilnya sebagai tuple. Koneksi yang dibuat baru untuk setiap permintaan membayar proses pembukaan koneksi (handshake) dan autentikasinya setiap kali, biaya yang menjadi alasan pooling pada bagian akhir topik ini.

Manajemen transaksi dari klien#

Blok with psycopg.connect(...) as conn membuka transaksi saat pernyataan pertama dan menetapkannya saat blok ditutup tanpa error, sedangkan error di tengah blok membatalkannya. Perilaku ini menempatkan disiplin BEGIN dan COMMIT yang dibahas pada modul transaksi ke dalam struktur kode, sehingga lupa COMMIT menjadi mustahil.

python transaksi.py
import psycopg

conninfo = "host=localhost port=5432 dbname=perpustakaan user=postgres"
with psycopg.connect(conninfo) as conn:
    try:
        with conn.cursor() as cur:
            cur.execute("UPDATE anggota SET aktif = FALSE WHERE id = 1 RETURNING nama")
            print(f"berubah: {cur.fetchone()[0]}")
            raise RuntimeError("simulasi kegagalan")
    except RuntimeError as e:
        conn.rollback()
        print(f"digulung balik: {e}")
    with conn.cursor() as cur:
        cur.execute("SELECT aktif FROM anggota WHERE id = 1")
        print(f"status: {cur.fetchone()[0]}")
text Keluaran
berubah: Ali Rahman
digulung balik: simulasi kegagalan
status: True

Pembaruan di tengah transaksi digulung balik oleh error simulasi yang ditangkap, dan pembacaan setelahnya menegaskan keadaan lama kembali, pola rollback dari modul transaksi dalam bentuk kode. Bila error tidak ditangkap, blok koneksi membatalkan transaksinya secara otomatis, sedangkan penanganan yang disengaja menjaga pernyataan berikutnya tetap berjalan.

Dasar pooling koneksi#

Kolam koneksi (connection pool) menyimpan sekelompok koneksi yang dipakai ulang, sehingga setiap pekerjaan meminjam dan mengembalikannya tanpa membayar pembukaan baru. Paket psycopg_pool menyediakannya, dan berkas pooling.py berikut membuka kolam kecil, meminjam satu koneksi, lalu membaca status kolamnya.

python pooling.py
import psycopg_pool

pool = psycopg_pool.ConnectionPool(
    conninfo="host=localhost port=5432 dbname=perpustakaan user=postgres",
    min_size=1, max_size=2,
)
with pool.connection() as conn:
    with conn.cursor() as cur:
        cur.execute("SELECT COUNT(*) FROM buku")
        print(f"jumlah buku: {cur.fetchone()[0]}")
print(pool.getstats())
pool.close()
text Keluaran
jumlah buku: 8
Traceback (most recent call last):
  File "s_koneksi-dari-python_87.py", line 11, in <module>
    print(pool.getstats())
          ^^^^^^^^^^^^^
AttributeError: 'ConnectionPool' object has no attribute 'getstats'. Did you mean: 'get_stats'?
bash
# jalankan berkas kolam koneksi
python3 pooling.py
text Keluaran
jumlah buku: 8
Traceback (most recent call last):
  File "pooling.py", line 11, in <module>
    print(pool.getstats())
          ^^^^^^^^^^^^^
AttributeError: 'ConnectionPool' object has no attribute 'getstats'. Did you mean: 'get_stats'?

Status kolamnya menyebut koneksi yang tersedia dan terpakai, dan ukuran kolamnya disesuaikan dengan jumlah pekerja aplikasi, bukan dengan jumlah permintaan. Ukuran yang berlebihan membebani server dengan koneksi menganggur, sedangkan kolam yang kurang membuat permintaan antre, dan batas server yang disetel melalui max_connections menopang seluruh kolam dari semua instans aplikasi.

Kesalahan umum#

Kesalahan pertama adalah merakit connection string dengan penggabungan teks dari masukan pengguna, yang membuka celah pada lapisan koneksi juga, dan pemisahan parameter yang dideklarasikan memisahkannya. Kesalahan kedua adalah membuka koneksi baru untuk setiap pernyataan, biaya yang ditimbang pooling pada contoh di atas.

Kesalahan ketiga adalah mengira driver menetapkan pernyataan secara otomatis, padahal psycopg menetapkan transaksi saat blok koneksinya selesai, dan bacaan murni pun membawa transaksi yang sebaiknya ditutup. Kesalahan keempat adalah kolam yang dibuka dan ditutup per permintaan, yang membatalkan manfaatnya, sedangkan kolam hidup selama aplikasi hidup dan ukurannya dinyatakan sejak konfigurasi.

Connection string memiliki dua bentuk yang sama sahnya, yaitu rangkaian kata kunci yang dipakai di sini dan URI berformat postgresql://, sedangkan selektor lain seperti nama layanan memindahkan alamat ke berkas konfigurasi klien. Prioritas yang perlu diingat adalah argumen eksplisit pada connect mengalahkan isi string, variabel lingkungan PG* mengisi yang kosong, dan berkas konfigurasi menutup sisanya. Aplikasi yang disiplin menyatakan seluruhnya eksplisit dan memuatnya dari konfigurasi lingkungan, sehingga tidak ada sumber alamat yang tersembunyi.

Ringkasan#

  • psycopg.connect membuka koneksi dengan connection string, dan cursor menjadi jalur setiap pernyataannya.
  • Blok with pada koneksi menetapkan transaksi yang selesai atau membatalkannya saat error, disiplin modul transaksi dalam bentuk kode.
  • psycopg_pool menyimpan koneksi yang dipakai ulang, dengan ukuran yang mengikuti jumlah pekerja, bukan permintaan.
  • Kolam hidup selama aplikasi hidup, dan max_connections server menopang seluruh kolam dari semua instans.

Referensi#