MariaDB
 sql >> Teknologi Basis Data >  >> RDS >> MariaDB

Beberapa Budak Replikasi Tertunda untuk Pemulihan Bencana dengan RTO Rendah

Replikasi yang tertunda memungkinkan slave replikasi untuk secara sengaja tertinggal di belakang master setidaknya dalam jangka waktu tertentu. Sebelum mengeksekusi suatu event, slave terlebih dahulu akan menunggu, jika perlu, hingga waktu yang ditentukan telah berlalu sejak event tersebut dibuat pada master. Hasilnya adalah bahwa budak akan mencerminkan keadaan tuannya beberapa waktu lalu. Fitur ini didukung sejak MySQL 5.6 dan MariaDB 10.2.3. Ini dapat berguna jika terjadi penghapusan data yang tidak disengaja, dan harus menjadi bagian dari rencana pemulihan bencana Anda.

Masalah saat menyiapkan budak replikasi tertunda adalah berapa banyak penundaan yang harus kita lakukan. Waktu yang terlalu singkat dan Anda mempertaruhkan kueri yang buruk untuk mendapatkan budak yang tertunda sebelum Anda dapat melakukannya, sehingga membuang-buang waktu untuk memiliki budak yang tertunda. Secara opsional, Anda dapat membuat waktu tunda Anda menjadi sangat lama sehingga diperlukan waktu berjam-jam bagi slave Anda yang tertunda untuk mengejar tempat master berada pada saat kesalahan.

Untungnya dengan Docker, isolasi proses adalah kekuatannya. Menjalankan beberapa instance MySQL cukup nyaman dengan Docker. Hal ini memungkinkan kita untuk memiliki beberapa budak tertunda dalam satu host fisik untuk meningkatkan waktu pemulihan dan menghemat sumber daya perangkat keras. Jika menurut Anda penundaan 15 menit terlalu singkat, kami dapat membuat instance lain dengan penundaan 1 jam atau 6 jam untuk snapshot database kami yang lebih lama.

Dalam posting blog ini, kita akan menerapkan beberapa budak tertunda MySQL pada satu host fisik tunggal dengan Docker, dan menunjukkan beberapa skenario pemulihan. Diagram berikut mengilustrasikan arsitektur akhir yang ingin kita bangun:

Arsitektur kami terdiri dari Replikasi MySQL 2-simpul yang sudah digunakan yang berjalan di server fisik (biru) dan kami ingin menyiapkan tiga budak MySQL lainnya (hijau) dengan perilaku berikut:

  • penundaan 15 menit
  • Keterlambatan 1 jam
  • tertunda 6 jam

Perhatikan bahwa kita akan memiliki 3 salinan data yang sama persis di server fisik yang sama. Pastikan host Docker kami memiliki penyimpanan yang diperlukan, jadi alokasikan ruang disk yang cukup sebelumnya.

Persiapan Master MySQL

Pertama, login ke server master dan buat pengguna replikasi:

mysql> GRANT REPLICATION SLAVE ON *.* TO [email protected]'%' IDENTIFIED BY 'YlgSH6bLLy';

Kemudian, buat cadangan yang kompatibel dengan PITR di master:

$ mysqldump -uroot -p --flush-privileges --hex-blob --opt --master-data=1 --single-transaction --skip-lock-tables --skip-lock-tables --triggers --routines --events --all-databases | gzip -6 -c > mysqldump_complete.sql.gz

Jika Anda menggunakan ClusterControl, Anda dapat membuat cadangan yang kompatibel dengan PITR dengan mudah. Buka Cadangan -> Buat Cadangan dan pilih "Complete PITR-compatible" di bawah dropdown "Dump Type":

Terakhir, transfer cadangan ini ke host Docker:

$ scp mysqldump_complete.sql.gz [email protected]:~

File cadangan ini akan digunakan oleh wadah budak MySQL selama proses bootstrap budak, seperti yang ditunjukkan di bagian berikutnya.

Penerapan Budak Tertunda

Siapkan direktori container Docker kami. Buat 3 direktori (mysql.conf.d, datadir dan sql) untuk setiap container MySQL yang akan kita luncurkan (Anda dapat menggunakan loop untuk menyederhanakan perintah di bawah):

