<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>sade.dev — PostgreSQL Üretimde</title><description>Tek bir PostgreSQL kümesini üretimde ayakta tutmanın pratiği: bağlantı havuzu, yedekleme, okuma/yazma ayrımı, sıfır kesintili göç ve geri-dönüş testi.</description><link>https://sade.dev/tr/series/postgresql-in-production/</link><language>tr-TR</language><atom:link href="https://sade.dev/tr/series/postgresql-in-production/rss.xml" rel="self" type="application/rss+xml"/><item><title>pgBouncer auth_query ile Çoklu DB Kullanıcısı</title><link>https://sade.dev/tr/notes/pgbouncer-auth-query/</link><guid isPermaLink="true">https://sade.dev/tr/notes/pgbouncer-auth-query/</guid><description>pgBouncer kullanırken `userlist.txt` yerine `auth_query` ile dinamik kullanıcı doğrulamasının pratik yapılandırması.</description><pubDate>Sun, 19 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;pgBouncer&lt;/code&gt; connection pooler olarak harika; ancak default yapılandırması bir düşmanlığı kabul ediyor: kullanıcı listenizi manuel olarak &lt;code&gt;userlist.txt&lt;/code&gt;’ye yazmak zorundasınız. Her yeni DB kullanıcısı eklediğinizde dosyayı güncellemek, hash’i hesaplamak, pgBouncer’ı reload etmek. Operasyonel bir baş ağrısı.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;auth_query&lt;/code&gt; bunu çözüyor: pgBouncer doğrulama için &lt;strong&gt;PostgreSQL’in kendisine&lt;/strong&gt; sorgu atıyor.&lt;/p&gt;
&lt;h2 id=&quot;auth_user-kurulumu&quot;&gt;auth_user kurulumu&lt;/h2&gt;
&lt;p&gt;PostgreSQL tarafında düşük yetkili bir kullanıcı ve &lt;code&gt;pg_shadow&lt;/code&gt;’u okuyan bir fonksiyon oluşturuyoruz:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;sql&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;CREATE&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; ROLE&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; pgbouncer &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;LOGIN&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; PASSWORD&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;s3cr€t_p@ssw0rd&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;CREATE&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; SCHEMA&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; pgbouncer&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;GRANT&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; USAGE &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;ON&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; SCHEMA&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; pgbouncer &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;TO&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; pgbouncer;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;CREATE OR REPLACE&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; FUNCTION&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; pgbouncer&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;.user_lookup(&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; i_username &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;                                                 out&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; uname &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;                                                 out&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; phash &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;RETURNS&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; record &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;AS&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; $$&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;BEGIN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    SELECT&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; usename, passwd &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; pg_catalog&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt;pg_shadow&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    WHERE&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; usename &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; i_username &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;INTO&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; uname, phash;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    RETURN&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;END&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;$$ &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;LANGUAGE&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; plpgsql &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;SECURITY&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; DEFINER;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;REVOKE&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; ALL &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;ON&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; FUNCTION&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; pgbouncer&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt;user_lookup&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; PUBLIC;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;GRANT&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; EXECUTE&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; ON&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; FUNCTION&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; pgbouncer&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt;user_lookup&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;TO&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; pgbouncer;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;SECURITY DEFINER&lt;/code&gt; önemli — fonksiyon &lt;code&gt;pg_shadow&lt;/code&gt;’a erişim için tanımlayan kullanıcının yetkilerini kullanıyor. Aksi takdirde &lt;code&gt;pgbouncer&lt;/code&gt; rolü &lt;code&gt;pg_shadow&lt;/code&gt;’u okuyamaz.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;auth_query&lt;/code&gt; hedef veritabanının içinde çalışır; bu yüzden fonksiyonu pgBouncer’ın hizmet vereceği her veritabanına kurmanız gerekir.&lt;/p&gt;
&lt;h2 id=&quot;pgbouncer-config&quot;&gt;pgBouncer config&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;pgbouncer.ini&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;databases&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;* = &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;host&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;127.0.0.1 &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;port&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;5432&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;pgbouncer&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;listen_addr&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; 127.0.0.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;listen_port&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; 6432&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;auth_type&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; scram-sha-256&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;auth_user&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; pgbouncer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;auth_query&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; SELECT uname, phash FROM pgbouncer.user_lookup($1)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;pool_mode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; transaction&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;max_client_conn&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; 1000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;default_pool_size&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; 25&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Artık &lt;code&gt;userlist.txt&lt;/code&gt; yok. PostgreSQL’de yeni bir rol oluştursanız, pgBouncer restart’ı gerektirmeden doğrulama çalışıyor.&lt;/p&gt;
&lt;h2 id=&quot;md5-mi-scram-sha-256-mı&quot;&gt;md5 mi, scram-sha-256 mı?&lt;/h2&gt;
&lt;p&gt;PostgreSQL 14+ default olarak &lt;code&gt;scram-sha-256&lt;/code&gt; kullanıyor. pgBouncer bunu 1.11’den beri destekliyor; &lt;code&gt;auth_query&lt;/code&gt; ile SCRAM pass-through ise 1.14 ile geldi. Eski cluster’larda md5’e düşmek zorunda kalıyorsanız:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;auth_type&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; md5&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ama yeni kurulum yapıyorsanız &lt;code&gt;scram-sha-256&lt;/code&gt; kullanın — md5 artık zayıf.&lt;/p&gt;
&lt;h2 id=&quot;hangi-pool-mode-seçilmeli&quot;&gt;Hangi pool mode seçilmeli?&lt;/h2&gt;
&lt;p&gt;Üç mod var, çoğu Laravel/Django uygulaması için tercih:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;session&lt;/strong&gt; — bağlantı baştan sona aynı. Prepared statement’lar çalışır, ama pool faydası zayıf.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;transaction&lt;/strong&gt; — her transaction sonunda bağlantı serbest. En yaygın tercih. Protokol düzeyi prepared statement’lar 1.21+ gerektiriyor; 1.24’ten beri varsayılan olarak açık (&lt;code&gt;max_prepared_statements = 200&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;statement&lt;/strong&gt; — her query sonunda. Çoğu ORM ile sorunlu.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pratik: &lt;strong&gt;transaction&lt;/strong&gt;’la başlayın, sorun yaşarsanız session’a geçin.&lt;/p&gt;
&lt;h2 id=&quot;yaygın-hata-read-only-roleün-passwordü-yok&quot;&gt;Yaygın hata: read-only role’ün password’ü yok&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;auth_query&lt;/code&gt; &lt;code&gt;pg_shadow.passwd&lt;/code&gt;’i okuyor. Eğer kullanıcı SSO ile geliyorsa veya peer authentication kullanıyorsa, passwd alanı NULL’dur — pgBouncer login’i reddeder. Bu durumda &lt;code&gt;pg_hba.conf&lt;/code&gt; üzerinden trust veya peer kurmak gerekir, ama production’da bunu istemezsiniz.&lt;/p&gt;
&lt;h2 id=&quot;i̇zleme&quot;&gt;İzleme&lt;/h2&gt;
&lt;p&gt;pgBouncer kendi admin DB’sini sağlıyor:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;psql -p 6432 pgbouncer -U pgbouncer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt; SHOW POOLS;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt; SHOW STATS;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt; SHOW CLIENTS;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;cl_waiting &amp;gt; 0&lt;/code&gt; görüyorsanız &lt;code&gt;default_pool_size&lt;/code&gt;’ı arttırmak gerekiyor olabilir.
&lt;code&gt;sv_idle&lt;/code&gt; çok yüksekse tam tersi — pool overprovision’da.&lt;/p&gt;</content:encoded><category>postgresql</category><category>pgbouncer</category><category>production</category><category>Note</category><author>info@muhammetsafak.com.tr (Muhammet Şafak)</author></item><item><title>pgBackRest ile PostgreSQL Backup Stratejisi</title><link>https://sade.dev/tr/notes/pgbackrest-postgresql-backup-stratejisi/</link><guid isPermaLink="true">https://sade.dev/tr/notes/pgbackrest-postgresql-backup-stratejisi/</guid><description>pg_dump tek başına yedek değil. pgBackRest ile PITR destekli, sıkıştırılmış, doğrulanabilir yedek mimarisi.</description><pubDate>Fri, 08 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;pg_dump&lt;/code&gt; ile uğraşmayı bıraktığım gün, sistem yazılım mühendisi olarak ciddiyetimin arttığını düşünüyorum. &lt;code&gt;pg_dump&lt;/code&gt; bir araştırma aracı — production yedek aracı değil.&lt;/p&gt;
&lt;p&gt;Üretim yedeği üç şeyi sağlamalı:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Point-in-time recovery (PITR).&lt;/strong&gt; “Dün gece 02:13’te silindi” diyebilmek.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bütünlük doğrulaması.&lt;/strong&gt; Yedek dosyasının açıldığında çalıştığını test etmek.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Saklamayı politikayla yönetmek.&lt;/strong&gt; 6 saatlik/günlük/haftalık rotasyon.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;pg_dump&lt;/code&gt; üç maddeyi de karşılamıyor.&lt;/p&gt;
&lt;h2 id=&quot;pgbackrest-kurulumu&quot;&gt;pgBackRest kurulumu&lt;/h2&gt;
&lt;p&gt;Ben hem yerel hem de S3 yedeği almayı tercih ve tavsiye ediyorum. Yerel yedekler hızlı, S3 yedekleri ise yangına dayanıklı.&lt;/p&gt;
&lt;h3 id=&quot;off-site-kopya&quot;&gt;Off-site kopya&lt;/h3&gt;
&lt;p&gt;Repository tek bir diskte duruyorsa bir VPS yangını her şeyi siler. pgBackRest S3 veya S3-uyumlu (R2, MinIO) bucket’a yazabiliyor:&lt;/p&gt;
&lt;p&gt;Tercih: bucket’ı versiyon-kilitli ve &lt;strong&gt;delete’i yasaklayan&lt;/strong&gt; bir policy ile koruyun. Ransomware senaryosunda kritik.&lt;/p&gt;
&lt;p&gt;Maliyetleri uzun vadeli kontrol altında tutmak için bucket yaşam döngüsünü ayarlayın:&lt;/p&gt;
&lt;p&gt;30 Gün → Glacier Anında Alma → 120 Gün → Sil&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/pgbackrest.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;global&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;###################################&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;# Local Repository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;###################################&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo1-path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;/var/lib/pgbackrest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo1-retention-full&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo1-retention-diff&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;14&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo1-bundle&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;y&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo1-block&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;y&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo1-cipher-pass&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo1-cipher-type&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;aes-256-cbc&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;###################################&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;# AWS S3 Repository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;###################################&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo2-type&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;s3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo2-path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;/main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo2-s3-bucket&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo2-s3-endpoint&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;s3.{region_name}.amazonaws.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo2-s3-region&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;{region_name}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo2-s3-key&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo2-s3-key-secret&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo2-bundle&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;y&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo2-block&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;y&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo2-cipher-type&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;aes-256-cbc&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;repo2-cipher-pass&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;###################################&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;compress-type&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;zst&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;compress-level&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;process-max&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;start-fast&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;y&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;log-level-console&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;info&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;log-level-file&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;detail&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;log-path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;/var/log/pgbackrest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;pg1-path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;/var/lib/postgresql/16/main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;pg1-port&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;5432&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yetkileri sıkılaştırın:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; chown&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; postgres:postgres&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; /etc/pgbackrest.conf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; chmod&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt; 640&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; /etc/pgbackrest.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Stanza oluşturun:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; -u&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; postgres&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; pgbackrest&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --stanza=main&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; stanza-create&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;postgresql.conf&lt;/code&gt; tarafında ise WAL archiving’i pgBackRest’e yönlendiriyoruz:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;archive_mode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; on&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;archive_command&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;pgbackrest --stanza=main archive-push %p&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;archive_timeout&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;wal_level&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; replica&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;archive_timeout = 60&lt;/code&gt; — son 60 saniyenin işlemleri kaybedilebilir; çoğu uygulama için yeterli, finansal işlemler için değil.&lt;/p&gt;
&lt;h2 id=&quot;backup-planı&quot;&gt;Backup planı&lt;/h2&gt;
&lt;p&gt;Cron tarafında:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Yerel repository (repo1)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;0 2 * * 0          pgbackrest --stanza=main --repo=1 --type=full backup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;0 2 * * 1-6        pgbackrest --stanza=main --repo=1 --type=diff backup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;0 0,6,12,18 * * *  pgbackrest --stanza=main --repo=1 --type=incr backup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# S3 repository (repo2)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;0 4 * * 0          pgbackrest --stanza=main --repo=2 --type=full backup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;0 4 * * 1-6        pgbackrest --stanza=main --repo=2 --type=diff backup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;0 3,9,15,21 * * *  pgbackrest --stanza=main --repo=2 --type=incr backup&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;İki repo yapılandırıldığında &lt;code&gt;--repo&lt;/code&gt; şart: verilmezse pgBackRest yalnızca en yüksek öncelikli repoya (repo1) yedek alır, S3’te arşivlenen WAL’dan başka bir şey durmaz — tek başına restore edilemeyen bir kopya.&lt;/p&gt;
&lt;p&gt;Her repoda haftalık full, günlük diff, 6 saatlik incremental. Storage tüketimi şaşırtıcı derecede düşük — konfigürasyondaki &lt;code&gt;repoN-bundle&lt;/code&gt;/&lt;code&gt;repoN-block&lt;/code&gt; satırları block incremental yedeği açıyor, böylece pgBackRest dosyanın tamamını değil yalnızca değişen bloklarını kopyalıyor; ikisi de varsayılanda kapalı.&lt;/p&gt;
&lt;h2 id=&quot;bütünlük-yedek-varsa-restore-edilebilir-mi&quot;&gt;Bütünlük: yedek varsa restore edilebilir mi?&lt;/h2&gt;
&lt;p&gt;Yedeği almak yeterli değil. Haftada bir restore tatbikatı:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;pgbackrest&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --stanza=main&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --delta&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; restore&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --pg1-path=/tmp/pg_restore_test&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sonra o instance’ı geçici bir port üzerinde başlatıp birkaç sanity query çalıştırıyorum. Bu adım atlanırsa yedek &lt;strong&gt;sahte güven&lt;/strong&gt; olur.&lt;/p&gt;
&lt;h2 id=&quot;pitr-provası&quot;&gt;PITR provası&lt;/h2&gt;
&lt;p&gt;Belirli bir zamana geri dönmek:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;pgbackrest&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --stanza=main&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt;  --type=time&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt;  --target=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;2026-05-07 02:13:00+03&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;  restore&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bu komutun &lt;em&gt;gerçek bir incident’te&lt;/em&gt; ilk kez çalıştırılması felakettir. Provayı önceden yapın — adımlar runbook’a yazılsın.&lt;/p&gt;
&lt;h2 id=&quot;saklama&quot;&gt;Saklama&lt;/h2&gt;
&lt;p&gt;Repository’deki kullanılmayan yedeklerin temizliği &lt;code&gt;expire&lt;/code&gt; komutuyla:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;pgbackrest&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --stanza=main&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; expire&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;repo1-retention-full=4&lt;/code&gt; ile son 4 full yedek tutuluyor; daha eskileri (ve onlara bağlı diff/incr’ler) otomatik temizleniyor.&lt;/p&gt;
&lt;h2 id=&quot;sonuç&quot;&gt;Sonuç&lt;/h2&gt;
&lt;p&gt;Schema kopyalamak veya küçük bir tabloyu taşımak için &lt;code&gt;pg_dump&lt;/code&gt; hâlâ doğru araç. Ama PITR isteyen herhangi bir sistemde yedek yazılımı pgBackRest (veya barman, ya da veritabanı sağlayıcının kendi aracı) olmalı. Backup’ın &lt;em&gt;test edilmediği&lt;/em&gt; anda “backup’ım var” demek yalan söylemenin sofistike bir yoludur.&lt;/p&gt;</content:encoded><category>postgresql</category><category>backup</category><category>production</category><category>pgbackrest</category><category>Note</category><author>info@muhammetsafak.com.tr (Muhammet Şafak)</author></item><item><title>Read/Write Splitting: Okuma ve Yazma Yükünü Ayırmak</title><link>https://sade.dev/tr/notes/read-write-splitting/</link><guid isPermaLink="true">https://sade.dev/tr/notes/read-write-splitting/</guid><description>Veritabanını büyütmeden önce: okuma trafiğini replica&apos;lara taşımak, replication lag&apos;in yarattığı tuzaklar ve ne zaman gerçekten gerektiği.</description><pubDate>Sat, 22 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Primary veritabanının CPU’su sürekli tavandaydı. İlginç olan şuydu: yazma oranı düşüktü. Yükün neredeyse tamamı okumaydı — rapor sayfaları, listeleme endpoint’leri, arama. Tek bir primary, kendisine hiç ihtiyaç duymayan bir sürü okumayı taşımaya çalışıyordu.&lt;/p&gt;
&lt;p&gt;Okuma ve yazma yükünü ayırmak — read/write splitting — bu tablonun bilinen çözümü. Ama doğru zamanda ve doğru tuzakların farkında uygulanmazsa, çözdüğünden çok problem getirir.&lt;/p&gt;
&lt;h2 id=&quot;çoğu-yük-okuma-ağırlıklıdır&quot;&gt;Çoğu yük okuma ağırlıklıdır&lt;/h2&gt;
&lt;p&gt;Trafiğin ne kadarının okuma olduğu iş yüküne bağlıdır: standart OLTP benchmark’larının ölçümünde TPC-E %90,69 okumadır, TPC-C ise %65,71’de kalır — yani kendi oranınızı varsaymak yerine ölçün. Her sipariş bir kez yazılır, ama onlarca kez okunur: listede, detayda, raporda, panelde. Bu asimetri, read/write splitting’i cazip kılan şeydir — çünkü ölçeklemeniz gereken taraf bellidir.&lt;/p&gt;
&lt;h2 id=&quot;önce-bu-gerçekten-bir-kapasite-sorunu-mu&quot;&gt;Önce: bu gerçekten bir kapasite sorunu mu?&lt;/h2&gt;
&lt;p&gt;Replica eklemeden önce durun. Primary’nin dolu olması her zaman “kapasite bitti” demek değildir. Çoğu zaman tek bir eksik indeks, primary’yi olduğundan kat kat meşgul gösterir.&lt;/p&gt;
&lt;p&gt;Bir replica eklemek — yeni bir sunucu, replication kurulumu, lag izleme — aslında bir &lt;code&gt;CREATE INDEX&lt;/code&gt;’in çözeceği bir problemi pahalıya satın almak olabilir. &lt;a href=&quot;/tr/systems/veri-yogunluklu-sistemler-kirilma-noktalari/&quot;&gt;Veri yoğunluklu sistemlerin kırılma noktaları&lt;/a&gt; yazısındaki sıra nettir: önce indeks disiplini, sonra replica. Bu sırayı atlamayın.&lt;/p&gt;
&lt;p&gt;Sorgularınızı &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; ile ölçün. Primary, doğru indekslenmiş sorgular altında gerçekten doyuyorsa — işte o zaman replica.&lt;/p&gt;
&lt;h2 id=&quot;replica-okumayı-ölçekler-yazmayı-değil&quot;&gt;Replica okumayı ölçekler, yazmayı değil&lt;/h2&gt;
&lt;p&gt;Net olalım: bir read replica, yazma kapasitenize &lt;strong&gt;hiçbir şey&lt;/strong&gt; katmaz. Aynı yazmalar her replica’da yeniden oynanır. Replica okuma yükü problemini çözer; yazma yükü probleminiz varsa replica yanlış araçtır.&lt;/p&gt;
&lt;h2 id=&quot;laravelde-kurulum&quot;&gt;Laravel’de kurulum&lt;/h2&gt;
&lt;p&gt;Laravel, okuma/yazma bağlantı ayrımını doğal destekler:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;// config/database.php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;pgsql&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;    &amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;driver&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;pgsql&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;    &amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;read&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;   =&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;host&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;10.0.0.2&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]],&lt;/span&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;   // replica&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;    &amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;write&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  =&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;host&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;10.0.0.1&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]],&lt;/span&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;   // primary&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;    &amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;sticky&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;    // ...ortak ayarlar&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;],&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;SELECT&lt;/code&gt;’ler replica’ya, &lt;code&gt;INSERT/UPDATE/DELETE&lt;/code&gt;’ler primary’ye gider. Replica’nın kendi bağlantı havuzu olur — primary’ninkinden ayrı; &lt;a href=&quot;/tr/notes/pgbouncer-auth-query/&quot;&gt;pgBouncer&lt;/a&gt; kullanıyorsanız ikisi ayrı havuzdur.&lt;/p&gt;
&lt;h2 id=&quot;replication-lag-asıl-fatura&quot;&gt;Replication lag: asıl fatura&lt;/h2&gt;
&lt;p&gt;Replica, primary’nin birkaç milisaniye — yük altında birkaç saniye — gerisindedir. Bu gecikme, read/write splitting’in gerçek bedelidir ve adı &lt;strong&gt;read-after-write&lt;/strong&gt; problemidir.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sticky =&amp;gt; true&lt;/code&gt; bunu kısmen çözer: bir istek içinde yazma yaptıysanız, aynı istekteki sonraki okumalar primary’ye gider. Ama &lt;code&gt;sticky&lt;/code&gt; yalnızca &lt;strong&gt;tek bir istek&lt;/strong&gt; sınırında çalışır.&lt;/p&gt;
&lt;p&gt;Sınırın dışı hâlâ açıktır: kullanıcı profilini günceller (istek 1, primary’ye yazıldı), sonraki sayfaya geçer (istek 2, replica’dan okur) ve henüz replica’da oynanmamış eski profilini görür. Kullanıcı kendi yazdığı veriyi bayat görür. Bu bir bug gibi görünür ama aslında mimarinin kabul ettiği bir ödündür — bilerek kabul edilmesi gereken bir ödün.&lt;/p&gt;
&lt;h2 id=&quot;sorgu-sınıflandırma-disiplini&quot;&gt;Sorgu sınıflandırma disiplini&lt;/h2&gt;
&lt;p&gt;Read/write splitting’i kurmak, her okumanın şu soruya cevap vermesini gerektirir: &lt;strong&gt;bu sorgu bayat veri okuyabilir mi?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Replica’dan okunabilir:&lt;/strong&gt; listeler, raporlar, arama sonuçları, paneller. Birkaç saniye gecikme önemsiz.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Primary’den okunmalı:&lt;/strong&gt; hesap bakiyesi, stok adedi, yetki kontrolü, bir yazma kararının dayandığı her okuma.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bu sınıflandırma artık mimarinin bir parçasıdır ve dokümante edilmesi gerekir. Yeni gelen bir geliştiricinin “bu sorgu nereden okumalı” sorusuna bakacak bir yeri olmalı — yoksa sınıflandırma sessizce çürür.&lt;/p&gt;
&lt;h2 id=&quot;ne-zaman-gerçekten-gerekir&quot;&gt;Ne zaman gerçekten gerekir?&lt;/h2&gt;
&lt;p&gt;Read/write splitting şu üç koşul birlikte sağlandığında doğru hamledir:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;İndeks disiplini tamamlanmış; sorgular doğru indeksli ve hâlâ primary doyuyor.&lt;/li&gt;
&lt;li&gt;Yük ölçülmüş biçimde okuma ağırlıklı.&lt;/li&gt;
&lt;li&gt;Bayat-okuma sınıflandırmasını yapıp dokümante edecek disiplin var.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Bunlardan biri eksikse, replica’yı ertelemek daha ucuzdur.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;Read/write splitting, dikey büyümeden ucuz bir ölçek hamlesidir — ama bedava değildir. Bedeli replication lag’dir ve onu görmezden gelmek, kullanıcıya kendi verisini eski göstermekle ödenir.&lt;/p&gt;
&lt;p&gt;Okumayı ayırmadan önce, hangi okumanın bayatlığa dayanabileceğini bilin.&lt;/p&gt;</content:encoded><category>postgresql</category><category>database</category><category>scaling</category><category>performance</category><category>Note</category><author>info@muhammetsafak.com.tr (Muhammet Şafak)</author></item><item><title>Canlıda Sıfır Kesintiyle Şema Değiştirmek</title><link>https://sade.dev/tr/notes/zero-downtime-veritabani-gocleri/</link><guid isPermaLink="true">https://sade.dev/tr/notes/zero-downtime-veritabani-gocleri/</guid><description>Production&apos;da kesinti yaratmadan migration almak: geri uyumlu adımlar, çok aşamalı kolon değişiklikleri ve tablo kilitlenmesini önleme.</description><pubDate>Sat, 29 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Bir deploy, 50 milyon satırlık bir tabloda tek bir &lt;code&gt;ALTER TABLE&lt;/code&gt; çalıştırdı ve tabloyu dört dakika kilitledi. Dört dakika boyunca o tabloya değen her istek bekledi; site fiilen çöktü. Migration’ın kendisi doğruydu — sorun, onun tek adımda yapılmasıydı.&lt;/p&gt;
&lt;p&gt;Tehlikeli olan şema değişikliği değildir. Tehlikeli olan, şema değişikliğini tek seferde, geri uyumluluğu düşünmeden yapmaktır.&lt;/p&gt;
&lt;h2 id=&quot;locku-kim-alır&quot;&gt;Lock’u kim alır?&lt;/h2&gt;
&lt;p&gt;Her şema değişikliği aynı maliyette değildir. Modern PostgreSQL’de sabit &lt;code&gt;DEFAULT&lt;/code&gt; ile kolon eklemek bir metadata işlemidir — hızlıdır. Asıl tehlike, tabloyu uzun süre kilitleyen işlemlerdedir:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CREATE INDEX&lt;/code&gt; — &lt;code&gt;CONCURRENTLY&lt;/code&gt; olmadan tabloyu yazmaya kapatır.&lt;/li&gt;
&lt;li&gt;Tabloyu yeniden yazan &lt;code&gt;ALTER COLUMN ... TYPE&lt;/code&gt; değişiklikleri.&lt;/li&gt;
&lt;li&gt;Tüm tabloyu tarayan &lt;code&gt;NOT NULL&lt;/code&gt;, &lt;code&gt;CHECK&lt;/code&gt; veya foreign key eklemeleri.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bu işlemler güçlü bir lock alır; &lt;code&gt;ACCESS EXCLUSIVE&lt;/code&gt; alanlar — tablo yeniden yazımı ya da &lt;code&gt;NOT NULL&lt;/code&gt;/&lt;code&gt;CHECK&lt;/code&gt; eklemesi — tabloya değen her sorguyu, okuma dahil, kuyruğa sokar. Geri kalanı yalnızca yazmaları bloklar. Tablo büyükse kuyruk uzar.&lt;/p&gt;
&lt;h2 id=&quot;tehlikeli-olan-tek-adımdır&quot;&gt;Tehlikeli olan tek adımdır&lt;/h2&gt;
&lt;p&gt;Çözüm migration yapmamak değil; her tehlikeli migration’ı, her biri tek başına güvenli ve geri uyumlu olan küçük adımlara bölmektir. Bunun adı &lt;strong&gt;expand-contract&lt;/strong&gt; (genişlet–daralt) kalıbıdır.&lt;/p&gt;
&lt;p&gt;Bir kolonu yeniden adlandırmak istediğinizi düşünün. Tek adımda &lt;code&gt;RENAME COLUMN&lt;/code&gt;, eski kolonu okuyan çalışan kodu anında kırar. Bunun yerine üç deploy:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Genişlet.&lt;/strong&gt; Yeni kolonu ekle. Kod hem eskiye hem yeniye yazsın, hâlâ eskiden okusun.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Geçiş.&lt;/strong&gt; Eski veriyi yeni kolona batch’ler hâlinde taşı. Kod artık yeniden okusun.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Daralt.&lt;/strong&gt; Eski kolonu sil.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Her adım, hem bir önceki sürümdeki kod hem de yeni kod ile çalışır. Hiçbir an, çalışan kod ile şema uyumsuz değildir.&lt;/p&gt;
&lt;h2 id=&quot;güvenli-reçeteler&quot;&gt;Güvenli reçeteler&lt;/h2&gt;
&lt;p&gt;Sık yapılan değişikliklerin kesintisiz hâlleri:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Yeni &lt;code&gt;NOT NULL&lt;/code&gt; kolon.&lt;/strong&gt; Tek adımda &lt;code&gt;NOT NULL&lt;/code&gt; eklemek tabloyu tarar. Bölün:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;sql&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;-- 1. Önce nullable ekle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;ALTER&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; TABLE&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; orders &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;ADD&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; COLUMN &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; text&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;-- 2. Mevcut satırları batch&amp;#39;ler hâlinde doldur (uygulama tarafında)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5F6E5F;--shiki-dark:#8A9A8A&quot;&gt;-- 3. Kısıtı önce doğrulamadan ekle, sonra ayrı adımda doğrula&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;ALTER&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; TABLE&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; orders &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;ADD&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; CONSTRAINT&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; orders_status_not_null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    CHECK&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; IS NOT NULL&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;NOT&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; VALID;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;ALTER&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; TABLE&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; orders VALIDATE &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;CONSTRAINT&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; orders_status_not_null;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;NOT VALID&lt;/code&gt; ile eklenen kısıt yeni satırlar için hemen geçerlidir ama mevcut satırları taramaz; &lt;code&gt;VALIDATE&lt;/code&gt; ise tabloyu yalnızca &lt;code&gt;SHARE UPDATE EXCLUSIVE&lt;/code&gt; lock ile tarar — yazmaları engellemez.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;İndeks.&lt;/strong&gt; Her zaman &lt;code&gt;CONCURRENTLY&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;sql&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;CREATE&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; INDEX&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; CONCURRENTLY&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; idx_orders_status &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;ON&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; orders (&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Foreign key.&lt;/strong&gt; Aynı iki adımlı kalıp: önce &lt;code&gt;ADD CONSTRAINT ... NOT VALID&lt;/code&gt;, sonra &lt;code&gt;VALIDATE CONSTRAINT&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;lock_timeout&lt;/code&gt;.&lt;/strong&gt; Bir migration, uzun süren bir sorgunun arkasında lock için beklerken kendisi de arkasındaki her şeyi bloklar. Bunu önlemek için migration oturumuna kısa bir &lt;code&gt;lock_timeout&lt;/code&gt; verin — lock hemen alınamıyorsa migration beklemek yerine başarısız olsun, siz tekrar deneyin.&lt;/p&gt;
&lt;h2 id=&quot;kod-ve-şema-birlikte-uyumlu-olmalı&quot;&gt;Kod ve şema birlikte uyumlu olmalı&lt;/h2&gt;
&lt;p&gt;Expand-contract’ın özü tek bir kuralda: her ara adımda, hem &lt;strong&gt;çalışmakta olan eski kod&lt;/strong&gt; hem de &lt;strong&gt;yeni kod&lt;/strong&gt; mevcut şema ile çalışabilmeli.&lt;/p&gt;
&lt;p&gt;Veritabanı şeması, &lt;a href=&quot;/tr/journal/ileride-lazim-olur-kodunun-faturasi/&quot;&gt;ileride lazım olur&lt;/a&gt; yazısında “tek yönlü kapı” dediğim şeydir — geri almak pahalıdır. Bu yüzden değişikliği büyük ve geri dönülmez tek bir adım olarak değil, her biri ayrı ayrı geri alınabilir küçük adımlar olarak tasarlayın.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;Sıfır kesintili migration bir araç değil, bir disiplindir: her şema değişikliğini, çalışan kodun hiç fark etmeyeceği kadar küçük adımlara bölmek.&lt;/p&gt;
&lt;p&gt;Tehlikeli olan değişikliğin kendisi değil, onu tek nefeste yapmaktır.&lt;/p&gt;</content:encoded><category>postgresql</category><category>database</category><category>migrations</category><category>production</category><category>Note</category><author>info@muhammetsafak.com.tr (Muhammet Şafak)</author></item><item><title>İndeksler: Eksik Yavaşlatır, Fazlası Yazmayı Öldürür</title><link>https://sade.dev/tr/notes/indeks-yonetimi/</link><guid isPermaLink="true">https://sade.dev/tr/notes/indeks-yonetimi/</guid><description>Eksik indeks sorguyu yavaşlatır, gereğinden fazla indeks her yazma işlemini pahalılaştırır. Doğru dengeyi tahminle değil ölçümle kurmak.</description><pubDate>Sat, 05 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;İki karşıt ekip gördüm. Biri tabloya hiç indeks koymamıştı — her sorgu bir sequential scan, her liste sayfası saniyeler sürüyordu. Diğeri tam tersini yapmıştı: “ne olur ne olmaz” diye her kolona bir indeks açmıştı ve şimdi her &lt;code&gt;INSERT&lt;/code&gt; ağır aksak ilerliyordu.&lt;/p&gt;
&lt;p&gt;İkisi de aynı yanılgının iki ucu: indeksi ücretsiz bir hız kaynağı sanmak.&lt;/p&gt;
&lt;h2 id=&quot;i̇ndeks-ücretsiz-hız-değildir&quot;&gt;İndeks ücretsiz hız değildir&lt;/h2&gt;
&lt;p&gt;Bir indeks okumayı hızlandırır, çünkü veritabanı tüm tabloyu taramak yerine sıralı bir yapıya bakar. Ama o sıralı yapı kendi kendine güncel kalmaz: tablodaki bir &lt;code&gt;INSERT&lt;/code&gt;, o tablonun &lt;strong&gt;her indeksine&lt;/strong&gt; bir girdi yazar; bir &lt;code&gt;UPDATE&lt;/code&gt; de indeksli bir kolona dokunduğu anda aynısını yapar. İstisnalar var — indeksli hiçbir kolonu değiştirmeyen HOT güncellemesi indekslere dokunmaz, kısmi indeks yüklemi dışında kalan satırlar için güncellenmez, &lt;code&gt;DELETE&lt;/code&gt; ise indeks girdilerini temizlemeyi &lt;code&gt;VACUUM&lt;/code&gt;’a bırakır — ama bunlar istisna, varsayılan değil.&lt;/p&gt;
&lt;p&gt;Yani her indeks bir takastır: okuma hızını, yazma maliyetiyle satın alırsınız. Beş indeksli bir tabloya tek satır eklemek, altı yapıyı birden güncellemektir. İndeks “bedava okuma hızı” değil, “yazmayla ödenmiş okuma hızı”dır.&lt;/p&gt;
&lt;h2 id=&quot;eksik-indeks-tahmin-değil-ölçüm&quot;&gt;Eksik indeks: tahmin değil, ölçüm&lt;/h2&gt;
&lt;p&gt;Eksik indeksi sorgu planından bulun, tahminden değil:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;sql&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;EXPLAIN ANALYZE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;SELECT&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; FROM&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; orders &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;WHERE&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; status&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;pending&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Çıktıda büyük bir tabloda &lt;code&gt;Seq Scan&lt;/code&gt; görüyorsanız ve sorgu satırların küçük bir kısmını döndürüyorsa, muhtemelen bir indeks eksiktir. &lt;code&gt;pg_stat_user_tables&lt;/code&gt; tablosunda &lt;code&gt;seq_scan&lt;/code&gt; sayısı yüksek olan büyük tabloları tarayın — bunlar adaylarınızdır.&lt;/p&gt;
&lt;p&gt;İndeksi gerçek, yavaş bir sorgu için ekleyin. “İleride lazım olabilir” diye eklenen indeks, &lt;a href=&quot;/tr/journal/ileride-lazim-olur-kodunun-faturasi/&quot;&gt;spekülatif genelliğin&lt;/a&gt; veritabanı katmanındaki hâlidir.&lt;/p&gt;
&lt;h2 id=&quot;fazla-indeks-kullanılmayanı-bul&quot;&gt;Fazla indeks: kullanılmayanı bul&lt;/h2&gt;
&lt;p&gt;Ters yönde, hiç kullanılmayan indeksler her yazmayı boş yere pahalılaştırır. Onları da ölçümle bulun:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;sql&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;SELECT&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; indexrelname, idx_scan&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; pg_stat_user_indexes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;WHERE&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; idx_scan &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;idx_scan = 0&lt;/code&gt; olan bir indeks hiç okuma hızlandırmıyor ama her yazmaya maliyet ekliyor demektir. (Primary key ve unique kısıtların indekslerini elbette koruyun.) Bu, &lt;a href=&quot;/tr/systems/veri-yogunluklu-sistemler-kirilma-noktalari/&quot;&gt;veri yoğunluklu sistemlerin&lt;/a&gt; yazma kırılma noktasında sayılan ilk temizliktir.&lt;/p&gt;
&lt;h2 id=&quot;doğru-indeksi-nasıl-seçersiniz&quot;&gt;Doğru indeksi nasıl seçersiniz?&lt;/h2&gt;
&lt;p&gt;İndeks “var/yok”tan ibaret değildir; doğru türü ve şekli seçmek önemlidir:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bileşik indekste kolon sırası.&lt;/strong&gt; &lt;code&gt;(a, b)&lt;/code&gt; indeksi, &lt;code&gt;a&lt;/code&gt;’da eşitlik + &lt;code&gt;b&lt;/code&gt;’de aralık sorgusuna hizmet eder; &lt;code&gt;b&lt;/code&gt;’de tek başına yapılan bir arama da bu indeksi kullanabilir ama taranan indeks bölümünü daraltmaz — PostgreSQL 18 öncesinde indeksin tamamı taranır, 18’den itibaren skip scan optimizasyonu bunu daraltır. Eşitlikle filtrelenen kolonu başa, aralıkla filtreleneni sona koyun.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kısmi indeks (partial).&lt;/strong&gt; Sorgu hep aynı alt kümeye bakıyorsa indeksi de o alt kümeyle sınırlayın: &lt;code&gt;CREATE INDEX ... WHERE status = &amp;#39;active&amp;#39;&lt;/code&gt;. Daha küçük indeks, daha ucuz bakım.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Covering indeks.&lt;/strong&gt; &lt;code&gt;INCLUDE&lt;/code&gt; ile sık okunan kolonları indekse ekleyip veritabanının tabloya hiç gitmemesini sağlayabilirsiniz.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B-tree dışı türler.&lt;/strong&gt; Eşitlik/sıralama için B-tree; &lt;code&gt;jsonb&lt;/code&gt; ve tam metin için GIN; saf sıralı, append ağırlıklı veri için küçük ve ucuz BRIN.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;duplikat-ve-örtüşen-indeksler&quot;&gt;Duplikat ve örtüşen indeksler&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;(a)&lt;/code&gt; üzerindeki bir indeks, zaten &lt;code&gt;(a, b)&lt;/code&gt; indeksiniz varsa gereksizdir — bileşik indeks &lt;code&gt;a&lt;/code&gt; ile başlayan sorgulara da hizmet eder. Bu tür örtüşmeleri periyodik olarak temizleyin; her biri sessiz bir yazma vergisidir.&lt;/p&gt;
&lt;h2 id=&quot;denge-sorgu-planıyla-kurulur&quot;&gt;Denge sorgu planıyla kurulur&lt;/h2&gt;
&lt;p&gt;Tek kural: indeksi ne tahminle ekleyin ne tahminle silin. Bir indeks, gerçek bir sorgu planı onu istediği için eklenir; bir indeks, istatistikler onu kimsenin kullanmadığını gösterdiği için silinir. Eksik ile fazla arasındaki denge, hislerle değil, &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; ve &lt;code&gt;pg_stat_user_indexes&lt;/code&gt; ile kurulur.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;İndeks yönetimi, “daha çok indeks daha iyi” sanısından kurtulmakla başlar. Her indeks bir okuma kazancı ve bir yazma maliyetidir; iyi yönetim, bu ikisini ölçerek dengede tutmaktır.&lt;/p&gt;
&lt;p&gt;Eksik indeks sorguyu yavaşlatır; fazlası ise tüm tabloyu. İkisinden de ölçüm korur.&lt;/p&gt;</content:encoded><category>postgresql</category><category>database</category><category>performance</category><category>indexing</category><category>Note</category><author>info@muhammetsafak.com.tr (Muhammet Şafak)</author></item></channel></rss>