Saturday, July 13, 2019

MySQL How do you restore tablespace

MySQL How do you restore tablespace?

This is not new information but I have not covered it much so addressing it now for those that need it.

If you lose your ibd files... you lose your data. So if you have a copy of one available.. or even if you are syncing from another database you can still import it.  What/how do you lose tablespace?

Here is a simple example to recover tablespace.



mysql> Create database demo;

mysql> use demo;

mysql> CREATE TABLE `demotable` (
    ->   `id` int(11) NOT NULL AUTO_INCREMENT,
    ->   `dts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    ->   PRIMARY KEY (`id`)
    -> ) ENGINE=InnoDB;


Now we store some data...


mysql> INSERT INTO demotable (id) VALUES (NULL);
Query OK, 1 row affected (0.10 sec)

mysql> INSERT INTO demotable (id) VALUES (NULL);
Query OK, 1 row affected (0.08 sec)

mysql> SELECT * FROM demotable;
+----+---------------------+
| id | dts                 |
+----+---------------------+
|  1 | 2019-07-12 23:31:34 |
|  2 | 2019-07-12 23:31:35 |
+----+---------------------+
2 rows in set (0.00 sec)


OK now lets break it..


# systemctl stop mysqld
# cd /var/lib/mysql/demo/
# ls -ltr
total 80
-rw-r-----. 1 mysql mysql 114688 Jul 12 23:31 demotable.ibd
# mv demotable.ibd /tmp/

# systemctl start mysqld
# mysql demo

mysql> show tables;
+----------------+
| Tables_in_demo |
+----------------+
| demotable      |
+----------------+
1 row in set (0.00 sec)

mysql> desc demotable;
+-------+-----------+------+-----+-------------------+-----------------------------------------------+
| Field | Type      | Null | Key | Default           | Extra                                         |
+-------+-----------+------+-----+-------------------+-----------------------------------------------+
| id    | int(11)   | NO   | PRI | NULL              | auto_increment                                |
| dts   | timestamp | NO   |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED on update CURRENT_TIMESTAMP |
+-------+-----------+------+-----+-------------------+-----------------------------------------------+
2 rows in set (0.01 sec)

mysql> INSERT INTO demotable (id) VALUES (NULL);
ERROR 1812 (HY000): Tablespace is missing for table `demo`.`demotable`.


Broken and lost tablespace... Now we can recover it..


demo]# cp /tmp/demotable.ibd .

mysql> ALTER TABLE demotable DISCARD TABLESPACE;

demo]# cp /tmp/demotable.ibd .
demo]# ls -ltr
total 112
-rw-r-----. 1 root root 114688 Jul 12 23:50 demotable.ibd
demo]# chown mysql:mysql demotable.ibd
demo]# mysql demo
mysql> ALTER TABLE demotable IMPORT TABLESPACE;
ERROR 1034 (HY000): Incorrect key file for table 'demotable'; try to repair it

mysql> REPAIR TABLE demotable;
+----------------+--------+----------+---------------------------------------------------------+
| Table          | Op     | Msg_type | Msg_text                                                |
+----------------+--------+----------+---------------------------------------------------------+
| demo.demotable | repair | note     | The storage engine for the table doesn't support repair |
+----------------+--------+----------+---------------------------------------------------------+


Notice now we also got another error.. This is usually tied to space available to tmpdir, and repair doesn't work for .ibd anyway.


mysql> select @@tmpdir;
+----------+
| @@tmpdir |
+----------+
| /tmp     |
+----------+

# vi /etc/my.cnf
tmpdir=/var/lib/mysql-files/

# systemctl restart mysqld
# mysql demo


OK used the mysql-files directory just for example.
Now we can try again.


mysql> ALTER TABLE demotable IMPORT TABLESPACE;
Query OK, 0 rows affected, 1 warning (0.61 sec)

mysql>  INSERT INTO demotable (id) VALUES (NULL);
Query OK, 1 row affected (0.11 sec)

mysql>  SELECT * FROM demotable;
+----+---------------------+
| id | dts                 |
+----+---------------------+
|  1 | 2019-07-12 23:31:34 |
|  2 | 2019-07-12 23:31:35 |
|  3 | 2019-07-12 23:56:08 |
+----+---------------------+


OK worked.
Now, this is all nice and simple if you just have one table. But what about 100s...

Automate it, of course, and use your information_schema to help.

Make a few more copies for test.

mysql> create table demotable1 like demotable;
Query OK, 0 rows affected (0.51 sec)

mysql> create table demotable2 like demotable;
Query OK, 0 rows affected (1.04 sec)

mysql> create table demotable3 like demotable;
Query OK, 0 rows affected (0.74 sec)

mysql> create table demotable4 like demotable;
Query OK, 0 rows affected (2.21 sec)


break them all..

demo]# mv *.ibd /tmp/


Now using your information_schema.tables table, you can build out all the commands you will need.

# vi build_discard.sql
# cat build_discard.sql
SELECT CONCAT(" ALTER TABLE ",TABLE_SCHEMA,".",TABLE_NAME," DISCARD TABLESPACE;  ") as CMD FROM information_schema.TABLES WHERE TABLE_SCHEMA='demo';

# vi build_import.sql
# cat build_import.sql
SELECT CONCAT(" ALTER TABLE ",TABLE_SCHEMA,".",TABLE_NAME," IMPORT TABLESPACE;  ") as CMD FROM information_schema.TABLES WHERE TABLE_SCHEMA='demo';



# mysql -N < build_import.sql > import_tablespace.sql
# mysql -N < build_discard.sql  | mysql demo

demo]# cp /tmp/*.ibd .
demo]# chown mysql:mysql *.ibd
# systemctl restart mysqld
# mysql demo < import_tablespace.sql
# mysql demo

mysql> INSERT INTO demotable (id) VALUES (NULL);
Query OK, 1 row affected (0.08 sec)

mysql> INSERT INTO demotable1 (id) VALUES (NULL);
Query OK, 1 row affected (0.05 sec)

mysql> INSERT INTO demotable2 (id) VALUES (NULL);
Query OK, 1 row affected (0.09 sec)

mysql> INSERT INTO demotable3 (id) VALUES (NULL);
^[[AQuery OK, 1 row affected (0.37 sec)

mysql> INSERT INTO demotable4 (id) VALUES (NULL);
Query OK, 1 row affected (0.12 sec)



And it worked.


Friday, July 12, 2019

MySQL Binlogs:: How to recover

So I realized I had not made a post about this after this situation that recently came up.

Here is the scenario: A backup was taken at midnight, they used MySQL dumps per database. Then at ten am the next day the database crashed. A series of events happened before I was called in, but they got it up to a version of the database with MyISAM tables and the IBD files missing from the tablespace.

So option 1, restoring from backup would get us to midnight and we would lose hours of data. Option 2, we reimport the 1000's of ibd files and keep everything. Then we had option 3, restore from backup, then apply the binlogs for recent changes.

To make it more interesting, they didn't have all of the ibd files I was told, and I did see some missing. So not sure how that was possible but option 2 became an invalid option. They, of course, wanted the least data loss possible, so we went with option 3.

To do this safely I started up another instance of MySQL under port 3307. This allowed me a safe place to work while traffic had read access to the MyISAM data on the port 3306 instance.

Once all the backup dump files uncompressed and imported into the 3307 instance I was able to focus on the binlog files.

At first this concept sounds much harder risky than it really is. It is actually pretty straight forward and simple.

So first you have to find the data your after. A review of the binlog files gives you a head start as to what files are relevant. In my case, somehow they managed to reset the binlog so the 117 file had 2 date ranges within it.

First for binlog review, the following command outputs the data in human-readable format.
mysqlbinlog --defaults-file=/root/.my.cnf  --base64-output=DECODE-ROWS  --verbose mysql-bin.000117 >   review_mysql-bin.000117.sql

*Note... Be careful running the above command. Notice I have it dumping the file directly in same location as binlog. So VALIDATE that your file name is valid.  This mysql-bin.000117.sql is different than this mysql-bin.000117 .sql  . You will loose your binlog with the 2nd option and a space before .sql.

Now to save the data so it can be applied. Since I had several binlogs I created a file and I wanted to double-check the time ranges anyway.


mysqlbinlog --defaults-file=/root/.my.cnf --start-datetime="2019-07-09 00:00:00" --stop-datetime="2019-07-10 00:00:00" mysql-bin.000117 > binlog_restore.sql
mysqlbinlog --defaults-file=/root/.my.cnf mysql-bin.000118 >> binlog_restore.sql
mysqlbinlog --defaults-file=/root/.my.cnf mysql-bin.000119 >> binlog_restore.sql
mysqlbinlog --defaults-file=/root/.my.cnf --start-datetime="2019-07-10 00:00:00" --stop-datetime="2019-07-10 10:00:00" mysql-bin.000117 >> binlog_restore.sql
mysqlbinlog --defaults-file=/root/.my.cnf --stop-datetime="2019-07-10 10:00:00" mysql-bin.000120 >> binlog_restore.sql
mysqlbinlog --defaults-file=/root/.my.cnf --stop-datetime="2019-07-10 10:00:00" mysql-bin.000121 >> binlog_restore.sql

mysql --socket=/var/lib/mysql_restore/mysql.sock -e "source /var/lib/mysql/binlog_restore.sql"


Now I applied all the data from those binlogs for the given time ranges. The client double-checked all data and was very happy to have it all back.

Several different options existed for this situation, this happened to workout best with the client.

Once the validated all was ok on the restored version it was a simple stop both databases, moved the data directories (wanted to keep the datadir defaults intact) , chown the directories just to be safe and start up MySQL. Now the restored instance was up on port 3306.


Monday, June 17, 2019

MySQL Group Replication

So MySQL's group replication came out with MySQL 5.7. Now that is has been out a little while people are starting to ask more about it.
Below is an example of how to set this up and a few pain point examples as I poked around with it.
I am using three different servers,

 Server CENTOSA

mysql> INSTALL PLUGIN group_replication SONAME 'group_replication.so';
Query OK, 0 rows affected (0.02 sec)

vi my.cnf
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
server_id=1
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE

log_bin=binlog
log_slave_updates=ON
binlog_format=ROW
master_info_repository=TABLE
relay_log_info_repository=TABLE

transaction_write_set_extraction=XXHASH64
group_replication_group_name="90d8b7c8-5ce1-490e-a448-9c8d176b54a8"
group_replication_start_on_boot=off
group_replication_local_address= "192.168.111.17:33061"
group_replication_group_seeds= "192.168.111.17:33061,192.168.111.89:33061,192.168.111.124:33061"
group_replication_bootstrap_group=off

mysql> SET SQL_LOG_BIN=0;
mysql> CREATE USER repl@'%' IDENTIFIED BY 'replpassword';
mysql> GRANT REPLICATION SLAVE ON *.* TO repl@'%';
mysql> FLUSH PRIVILEGES;
mysql> SET SQL_LOG_BIN=1;


CHANGE MASTER TO
MASTER_USER='repl',
MASTER_PASSWORD='replpassword'
FOR CHANNEL 'group_replication_recovery';


mysql> SET GLOBAL group_replication_bootstrap_group=ON;
Query OK, 0 rows affected (0.00 sec)


mysql> START GROUP_REPLICATION;
Query OK, 0 rows affected (3.11 sec)


mysql> SET GLOBAL group_replication_bootstrap_group=OFF;
Query OK, 0 rows affected (0.00 sec)


mysql> SELECT * FROM performance_schema.replication_group_members \G

*************************** 1. row ***************************
CHANNEL_NAME: group_replication_applier
MEMBER_ID: 1ab30239-5ef6-11e9-9b4a-08002712f4b1
MEMBER_HOST: centosa
MEMBER_PORT: 3306
MEMBER_STATE: ONLINE
MEMBER_ROLE: PRIMARY
MEMBER_VERSION: 8.0.15

So now we can add more servers.
Server CENTOSB

vi my.cnf
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
server_id=2
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE

log_bin=binlog
log_slave_updates=ON
binlog_format=ROW
master_info_repository=TABLE
relay_log_info_repository=TABLE


transaction_write_set_extraction=XXHASH64
group_replication_group_name="90d8b7c8-5ce1-490e-a448-9c8d176b54a8"
group_replication_start_on_boot=off
group_replication_local_address= "192.168.111.89:33061"
group_replication_group_seeds= "192.168.111.17:33061,192.168.111.89:33061,192.168.111.124:33061"
group_replication_bootstrap_group=off

mysql> CHANGE MASTER TO
MASTER_USER='repl',
MASTER_PASSWORD='replpassword'
FOR CHANNEL 'group_replication_recovery';
Query OK, 0 rows affected, 2 warnings (0.02 sec)

mysql> CHANGE MASTER TO GET_MASTER_PUBLIC_KEY=1;
Query OK, 0 rows affected (0.02 sec)

mysql> START GROUP_REPLICATION;
Query OK, 0 rows affected (4.03 sec)

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 1ab30239-5ef6-11e9-9b4a-08002712f4b1 | centosa | 3306 | ONLINE | PRIMARY | 8.0.15 |
| group_replication_applier | 572ca2fa-5eff-11e9-8df9-08002712f4b1 | centosb | 3306 | RECOVERING | SECONDARY | 8.0.15 |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
2 rows in set (0.00 sec)


Server CENTOSC

vi my.cnf
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
server_id=3
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE
log_bin=binlog
log_slave_updates=ON
binlog_format=ROW
master_info_repository=TABLE
relay_log_info_repository=TABLE

transaction_write_set_extraction=XXHASH64
group_replication_group_name="90d8b7c8-5ce1-490e-a448-9c8d176b54a8"
group_replication_start_on_boot=off
group_replication_local_address= "192.168.111.124:33061"
group_replication_group_seeds= "192.168.111.17:33061,192.168.111.89:33061,192.168.111.124:33061"
group_replication_bootstrap_group=off

mysql> CHANGE MASTER TO
-> MASTER_USER='repl',
-> MASTER_PASSWORD='replpassword'
-> FOR CHANNEL 'group_replication_recovery';
Query OK, 0 rows affected, 2 warnings (0.02 sec)

mysql> CHANGE MASTER TO GET_MASTER_PUBLIC_KEY=1;
Query OK, 0 rows affected (0.02 sec)

mysql> START GROUP_REPLICATION;
Query OK, 0 rows affected (3.58 sec)
mysql> SELECT * FROM performance_schema.replication_group_members \G
*************************** 1. row ***************************
CHANNEL_NAME: group_replication_applier
MEMBER_ID: 1ab30239-5ef6-11e9-9b4a-08002712f4b1
MEMBER_HOST: centosa
MEMBER_PORT: 3306
MEMBER_STATE: ONLINE
MEMBER_ROLE: PRIMARY
MEMBER_VERSION: 8.0.15

*************************** 2. row ***************************
CHANNEL_NAME: group_replication_applier
MEMBER_ID: 572ca2fa-5eff-11e9-8df9-08002712f4b1
MEMBER_HOST: centosb
MEMBER_PORT: 3306
MEMBER_STATE: ONLINE
MEMBER_ROLE: SECONDARY
MEMBER_VERSION: 8.0.15

*************************** 3. row ***************************
CHANNEL_NAME: group_replication_applier
MEMBER_ID: c5f3d1d2-8dd8-11e9-858d-08002773d1b6
MEMBER_HOST: centosc
MEMBER_PORT: 3306
MEMBER_STATE: ONLINE
MEMBER_ROLE: SECONDARY
MEMBER_VERSION: 8.0.15
3 rows in set (0.00 sec)


So this is all great but it doesn't always mean they go online, they can often sit in recovery mode.
I have seen this fail with MySQL crashes so far so need to ensure it stable.
mysql> create database testcentosb;<br> ERROR 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement<br>
Side Note to address some of those factors --
mysql> START GROUP_REPLICATION;
ERROR 3094 (HY000): The START GROUP_REPLICATION command failed as the applier module failed to start.

mysql> reset slave all;
Query OK, 0 rows affected (0.03 sec)

-- Then start over from Change master command
mysql> START GROUP_REPLICATION;
ERROR 3092 (HY000): The server is not configured properly to be an active member of the group. Please see more details on error log.

[ERROR] [MY-011735] [Repl] Plugin group_replication reported: '[GCS] Error on opening a connection to 192.168.111.17:33061 on local port: 33061.'
[ERROR] [MY-011526] [Repl] Plugin group_replication reported: 'This member has more executed transactions than those present in the group. Local transactions: c5f3d1d2-8dd8-11e9-858d-08002773d1b6:1-4 >
[ERROR] [MY-011522] [Repl] Plugin group_replication reported: 'The member contains transactions not present in the group. The member will now exit the group.'

https://ronniethedba.wordpress.com/2017/04/22/this-member-has-more-executed-transactions-than-those-present-in-the-group/ 


 [ERROR] [MY-011620] [Repl] Plugin group_replication reported: 'Fatal error during the recovery process of Group Replication. The server will leave the group.'
[ERROR] [MY-013173] [Repl] Plugin group_replication reported: 'The plugin encountered a critical error and will abort: Fatal error during execution of Group Replication'

SELECT * FROM performance_schema.replication_connection_status\G


My thoughts...
Keep in mind that group replication can be set up in single primary mode or multi-node
mysql> select @@group_replication_single_primary_mode\G
*************************** 1. row ***************************
@@group_replication_single_primary_mode: 1

mysql> create database testcentosb;
ERROR 1290 (HY000): The MySQL server is running with the --super-read-only option so it cannot execute this statement
you will of course get an error if you write to none primary node.


group-replication-single-primary-mode=off  <-- added to the cnf files. 
mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 1ab30239-5ef6-11e9-9b4a-08002712f4b1 | centosa     |        3306 | RECOVERING   | PRIMARY     | 8.0.15         |
| group_replication_applier | 572ca2fa-5eff-11e9-8df9-08002712f4b1 | centosb     |        3306 | ONLINE       | PRIMARY     | 8.0.15         |
| group_replication_applier | c5f3d1d2-8dd8-11e9-858d-08002773d1b6 | centosc     |        3306 | RECOVERING   | PRIMARY     | 8.0.15         |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+

3 rows in set (0.00 sec)


It is now however if you use Keepalived, MySQL router, ProxySQL etc to handle your traffic to automatically roll over in case of a failover. We can see from below it failed over right away when I stopped the primary.

mysql> SELECT * FROM performance_schema.replication_group_members ;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 1ab30239-5ef6-11e9-9b4a-08002712f4b1 | centosa | 3306 | ONLINE | PRIMARY | 8.0.15 |
| group_replication_applier | 572ca2fa-5eff-11e9-8df9-08002712f4b1 | centosb | 3306 | ONLINE | SECONDARY | 8.0.15 |
| group_replication_applier | c5f3d1d2-8dd8-11e9-858d-08002773d1b6 | centosc | 3306 | ONLINE | SECONDARY | 8.0.15 |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
3 rows in set (0.00 sec)

[root@centosa]# systemctl stop mysqld

mysql> SELECT * FROM performance_schema.replication_group_members ;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 572ca2fa-5eff-11e9-8df9-08002712f4b1 | centosb | 3306 | ONLINE | PRIMARY | 8.0.15 |
| group_replication_applier | c5f3d1d2-8dd8-11e9-858d-08002773d1b6 | centosc | 3306 | ONLINE | SECONDARY | 8.0.15 |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
2 rows in set (0.00 sec)

[root@centosa]# systemctl start mysqld
[root@centosa]# mysql
mysql> START GROUP_REPLICATION;
Query OK, 0 rows affected (3.34 sec)

mysql> SELECT * FROM performance_schema.replication_group_members ;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 1ab30239-5ef6-11e9-9b4a-08002712f4b1 | centosa | 3306 | RECOVERING | SECONDARY | 8.0.15 |
| group_replication_applier | 572ca2fa-5eff-11e9-8df9-08002712f4b1 | centosb | 3306 | ONLINE | PRIMARY | 8.0.15 |
| group_replication_applier | c5f3d1d2-8dd8-11e9-858d-08002773d1b6 | centosc | 3306 | ONLINE | SECONDARY | 8.0.15 |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
3 rows in set (0.00 sec)


Now the recovery was still an issue, as it is would not simply join back. Had to review all accounts and steps again but I did get it back eventually.

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 1ab30239-5ef6-11e9-9b4a-08002712f4b1 | centosa | 3306 | ONLINE | SECONDARY | 8.0.15 |
| group_replication_applier | 572ca2fa-5eff-11e9-8df9-08002712f4b1 | centosb | 3306 | ONLINE | PRIMARY | 8.0.15 |
| group_replication_applier | c5f3d1d2-8dd8-11e9-858d-08002773d1b6 | centosc | 3306 | ONLINE | SECONDARY | 8.0.15 |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
3 rows in set (0.00 sec)


I need to test more with this as I am not 100% sold yet as to needing this as I lean towards Galera replication still.

URLS of Interest


  • https://dev.mysql.com/doc/refman/8.0/en/group-replication.html
  • https://dev.mysql.com/doc/refman/8.0/en/group-replication-deploying-in-single-primary-mode.html
  • http://datacharmer.blogspot.com/2017/01/mysql-group-replication-vs-multi-source.html 
  • https://dev.mysql.com/doc/refman/8.0/en/group-replication-launching.html
  • https://dev.mysql.com/doc/refman/8.0/en/group-replication-configuring-instances.html
  • https://dev.mysql.com/doc/refman/8.0/en/group-replication-adding-instances.html
  • https://ronniethedba.wordpress.com/2017/04/22/how-to-setup-mysql-group-replication/
  • https://www.digitalocean.com/community/tutorials/how-to-configure-mysql-group-replication-on-ubuntu-16-04 
  • https://dev.mysql.com/doc/refman/8.0/en/group-replication-options.html#sysvar_group_replication_group_seeds 
  • https://bugs.mysql.com/bug.php?id=90534
  • https://www.percona.com/blog/2017/02/24/battle-for-synchronous-replication-in-mysql-galera-vs-group-replication/
  • https://lefred.be/content/mysql-group-replication-is-sweet-but-can-be-sour-if-you-misunderstand-it/
  • https://www.youtube.com/watch?v=IfZK-Up03Mw
  • https://mysqlhighavailability.com/mysql-group-replication-a-quick-start-guide/


  • Monday, June 3, 2019

    Max_connections 214 4.15.0-46-generic #49-Ubuntu


    So the issue of max_connections dropping from the value set in your my.cnf file down to 214 has been around for a little while on Ubuntu.

    As an example, it was noted here back in 2015



    I ran into this again recently and was resolved with the following steps.


    # cp /lib/systemd/system/mysql.service /etc/systemd/system/
    # cd /etc/systemd/system/
    # vi mysql.service

    LimitNOFILE=infinity
    LimitMEMLOCK=infinity

    # systemctl daemon-reload
    # systemctl restart mysql


    Once those steps completed the MySQL connections were stable at the given parameter in the my.cnf file. 

    Thursday, April 25, 2019

    Slack

    Just a simple post here to give support to the slack community channels.

    If you have not joined some of these... this is a great way to communicate with the community.

    MySQL -- https://mysqlcommunity.slack.com  https://mysql-casual.slack.com/

    PostgreSQL - https://postgresteam.slack.com  https://postgres-slack.herokuapp.com/

    MongoDB -- https://mongo-db.slack.com

    Of course, Freenode is still available as well -- http://chat.freenode.net (https://freenode.net/)

    Sunday, April 14, 2019

    Simple KeepaliveD set up

    So keepalived has been around for quite a while now .... however it is still a mystery to many.
    So this is a very simple example of how keepalived can work with MySQL. Hopefully, this can help those with questions.

    We will have a Simple master to slave set up. Meaning.. we write to one unless we have failover to the second for some event.

    1st - install keepalived


    # yum search keepalived
    keepalived.x86_64 : Load balancer and high availability service

      Name and summary matches only, use "search all" for everything.
    # yum -y install keepalived

    You should now have an config file 

    # ls -ltr /etc/keepalived/keepalived.conf 

    Keep the original as you always backup .. right....
    # cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.orig

    So you need to figure out an ipaddress you can use for your virtual ip.  I picked 192.168.0.123 for this example. 

    Next, we will set up a script to be used for our new config file. 

    A few things I did here..
    I left a .cnf file for keepalived and a log all in the /etc/keepalived.
    This makes it simple for the example so you can do this or use your own cnf files.

    A script:

    cat /etc/keepalived/keepalived_check.sh 
    #!/bin/bash

    # monitor mysql status

    # if this node mysql is dead

    # and its slave is alive , then stop its keepalived. The other node will bind the IP.



    export MYSQL_HOME=/etc/keepalived/

    export PATH=$MYSQL_HOME/bin:$PATH



    mysql="/usr/bin/mysql"

    mysqladmin="/usr/bin/mysqladmin"

    delay_file="$MYSQL_HOME/slave_delay_second.log"

    slave_host=$1





    ALIVE=$($mysqladmin --defaults-file=$MYSQL_HOME/.my.localhost.cnf  ping | grep alive | wc -l );

    REMOTEALIVE=$($mysqladmin --defaults-file=$MYSQL_HOME/.my.remotehost.cnf  ping | grep alive | wc -l );



    if [[ $ALIVE -ne 1 ]]

    then

    #echo "MySQL is down"

            if [[ $REMOTEALIVE -eq 1 ]]

            then

    #        echo "Shutdown keep alive "

                systemctl stop keepalived  

    #       echo " keepalived stop "

            fi

    #else

    #echo "MySQL is up"

    #date

    fi



    exit 0 #all done

    New config file

    cat /etc/keepalived/keepalived.conf
    global_defs {



          notification_email {

            anothermysqldba@gmail.com 

          }



          notification_email_from anothermysqldba@gmail.com 

          smtp_server localhost

          smtp_connect_timeout 30



          }







    vrrp_script check_mysql {

       script "/etc/keepalived/keepalived_check.sh "

       interval 2

       weight 2

    }







    vrrp_instance VI_1 {



          state MASTER

          interface enp0s8  # <--- WHAT INTERFACE NAME HOLDS YOUR REAL IP /sbin/ifconfig

            # found with ip link show

          virtual_router_id 51

          priority 101

          advert_int 1

          nopreempt  # only needed on higher priority node

          authentication {

            auth_type PASS

            auth_pass 1111

          }





          track_script {

            check_mysql

          }



          virtual_ipaddress {

            192.168.0.123 

          }




    }



    This is all great but does it work....

    So we have 2 hosts

    [root@centosa keepalived]# hostname

    centosa

    [root@centosb keepalived]# hostname
    centosb

    Start keepalived  

    [root@centosa keepalived]# systemctl status keepalived
    ● keepalived.service - LVS and VRRP High Availability Monitor
       Loaded: loaded (/usr/lib/systemd/system/keepalived.service; disabled; vendor preset: disabled)
       Active: inactive (dead)
    [root@centosa keepalived]# systemctl restart keepalived
    [root@centosa keepalived]# systemctl status keepalived
    keepalived.service - LVS and VRRP High Availability Monitor
       Loaded: loaded (/usr/lib/systemd/system/keepalived.service; disabled; vendor preset: disabled)
       Active: active (running)

    [root@centosa keepalived]# ssh 192.168.0.123 'hostname'
    root@192.168.0.123's password: 

    centosa

    Prove the connections work already

    [root@centosa keepalived]# mysql --defaults-file=.my.remotehost.cnf --host=192.168.0.101   -e "select @@hostname"
    +------------+
    | @@hostname |
    +------------+
    | centosb    |
    +------------+
    [root@centosa keepalived]# mysql --defaults-file=.my.remotehost.cnf --host=192.168.0.102   -e "select @@hostname"
    +------------+
    | @@hostname |
    +------------+
    | centosa    |
    +------------+

    Double check that it is running... 

    [root@centosa keepalived]# systemctl status keepalived | grep active
       Active: active 

    [root@centosb keepalived]# systemctl status keepalived | grep active
       Active: active 

    Test current VIP .. stop mysql and watch same VIP change hosts ... 

    [root@centosa keepalived]# mysql --defaults-file=.my.remotehost.cnf --host=192.168.0.123   -e "select @@hostname"
    +------------+
    | @@hostname |
    +------------+
    | centosa    |
    +------------+
    [root@centosa keepalived]# systemctl stop mysqld 
    [root@centosa keepalived]# mysql --defaults-file=.my.remotehost.cnf --host=192.168.0.123   -e "select @@hostname"
    +------------+
    | @@hostname |
    +------------+
    | centosb    |
    +------------+




    Tuesday, April 9, 2019

    Sometimes the slow database.. is not the database...

    So I was recently asked to look into why the updated MySQL 5,.6 was slower than the older 5.5 

    So I started by poking around looking over the standard variables and caches and etc.

    The test case was a simple routine that took about twice as long to run on 5.6 than it did on 5.5. 

    To add to the mix.. the 5.6 version had double the Innodb_buffer_pool_size and of course more ram overall.  

    So I started some tests with MySQLslap...

    Mysqlslap tests show it slower on 5.6 

    5.6:
    mysqlslap --defaults-file=./.my.cnf --concurrency=150 --iterations=130 -query=/test.sql --create-schema=applicationdata --verbose 
    Benchmark
    Average number of seconds to run all queries: 0.028 seconds
    Minimum number of seconds to run all queries: 0.019 seconds
    Maximum number of seconds to run all queries: 0.071 seconds
    Number of clients running queries: 150
    Average number of queries per client: 1

    5.5:
    mysqlslap --defaults-file=./.my.cnf --concurrency=150 --iterations=130 --query=/test.sql --create-schema=applicationdata --verbose 
    Benchmark
    Average number of seconds to run all queries: 0.015 seconds
    Minimum number of seconds to run all queries: 0.011 seconds
    Maximum number of seconds to run all queries: 0.024 seconds
    Number of clients running queries: 150
    Average number of queries per client: 1


    All of this goes against the public benchmarks 
    http://dimitrik.free.fr/blog/archives/2013/02/mysql-performance-mysql-56-ga-vs-mysql-55-32cores.html 

    So I checked disk level --

    5.6:
    # dd if=/dev/zero of=test bs=1048576 count=2048
    2048+0 records in
    2048+0 records out
    2147483648 bytes (2.1 GB) copied, 25.7401 s, 83.4 MB/s

    # dd if=test of=/dev/null bs=1048576
    2048+0 records in
    2048+0 records out
    2147483648 bytes (2.1 GB) copied, 29.1527 s, 73.7 MB/s

    5.5:
    # dd if=/dev/zero of=test bs=1048576 count=2048
    2048+0 records in
    2048+0 records out
    2147483648 bytes (2.1 GB) copied, 19.9214 seconds, 108 MB/s

    # dd if=test of=/dev/null bs=1048576
    2048+0 records in
    2048+0 records out
    2147483648 bytes (2.1 GB) copied, 20.0243 seconds, 107 MB/s



    Here the disks with 5.5 is slower regardless of MySQL. So in this case.... Look to fixing the disk speed.. MySQL  was running fine and will. 

    Wednesday, March 27, 2019

    Every MySQL should have these variables set ...

    So over the years, we all learn more and more about what we like and use often in MySQL. 

    Currently, I step in and out of a robust about of different systems. I love it being able to see how different companies use MySQL.  I also see several aspect and settings that often get missed. So here are a few things I think should always be set and they not impact your MySQL database. 

    At a high level:

    • >Move the Slow log to a table 
    • Set report_host_name 
    • Set master & slaves to use tables
    • Turn off log_queries_not_using_indexes until needed 
    • Side note -- USE  ALGORITHM=INPLACE
    • Side note -- USE mysql_config_editor
    • Side note -- USE  mysql_upgrade  --upgrade-system-tables






    Move the Slow log to a table 

    This is a very simple process with a great return. YES you can use Percona toolkit to analyze the slow logs. However, I like being able to query against the table and find duplicate queries or by times and etc with a simple query call. 



    mysql> select count(*) from mysql.slow_log;
    +----------+
    | count(*) |
    +----------+
    |       0 |
    +----------+
    1 row in set (0.00 sec)

    mysql> select @@slow_query_log,@@sql_log_off;
    +------------------+---------------+
    | @@slow_query_log | @@sql_log_off |
    +------------------+---------------+
    |                1 |            0 |
    +------------------+---------------+

    mysql> set GLOBAL slow_query_log=0;
    Query OK, 0 rows affected (0.04 sec)

    mysql> set GLOBAL sql_log_off=1;
    Query OK, 0 rows affected (0.00 sec)

    mysql> ALTER TABLE mysql.slow_log ENGINE = MyISAM;
    Query OK, 0 rows affected (0.39 sec)

    mysql> set GLOBAL slow_query_log=0;
    Query OK, 0 rows affected (0.04 sec)

    mysql> set GLOBAL sql_log_off=1;
    Query OK, 0 rows affected (0.00 sec)

    mysql> ALTER TABLE mysql.slow_log ENGINE = MyISAM;
    Query OK, 0 rows affected (0.39 sec)

    mysql> set GLOBAL slow_query_log=1;
    Query OK, 0 rows affected (0.00 sec)

    mysql> set GLOBAL sql_log_off=0;
    Query OK, 0 rows affected (0.00 sec)

    mysql> SET GLOBAL log_output = 'TABLE';
    Query OK, 0 rows affected (0.00 sec)

    mysql> SET GLOBAL log_queries_not_using_indexes=0;
    Query OK, 0 rows affected (0.00 sec)

    mysql> select count(*) from mysql.slow_log;
    +----------+
    | count(*) |
    +----------+
    |       0 |
    +----------+
    1 row in set (0.00 sec)
    mysql> select @@slow_launch_time;
    +--------------------+
    | @@slow_launch_time |
    +--------------------+
    |                   2 |
    +--------------------+
    1 row in set (0.00 sec)

    mysql> SELECT SLEEP(10);
    +-----------+
    | SLEEP(10) |
    +-----------+
    |         0 |
    +-----------+
    1 row in set (9.97 sec)

    mysql> select count(*) from mysql.slow_log;
    +----------+
    | count(*) |
    +----------+
    |         1 |
    +----------+
    1 row in set (0.00 sec)

    mysql> select * from   mysql.slow_log\G
    *************************** 1. row ***************************
        start_time: 2019-03-27 18:02:32
         user_host: klarson[klarson] @ localhost []
        query_time: 00:00:10
         lock_time: 00:00:00
         rows_sent: 1
    rows_examined: 0
                db:
    last_insert_id: 0
         insert_id: 0
         server_id: 502
          sql_text: SELECT SLEEP(10)
         thread_id: 16586457

    Now you can truncate it or dump it or whatever you like to do with this data easily also.
    Note variable values into your my.cnf file to enable upon restart.


    Set report_host_name 


    This is a simple my.cnf file edit in all my.cnf files but certainly the slaves my.cnf files. On a master.. this is just set for when it ever gets flipped and becomes a slave.



    report_host                     = <hostname>  <or whatever you want to call it>
    This allows you from the master to do



    mysql> show slave hosts;
    +-----------+-------------+------+-----------+--------------------------------------+
    | Server_id | Host         | Port | Master_id | Slave_UUID                           |
    +-----------+-------------+------+-----------+--------------------------------------+
    |   21235302 | <hostname>  | 3306 |   
    21235301| a55faa32-c832-22e8-b6fb-e51f15b76554 |
    +-----------+-------------+------+-----------+--------------------------------------+

    Set master & slaves to use tables

    mysql> show variables like '%repository';
    +---------------------------+-------+
    | Variable_name             | Value |
    +---------------------------+-------+
    | master_info_repository     | FILE   |
    | relay_log_info_repository | FILE   |
    +---------------------------+-------+

    mysql_slave> stop slave;
    mysql_slave> SET GLOBAL master_info_repository = 'TABLE'; 
    mysql_slave> SET GLOBAL relay_log_info_repository = 'TABLE'; 
    mysql_slave> start slave;

    Make sure you add to my.cnf to you do not lose binlog and position at a restart. It will default to FILE otherwise.

    • master-info-repository =TABLE 
    • relay-log-info-repository =TABLE

    mysql> show variables like '%repository';
    ---------------------+-------+
    | Variable_name             | Value |
    +---------------------------+-------+
    | master_info_repository     | TABLE |
    | relay_log_info_repository | TABLE |
    +---------------------------+-------+


    All data is available in tables now and easily stored with backups



    mysql> desc mysql.slave_master_info;
    +------------------------+---------------------+------+-----+---------+-------+
    | Field                   | Type                 | Null | Key | Default | Extra |
    +------------------------+---------------------+------+-----+---------+-------+
    | Number_of_lines         | int(10) unsigned     | NO   |     | NULL     |       |
    | Master_log_name         | text                 | NO   |     | NULL     |       |
    | Master_log_pos         | bigint(20) unsigned | NO   |     | NULL     |       |
    | Host                   | char(64)             | YES   |     | NULL     |       |
    | User_name               | text                 | YES   |     | NULL     |       |
    | User_password           | text                 | YES   |     | NULL     |       |
    | Port                   | int(10) unsigned     | NO   |     | NULL     |       |
    | Connect_retry           | int(10) unsigned     | NO   |     | NULL     |       |
    | Enabled_ssl             | tinyint(1)           | NO   |     | NULL     |       |
    | Ssl_ca                 | text                 | YES   |     | NULL     |       |
    | Ssl_capath             | text                 | YES   |     | NULL     |       |
    | Ssl_cert               | text                 | YES   |     | NULL     |       |
    | Ssl_cipher             | text                 | YES   |     | NULL     |       |
    | Ssl_key                 | text                 | YES   |     | NULL     |       |
    | Ssl_verify_server_cert | tinyint(1)           | NO   |     | NULL     |       |
    | Heartbeat               | float               | NO   |     | NULL     |       |
    | Bind                   | text                 | YES   |     | NULL     |       |
    | Ignored_server_ids     | text                 | YES   |     | NULL     |       |
    | Uuid                   | text                 | YES   |     | NULL     |       |
    | Retry_count             | bigint(20) unsigned | NO   |     | NULL     |       |
    | Ssl_crl                 | text                 | YES   |     | NULL     |       |
    | Ssl_crlpath             | text                 | YES   |     | NULL     |       |
    | Enabled_auto_position   | tinyint(1)           | NO   |     | NULL     |       |
    | Channel_name           | char(64)             | NO   | PRI | NULL     |       |
    | Tls_version             | text                 | YES   |     | NULL     |       |
    | Public_key_path         | text                 | YES   |     | NULL     |       |
    | Get_public_key         | tinyint(1)           | NO   |     | NULL     |       |
    +------------------------+---------------------+------+-----+---------+-------+
    27 rows in set (0.05 sec)

    mysql> desc mysql.slave_relay_log_info;
    +-------------------+---------------------+------+-----+---------+-------+
    | Field             | Type                 | Null | Key | Default | Extra |
    +-------------------+---------------------+------+-----+---------+-------+
    | Number_of_lines   | int(10) unsigned     | NO   |     | NULL     |       |
    | Relay_log_name     | text                 | NO   |     | NULL     |       |
    | Relay_log_pos     | bigint(20) unsigned | NO   |     | NULL     |       |
    | Master_log_name   | text                 | NO   |     | NULL     |       |
    | Master_log_pos     | bigint(20) unsigned | NO   |     | NULL     |       |
    | Sql_delay         | int(11)             | NO   |     | NULL     |       |
    | Number_of_workers | int(10) unsigned     | NO   |     | NULL     |       |
    | Id                 | int(10) unsigned     | NO   |     | NULL     |       |
    | Channel_name       | char(64)             | NO   | PRI | NULL     |       |

    +-------------------+---------------------+------+-----+---------+-------+


    Turn off log_queries_not_using_indexes until needed 


    This was shown above also. This is a valid variable .. but depending on application it can load a slow log with useless info. Some tables might have 5 rows in it, you use it for some random drop down and you never put an index on it. With this enabled every time you query that table it gets logged. Now.. I am a big believer in you should put an index on it anyway. But focus this variable when you are looking to troubleshoot and optimize things. Let it run for at least 24hours so you get a full scope of a system if not a week.


    mysql> SET GLOBAL log_queries_not_using_indexes=0;
    Query OK, 0 rows affected (0.00 sec)


    To turn on 

    mysql> SET GLOBAL log_queries_not_using_indexes=1;

    Query OK, 0 rows affected (0.00 sec)

    Note variable values into your my.cnf file to enable upon restart. 


    Side note -- USE  ALGORITHM=INPLACE 


    OK this is not a variable but more of a best practice. You should already be using EXPLAIN before you run a query, This shows you the query plan and lets you be sure all syntax is valid.  I have seen more than once a Delete query executed without an WHERE by mistake. So 1st always use EXPLAIN to double check what you plan to do.  Not the other process you should always do is try to use an ALGORITHM=INPLACE or  ALGORITHM=COPY when altering tables. 



    mysql> ALTER TABLE TABLE_DEMO   ALGORITHM=INPLACE, ADD INDEX `datetime`(`datetime`);
    Query OK, 0 rows affected (1.49 sec)
    Records: 0   Duplicates: 0   Warnings: 0




    mysql> ALTER TABLE TABLE_DEMO   ALGORITHM=INPLACE, ADD INDEX `datetime`(`datetime`);
    Query OK, 0 rows affected (1.49 sec)
    Records: 0   Duplicates: 0   Warnings: 0


    A list of online DLL operations is here







    Side note -- USE mysql_config_editor

    Previous blog post about this is here 


    The simple example

    mysql_config_editor set  --login-path=local --host=localhost --user=root --password
    Enter password:
    # mysql_config_editor print --all
    [local]
    user = root
    password = *****
    host = localhost

    # mysql
    ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)


    # mysql  --login-path=local
    Welcome to the MySQL monitor.

    # mysql  --login-path=local -e 'SELECT NOW()';


    Side note -- USE  mysql_upgrade  --upgrade-system-tables

    Don't forget to use mysql_upgrade after you actually upgrade. 
    This is often forgotten and leads to issues and errors at start up. You do not have to run upgrade across every table that exists though.  The focus of most upgrade are the system tables. So at the very least focus on those. 

    mysql_upgrade --login-path=local  --upgrade-system-tables