$ mkdir -p /storage/mysql-slave-15m/mysql.conf.d
$ mkdir -p /storage/mysql-slave-15m/datadir
$ mkdir -p /storage/mysql-slave-15m/sql
$ mkdir -p /storage/mysql-slave-1h/mysql.conf.d
$ mkdir -p /storage/mysql-slave-1h/datadir
$ mkdir -p /storage/mysql-slave-1h/sql
$ mkdir -p /storage/mysql-slave-6h/mysql.conf.d
$ mkdir -p /storage/mysql-slave-6h/datadir
$ mkdir -p /storage/mysql-slave-6h/sql

Direktori "mysql.conf.d" akan menyimpan file konfigurasi MySQL kustom kami dan akan dipetakan ke dalam wadah di bawah /etc/mysql.conf.d. "datadir" adalah tempat kami ingin Docker menyimpan direktori data MySQL, yang memetakan ke /var/lib/mysql dari wadah dan direktori "sql" menyimpan file SQL kami - file cadangan dalam format .sql atau .sql.gz ke stage slave sebelum mereplikasi dan juga file .sql untuk mengotomatiskan konfigurasi replikasi dan startup.

Budak Tertunda 15 menit

Siapkan file konfigurasi MySQL untuk budak tertunda 15 menit kami:

$ vim /storage/mysql-slave-15m/mysql.conf.d/my.cnf

Dan tambahkan baris berikut:

[mysqld]
server_id=10015
binlog_format=ROW
log_bin=binlog
log_slave_updates=1
gtid_mode=ON
enforce_gtid_consistency=1
relay_log=relay-bin
expire_logs_days=7
read_only=ON

** Nilai server-id yang kami gunakan untuk slave ini adalah 10015.

Selanjutnya, di bawah direktori /storage/mysql-slave-15m/sql, buat dua file SQL, satu untuk RESET MASTER (1reset_master.sql) dan satu lagi untuk membuat link replikasi menggunakan pernyataan CHANGE MASTER (3setup_slave.sql).

Buat file teks 1reset_master.sql dan tambahkan baris berikut:

RESET MASTER;

Buat file teks 3setup_slave.sql dan tambahkan baris berikut:

CHANGE MASTER TO MASTER_HOST = '192.168.55.171', MASTER_USER = 'rpl_user', MASTER_PASSWORD = 'YlgSH6bLLy', MASTER_AUTO_POSITION = 1, MASTER_DELAY=900;
START SLAVE;

MASTER_DELAY=900 sama dengan 15 menit (dalam detik). Kemudian salin file cadangan yang diambil dari master kami (yang telah ditransfer ke host Docker kami) ke direktori "sql" dan beri nama 2mysqldump_complete.sql.gz:

$ cp ~/mysqldump_complete.tar.gz /storage/mysql-slave-15m/sql/2mysqldump_complete.tar.gz

Tampilan akhir dari direktori "sql" kita seharusnya seperti ini:

$ pwd
/storage/mysql-slave-15m/sql
$ ls -1
1reset_master.sql
2mysqldump_complete.sql.gz
3setup_slave.sql

Perhatikan bahwa kami mengawali nama file SQL dengan bilangan bulat untuk menentukan urutan eksekusi saat Docker menginisialisasi wadah MySQL.

Setelah semuanya siap, jalankan penampung MySQL untuk budak tertunda 15 menit kami:

$ docker run -d \
--name mysql-slave-15m \
-e MYSQL_ROOT_PASSWORD=password \
--mount type=bind,source=/storage/mysql-slave-15m/datadir,target=/var/lib/mysql \
--mount type=bind,source=/storage/mysql-slave-15m/mysql.conf.d,target=/etc/mysql/mysql.conf.d \
--mount type=bind,source=/storage/mysql-slave-15m/sql,target=/docker-entrypoint-initdb.d \
mysql:5.7

** Nilai MYSQL_ROOT_PASSWORD harus sama dengan kata sandi root MySQL pada master.

Baris berikut adalah apa yang kami cari untuk memverifikasi apakah MySQL berjalan dengan benar dan terhubung sebagai budak ke master kami (192.168.55.171):

