Photo by Ross Joyner on Unsplash
Eksplorasi Replikasi MySQL
Replikasi sederhana, replikasi berantai, hingga replikasi grup
Jika melihat dunia komputasi modern, saya rasa cukup mudah dilihat bahwa trennya adalah makin hari makin banyak sistem terdistribusi. Baik itu sistem yang dari awal memang didesain sebagai sistem terdistribusi, ataupun sistem tidak terdistribusi yang kemudian diadaptasikan menjadi sistem terdistribusi karena kebutuhan penanganan beban kerja yang tinggi, keduanya makin banyak bermunculan. Hal ini saya rasa tidak dapat dihindari karena satu-satunya cara untuk membuat sebuah sistem dapat menangani beban kerja yang masif yang tidak dapat ditangani oleh satu mesin saja karena keterbatasan perangkat kerasnya adalah dengan mendistribusikan beban kerja itu ke beberapa atau banyak komputer. Dan salah satu jenis perangkat lunak yang biasanya dari awal sudah didesain untuk dapat beroperasi dalam bentuk terdistribusi adalah basis data.
Replikasi adalah salah satu fitur yang biasanya dimanfaatkan jika dirasa perlu untuk mendapatkan manfaat-manfaat sistem terdistribusi dari suatu sistem basis data. Dengan replikasi sistem basis data bisa diatur sedemikian rupa sehingga lebih tahan terhadap kegagalan komponen, bisa menangani beban kerja yang lebih tinggi dengan pembagian tanggung jawab, serta lebih tangguh dalam menghadapi bencana alam.
Saya sudah cukup lama familiar dengan konsep replikasi basis data tetapi belum pernah mencoba untuk praktek secara langsung. Jadi minggu lalu saya berpikir "Kenapa tidak dicoba saja?" Toh pemahaman yang lebih dalam terhadap mekanisme replikasi basis data untuk salah satu sistem basis data yang populer akan bermanfaat pula untuk saya. Tetapi saya ingin mendapatkan gambaran dan pemahaman yang lebih komprehensif terhadap mekanisme replikasi di MySQL, sehingga saya memutuskan untuk mencoba beberapa konfigurasi dan topologi alih-alih hanya mencoba satu jenis konfigurasi saja. Tulisan ini adalah rekap dari eksplorasi yang saya lakukan.
Dua Metode Replikasi MySQL
Di sistem basis data MySQL kurang lebih replikasi dapat dilakukan menggunakan dua metode, yakni metode replikasi berbasiskan posisi binlog, dan metode replikasi berbasiskan GTID atau Global Transaction Identifier.
Metode binlog merupakan metode replikasi yang sudah lama digunakan oleh MySQL, ini adalah metode bawaan sebelum diperkenalkannya metode replikasi berdasarkan GTID. Metode ini menggunakan sebuah berkas yang disebut binlog, yang merupakan berkas untuk menampung catatan perubahan yang terjadi pada sebuah basis data MySQL. Instansi yang bertindak sebagai replika akan membaca data dari berkas binlog ini dan menerapkan perubahannya. Dalam penggunaan metode ini diperlukan penentuan dari posisi mana di dalam binlog replikasi ingin dimulai, apakah dari awal, atau dari pertengahan, atau dari akhir saja.
Sedangkan metode replikasi berbasiskan GTID merupakan metode replikasi yang lebih baru yang baru muncul pada MySQL 5.6. Pada dasarnya mekanisme replikasi menggunakan metode ini juga masih mengandalkan binlog, hanya saja kita sebagai administrator basis data tidak perlu menentukan posisi binlog ketika hendak menjalankan replikasi, sehingga prosesnya pembuatan replikasinya lebih sederhana dan mulus.
Kendati demikian, pada dasarnya alur replikasinya sama saja, yakni digambarkan oleh gambar berikut:
Eksplorasi Replikasi
Eksplorasi yang saya lakukan berupa percobaan untuk membuat setup replikasi MySQL dengan beberapa kombinasi metode, konfigurasi, dan topologi. Berikut adalah daftar lengkapnya:
Replikasi sederhana dengan metode binlog
Replikasi sederhana dengan metode GTID
Replikasi berantai dengan metode binlog
Replikasi berantai dengan metode GTID
Replikasi berantai dengan metode binlog dan GTID
Replikasi grup
Replikasi grup dengan InnoDB Cluster
Singkatnya beberapa setup yang saya coba dapat dikategorikan menjadi 3 kategori, yaitu replikasi sederhana, replikasi berantai, dan replikasi grup. Setiap eksplorasi yang saya lakukan berdasarkan Docker Compose agar lebih mudah untuk direplikasi dan direproduksi.
Replikasi Sederhana
Pada setup ini saya melakukan replikasi hanya dengan melibatkan dua basis data, yakni Primary dan Secondary, dimana Secondary melakukan replikasi dari Primary.
Replikasi Sederhana Menggunakan Binlog
Setup replikasi sederhana ini cukup mudah untuk disiapkan. Untuk metode berbasis binlog, cukup menambahkan dua baris berikut ke dalam berkas pengaturan mysqld pada instansi basis data yang dijadikan instansi Primary:
[mysqld]
server_id = 1
binlog_do_db = database1
Konfigurasi server_id
digunakan untuk menentukan id
dari instansi basis data itu, nilai ini tidak boleh sama antar instansi. Sedangkan konfigurasi binlog_do_db
digunakan untuk menentukan basis data mana yang perubahannya hendak dicatat ke dalam binlog.
Setelah mengatur konfigurasi instansi Primary, untuk instansi Secondary cukup menambahkan konfigurasi server_id
saja yang nilainya tidak sama dengan nilai server_id
di instansi basis data Primary:
[mysqld]
server_id = 2
Untuk menghubungkan instansi Secondary dengan instansi Primary, jalankan perintah berikut:
CHANGE REPLICATION SOURCE TO
SOURCE_HOST = '<host primary>',
SOURCE_USER = '<user khusus replikasi>',
SOURCE_PASSWORD = '<password user di atas>',
SOURCE_LOG_FILE = '<nama binlog di primary>',
SOURCE_LOG_POS = <posisi binlog di mana replikasi hendak mulai dilakukan>;
Seperti yang dapat dilihat di atas, perlu terdapat user khusus yang memiliki privilege replikasi di instansi Primary yang dapat digunakan oleh instansi Secondary untuk terhubung. Adapun cara untuk membuat user tersebut saya rasa sudah cukup banyak referensinya di internet, jadi saya tidak akan menuliskannya lagi di sini.
Selain itu, terdapat variabel lain yang diperlukan seperti SOURCE_LOG_FILE
dan SOURCE_LOG_POS
. Kedua variabel tersebut berkaitan dengan nama binlog dan posisi binlog di mana replikasi akan dimulai, kedua informasi ini dapat dilihat dengan menjalankan perintah SQL berikut di instansi Primary:
SHOW BINARY LOG STATUS;
Yang akan menghasilkan luaran berikut:
Nilai dari kolom File
untuk variabel SOURCE_LOG_FILE
, sedangkan nilai dari kolom Position
untuk variabel SOURCE_LOG_POS
.
Lalu untuk benar-benar memulai replikasi, jalankan perintah SQL berikut:
START REPLICATION;
Status replikasi dapat dicek menggunakan perintah berikut:
SHOW REPLICA STATUS;
Jika replikasi berjalan, seharusnya nilai kolom Replica_IO_State
akan bernilai seperti berikut:
Replikasi Sederhana Menggunakan GTID
Untuk proses persiapan replikasi sederhana menggunakan metode GTID konfigurasi yang perlu dilakukan lebih sederhana daripada pada penggunaan metode binlog. Berikut adalah berkas konfigurasi mysqld untuk metode ini untuk instansi Primary:
[mysqld]
server_id = 1
gtid_mode = ON
enforce_gtid_consistency = ON
Sedangkan berikut adalah konfigurasi untuk instansi Secondary:
[mysqld]
server_id = 2
gtid_mode = ON
enforce_gtid_consistency = ON
Singkatnya, kedua instansi memiliki konfigurasi yang sama selain nilai server_id
-nya yang memang harus berbeda antar instansi.
Proses untuk memulai replikasi dari Primary ke Secondary pun lebih mudah dari metode binlog, cukup jalankan perintah SQL di instansi Secondary:
CHANGE REPLICATION SOURCE TO
SOURCE_HOST = '<host primary>',
SOURCE_USER = '<user khusus replikasi>',
SOURCE_PASSWORD = '<password user di atas>',
SOURCE_AUTO_POSITION = 1;
Replikasi akan otomatis dimulai tanpa harus menjalankan perintah START REPLICATION
. Sebagaimana metode binlog, status replikasi dapat dilihat dengan menjalankan perintah berikut di instansi Secondary:
SHOW REPLICA STATUS;
Replikasi Berantai
Pada setup ini ekplorasi yang saya lakukan melibatkan 3 basis data MySQL, yakni Primary, Secondary 1, dan Secondary 2. Secondary 1 melakukan replikasi dari Primary, dan Secondary 2 melakukan replikasi dari Secondary 1.
Replikasi Berantai Campuran
Untuk replikasi berantai saya hanya akan menuliskan terkait replikasi berantai campuran karena pada dasarnya untuk melakukan replikasi berantai satu metode langkah-langkahnya sama dengan replikasi sederhana di atas, hanya saja ditambahkan satu instansi basis data lagi yang mereplikasi dari Secondary 1.
Replikasi berantai campuran adalah setup replikasi di mana Secondary 1 akan mereplikasi dari Primary menggunakan metode binlog, dan Secondary 2 akan mereplikasi dari Secondary 1 menggunakan metode GTID. Sebagaimana terlukiskan pada gambar berikut.
Yang perlu dilakukan untuk menjalankan setup ini adalah dengan mengatur konfigurasi Primary layaknya konfigurasi untuk replikasi binlog sederhana, lalu mengatur konfigurasi Secondary 1 dan Secondary 2 layaknya konfigurasi untuk replikasi GTID sederhana. Berikut konfigurasi untuk instansi Primary.
[mysqld]
server_id = 1
binlog_do_db = database1
Serta berikut konfigurasi untuk instansi Secondary 1 dan Secondary 2:
[mysqld]
server_id = <id unik>
gtid_mode = ON
enforce_gtid_consistency = ON
Perbedaannya hanya terletak pada saat hendak memulai replikasi dari Primary ke Secondary 1, berikut adalah perintah SQL yang perlu dijalankan pada instansi Secondary 1:
CHANGE REPLICATION SOURCE TO
ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS = LOCAL,
SOURCE_HOST = '<host primary>',
SOURCE_USER = '<user khusus replikasi>',
SOURCE_PASSWORD = '<password user di atas>',
SOURCE_LOG_FILE = '<nama binlog di primary>',
SOURCE_LOG_POS = <posisi binlog di mana replikasi hendak mulai dilakukan>;
Dapat dilihat perbedaannya pun cukup minor dengan hanya penambahan satu baris, yakni ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS = LOCAL
yang menginstruksikan MySQL untuk memberikan GTID kepada transaksi yang terjadi di instansi Primary.
Setelah menjalankan perintah tersebut maka replikasi dari Primary ke Secondary 1 akan dimulai secara otomatis. Sedangkan untuk memulai replikasi dari Secondary 1 ke Secondary 2 perlu dijalankan perintah berikut di Secondary 2:
CHANGE REPLICATION SOURCE TO
SOURCE_HOST = '<host secondary 1>',
SOURCE_USER = '<user khusus replikasi>',
SOURCE_PASSWORD = '<password user di atas>',
SOURCE_AUTO_POSITION = 1;
Setelah menjalankan dua perintah di atas, seharusnya keseluruhan setup sudah berjalan dengan baik dan dapat dilakukan pengetesan.
Replikasi Grup
Pada setup ini saya melakukan eksplorasi yang melibatkan 3 basis data, yakni Primary, Secondary 1, dan Secondary 2, dengan Secondary 1 dan Secondary 2 dua-duanya merupakan replikasi dari Primary.
Perbedaan antara replikasi grup dengan replikasi biasa terletak pada sifat high-availability yang dimiliki oleh replikasi grup. Dalam mode kluster satu Primary, instansi-instansi Secondary dalam replikasi grup otomatis hanya bertugas melayani kueri yang sifatnya pembacaan, sedangkan kueri-kueri yang sifatnya mutasi akan dilayani oleh Primary. Selain itu untuk ketika instansi Primary mati, maka salah satu dari instansi Secondary akan otomatis diangkat sebagai Primary, sehingga kluster dapat tetap melayani kueri mutasi. MySQL menggunakan Algoritma Konsensus Terdistribusi Paxos untuk pengelolaan kluster ini.
Replikasi grup MySQL juga mendukung mode kluster multi-Primary, di mana terdapat lebih dari satu instansi yang bertugas melayani kueri mutasi. Tetapi mode ini tidak termasuk dalam eksplorasi yang saya lakukan sehingga tidak akan saya bahas pada tulisan ini.
Untuk menjalankan replikasi grup dengan mode kluster satu Primary, diperlukan minimal 3 instansi basis data. Berikut adalah konfigurasi untuk instansi Primary:
[mysqld]
server_id = 1
gtid_mode = ON
enforce_gtid_consistency = ON
binlog_checksum = NONE
relay_log = primary-relay-bin
plugin_load_add = group_replication.so
loose_group_replication_group_name = aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
loose_group_replication_start_on_boot = OFF
loose_group_replication_local_address = "<host primary>:6606"
loose_group_replication_group_seeds = "<host primary>:6606,<host secondary 1>:6606,<host secondary 2>:6606"
loose_group_replication_bootstrap_group = OFF
loose_group_replication_single_primary_mode = ON
loose_group_replication_enforce_update_everywhere_checks = OFF
Jika diperhatikan dapat dilihat di atas bahwa replikasi grup dilakukan dengan menggunakan metode replikasi berbasiskan GTID. Selain itu, terdapat beberapa konfigurasi tambahan seperti loose_group_replication_local_address
yang harus diisi dengan nilai <host primary>:<port yang ingin digunakan>
, serta loose_group_replication_group_seeds
yang harus diisi dengan <host>:<port>
dari semua anggota kluster, hingga loose_group_replication_single_primary_mode
yang digunakan untuk menentukan apakah kluster akan berjalan dalam mode satu Primary atau multi-Primary.
Sedangkan untuk instansi-instansi Secondary, berikut adalah konfigurasinya:
[mysqld]
server_id = <id unik>
gtid_mode = ON
enforce_gtid_consistency = ON
binlog_checksum = NONE
relay_log = secondary-relay-bin
plugin_load_add = group_replication.so
loose_group_replication_group_name = aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
loose_group_replication_start_on_boot = OFF
loose_group_replication_local_address = "<host secondary>:6606"
loose_group_replication_group_seeds = "<host primary>:6606,<host secondary 1>:6606,<host secondary 2>:6606"
loose_group_replication_bootstrap_group = OFF
loose_group_replication_single_primary_mode = ON
loose_group_replication_enforce_update_everywhere_checks = OFF
Dapat dilihat perbedaannya hanya terletak pada nilai server_id
dan loose_group_replication_local_address
, sisanya sama saja.
Untuk memulai replikasi grup, jalankan perintah berikut di Primary:
SET @@GLOBAL.group_replication_bootstrap_group = ON;
CREATE USER IF NOT EXISTS 'repl'@'%';
GRANT REPLICATION SLAVE ON *.* TO repl@'%';
FLUSH PRIVILEGES;
CHANGE REPLICATION SOURCE TO master_user='repl'
FOR CHANNEL 'group_replication_recovery';
START GROUP_REPLICATION;
Perintah di atas akan membuat user baru di Primary dengan nama repl
, lalu Primary akan mem-boostrap kluster replikasi grup.
Setelah menjalankan perintah di atas, jalankan perintah berikut untuk mengecek apakah kluster replikasi grup sudah berjalan:
SELECT * FROM performance_schema.replication_group_members;
Jika kluster replikasi grup berhasil dijalankan, maka seharusnya luarannya akan menampilkan data sebagai berikut:
Lalu di masing-masing instansi Secondary, jalankan perintah berikut:
CHANGE REPLICATION SOURCE TO master_user='repl'
FOR CHANNEL 'group_replication_recovery';
START GROUP_REPLICATION;
SET @@global.read_only = 1;
Lalu jalankan lagi perintah berikut di salah satu anggota kluster:
SELECT * FROM performance_schema.replication_group_members;
Jika instansi-instansi Secondary berhasil terhubung ke kluster, maka luarannya seharusnya akan terlihat seperti berikut:
Pengetesan
Fungsionalitas utama replikasi masing-masing setup dapat dites dengan tes sederhana, yaitu dengan menjalankan sekumpulan kueri mutasi di Primary lalu melihat apakah hasil kueri-kueri tersebut tercermin di instansi-instansi Secondary.
Sebagai contoh, untuk mengetes apakah replikasi pada setup replikasi sederhana menggunakan metode GTID berjalan dengan baik, saya dapat menjalankan kueri berikut di Primary:
CREATE TABLE users(
uid VARCHAR(255) NOT NULL PRIMARY KEY,
full_name VARCHAR(255) NOT NULL,
gender ENUM('Men', 'Women') NOT NULL,
age INT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Lalu dapat dicek pada instansi Secondary, jika replikasi berjalan dengan baik maka akan muncul tabel yang sama:
Mengetes Replikasi Berantai
Untuk mengetes replikasi berantai, dapat dilakukan tes yang sama dengan tes di atas, tetapi pengecekannya dapat ditambah dengan melihat apakah tabel tersebut tercermin pula ke instansi Secondary 2. Jika replikasi berjalan dengan baik, seharusnya jawabannya adalah iya.
Selain tes di atas, dapat juga dilakukan tes dengan menjalankan kueri mutasi di instansi Primary lalu menjalankan kueri mutasi yang berbeda di instansi Secondary 1 dengan ekspektasi keduanya akan tercermin di instansi Secondary 2. Untuk itu dapat dijalankan kueri berikut di instansi Primary:
INSERT INTO database1.users (uid, full_name, gender, age)
VALUES
('One', 'Daniel', 'Men', 22),
('Two', 'Ishmael', 'Men', 28),
('Three', 'Elizabeth', 'Women', 26);
Lalu dilanjutkan dengan menjalankan kueri berikut di instansi Secondary 1:
INSERT INTO database1.users (uid, full_name, gender, age)
VALUES
('Four', 'Muhammad', 'Men', 40),
('Five', 'Yahya', 'Men', 33);
Jika replikasi berjalan dengan baik, seharusnya pada instansi Secondary 2 terdapat 5 baris di tabel users
yang merupakan gabungan antara dua kueri di atas:
Mengetes Replikasi Grup
Untuk pengetesan tambahan khusus replikasi grup, selain melakukan pengetesan dasar untuk fungsionalitas replikasi menggunakan cara testing di atas. Dapat juga dilakukan pengetesan pada sifat high-availability klusternya, yaitu dengan mengetes apakah *automatic failover-*nya berjalan dengan baik.
Jika instansi Primary dimatikan maka yang terjadi adalah salah satu instansi Secondary seharusnya akan otomatis diangkat menjadi Primary, sebagaimana diperlihatkan pada gambar berikut:
Untuk membuat instansi Primary kembali ke kluster, hidupkan kembali instansi Primary lalu jalankan perintah berikut di instansi tersebut:
CHANGE REPLICATION SOURCE TO master_user='repl' FOR CHANNEL 'group_replication_recovery';
START GROUP_REPLICATION;
Jika berhasil, maka Primary akan bergabung kembali ke kluster tetapi dengan status sebagai instansi Secondary, bukannya Primary. Ini merupakan perilaku bawaan di setup replikasi grup MySQL.
Namun jika dilakukan kueri mutasi terhadap instansi Primary, maka akan tertolak karena instansi bukan merupakan instansi Primary sehingga otomatis diatur sebagai instansi read only. Berikut adalah kueri yang coba dijalankan pada Primary setelah bergabung kembali ke kluster:
INSERT INTO database1.users (uid, full_name, gender, age)
VALUES
('Six', 'Khalid', 'Men', 34),
('Seven', 'Hannibal', 'Men', 38);
Seharusnya akan didapati galat seperti berikut:
Tetapi jika kueri tersebut dijalankan di instansi Secondary 1 yang notabene merupakan instansi primary yang baru, maka hasilnya adalah tidak terdapat galat:
Dan hasilnya pun akan tereplikasi di instansi Primary yang lama:
Akhir Kata
Replikasi adalah konsep yang sangat penting dalam sistem basis data modern. Dengan makin banyaknya data yang perlu ditangani oleh sistem-sistem modern, kebutuhan untuk mendistribusikan beban komputasi dan penyimpanan dari satu komputer kepada beberapa komputer yang saling terhubung menjadi makin krusial.
Sebagai seorang pemrogram, saya rasa pemahaman terkait prosedur dan cara kerja replikasi dari beberapa basis data yang sering digunakan memiliki nilai yang tinggi, karena ini berarti kita memiliki satu lagi referensi solusi untuk menyelesaikan masalah. Saya percaya semakin banyak referensi untuk penyelesaian masalah yang ada di kepala seorang pemrogram, ia akan semakin berpeluang untuk memberikan nilai dan dampak yang lebih besar, karena ia akan memiliki banyak pembanding dan dapat menentukan mana solusi yang paling optimal, sehingga kualitas delivery yang dihasilkan juga lebih tinggi.
Saya rasa saya juga perlu menuliskan disclaimer bahwa eksplorasi yang saya lakukan ini hanya bertujuan untuk mendapatkan gambaran umum terkait prosedur melakukan replikasi menggunakan basis data MySQL, tidak dimaksudkan untuk mencoba setiap kombinasi konfigurasi, topologi, dan mode replikasi yang disediakan oleh MySQL. Oleh karena itu masih banyak hal yang berkaitan dengan replikasi yang tidak saya bahas pada tulisan ini. Sebagai contoh, di tulisan ini saya tidak membahas replikasi semi-sinkronus dan replikasi multi-sumber meskipun keduanya didukung oleh MySQL. Oleh karena itu, saya mendorong pembaca untuk mengeksplorasi dokumentasi resmi MySQL jika ingin menyelami lebih dalam.
Jika Anda tertarik untuk melihat berkas-berkas konfigurasi dan kode yang saya gunakan dalam eksplorasi ini Anda dapat mengunjungi repositori berikut:
Anda hanya memerlukan Docker Compose untuk menjalankan eksperimen-eksperimen yang saya lakukan di repositori tersebut. Saya telah menulis panduan di masing-masing direktori yang menjelaskan cara menjalankan dan mengetes setup di direktori tersebut.
Happy hacking!