PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Upgrade Berbasis Pemicu Kustom untuk PostgreSQL

ATURAN ke-1: Anda tidak memutakhirkan PostgreSQL dengan replikasi berbasis pemicu
ATURAN ke-2: Anda TIDAK memutakhirkan PostgreSQL dengan replikasi berbasis pemicu
ATURAN ke-3: Jika Anda memutakhirkan PostgreSQL dengan replikasi berbasis pemicu, bersiaplah untuk menderita. Dan persiapkan dengan baik.

Pasti ada alasan yang sangat serius untuk tidak menggunakan pg_upgrade untuk memutakhirkan PostgreSQL.

Oke, katakanlah Anda tidak dapat membayar lebih dari beberapa detik waktu henti. Gunakan pglogical.

OK katakanlah Anda menjalankan 9.3 dan karenanya tidak dapat menggunakan pglogical. Gunakan Londiste.

Tidak dapat menemukan README yang dapat dibaca? Gunakan SLONY.

Terlalu rumit? Gunakan replikasi streaming - promosikan slave dan jalankan pg_upgrade di atasnya - lalu alihkan aplikasi agar berfungsi dengan server baru yang dipromosikan.

Aplikasi Anda relatif intensif menulis sepanjang waktu? Anda melihat semua solusi yang mungkin dan masih ingin menyiapkan replikasi berbasis pemicu khusus? Ada hal-hal yang harus Anda perhatikan kemudian:

  • Semua tabel membutuhkan PK. Anda tidak boleh mengandalkan ctid (bahkan dengan autovacuum dinonaktifkan)
  • Anda harus mengaktifkan pemicu untuk semua tabel terikat batasan (dan mungkin memerlukan FK yang Ditangguhkan)
  • Urutan memerlukan sinkronisasi manual
  • Izin tidak direplikasi (kecuali Anda juga menyiapkan pemicu peristiwa)
  • Pemicu peristiwa dapat membantu dengan otomatisasi dukungan untuk tabel baru, tetapi lebih baik tidak terlalu memperumit proses yang sudah rumit. (seperti membuat pemicu dan tabel asing pada pembuatan tabel, juga membuat tabel yang sama di server asing, atau mengubah tabel server jauh dengan perubahan yang sama, yang Anda lakukan pada db lama)
  • Untuk setiap pemicu pernyataan kurang dapat diandalkan tetapi mungkin lebih sederhana
  • Anda harus membayangkan dengan jelas proses migrasi data yang sudah ada sebelumnya
  • Anda harus merencanakan aksesibilitas tabel terbatas saat menyiapkan dan mengaktifkan replikasi berbasis pemicu
  • Anda harus benar-benar mengetahui dependensi dan batasan relasi Anda sebelum melakukan ini.

Cukup peringatan? Anda sudah ingin bermain? Mari kita mulai dengan beberapa kode.

Sebelum menulis pemicu apa pun, kita harus membuat beberapa kumpulan data tiruan. Mengapa? Bukankah lebih mudah untuk memiliki pemicu sebelum kita memiliki data? Jadi data akan direplikasi ke cluster "upgrade" sekaligus? Tentu saja. Tapi lalu apa yang ingin kita tingkatkan? Cukup buat kumpulan data pada versi yang lebih baru. Jadi ya, jika Anda berencana memutakhirkan ke versi yang lebih tinggi dan perlu menambahkan beberapa tabel, membuat pemicu replikasi sebelum Anda memasukkan data, itu akan menghilangkan kebutuhan untuk menyinkronkan data yang tidak direplikasi nanti. Tapi tabel baru seperti itu, bisa kita katakan, adalah bagian yang mudah. Jadi, pertama-tama mari kita buat contoh kasus ketika kita memiliki data sebelum memutuskan untuk mengupgrade.

Mari kita asumsikan bahwa server yang sudah usang disebut p93 (terlama yang didukung) dan yang kita replikasi disebut p10 (11 sedang dalam perjalanan kuartal ini, tetapi masih belum terjadi):

\c PostgreSQL
select pg_terminate_backend(pid) from pg_stat_activity where datname in ('p93','p10');
drop database if exists p93;
drop database if exists p10;