$ docker logs -f mysql-slave-15m
...
2018-12-04T04:05:24.890244Z 0 [Note] mysqld: ready for connections.
Version: '5.7.24-log'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
2018-12-04T04:05:25.010032Z 2 [Note] Slave I/O thread for channel '': connected to master '[email protected]:3306',replication started in log 'FIRST' at position 4

Anda kemudian dapat memverifikasi status replikasi dengan pernyataan berikut:

$ docker exec -it mysql-slave-15m mysql -uroot -p -e 'show slave status\G'
...
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
                    SQL_Delay: 900
                Auto_Position: 1
...

Pada titik ini, penampung budak tertunda 15 menit kami mereplikasi dengan benar dan arsitektur kami terlihat seperti ini:

Budak Tertunda 1 jam

Siapkan file konfigurasi MySQL untuk budak tertunda 1 jam kami:

$ vim /storage/mysql-slave-1h/mysql.conf.d/my.cnf

Dan tambahkan baris berikut:

[mysqld]
server_id=10060
binlog_format=ROW
log_bin=binlog
log_slave_updates=1
gtid_mode=ON
enforce_gtid_consistency=1
relay_log=relay-bin
expire_logs_days=7
read_only=ON

** Nilai server-id yang kami gunakan untuk slave ini adalah 10060.

Selanjutnya, di bawah direktori /storage/mysql-slave-1h/sql, buat dua file SQL, satu untuk RESET MASTER (1reset_master.sql) dan satu lagi untuk membuat link replikasi menggunakan pernyataan CHANGE MASTER (3setup_slave.sql).

Buat file teks 1reset_master.sql dan tambahkan baris berikut:

RESET MASTER;

Buat file teks 3setup_slave.sql dan tambahkan baris berikut:

CHANGE MASTER TO MASTER_HOST = '192.168.55.171', MASTER_USER = 'rpl_user', MASTER_PASSWORD = 'YlgSH6bLLy', MASTER_AUTO_POSITION = 1, MASTER_DELAY=3600;
START SLAVE;

MASTER_DELAY=3600 sama dengan 1 jam (dalam detik). Kemudian salin file cadangan yang diambil dari master kami (yang telah ditransfer ke host Docker kami) ke direktori "sql" dan beri nama 2mysqldump_complete.sql.gz:

$ cp ~/mysqldump_complete.tar.gz /storage/mysql-slave-1h/sql/2mysqldump_complete.tar.gz

Tampilan akhir dari direktori "sql" kita seharusnya seperti ini:

$ pwd
/storage/mysql-slave-1h/sql
$ ls -1
1reset_master.sql
2mysqldump_complete.sql.gz
3setup_slave.sql

Perhatikan bahwa kami mengawali nama file SQL dengan bilangan bulat untuk menentukan urutan eksekusi saat Docker menginisialisasi wadah MySQL.

Setelah semuanya siap, jalankan penampung MySQL untuk budak tertunda 1 jam kami:

$ docker run -d \
--name mysql-slave-1h \
-e MYSQL_ROOT_PASSWORD=password \
--mount type=bind,source=/storage/mysql-slave-1h/datadir,target=/var/lib/mysql \
--mount type=bind,source=/storage/mysql-slave-1h/mysql.conf.d,target=/etc/mysql/mysql.conf.d \
--mount type=bind,source=/storage/mysql-slave-1h/sql,target=/docker-entrypoint-initdb.d \
mysql:5.7

** Nilai MYSQL_ROOT_PASSWORD harus sama dengan kata sandi root MySQL pada master.

Baris berikut adalah apa yang kami cari untuk memverifikasi apakah MySQL berjalan dengan benar dan terhubung sebagai budak ke master kami (192.168.55.171):

$ docker logs -f mysql-slave-1h
...
2018-12-04T04:05:24.890244Z 0 [Note] mysqld: ready for connections.
Version: '5.7.24-log'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
2018-12-04T04:05:25.010032Z 2 [Note] Slave I/O thread for channel '': connected to master '[email protected]:3306',replication started in log 'FIRST' at position 4

