Today we will see how to use MySQL Database Service aka MDS with WordPress.
To achieve this easy task, we will use the architecture we already deployed in this article.
We have then two Compute Instances on OCI, 1 running WordPress (Apache and PHP) and one running MySQL 8.0.
The Plan
This is how we will proceed to migrate to MDS with minimal maintenance time, we will:
- create a MDS instance
- verify if the database is ready to act as replication source
- dump the MySQL instance running on OCI for being migrated to MDS.
- load the dump in MDS
- create a user dedicated to the replication
- create a replication channel on MDS (from OCI to MDS)
- modify WordPress config to point to MDS
Create a MDS instance
The first step is of course to connect to OCI’s dashboard. We should see our two running compute instances:
Let’s now create the MySQL Database Service instance, you will find the service in the left column;
Now we just follow the creation wizard that is very simple:
It’s very important to create an admin user (the name can be whatever you want) and don’t forget the password. We will put our service in the private subnet we created earlier (see also this post):
The last screen of the wizard is related to the automatic backups:
The MDS instance will be provisioned after a short time and you can see that in its detailed view:
All good for step 1, we can now proceed with the second one.
Verify our database to act as replication source
We now connect to our current MySQL running on OCI and verify that all settings required to replication with GTID are in place.
mysql> select @@gtid_mode; +-------------+ | @@gtid_mode | +-------------+ | OFF | +-------------+ 1 row in set (0.00 sec)
By default GTID mode is disabled and we need to enable it. To be able to perform this operation without restarting the MySQL instance, this is how to proceed:
mysql> SET PERSIST server_id=1; mysql> SET PERSIST enforce_gtid_consistency=true; mysql> SET PERSIST gtid_mode=off_permissive; mysql> SET PERSIST gtid_mode=on_permissive; mysql> SET PERSIST gtid_mode=on; mysql> select @@gtid_mode; +-------------+ | @@gtid_mode | +-------------+ | ON | +-------------+
All good now !
MySQL Shell Dump & Load
Instead of using mysqldump
or mysqlpump
that are not efficient in case of large dataset and are certainly not optimized for Cloud, we will use the new amazing feature of MySQL Shell (since 8.0.21): Dump & Load.
Please check those links for more details:
- https://docs.cloud.oracle.com/en-us/iaas/mysql-database/doc/importing-and-exporting-databases.html
- https://mysqlserverteam.com/mysql-shell-dump-load-part-1-demo/
- https://mysqlserverteam.com/mysql-shell-dump-load-part-2-benchmarks/
- https://mysqlserverteam.com/mysql-shell-dump-load-part-3-load-dump/
- https://mysqlserverteam.com/mysql-shell-8-0-21-speeding-up-the-dump-process/
We connect in ssh to our MySQL instance in OCI and we can start the dump.
There are multiple ways to perform such operation. As my WordPress dataset is not so large and already in the cloud, I use the filesystem. If it was on premise with a large dataset I would have used Object Store.
I start MySQL Shell and connect to my MySQL instance on OCI (the one WordPress is using for the moment). As I’m connected via ssh on the server, I use localhost
to connect using Shell:
$ mysqlsh root@localhost
And now I just dump my instance. As I know this dump will be used to be loaded in MDS, I will add some options:
MySQL localhost:33060+ ssl JS > util.dumpInstance('file://dump', {ocimds: true, compatibility: ["strip_restricted_grants"]})
This is the full output of the command:
Checking for compatibility with MySQL Database Service 8.0.21 NOTE: User root@localhost had restricted privileges (RELOAD, FILE, SUPER, BINLOG_ADMIN, SET_USER_ID) removed NOTE: Database wordpress had unsupported ENCRYPTION option commented out Compatibility issues with MySQL Database Service 8.0.21 were found and repaired. Please review the changes made before loading them. Acquiring global read lock All transactions have been started Locking instance for backup Global read lock has been released Writing global DDL files Writing users DDL Preparing data dump for tablewordpress
.wp_commentmeta
Writing DDL for schemawordpress
Writing DDL for tablewordpress
.wp_commentmeta
Data dump for tablewordpress
.wp_commentmeta
will be chunked using columnmeta_id
Preparing data dump for tablewordpress
.wp_comments
Data dump for tablewordpress
.wp_comments
will be chunked using columncomment_ID
Preparing data dump for tablewordpress
.wp_links
Data dump for tablewordpress
.wp_links
will be chunked using columnlink_id
Preparing data dump for tablewordpress
.wp_options
Data dump for tablewordpress
.wp_options
will be chunked using columnoption_id
Preparing data dump for tablewordpress
.wp_postmeta
Data dump for tablewordpress
.wp_postmeta
will be chunked using columnmeta_id
Preparing data dump for tablewordpress
.wp_posts
Data dump for tablewordpress
.wp_posts
will be chunked using columnID
Preparing data dump for tablewordpress
.wp_term_relationships
Data dump for tablewordpress
.wp_term_relationships
will be chunked using columnobject_id
Preparing data dump for tablewordpress
.wp_term_taxonomy
Data dump for tablewordpress
.wp_term_taxonomy
will be chunked using columnterm_taxonomy_id
Preparing data dump for tablewordpress
.wp_termmeta
Data dump for tablewordpress
.wp_termmeta
will be chunked using columnmeta_id
Preparing data dump for tablewordpress
.wp_terms
Data dump for tablewordpress
.wp_terms
will be chunked using columnterm_id
Preparing data dump for tablewordpress
.wp_usermeta
Writing DDL for tablewordpress
.wp_comments
Writing DDL for tablewordpress
.wp_links
Writing DDL for tablewordpress
.wp_options
Data dump for tablewordpress
.wp_usermeta
will be chunked using columnumeta_id
Preparing data dump for tablewordpress
.wp_users
Data dump for tablewordpress
.wp_users
will be chunked using columnID
Running data dump using 4 threads. NOTE: Progress information uses estimated values and may not be accurate. Writing DDL for tablewordpress
.wp_postmeta
Writing DDL for tablewordpress
.wp_posts
Writing DDL for tablewordpress
.wp_term_relationships
Writing DDL for tablewordpress
.wp_term_taxonomy
Writing DDL for tablewordpress
.wp_termmeta
Writing DDL for tablewordpress
.wp_terms
Writing DDL for tablewordpress
.wp_usermeta
Writing DDL for tablewordpress
.wp_users
Data dump for tablewordpress
.wp_commentmeta
will be written to 1 file Data dump for tablewordpress
.wp_comments
will be written to 1 file Data dump for tablewordpress
.wp_links
will be written to 1 file Data dump for tablewordpress
.wp_postmeta
will be written to 1 file Data dump for tablewordpress
.wp_options
will be written to 1 file Data dump for tablewordpress
.wp_term_taxonomy
will be written to 1 file Data dump for tablewordpress
.wp_term_relationships
will be written to 1 file Data dump for tablewordpress
.wp_terms
will be written to 1 file Data dump for tablewordpress
.wp_posts
will be written to 1 file Data dump for tablewordpress
.wp_termmeta
will be written to 1 file Data dump for tablewordpress
.wp_users
will be written to 1 file Data dump for tablewordpress
.wp_usermeta
will be written to 1 file 1 thds dumping - 110% (2.22K rows / ~2.00K rows), 0.00 rows/s, 0.00 B/s uncompressed, 0.00 B/s compressed Duration: 00:00:00s Schemas dumped: 1 Tables dumped: 12 Uncompressed data size: 1.49 MB Compressed data size: 341.04 KB Compression ratio: 4.4 Rows written: 2216 Bytes written: 341.04 KB Average uncompressed throughput: 1.49 MB/s Average compressed throughput: 341.04 KB/s
We can now load that dump to our MDS Instance easily. From MySQL Shell we connect to our instance using the IP we have in the detailed view or using the name we gave it during setup:
MySQL localhost:33060+ ssl JS > \c admin@10.0.1.9 Creating a session to 'admin@10.0.1.11' Please provide the password for 'admin@10.0.1.11': *** Save password for 'admin@10.0.1.9'? [Y]es/[N]o/Ne[v]er (default No): y Fetching schema names for autocompletion… Press ^C to stop. Closing old connection… Your MySQL connection id is 9 (X protocol) Server version: 8.0.21-u1-cloud MySQL Enterprise - Cloud No default schema selected; type \use to set one.
Perfect ! We can start the load:
MySQL 10.0.1.9:33060+ ssl JS > util.loadDump('file://dump') Loading DDL and Data from 'file://dump' using 4 threads. Target is MySQL 8.0.21-u1-cloud. Dump was produced from MySQL 8.0.21 Checking for pre-existing objects… Executing common preamble SQL Executing DDL script for schemawordpress
Executing DDL script forwordpress
.wp_users
Executing DDL script forwordpress
.wp_usermeta
Executing DDL script forwordpress
.wp_links
Executing DDL script forwordpress
.wp_options
Executing DDL script forwordpress
.wp_termmeta
Executing DDL script forwordpress
.wp_comments
Executing DDL script forwordpress
.wp_commentmeta
Executing DDL script forwordpress
.wp_postmeta
Executing DDL script forwordpress
.wp_term_taxonomy
Executing DDL script forwordpress
.wp_term_relationships
Executing DDL script forwordpress
.wp_posts
Executing DDL script forwordpress
.wp_terms
[Worker003] wordpress@wp_options@@0.tsv.zst: Records: 128 Deleted: 0 Skipped: 0 Warnings: 0 [Worker003] wordpress@wp_usermeta@@0.tsv.zst: Records: 18 Deleted: 0 Skipped: 0 Warnings: 0 [Worker003] wordpress@wp_term_relationships@@0.tsv.zst: Records: 177 Deleted: 0 Skipped: 0 Warnings: 0 [Worker003] wordpress@wp_users@@0.tsv.zst: Records: 1 Deleted: 0 Skipped: 0 Warnings: 0 [Worker003] wordpress@wp_terms@@0.tsv.zst: Records: 1 Deleted: 0 Skipped: 0 Warnings: 0 [Worker003] wordpress@wp_term_taxonomy@@0.tsv.zst: Records: 1 Deleted: 0 Skipped: 0 Warnings: 0 [Worker002] wordpress@wp_comments@@0.tsv.zst: Records: 201 Deleted: 0 Skipped: 0 Warnings: 0 [Worker001] wordpress@wp_postmeta@@0.tsv.zst: Records: 1266 Deleted: 0 Skipped: 0 Warnings: 0 [Worker000] wordpress@wp_posts@@0.tsv.zst: Records: 423 Deleted: 0 Skipped: 0 Warnings: 0 Executing common postamble SQL 9 chunks (2.22K rows, 1.49 MB) for 9 tables in 1 schemas were loaded in 1 sec (avg throughput 1.49 MB/s) 0 warnings were reported during the load.
Et voilà ! All is imported.
In MDS with MySQL Shell 8.0.21, loading users can be problematic. You can try with
{loadUsers: true}
but it will fails for the userroot
. In 8.0.22, you will have to possibility to exclude or include some users.Therefor, in our case the
wp
user needs to be created manually in MDS (you will find the statements in the dump@.users.sql
file.
If you are a specialist of MySQL Replication, you already noticed that we are missing on important thing to after have loaded the dump.
We still need to set the GTID purged information from when the dump was taken.
In MDS, this operation can be achieved calling a dedicated procedure called sys.set_gtid_purged()
Now let’s find the value we need to add there. The value of GTID executed from the dump is written in the file @.json
in the dump directory:
$ cat dump/@.json | grep gtidEx
"gtidExecuted": "7c9286c7-dd49-11ea-8564-020017013873:1-369",
Now that we know the value, we can specify it in our MDS instance:
MySQL 10.0.1.9:33060+ ssl JS > \sql call sys.set_gtid_purged("7c9286c7-dd49-11ea-8564-020017013873:1-369")
Replication user creation
On the production MySQL (the one still running on the OCI compute instance), we need to create a user dedicated to replication:
mysql> CREATE USER 'repl'@'10.0.1.%' IDENTIFIED BY 'C0mpl1c4t3d!Paddw0rd' REQUIRE SSL; mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.0.1.%';
Create the replication channel
We go back on OCI’s dashboard and in our MDS instance’s details page, we click on Channels
:
We now create a channel and follow the wizard:
We use the credentials we just created and as hostname we put or the IP or the fqdn of the compute instance.
After a little while, the channel will be created and in MySQL Shell when connected to your MDS instance, you can see that replication is running:
I invite you to use this query to have all the information you need (cut and paste it 😉 ):
SELECT concat(conn_status.channel_name, ' (', worker_id,')') AS channel, conn_status.service_state AS io_state, applier_status.service_state AS sql_state, format_pico_time(if(GTID_SUBTRACT(LAST_QUEUED_TRANSACTION, LAST_APPLIED_TRANSACTION) = "","0", abs(time_to_sec(if(time_to_sec(APPLYING_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP)=0,0, timediff(APPLYING_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP,now()))))) * 1000000000000) latency, format_pico_time((LAST_QUEUED_TRANSACTION_START_QUEUE_TIMESTAMP - LAST_QUEUED_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP) * 100000000000) transport_time, format_pico_time((LAST_QUEUED_TRANSACTION_END_QUEUE_TIMESTAMP - LAST_QUEUED_TRANSACTION_START_QUEUE_TIMESTAMP) * 1000000000000) time_to_relay_log, format_pico_time((LAST_APPLIED_TRANSACTION_END_APPLY_TIMESTAMP - LAST_APPLIED_TRANSACTION_START_APPLY_TIMESTAMP) * 1000000000000) apply_time, conn_status.LAST_QUEUED_TRANSACTION AS last_queued_transaction, applier_status.LAST_APPLIED_TRANSACTION AS last_applied_transaction FROM performance_schema.replication_connection_status AS conn_status JOIN performance_schema.replication_applier_status_by_worker AS applier_status ON applier_status.channel_name = conn_status.channel_name ;
This is an example of the output:
Point WordPress to MDS
Now you need to change the location of your MySQL Server in WordPress. If you prefer you can put WordPress in Maintenance Mode (there are few plugins to do that) or just change the wp-config.php
file immediately.
You can of course wait a but to see how MDS behave and to get familiar with it. Replication is setup and running. When you are ready, just make the change.
In my case I need to replace
/** MySQL hostname */ define( 'DB_HOST', '10.0.1.5' ); by /** MySQL hostname */ define( 'DB_HOST', '10.0.1.9' );
And that’s it ! Wooohoooo, it works \o/
We are now running WordPress with MDS !
After, you can stop the replication channel and terminal the MySQL Compute Instance.
[…] [Link]dev.mysql.com/mysql-shell-load-dump [Link]lefred.be/using-mysql-database-service [Link]관련된 […]
[…] Using MySQL Database Service for WordPress […]