Di sini saya menggunakan psql, sehingga dapat menggunakan \c meta-command untuk terhubung ke db lain. Jika Anda ingin mengikuti kode ini dengan klien lain, Anda harus menyambungkan kembali. Tentu saja Anda tidak memerlukan langkah ini jika Anda baru pertama kali menjalankannya. Saya harus membuat ulang kotak pasir saya beberapa kali, jadi saya menyimpan pernyataan…

create database p93; --old db (I use 9.3 as oldest supported ATM version)
create database p10; --new db 

Jadi kami membuat dua database baru. Sekarang saya akan menghubungkan ke yang ingin kita tingkatkan dan akan membuat beberapa tipe data funkey dan menggunakannya untuk mengisi tabel yang akan kita anggap sudah ada sebelumnya:

\c p93
create type myenum as enum('a', 'b');--adding some complex types
create type mycomposit as (a int, b text); --and again...
create table t(i serial not null primary key, ts timestamptz(0) default now(), j json, t text, e myenum, c mycomposit);
insert into t values(0, now(), '{"a":{"aa":[1,3,2]}}', 'foo', 'b', (3,'aloha'));
insert into t (j,e) values ('{"b":null}', 'a');
insert into t (t) select chr(g) from generate_series(100,240) g;--add some more data
delete from t where i > 3 and i < 142; --mockup activity and mix tuples to be not sequential
insert into t (t) select null;

Sekarang apa yang kita miliki?

  ctid   |  i  |           ts           |          j           |  t  | e |     c     
---------+-----+------------------------+----------------------+-----+---+-----------
 (0,1)   |   0 | 2018-07-08 08:03:00+03 | {"a":{"aa":[1,3,2]}} | foo | b | (3,aloha)
 (0,2)   |   1 | 2018-07-08 08:03:00+03 | {"b":null}           |     | a | 
 (0,3)   |   2 | 2018-07-08 08:03:00+03 |                      | d   |   | 
 (0,4)   |   3 | 2018-07-08 08:03:00+03 |                      | e   |   | 
 (0,143) | 142 | 2018-07-08 08:03:00+03 |                      | ð   |   | 
 (0,144) | 143 | 2018-07-08 08:03:00+03 |                      |     |   | 
(6 rows)

Oke, beberapa data - mengapa saya memasukkan dan kemudian menghapus begitu banyak? Nah, kami mencoba untuk meniru kumpulan data yang ada untuk sementara waktu. Jadi saya mencoba membuatnya sedikit tersebar. Mari pindahkan satu baris lagi (0,3) ke akhir halaman (0,145):

update t set j = '{}' where i =3; --(0,4)

Sekarang mari kita asumsikan kita akan menggunakan PostgreSQL_fdw (menggunakan dblink di sini pada dasarnya akan sama dan mungkin lebih cepat untuk 9.3, jadi silakan lakukan jika Anda mau).

create extension PostgreSQL_fdw;
create server p10 foreign data wrapper PostgreSQL_fdw options (host 'localhost', dbname 'p10'); --I know it's the same 9.3 server - change host to other version and use other cluster if you wish. It's not important for the sandbox...
create user MAPPING FOR vao SERVER p10 options(user 'vao', password 'tsun');

Sekarang kita dapat menggunakan pg_dump -s untuk mendapatkan DDL, tetapi saya hanya memilikinya di atas. Kita harus membuat tabel yang sama di cluster versi yang lebih tinggi untuk mereplikasi data ke:

\c p10
create type myenum as enum('a', 'b');--adding some complex types
create type mycomposit as (a int, b text); --and again...
create table t(i serial not null primary key, ts timestamptz(0) default now(), j json, t text, e myenum, c mycomposit);

Sekarang kita kembali ke 9.3 dan menggunakan tabel asing untuk migrasi data (saya akan menggunakan f_ konvensi untuk nama tabel di sini, f singkatan dari asing):

\c p93
create foreign table f_t(i serial, ts timestamptz(0) default now(), j json, t text, e myenum, c mycomposit) server p10 options (TABLE_name 't');

Akhirnya! Kami membuat fungsi insert dan trigger.

create or replace function tgf_i() returns trigger as $$
begin
  execute format('insert into %I select ($1).*','f_'||TG_RELNAME) using NEW;
  return NEW;
end;
$$ language plpgsql;

Di sini dan nanti saya akan menggunakan tautan untuk kode yang lebih panjang. Pertama, agar teks lisan tidak tenggelam dalam bahasa mesin. Kedua karena saya menggunakan beberapa versi dari fungsi yang sama untuk mencerminkan bagaimana kode harus berevolusi sesuai permintaan.