Anda kemudian dapat memverifikasi status replikasi dengan pernyataan berikut:

$ docker exec -it mysql-slave-1h mysql -uroot -p -e 'show slave status\G'
...
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
                    SQL_Delay: 3600
                Auto_Position: 1
...

Pada titik ini, penampung budak tertunda MySQL 15 menit dan 1 jam kami mereplikasi dari master dan arsitektur kami terlihat seperti ini:

Budak Tertunda 6 jam

Siapkan file konfigurasi MySQL untuk budak tertunda 6 jam kami:

$ vim /storage/mysql-slave-15m/mysql.conf.d/my.cnf

Dan tambahkan baris berikut:

[mysqld]
server_id=10006
binlog_format=ROW
log_bin=binlog
log_slave_updates=1
gtid_mode=ON
enforce_gtid_consistency=1
relay_log=relay-bin
expire_logs_days=7
read_only=ON

** Nilai server-id yang kami gunakan untuk slave ini adalah 10006.

Selanjutnya, di bawah direktori /storage/mysql-slave-6h/sql, buat dua file SQL, satu untuk RESET MASTER (1reset_master.sql) dan satu lagi untuk membuat link replikasi menggunakan pernyataan CHANGE MASTER (3setup_slave.sql).

Buat file teks 1reset_master.sql dan tambahkan baris berikut:

RESET MASTER;

Buat file teks 3setup_slave.sql dan tambahkan baris berikut:

CHANGE MASTER TO MASTER_HOST = '192.168.55.171', MASTER_USER = 'rpl_user', MASTER_PASSWORD = 'YlgSH6bLLy', MASTER_AUTO_POSITION = 1, MASTER_DELAY=21600;
START SLAVE;

MASTER_DELAY=21600 sama dengan 6 jam (dalam detik). Kemudian salin file cadangan yang diambil dari master kami (yang telah ditransfer ke host Docker kami) ke direktori "sql" dan beri nama 2mysqldump_complete.sql.gz:

$ cp ~/mysqldump_complete.tar.gz /storage/mysql-slave-6h/sql/2mysqldump_complete.tar.gz

Tampilan akhir dari direktori "sql" kita seharusnya seperti ini:

$ pwd
/storage/mysql-slave-6h/sql
$ ls -1
1reset_master.sql
2mysqldump_complete.sql.gz
3setup_slave.sql

Perhatikan bahwa kami mengawali nama file SQL dengan bilangan bulat untuk menentukan urutan eksekusi saat Docker menginisialisasi wadah MySQL.

Setelah semuanya siap, jalankan penampung MySQL untuk budak tertunda 6 jam kami:

$ docker run -d \
--name mysql-slave-6h \
-e MYSQL_ROOT_PASSWORD=password \
--mount type=bind,source=/storage/mysql-slave-6h/datadir,target=/var/lib/mysql \
--mount type=bind,source=/storage/mysql-slave-6h/mysql.conf.d,target=/etc/mysql/mysql.conf.d \
--mount type=bind,source=/storage/mysql-slave-6h/sql,target=/docker-entrypoint-initdb.d \
mysql:5.7

** Nilai MYSQL_ROOT_PASSWORD harus sama dengan kata sandi root MySQL pada master.

Baris berikut adalah apa yang kami cari untuk memverifikasi apakah MySQL berjalan dengan benar dan terhubung sebagai budak ke master kami (192.168.55.171):

$ docker logs -f mysql-slave-6h
...
2018-12-04T04:05:24.890244Z 0 [Note] mysqld: ready for connections.
Version: '5.7.24-log'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
2018-12-04T04:05:25.010032Z 2 [Note] Slave I/O thread for channel '': connected to master '[email protected]:3306',replication started in log 'FIRST' at position 4

Anda kemudian dapat memverifikasi status replikasi dengan pernyataan berikut:

$ docker exec -it mysql-slave-6h mysql -uroot -p -e 'show slave status\G'
...
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
                    SQL_Delay: 21600
                Auto_Position: 1
...

Pada titik ini, container slave tertunda 5 menit, 1 jam, dan 6 jam kami mereplikasi dengan benar dan arsitektur kami terlihat seperti ini:

Skenario Pemulihan Bencana

Katakanlah pengguna secara tidak sengaja menjatuhkan kolom yang salah di meja besar. Perhatikan pernyataan berikut yang dieksekusi pada master:

mysql> USE shop;
mysql> ALTER TABLE settings DROP COLUMN status;

Jika Anda cukup beruntung untuk segera menyadarinya, Anda dapat menggunakan budak tertunda 15 menit untuk mengejar momen sebelum bencana terjadi dan mempromosikannya menjadi master, atau mengekspor data yang hilang dan mengembalikannya ke master.

Pertama, kita harus menemukan posisi log biner sebelum bencana terjadi. Ambil waktu sekarang() pada master:

mysql> SELECT now();
+---------------------+
| now()               |
+---------------------+
| 2018-12-04 14:55:41 |
+---------------------+

Kemudian, dapatkan file log biner aktif di master:

mysql> SHOW MASTER STATUS;
+---------------+----------+--------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                                                                                                                                                                     |
+---------------+----------+--------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| binlog.000004 | 20260658 |              |                  | 1560665e-ed2b-11e8-93fa-000c29b7f985:1-12031,
1b235f7a-d37b-11e8-9c3e-000c29bafe8f:1-62519,
1d8dc60a-e817-11e8-82ff-000c29bafe8f:1-326575,
791748b3-d37a-11e8-b03a-000c29b7f985:1-374 |
+---------------+----------+--------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Menggunakan format tanggal yang sama, ekstrak informasi yang kita inginkan dari log biner, binlog.000004. Kami memperkirakan waktu mulai membaca dari binlog sekitar 20 menit yang lalu (04-12-2018 14:35:00) dan memfilter output untuk menampilkan 25 baris sebelum pernyataan "jatuhkan kolom":

$ mysqlbinlog --start-datetime="2018-12-04 14:35:00" --stop-datetime="2018-12-04 14:55:41" /var/lib/mysql/binlog.000004 | grep -i -B 25 "drop column"
'/*!*/;
# at 19379172
#181204 14:54:45 server id 1  end_log_pos 19379232 CRC32 0x0716e7a2     Table_map: `shop`.`settings` mapped to number 766
# at 19379232
#181204 14:54:45 server id 1  end_log_pos 19379460 CRC32 0xa6187edd     Write_rows: table id 766 flags: STMT_END_F

BINLOG '
tSQGXBMBAAAAPAAAACC0JwEAAP4CAAAAAAEABnNidGVzdAAHc2J0ZXN0MgAFAwP+/gME/nj+PBCi
5xYH
tSQGXB4BAAAA5AAAAAS1JwEAAP4CAAAAAAEAAgAF/+AYwwAAysYAAHc0ODYyMjI0NjI5OC0zNDE2
OTY3MjY5OS02MDQ1NTQwOTY1Ny01MjY2MDQ0MDcwOC05NDA0NzQzOTUwMS00OTA2MTAxNzgwNC05
OTIyMzM3NzEwOS05NzIwMzc5NTA4OC0yODAzOTU2NjQ2MC0zNzY0ODg3MTYzOTswMTM0MjAwNTcw
Ni02Mjk1ODMzMzExNi00NzQ1MjMxODA1OS0zODk4MDQwMjk5MS03OTc4MTA3OTkwNQEAAADdfhim
'/*!*/;
# at 19379460
#181204 14:54:45 server id 1  end_log_pos 19379491 CRC32 0x71f00e63     Xid = 622405
COMMIT/*!*/;
# at 19379491
#181204 14:54:46 server id 1  end_log_pos 19379556 CRC32 0x62b78c9e     GTID    last_committed=11507    sequence_number=11508   rbr_only=no
SET @@SESSION.GTID_NEXT= '1560665e-ed2b-11e8-93fa-000c29b7f985:11508'/*!*/;
# at 19379556
#181204 14:54:46 server id 1  end_log_pos 19379672 CRC32 0xc222542a     Query   thread_id=3162  exec_time=1     error_code=0
SET TIMESTAMP=1543906486/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=8/*!*/;
ALTER TABLE settings DROP COLUMN status

