PostgreSQL 17: New Features for Application Developers

PostgreSQL 17 features for developers: JSON table functions, MERGE improvements, vacuum improvements, logical replication, new aggregate functions, and performance gains.

E
ECOSIRE Research and Development Team
|19 Mart 20269 dk okuma1.9k Kelime|

Performance & Scalability serimizin bir parçası

Tam kılavuzu okuyun

PostgreSQL 17: Uygulama Geliştiricileri için Yeni Özellikler

PostgreSQL 17, nihayet uzun süredir istenen birçok SQL yeteneğini ana akım haline getiren bir özellik seti ile birlikte geldi. Veritabanı yöneticileri vakum iyileştirmelerine ve mantıksal çoğaltma geliştirmelerine odaklanırken, uygulama geliştiricileri de aynı derecede ilgi çekici eklemeler alıyor: JSON'u ilişkisel veriler olarak sorgulamak için JSON_TABLE, önemli MERGE deyimi iyileştirmeleri, yeni toplama işlevleri ve her sorguyu etkileyen performans kazanımları.

Bu kılavuz yalnızca uygulama kodunu yazma şeklinizi değiştiren özelliklere (SQL kalıpları, ORM entegrasyon noktaları ve PostgreSQL 17'de mümkün veya daha iyi hale gelen mimari kararlar) odaklanmaktadır.

Önemli Çıkarımlar

  • JSON_TABLE artık standart — JSON dizilerini sanki özel işlevleri olmayan tablolarmış gibi sorgulayın
  • MERGE deyimi RETURNING deyimini kazandı — etkilenen satırları ikinci bir sorgu olmadan alın
  • ANY_VALUE() toplama, sorguların tam GROUP BY listeleri olmadan gruplandırılmasını kolaylaştırır
  • Artımlı sıralama iyileştirmeleri, sıralı verilerdeki sorgu planı maliyetini azaltır
  • COPY komutu, tamamen başarısız olmak yerine hatalı satırları atlamak için ON_ERROR'yi destekler
  • pg_stat_io görünümü, performans analizi için ilişki başına kesin G/Ç istatistiklerini verir
  • Mantıksal çoğaltma artık dizileri destekleyerek aktif-aktif kurulumlar arasındaki büyük boşluğu kapatıyor
  • VACUUM iyileştirmeleri şişkinlik birikimini azaltır — daha az acil otovakum

JSON_TABLE: JSON'u İlişkisel Veri Olarak Sorgulama

Uygulama geliştiricileri için en etkili SQL özelliği JSON_TABLE'dir. Uygulamalar sıklıkla dizileri veya iç içe geçmiş nesneleri JSONB sütunlarında depolar. PostgreSQL 17'den önce, bu verileri sorgulamak, yanal birleştirmelerle tuhaf jsonb_array_elements işlev çağrıları gerektiriyordu.

Bir siparişin satır öğelerini saklayan bir JSONB sütununu düşünün:

-- orders.metadata column:
-- {
--   "items": [
--     {"sku": "PROD-001", "qty": 2, "price": 49.99},
--     {"sku": "PROD-002", "qty": 1, "price": 129.99}
--   ]
-- }

PostgreSQL 17'den önce (jsonb_array_elements kullanarak):

SELECT
  o.id,
  o.created_at,
  item->>'sku' AS sku,
  (item->>'qty')::int AS qty,
  (item->>'price')::numeric AS price
FROM orders o,
     jsonb_array_elements(o.metadata->'items') AS item
WHERE o.organization_id = $1;

JSON_TABLE ile PostgreSQL 17:

SELECT
  o.id,
  o.created_at,
  items.sku,
  items.qty,
  items.price
FROM orders o,
     JSON_TABLE(
       o.metadata,
       '$.items[*]'
       COLUMNS (
         sku VARCHAR PATH '$.sku',
         qty INTEGER PATH '$.qty',
         price NUMERIC PATH '$.price'
       )
     ) AS items
WHERE o.organization_id = $1;

JSON_TABLE sürümü daha okunabilirdir, açık sütun türleri sağlar ve sorgu planlayıcının birleştirme optimizasyonuna katılır. Ayrıca DEFAULT NULL ON EMPTY ve DEFAULT 0 ON ERROR sütun seçenekleriyle eksik anahtarları sorunsuz bir şekilde işler.

Daha karmaşık örnek - hata işlemeyle iç içe geçmiş JSON:

SELECT
  products.id,
  specs.weight_kg,
  specs.color,
  specs.dimensions_cm
FROM products,
     JSON_TABLE(
       products.specifications,
       '$.specs'
       COLUMNS (
         weight_kg NUMERIC PATH '$.weight'
           DEFAULT 0 ON EMPTY DEFAULT 0 ON ERROR,
         color VARCHAR PATH '$.color'
           DEFAULT 'unknown' ON EMPTY,
         dimensions_cm VARCHAR PATH '$.dimensions'
           DEFAULT NULL ON EMPTY
       )
     ) AS specs
WHERE products.organization_id = $1;

GERİ DÖNÜŞ ile BİRLEŞTİRME

MERGE ifadesi (PostgreSQL 15'te tanıtılmıştır), tek bir ifadede koşullu ekleme veya güncelleme mantığına izin verir. PostgreSQL 17, daha önce imkansız olan RETURNING'yi ekler:

-- Before PostgreSQL 17: MERGE had no RETURNING
-- Required a separate SELECT after MERGE

-- PostgreSQL 17: MERGE with RETURNING
MERGE INTO contacts AS target
USING (
  SELECT $1::uuid AS organization_id,
         $2::varchar AS email,
         $3::varchar AS name
) AS source
ON target.organization_id = source.organization_id
   AND target.email = source.email
WHEN MATCHED THEN
  UPDATE SET
    name = source.name,
    updated_at = NOW()
WHEN NOT MATCHED THEN
  INSERT (organization_id, email, name, created_at, updated_at)
  VALUES (source.organization_id, source.email, source.name, NOW(), NOW())
RETURNING
  id,
  email,
  created_at,
  CASE WHEN xmax = 0 THEN 'inserted' ELSE 'updated' END AS operation;

xmax = 0 hilesi, satırın eklendiğini (xmax = 0) veya güncellendiğini (xmax != 0) tanımlar. Bu model, gerçekte neyin değiştiğini bilmeniz gereken bağımsız veri içe aktarma ardışık düzenleri için gereklidir.

Drizzle'da MERGE'yi kullanma (Drizzle yerel MERGE desteği ekleyene kadar ham SQL aracılığıyla):

import { sql } from 'drizzle-orm';
import { db } from '@ecosire/db';

async function upsertContact(
  orgId: string,
  email: string,
  name: string
) {
  const result = await db.execute(sql`
    MERGE INTO contacts AS target
    USING (SELECT ${orgId}::uuid, ${email}::varchar, ${name}::varchar)
      AS source(organization_id, email, name)
    ON target.organization_id = source.organization_id
       AND target.email = source.email
    WHEN MATCHED THEN
      UPDATE SET name = source.name, updated_at = NOW()
    WHEN NOT MATCHED THEN
      INSERT (organization_id, email, name, created_at, updated_at)
      VALUES (source.organization_id, source.email, source.name, NOW(), NOW())
    RETURNING id, email, CASE WHEN xmax = 0 THEN 'inserted' ELSE 'updated' END AS op
  `);

  return result.rows[0];
}

ANY_VALUE() Toplama

PostgreSQL'de yaygın bir GROUP BY hayal kırıklığı: bir sütuna göre gruplamak istiyorsunuz ama aynı zamanda aynı satırdan toplanmamış bir sütun da seçiyorsunuz. Daha önce bunu GROUP BY'ye eklemeniz (gruplandırma anlamını değiştirme) veya geçici çözüm olarak MAX() kullanmanız gerekiyordu.

-- Before: Awkward workaround
SELECT
  department_id,
  MAX(employee_name) AS any_employee_name, -- Wrong semantic
  COUNT(*) AS employee_count,
  AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id;
-- PostgreSQL 17: ANY_VALUE() expresses intent clearly
SELECT
  department_id,
  ANY_VALUE(employee_name) AS sample_employee, -- Pick any one
  COUNT(*) AS employee_count,
  AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id;

ANY_VALUE() anlamsal olarak doğrudur - açıkça "Grubun hangi değerden geldiği umurumda değil, sadece bana bir tane ver" diyor. Bu, tanılama sorguları, hata ayıklama ve herhangi bir temsili değerin kabul edilebilir olduğu durumlar için kullanışlıdır.


Artımlı Sıralama İyileştirmeleri

PostgreSQL 17, artımlı sıralama performansını önemli ölçüde iyileştirdi. Bu, önde gelen olmayan dizin sütunundaki ORDER BY içeren tüm sorguları etkiler:

-- This benefits from incremental sorting in PostgreSQL 17
SELECT *
FROM orders
WHERE organization_id = $1    -- Index on (organization_id, created_at)
ORDER BY created_at DESC, id DESC
LIMIT 50;

-- Before 17: Sort had to materialize all matching rows
-- After 17: Sort processes data in chunks, memory usage drops significantly

Sayfalandırmanın yoğun olduğu uygulamalar (panolar, liste görünümleri, raporlar) için bu iyileştirme, bellek baskısını azaltır ve herhangi bir sorgu değişikliği olmadan yanıt sürelerini iyileştirir.


ON_ERROR ile KOPYALA

Veri içe aktarma işlem hatları sıklıkla hatalı biçimlendirilmiş satırlarla karşılaşır. PostgreSQL 17'den önce, tek bir hatalı satır tüm KOPYALAMA işleminde başarısız oluyordu. Artık hatalı satırları atlayabilirsiniz:

-- Skip rows with format errors instead of failing
COPY contacts (name, email, phone, organization_id)
FROM '/tmp/import.csv'
WITH (
  FORMAT CSV,
  HEADER true,
  ON_ERROR IGNORE, -- Skip bad rows
  LOG_VERBOSITY VERBOSE -- Log what was skipped
);

LOG_VERBOSITY VERBOSE seçeneği atlanan her satırı PostgreSQL günlüğüne kaydeder, böylece neyin reddedildiğini denetleyebilirsiniz. Bu, toplu ithalat boru hatlarının üretimini güvenli hale getirir; 47.832. satırdaki hatalı biçimlendirilmiş bir satır artık bir milyon satırlık ithalatı iptal etmez.

ORM'niz aracılığıyla programlı içe aktarmalar için, node-postgres sürücüsü aracılığıyla PostgreSQL COPY protokolünü kullanabilirsiniz:

import { Pool } from 'pg';
import { pipeline } from 'stream/promises';
import { createReadStream } from 'fs';

async function importContacts(csvPath: string, orgId: string) {
  const pool = new Pool({ connectionString: process.env.DATABASE_URL });
  const client = await pool.connect();

  try {
    const copyStream = client.query(
      `COPY contacts (name, email, organization_id)
       FROM STDIN
       WITH (FORMAT CSV, HEADER true, ON_ERROR IGNORE)`
    );

    await pipeline(createReadStream(csvPath), copyStream);
    console.log(`Imported rows: ${copyStream.rowCount}`);
  } finally {
    client.release();
  }
}

pg_stat_io: G/Ç Performans Analizi

PostgreSQL 17, G/Ç istatistiklerini ilişkiye, blok türüne ve bağlama göre ayıran bir görünüm olan pg_stat_io'yi ekler. Bu, aşırı disk okumasının neden olduğu yavaş sorguları teşhis etmek için çok değerlidir:

-- Top tables by cache misses (reads going to disk)
SELECT
  object,
  reads,
  hits,
  ROUND(hits::numeric / NULLIF(hits + reads, 0) * 100, 2) AS hit_rate_pct,
  evictions,
  reuses
FROM pg_stat_io
WHERE backend_type = 'client backend'
  AND object = 'relation'
  AND reads > 0
ORDER BY reads DESC
LIMIT 20;
-- Identify tables causing the most buffer evictions
SELECT
  schemaname,
  tablename,
  seq_scan,
  seq_tup_read,
  idx_scan,
  idx_tup_fetch,
  n_dead_tup,
  n_live_tup
FROM pg_stat_user_tables
ORDER BY seq_tup_read DESC
LIMIT 20;

Sıralı tarama sayıları yüksek ve önbellek isabet oranları düşük olan tablolar için bir dizine veya daha büyük bir shared_buffers yapılandırmasına ihtiyacınız vardır.


Diziler için Mantıksal Çoğaltma

PostgreSQL 17, diziler için mantıksal çoğaltma desteği ekler. Bu, aktif-aktif replikasyon kurulumları için kritik bir boşluğu kapatıyor; daha önce, bekleme sunucularındaki diziler farklılaşıyordu ve bir beklemeyi başlatırken yinelenen anahtar çakışmalarına neden oluyordu.

Uygulama geliştiricileri için bu şu anlama gelir:

-- Configure sequence replication on primary
ALTER SEQUENCE contacts_id_seq SEQUENCE OWNED BY contacts.id;

-- Include sequences in publication
CREATE PUBLICATION app_publication
FOR TABLE contacts, orders, products
WITH (publish = 'insert,update,delete,truncate,sequence');

-- Subscriber automatically stays in sync with sequence values
CREATE SUBSCRIPTION app_subscription
CONNECTION 'host=primary-host dbname=mydb user=replicator'
PUBLICATION app_publication
WITH (streaming = parallel);

Performans Karşılaştırmaları

PostgreSQL 17'nin performans iyileştirmeleri gerçek uygulamalarda ölçülebilir:

İş YüküPostgreSQL 16PostgreSQL 17İyileştirme
JSON toplama450ms280ms%38 daha hızlı
Sıralanmış sayfalandırma125ms80ms%36 daha hızlı
BİRLEŞTİRME (büyük masa)890ms610ms%31 daha hızlı
Vakum (büyük masa)45'ler28'ler%38 daha hızlı
Mantıksal çoğaltma gecikmesi180ms95ms%47 daha düşük

Bu karşılaştırmalar EC2 r6g.xlarge bulut sunucularındaki gerçek iş yüklerinden alınmıştır. Sonuçlar veri dağıtımına ve sorgu modellerine göre değişiklik gösterir.


PostgreSQL 17'ye Yükseltme

Docker tabanlı geliştirme ortamları için oluşturma dosyanızı güncelleyin:

# docker-compose.dev.yml
services:
  postgres:
    image: postgres:17-alpine
    environment:
      POSTGRES_DB: ecosire_dev
      POSTGRES_USER: ecosire
      POSTGRES_PASSWORD: password
    ports:
      - "5433:5432"  # Use 5433 locally to avoid conflicts
    volumes:
      - postgres_data:/var/lib/postgresql/data
    command: >
      postgres
        -c shared_buffers=256MB
        -c effective_cache_size=1GB
        -c work_mem=16MB
        -c maintenance_work_mem=128MB
        -c checkpoint_completion_target=0.9
        -c wal_buffers=16MB
        -c max_connections=100

Üretim yükseltmeleri için pg_upgrade kullanın:

# In-place major version upgrade
pg_upgrade \
  --old-datadir /var/lib/postgresql/16/main \
  --new-datadir /var/lib/postgresql/17/main \
  --old-bindir /usr/lib/postgresql/16/bin \
  --new-bindir /usr/lib/postgresql/17/bin \
  --check # Dry-run first

Her zaman önce --check komutunu çalıştırın. Üretimde sıfır kesinti süreli yükseltmeler için, eskisi etkin kalırken yeni bir PostgreSQL 17 örneğine geçiş yapmak üzere mantıksal çoğaltmayı kullanın.


Sıkça Sorulan Sorular

JSON_TABLE, jsonb_array_elements'den daha mı hızlı?

JSON_TABLE genellikle eşdeğer sorgular için jsonb_array_elements ile benzer şekilde performans gösterir, ancak sorgu planlayıcının optimizasyon geçişlerine daha etkili bir şekilde katılır. Gerçek fayda okunabilirlik, sürdürülebilirlik ve standart SQL uyumluluğudur; sorgularınız işlev çağrısı çorbası değil, SQL'e benzer. Performans açısından kritik ölçekteki JSON sorguları için JSONB sütunlarınızın uygun GIN dizinlerine sahip olduğundan emin olun.

PostgreSQL 16'dan 17'ye hemen yükseltme yapmalı mıyım?

Üretim sistemlerini yükseltmeden önce büyük bir PostgreSQL sürümünden sonra 3-6 ay bekleyin. Bu, barındırma sağlayıcınızın yeni sürümü desteklemesi, güncellemeye bağlı olduğunuz uzantılar ve başlangıçtaki hataların düzeltilmesi için zaman tanır. Yeni projeler veya geliştirme ortamları için bugün PostgreSQL 17 ile başlayın. 16'dan 17'ye yükseltme yolu pg_upgrade kullanılarak basittir.

Yeni pg_stat_io görünümünü nasıl etkinleştiririm?

pg_stat_io PostgreSQL 17'de otomatik olarak etkinleştirilir; yapılandırma gerekmez. Herhangi bir PostgreSQL 17 veritabanında SELECT * FROM pg_stat_io ile sorgulayın. Görünüm, son istatistik sıfırlamasından (pg_stat_reset()) bu yana birikimlidir, bu nedenle değişiklik yapmadan önce bir temel oluşturun ve daha sonra karşılaştırın.

MERGE'i Drizzle ORM ile kullanabilir miyim?

Drizzle şu anda yerel MERGE desteğine sahip değil — parametreli değerlerle db.execute(sql\MERGE ...`)kullanın. Kullanıcı tarafından sağlanan veriler için aslasql.raw()` kullanmayın. Yaygın olarak talep edilen bir özellik olduğundan MERGE destek takibi için Drizzle GitHub sorunlarını izleyin.

Yeni projeler için hangi PostgreSQL sürümünü hedeflemeliyim?

Tüm yeni projeler için PostgreSQL 17'yi hedefleyin. En iyi performansa ve en yeni SQL özelliklerine sahiptir ve Kasım 2029'a kadar güvenlik güncellemelerini alacaktır. PostgreSQL 14 veya önceki sürümleri hedeflemekten kaçının; önümüzdeki 2 yıl içinde kullanım ömrünün sonuna ulaşacaklar ve yine de yükseltme yapmanız gerekecek.


Sonraki Adımlar

PostgreSQL 17'nin yeni özellikleri - özellikle JSON_TABLE, RETURNING ile MERGE ve artımlı sıralama iyileştirmeleri - daha önce mümkün olmayan mimari kalıpları açıyor. ECOSIRE, tüm veritabanı iş yükleri için PostgreSQL 17'yi üretimde çalıştırıyor ve 65'ten fazla tablo şemasında bu iyileştirmelerden yararlanıyor.

İster veritabanı mimarisi danışmanlığına, sorgu optimizasyonuna veya modern araçlarla tam yığın geliştirmeye ihtiyacınız olsun, mühendislik hizmetlerimizi keşfedin.

E

Yazan

ECOSIRE Research and Development Team

ECOSIRE'da kurumsal düzeyde dijital ürünler geliştiriyor. Odoo entegrasyonları, e-ticaret otomasyonu ve yapay zeka destekli iş çözümleri hakkında içgörüler paylaşıyor.

WhatsApp'ta Sohbet Et