--OK - first table ready - lets try logical trigger based replication on inserts:
insert into t (t) select 'one';
--and now transactional:
begin;
  insert into t (t) select 'two';
  select ctid, * from f_t;
  select ctid, * from t;
rollback;
select ctid, * from f_t where i > 143;
select ctid, * from t where i > 143;

Hasil:

INSERT 0 1
BEGIN
INSERT 0 1
 ctid  |  i  |           ts           | j |  t  | e | c 
-------+-----+------------------------+---+-----+---+---
 (0,1) | 144 | 2018-07-08 08:27:15+03 |   | one |   | 
 (0,2) | 145 | 2018-07-08 08:27:15+03 |   | two |   | 
(2 rows)

  ctid   |  i  |           ts           |          j           |  t  | e |     c     
---------+-----+------------------------+----------------------+-----+---+-----------
 (0,1)   |   0 | 2018-07-08 08:27:15+03 | {"a":{"aa":[1,3,2]}} | foo | b | (3,aloha)
 (0,2)   |   1 | 2018-07-08 08:27:15+03 | {"b":null}           |     | a | 
 (0,3)   |   2 | 2018-07-08 08:27:15+03 |                      | d   |   | 
 (0,143) | 142 | 2018-07-08 08:27:15+03 |                      | ð   |   | 
 (0,144) | 143 | 2018-07-08 08:27:15+03 |                      |     |   | 
 (0,145) |   3 | 2018-07-08 08:27:15+03 | {}                   | e   |   | 
 (0,146) | 144 | 2018-07-08 08:27:15+03 |                      | one |   | 
 (0,147) | 145 | 2018-07-08 08:27:15+03 |                      | two |   | 
(8 rows)

ROLLBACK
 ctid  |  i  |           ts           | j |  t  | e | c 
-------+-----+------------------------+---+-----+---+---
 (0,1) | 144 | 2018-07-08 08:27:15+03 |   | one |   | 
(1 row)

  ctid   |  i  |           ts           | j |  t  | e | c 
---------+-----+------------------------+---+-----+---+---
 (0,146) | 144 | 2018-07-08 08:27:15+03 |   | one |   | 
(1 row)

Apa yang kita lihat di sini? Kami melihat bahwa data yang baru dimasukkan berhasil direplikasi ke database p10. Dan karenanya dibatalkan jika transaksi gagal. Sejauh ini baik. Tetapi Anda tidak dapat memperhatikan (ya, ya - tidak tidak) bahwa tabel pada p93 jauh lebih besar - data lama tidak dapat direplikasi. Bagaimana kita mendapatkannya di sana? Sederhana saja:

insert into … select local.* from ...outer join foreign where foreign.PK is null 

akan melakukan. Dan ini bukan perhatian utama di sini - Anda sebaiknya khawatir bagaimana Anda akan mengelola data yang sudah ada sebelumnya pada pembaruan dan penghapusan - karena pernyataan yang berhasil dijalankan pada versi db yang lebih rendah akan gagal atau hanya memengaruhi nol baris pada yang lebih tinggi - hanya karena tidak ada data yang sudah ada sebelumnya ! Dan di sini kita sampai pada detik-detik frase downtime. (Jika itu adalah film, tentu saja di sini kita akan memiliki kilas balik, tetapi sayangnya - jika frasa "detik henti" tidak menarik perhatian Anda sebelumnya, Anda harus pergi ke atas dan mencari frasa ...)

Untuk mengaktifkan semua pemicu pernyataan, Anda harus membekukan tabel, menyalin semua data, lalu mengaktifkan pemicu, sehingga tabel pada basis data versi yang lebih rendah dan lebih tinggi akan sinkron dan semua pernyataan akan memiliki kesamaan (atau sangat dekat, karena fisik distribusi akan berbeda, sekali lagi lihat di atas pada contoh pertama untuk kolom ctid) mempengaruhi. Tetapi menjalankan "nyalakan replikasi" seperti itu di atas meja dalam satu transaksi biiiiig tidak akan menjadi downtime dalam hitungan detik. Berpotensi itu akan membuat situs hanya-baca selama berjam-jam. Apalagi jika meja tersebut secara kasar terikat oleh FK dengan meja besar lainnya.