Di beberapa baris bawah dari output mysqlbinlog, Anda harus memiliki perintah yang salah yang dieksekusi pada posisi 19379556. Posisi yang harus kita pulihkan adalah satu langkah sebelumnya, yaitu pada posisi 19379491. Ini adalah posisi binlog yang kita inginkan. budak tertunda hingga.

Kemudian, pada slave tertunda yang dipilih, hentikan slave replikasi yang tertunda dan mulai lagi slave ke posisi akhir tetap yang kami temukan di atas:

$ docker exec -it mysql-slave-15m mysql -uroot -p
mysql> STOP SLAVE;
mysql> START SLAVE UNTIL MASTER_LOG_FILE = 'binlog.000004', MASTER_LOG_POS = 19379491;

Pantau status replikasi dan tunggu hingga Exec_Master_Log_Pos sama dengan nilai Hingga_Log_Pos. Ini bisa memakan waktu. Setelah tertangkap, Anda akan melihat yang berikut:

$ docker exec -it mysql-slave-15m mysql -uroot -p -e 'SHOW SLAVE STATUS\G'
... 
          Exec_Master_Log_Pos: 19379491
              Relay_Log_Space: 50552186
              Until_Condition: Master
               Until_Log_File: binlog.000004
                Until_Log_Pos: 19379491
...

Terakhir verifikasi apakah data hilang yang kita cari ada (kolom "status" masih ada):

mysql> DESCRIBE shop.settings;
+--------+------------------+------+-----+---------+----------------+
| Field  | Type             | Null | Key | Default | Extra          |
+--------+------------------+------+-----+---------+----------------+
| id     | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| sid    | int(10) unsigned | NO   | MUL | 0       |                |
| param  | varchar(100)     | NO   |     |         |                |
| value  | varchar(255)     | NO   |     |         |                |
| status | int(11)          | YES  |     | 1       |                |
+--------+------------------+------+-----+---------+----------------+

Kemudian ekspor tabel dari wadah budak kami dan transfer ke server master:

$ docker exec -it mysql-slave-1h mysqldump -uroot -ppassword --single-transaction shop settings > shop_settings.sql

Jatuhkan tabel yang bermasalah dan kembalikan ke master:

$ mysql -uroot -p -e 'DROP TABLE shop.settings'
$ mysqldump -uroot -p -e shop < shop_setttings.sql

Kami sekarang telah memulihkan meja kami kembali ke keadaan semula sebelum peristiwa bencana. Singkatnya, replikasi tertunda dapat digunakan untuk beberapa tujuan:

  • Untuk melindungi dari kesalahan pengguna pada master. DBA dapat memutar kembali slave yang tertunda ke waktu tepat sebelum bencana.
  • Untuk menguji bagaimana sistem berperilaku ketika ada lag. Misalnya, dalam sebuah aplikasi, lag mungkin disebabkan oleh beban yang berat pada slave. Namun, mungkin sulit untuk menghasilkan tingkat beban ini. Replikasi yang tertunda dapat mensimulasikan lag tanpa harus mensimulasikan beban. Ini juga dapat digunakan untuk men-debug kondisi yang terkait dengan slave yang tertinggal.
  • Untuk memeriksa seperti apa database sebelumnya, tanpa harus memuat ulang cadangan. Misalnya, jika penundaannya adalah satu minggu dan DBA perlu melihat seperti apa database sebelum pengembangan beberapa hari terakhir, slave yang tertunda dapat diperiksa.

Pemikiran Terakhir

Dengan Docker, menjalankan beberapa instance MySQL pada host fisik yang sama dapat dilakukan secara efisien. Anda dapat menggunakan alat orkestrasi Docker seperti Docker Compose dan Swarm untuk menyederhanakan penerapan multi-kontainer yang bertentangan dengan langkah-langkah yang ditampilkan dalam entri blog ini.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. MariaDB JSON_COMPACT() Dijelaskan

  2. Instal MariaDB di Mac

  3. Membandingkan Solusi Failover DBaaS dengan Pengaturan Pemulihan Manual

  4. Bagaimana YEARWEEK() Bekerja di MariaDB

  5. MariaDB GROUP_CONCAT()