Nah read-only tidak downtime lengkap. Tetapi nanti kita akan mencoba untuk membiarkan semua SELECT dan beberapa INSERT, DELETE, UPDATE berfungsi (pada data baru, gagal pada yang lama). Memindahkan tabel atau transaksi ke read-only dapat dilakukan dengan banyak cara - apakah itu pendekatan PostgreSQL, atau level aplikasi, atau bahkan mencabut izin sementara. Pendekatan ini sendiri dapat menjadi topik untuk blognya sendiri, jadi saya hanya akan menyebutkannya.

Bagaimanapun. Kembali ke pemicu. Untuk melakukan tindakan yang sama, membutuhkan bekerja pada baris yang berbeda (PERBARUI, HAPUS) pada tabel jarak jauh seperti yang Anda lakukan di lokal, kita perlu menggunakan kunci utama, karena lokasi fisik akan berbeda. Dan kunci utama dibuat pada tabel yang berbeda dengan kolom yang berbeda, jadi kita harus membuat fungsi unik untuk setiap tabel atau mencoba menulis beberapa generik. Mari (untuk kesederhanaan) asumsikan kita hanya memiliki satu kolom PK, maka fungsi ini akan membantu. Jadi akhirnya! Mari kita memiliki fungsi pembaruan di sini. Dan jelas menjadi pemicu:

create trigger tgu before update on t for each row execute procedure tgf_u();
Unduh Whitepaper Hari Ini Pengelolaan &Otomatisasi PostgreSQL dengan ClusterControlPelajari tentang apa yang perlu Anda ketahui untuk menerapkan, memantau, mengelola, dan menskalakan PostgreSQLUnduh Whitepaper

Dan mari kita lihat apakah itu berhasil:

begin;
        update t set j = '{"updated":true}' where i = 144;
        select * from t where i = 144;
        select * from f_t where i = 144;
Rollback;

Menghasilkan:

BEGIN
psql:blog.sql:71: INFO:  (144,"2018-07-08 09:09:20+03","{""updated"":true}",one,,)
UPDATE 1
  i  |           ts           |        j         |  t  | e | c 
-----+------------------------+------------------+-----+---+---
 144 | 2018-07-08 09:09:20+03 | {"updated":true} | one |   | 
(1 row)

  i  |           ts           |        j         |  t  | e | c 
-----+------------------------+------------------+-----+---+---
 144 | 2018-07-08 09:09:20+03 | {"updated":true} | one |   | 
(1 row)

ROLLBACK

OKE. Dan selagi masih panas, mari tambahkan fungsi pemicu hapus dan replikasi juga:

create trigger tgd before delete on t for each row execute procedure tgf_d();

Dan periksa:

begin;
        delete from t where i = 144;
        select * from t where i = 144;
        select * from f_t where i = 144;
Rollback;

Pemberian:

DELETE 1
 i | ts | j | t | e | c 
---+----+---+---+---+---
(0 rows)

 i | ts | j | t | e | c 
---+----+---+---+---+---
(0 rows)

Seperti yang kami ingat (siapa yang bisa melupakan ini!) kami tidak mengubah dukungan "replikasi" dalam transaksi. Dan kita harus melakukannya jika kita menginginkan data yang konsisten. Seperti yang dikatakan di atas, SEMUA pemicu pernyataan pada SEMUA tabel terkait FK harus diaktifkan dalam satu transaksi, sebelumnya disiapkan dengan menyinkronkan data. Kalau tidak, kita bisa jatuh ke dalam:

begin;
        select * from t where i = 3;
        delete from t where i = 3;
        select * from t where i = 3;
        select * from f_t where i = 3;
Rollback;

Pemberian:

p93=# begin;
BEGIN
p93=#         select * from t where i = 3;
 i |           ts           | j  | t | e | c 
---+------------------------+----+---+---+---
 3 | 2018-07-08 09:16:27+03 | {} | e |   | 
(1 row)

p93=#         delete from t where i = 3;
DELETE 1
p93=#         select * from t where i = 3;
 i | ts | j | t | e | c 
---+----+---+---+---+---
(0 rows)

p93=#         select * from f_t where i = 3;
 i | ts | j | t | e | c 
---+----+---+---+---+---
(0 rows)

p93=# rollback;

Yayki! Kami menghapus satu baris pada db versi lebih rendah dan bukan pada yang lebih baru! Hanya karena itu tidak ada. Ini tidak akan terjadi jika kita melakukannya dengan cara yang benar (begin;sync;enable trigger;end;). Tetapi cara yang benar akan membuat tabel hanya-baca untuk waktu yang lama! Pembaca yang paling hard-core bahkan akan mengatakan 'mengapa Anda melakukan replikasi berbasis pemicu sama sekali?'.

Anda dapat melakukannya dengan pg_upgrade seperti yang dilakukan orang "normal". Dan dalam hal replikasi streaming, Anda dapat membuat semua set hanya-baca. Jeda pemutaran ulang xlog dan perbarui master saat aplikasi masih RO slave.

Tepat! Bukankah saya sudah memulainya?

Replikasi berbasis pemicu muncul di panggung ketika Anda membutuhkan sesuatu yang sangat istimewa. Misalnya, Anda dapat mencoba mengizinkan SELECT dan beberapa modifikasi pada data yang baru dibuat, bukan hanya RO. Katakanlah Anda memiliki kuesioner online - daftar pengguna, jawaban, dapatkan bonus-poin-bebas-lain-tidak-membutuhkan-barang-hebat dan pergi. Dengan struktur seperti itu, Anda hanya dapat melarang modifikasi pada data yang belum ada di versi yang lebih tinggi, memungkinkan seluruh aliran data untuk pengguna baru.

Jadi, Anda mengabaikan beberapa orang yang bekerja di ATM online, membiarkan pendatang baru bekerja tanpa menyadari bahwa Anda sedang melakukan peningkatan. Kedengarannya mengerikan, tetapi bukankah saya mengatakannya secara hipotetis? saya tidak? Yah, aku bersungguh-sungguh.

Tidak peduli apa kasus kehidupan nyata, mari kita lihat bagaimana Anda dapat menerapkannya. Fungsi hapus dan perbarui akan berubah. Dan mari kita periksa skenario terakhir sekarang:

BEGIN
psql:blog.sql:86: ERROR:  This data is not replicated yet, thus can't be deleted
psql:blog.sql:87: ERROR:  current transaction is aborted, commands ignored until end of transaction block
psql:blog.sql:88: ERROR:  current transaction is aborted, commands ignored until end of transaction block
ROLLBACK

Baris tidak dihapus pada versi yang lebih rendah, karena tidak ditemukan pada versi yang lebih tinggi. Hal yang sama akan terjadi dengan diperbarui. Cobalah sendiri. Sekarang Anda dapat memulai sinkronisasi data tanpa menghentikan banyak modifikasi pada tabel yang Anda sertakan ke dalam replikasi berbasis pemicu.

Apakah lebih baik? Lebih buruk? Ini berbeda - ia memiliki banyak kekurangan dan beberapa keunggulan dibandingkan sistem RO global. Tujuan saya adalah menunjukkan mengapa seseorang ingin menggunakan metode rumit seperti itu daripada biasanya - untuk mendapatkan kemampuan khusus melalui proses yang stabil dan terkenal. Dengan biaya tertentu tentunya…

Jadi, sekarang ketika kita merasa sedikit lebih aman untuk konsistensi data dan sementara data yang sudah ada sebelumnya di tabel t disinkronkan dengan p10, kita bisa membicarakan tabel lain. Bagaimana semuanya bekerja dengan FK (setelah semua yang saya sebutkan FK berkali-kali, saya harus memasukkannya ke dalam sampel). Nah, mengapa menunggu?

create table c (i serial, t int references t(i), x text);
--and accordingly a foreign table - the one on newer version...
\c p10
create table c (i serial, t int references t(i), x text);
\c p93
create foreign table f_c(i serial, t int, x text) server p10 options (TABLE_name 'c');
--let’s pretend it had some data before we decided to migrate with triggers to a higher version
insert into c (t,x) values (1,'FK');
--- so now we add triggers to replicate DML:
create trigger tgi before insert on c for each row execute procedure tgf_i();
create trigger tgu before update on c for each row execute procedure tgf_u();
create trigger tgd before delete on c for each row execute procedure tgf_d();

Pasti layak untuk membungkus ketiganya menjadi suatu fungsi dengan tujuan untuk "memicu" banyak tabel. Tapi saya tidak akan melakukannya. Karena saya tidak akan menambahkan tabel lagi - dua database relasi yang direferensikan sudah sangat kacau!

--now, what would happen if we tr inserting referenced FK, that does not exist on remote db?..
insert into c (t,x) values (2,'FK');
/* it fails with:
psql:blog.sql:139: ERROR:  insert or update on table "c" violates foreign key constraint "c_t_fkey"
a new row isn't inserted neither on remote, nor local db, so we have safe data consistencyy, but inserts are blocked?..
Yes untill data that existed untill trigerising gets to remote db - ou cant insert FK with before triggerising keys, yet - a new (both t and c tables) data will be accepted:
*/
insert into t(i) values(4); --I use gap we got by deleting data above, so I dont need to "returning" and know the exact id -less coding in sample script
insert into c(t) values(4);
select * from c;
select * from f_c;

Menghasilkan:

psql:blog.sql:109: ERROR:  insert or update on table "c" violates foreign key constraint "c_t_fkey"
DETAIL:  Key (t)=(2) is not present in table "t".
CONTEXT:  Remote SQL command: INSERT INTO public.c(i, t, x) VALUES ($1, $2, $3)
SQL statement "insert into f_c select ($1).*"
PL/pgSQL function tgf_i() line 3 at EXECUTE statement
INSERT 0 1
INSERT 0 1
 i | t | x  
---+---+----
 1 | 1 | FK
 3 | 4 | 
(2 rows)

 i | t | x 
---+---+---
 3 | 4 | 
(1 row)

Lagi. Sepertinya konsistensi data sudah ada. Anda juga dapat mulai menyinkronkan data untuk tabel c baru…

Lelah? Saya pasti.

Kesimpulan

Sebagai kesimpulan, saya ingin menyoroti beberapa kesalahan yang saya buat saat melihat ke dalam pendekatan ini. Ketika saya sedang membangun pernyataan pembaruan, secara dinamis mendaftar semua kolom dari pg_attribute, saya kehilangan cukup satu jam. Bayangkan betapa kecewanya saya setelah mengetahui bahwa saya benar-benar lupa tentang konstruksi UPDATE (daftar) =(daftar)! Dan fungsi menjadi lebih pendek dan lebih mudah dibaca.

Jadi kesalahan nomor satu adalah - mencoba membangun semuanya sendiri, hanya karena terlihat sangat mudah dijangkau. Masih, tapi seperti biasa seseorang mungkin sudah melakukannya dengan lebih baik - menghabiskan dua menit hanya untuk memeriksa apakah itu memang bisa menghemat waktu Anda untuk berpikir nanti.

Dan kedua - hal yang tampak jauh lebih sederhana bagi saya di mana mereka ternyata jauh lebih dalam, dan saya terlalu memperumit banyak kasus yang dipegang dengan sempurna oleh model transaksi PostgreSQL.

Jadi hanya setelah mencoba membangun kotak pasir, saya mendapatkan pemahaman yang agak jelas tentang perkiraan pendekatan ini.

Jadi perencanaan jelas diperlukan, tetapi jangan merencanakan lebih dari yang sebenarnya bisa Anda lakukan.

Pengalaman datang dengan latihan.

Kotak pasir saya mengingatkan saya pada strategi komputer - Anda duduk di sana setelah makan siang dan berpikir - “aha, di sini saya membangun Piramida, di sana saya mendapatkan panahan, lalu saya mengubahnya menjadi Sons of Ra dan membangun 20 busur panjang, dan di sini saya menyerang yang menyedihkan tetangga. Dua jam kemuliaan.” Dan tiba-tiba Anda menemukan diri Anda keesokan paginya, dua jam sebelum bekerja dengan “Bagaimana saya bisa sampai di sini? Mengapa saya harus menandatangani aliansi yang memalukan ini dengan orang-orang barbar yang tidak dicuci untuk menyelamatkan busur panah terakhir saya dan apakah saya benar-benar perlu menjual Piramida saya yang dibangun dengan keras untuk itu?”

Bacaan:

  • https://www.PostgreSQL.org/docs/current/static/different-replication-solutions.html
  • https://stackoverflow.com/questions/15343075/update-multiple-columns-in-a-trigger-function-in-plpgsql

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Menjalankan PostgreSQL Menggunakan Amazon RDS

  2. Postgresql Stempel waktu saat ini pada Pembaruan

  3. Berikan hak istimewa pada tabel mendatang di PostgreSQL?

  4. Haruskah saya menentukan INDEX dan UNIQUE INDEX?

  5. Melakukan transaksi saat menjalankan Fungsi postgreql