Useful utility to check, repair, analyze and optimize mysql tables. Just be aware that it locks the table, so don't run on busy production tables if they are clustered as probe may time out.
mysqlcheck --auto-repair --all-databases –uroot
Useful utility to check, repair, analyze and optimize mysql tables. Just be aware that it locks the table, so don't run on busy production tables if they are clustered as probe may time out.
mysqlcheck --auto-repair --all-databases –uroot
Eg to extract the database OPALGATEWAY from the dump file all-06-11-11-17:00-mk-staging-1.sql.gz .
zcat all-06-11-11-17:00-mk-staging-1.sql.gz | awk '{ if ( $0 ~ /CREATE DATABASE.*OPALGATEWAY.*/ ) a=1; if ( $0 ~ /CREATE DATABASE / && $0 !~ /CREATE DATABASE.*OPALGATEWAY.*/ ) a=0; if (a==1) print $0 }' | gzip - | cat - > /tmp/OPALGATEWAY.sql.gz
slow_query_log = 1
slow_query_log_file = /ebill-slave-1/mysql-logs/mysqld-slow.log
long_query_time = 6000
log-queries-not-using-indexes
log-slow-admin-statements
max_connections=13000
..and
cd mk-myacct-dbslave-2/ebill-slave-2/mysql-logs >
touch mysqld-slow.log
chown mysql:mysql mysqld-slow.log
chmod 755 mysqld-slow.log
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State:
Master_Host: mk-mysqlcluster-2-miscdb-rw.uk.intranet
Master_User: replusr_miscdb
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000009
Read_Master_Log_Pos: 5506942
Relay_Log_File: mysql-relay.000516
Relay_Log_Pos: 72730
Relay_Master_Log_File: mysql-bin.000009
Slave_IO_Running: No
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB: sc3_test_database
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 5506847
Relay_Log_Space: 73121
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 1153
Last_IO_Error: Got a packet bigger than 'max_allowed_packet' bytes
Last_SQL_Errno: 0
Last_SQL_Error:
1 row in set (0.00 sec)
mysql> exit
SOLUTION:
1) Increase max_allowed_packet to 16M, by default its 1MB
my.cnf
[mysqldump]
max_allowed_packet = 16M
2) bounce database.
3) had to rebuild slave as well, as slave wasn't replicating even afterwards.
Execute permission for a Procedure or Function can be granted to Individually as shown below:
GRANT EXECUTE ON PROCEDURE `eonline`.`sp_getmailjoblist` TO 'eonline_rw'@'%' ;
GRANT EXECUTE ON FUNCTION `eonline`.`fn_getmailjobsub` TO 'eonline_rw'@'%' ;
To Grant execute permission on all Procedures and Functions of a particular database say 'eonline' to a particular user say 'eonline_rw' in this case, use the following syntax.
GRANT EXECUTE ON `eonline`.* TO 'eonline_rw'@'%' ;
This will grant the permissions for all the procedures and functions of this database to be executed from any host. To restrict this to a particular host , hostname may be specified in the grant statement.
Anonymous MySQL accounts allow clients to connect to the server without specifying a user name. To remove anonymous accounts, connect to the server as the MySQL root user to access the mysql database, then issue the following statements:
mysql> select user,host FROM user WHERE User = '';
+------+----------------------+
| user | host |
+------+----------------------+
| | localhost |
| | mk-myacct-dbmaster-1 |
+------+----------------------+
2 rows in set (0.00 sec)
mysql> DELETE FROM user WHERE User = '';
Query OK, 2 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.01 sec)
The DELETE statement removes accounts that have an empty value in the User column of the user table that lists MySQL accounts, and FLUSH PRIVILEGES tells the server to reload the grant tables so the changes take effect.
mysql> select user,host FROM user WHERE User = '';
Empty set (0.00 sec)
You can read about it here. "
For information about these features, see InnoDB Plugin 1.0 for MySQL 5.1 User's Guide.
To install innodb plugin on 5.1
1. Check what current state is
mysql> show plugins;
+------------+----------+----------------+---------+---------+
| Name | Status | Type | Library | License |
+------------+----------+----------------+---------+---------+
| binlog | ACTIVE | STORAGE ENGINE | NULL | GPL |
| partition | ACTIVE | STORAGE ENGINE | NULL | GPL |
| ARCHIVE | ACTIVE | STORAGE ENGINE | NULL | GPL |
| BLACKHOLE | ACTIVE | STORAGE ENGINE | NULL | GPL |
| CSV | ACTIVE | STORAGE ENGINE | NULL | GPL |
| FEDERATED | DISABLED | STORAGE ENGINE | NULL | GPL |
| MEMORY | ACTIVE | STORAGE ENGINE | NULL | GPL |
| InnoDB | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MyISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MRG_MYISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
+------------+----------+----------------+---------+---------+
You can see by default the builtin innoDB plugin is installed. To use new features need to install the innoDB plugin as follows.
2. change mycnf to disable the default builtin innodb and to activate the plugin
cp $MYCNF/my.cnf $MYCNF/my.cnf.20100629
vi $MYCNF/my.cnf
...insert after innodb_open_files = 1000
ignore-builtin-innodb
plugin-load=innodb=ha_innodb_plugin.so;innodb_trx=ha_innodb_plugin.so;innodb_locks=ha_innodb_plugin.so;innodb_lock_waits=ha_innodb_plugin.so;innodb_cmp=ha_innodb_plugin.so;innodb_cmp_reset=ha_innodb_plugin.so;innodb_cmpmem=ha_innodb_plugin.so;innodb_cmpmem_reset=ha_innodb_plugin.so
3.restart the database
../mysql-scripts/
4. Check
mysql> show plugins;
+---------------------+----------+--------------------+---------------------+---------+
| Name | Status | Type | Library | License |
+---------------------+----------+--------------------+---------------------+---------+
| binlog | ACTIVE | STORAGE ENGINE | NULL | GPL |
| partition | ACTIVE | STORAGE ENGINE | NULL | GPL |
| ARCHIVE | ACTIVE | STORAGE ENGINE | NULL | GPL |
| BLACKHOLE | ACTIVE | STORAGE ENGINE | NULL | GPL |
| CSV | ACTIVE | STORAGE ENGINE | NULL | GPL |
| FEDERATED | DISABLED | STORAGE ENGINE | NULL | GPL |
| MEMORY | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MyISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MRG_MYISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
| InnoDB | ACTIVE | STORAGE ENGINE | ha_innodb_plugin.so | GPL |
| INNODB_TRX | ACTIVE | INFORMATION SCHEMA | ha_innodb_plugin.so | GPL |
| INNODB_LOCKS | ACTIVE | INFORMATION SCHEMA | ha_innodb_plugin.so | GPL |
| INNODB_LOCK_WAITS | ACTIVE | INFORMATION SCHEMA | ha_innodb_plugin.so | GPL |
| INNODB_CMP | ACTIVE | INFORMATION SCHEMA | ha_innodb_plugin.so | GPL |
| INNODB_CMP_RESET | ACTIVE | INFORMATION SCHEMA | ha_innodb_plugin.so | GPL |
| INNODB_CMPMEM | ACTIVE | INFORMATION SCHEMA | ha_innodb_plugin.so | GPL |
| INNODB_CMPMEM_RESET | ACTIVE | INFORMATION SCHEMA | ha_innodb_plugin.so | GPL |
+---------------------+----------+--------------------+---------------------+---------+
chown -R mysql:mysql mysql-advanced-gpl-5.1.47-solaris10-x86_64
ln -s mysql-advanced-gpl-5.1.47-solaris10-x86_64 mysql
-change the bind address to the same as that in /etc/hosts
- change the path names
- if it's a slave then make sure the server-id is different from the master
cd …/mysql-bin/mysql
./scripts/mysql_install_db --user=mysql --basedir=/ebilldb-master /mysql-bin/mysql --datadir=/ ebilldb-master /mysql-data --defaults-file=/ ebilldb-master /mysql-data/my.cnf
ERROR:
091104 11:12:49 [ERROR] Failed to open the relay log '/var/run/mysqld/mysqld-relay-bin.9339843' (relay_log_pos 233)
091104 11:12:49 [ERROR] Could not find target log during relay log initialization
091104 11:12:49 [ERROR] Failed to initialize the master info structure
Due to the way in which the master and slaves where stopped the relay logs got out of sync, hence replication would not restart.
Found the solution was to
a) make a note of Master_Log_File and Exec_Master_Log_Pos
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State:
Master_Host: mk-tacacs-1.auth.as9105.net
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: log-bin.000008
Read_Master_Log_Pos: 9684
Relay_Log_File: mysqld-relay-bin.9339637
Relay_Log_Pos: 233
Relay_Master_Log_File: log-bin.000008
Slave_IO_Running: No
Slave_SQL_Running: No
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 9684
Relay_Log_Space: 0
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL
b) reset the slave and bypass the relay logs by configuring the replication again.
reset slave;
CHANGE MASTER TO MASTER_HOST='mk-tacacs-1.auth.as9105.net'
CHANGE MASTER TO MASTER_LOG_FILE='log-bin.000008';
CHANGE MASTER TO MASTER_LOG_POS=9684;
start slave;
PROVSERVICE table on VMS ANYPROV has a column which is auto increment
+---------------------+--------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------------------+----------------+
| PROVSERVICEID | int(11) | NO | PRI | NULL | auto_increment |
Development were getting an error suggesting an issue with the auto increment column
ERROR 1062 (23000): Duplicate entry '2147483647' for key 1
When we checked the next value for the auto increment field it was as follows
mysql> SHOW TABLE STATUS LIKE 'PROVSERVICE';
+-------------+--------+---------+------------+---------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+---------------------+---------------------+-----------------+----------+----------------+---------+
| Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time | Update_time | Check_time | Collation | Checksum | Create_options | Comment |
+-------------+--------+---------+------------+---------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+---------------------+---------------------+-----------------+----------+----------------+---------+
| PROVSERVICE | MyISAM | 10 | Dynamic | 2025198 | 108 | 219489492 | 281474976710655 | 190609408 | 0 | 2147483648 | 2009-10-23 15:50:01 | 2009-10-23 16:12:06 | 2009-10-23 15:53:03 | utf8_general_ci | NULL | | |
+-------------+--------+---------+------------+---------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+---------------------+---------------------+-----------------+----------+----------------+---------+
1 row in set (0.00 sec)
Although the PROVSERVICEID column is int(11) it would appear that 2147483647 is the maximum allowable value.
When we checked the data in the table we found that although this was the max value for PROVSERVICEID in the PROVSERVICE table, the next value below it was 2043538
select max(PROVSERVICEID) from PROVSERVICE where PROVSERVICEID < 2147483647;
+--------------------+
| max(PROVSERVICEID) |
+--------------------+
| 2043538 |
+--------------------+
1 row in set (0.00 sec)
So somehow all values for PROVSERVICEID between 2043538 and 2147483647 had been missed.
To fix this, and to be able to use the missing values we had to
update PROVSERVICE set PROVSERVICEID=2043539 where PROVSERVICEID=2147483647;
alter table PROVSERVICE auto_increment=1;
This takes the current max value and adds 1 to give the next auto increment value
SHOW TABLE STATUS LIKE 'PROVSERVICE';
+-------------+--------+---------+------------+---------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+---------------------+---------------------+-----------------+----------+----------------+---------+
| Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time | Update_time | Check_time | Collation | Checksum | Create_options | Comment |
+-------------+--------+---------+------------+---------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+---------------------+---------------------+-----------------+----------+----------------+---------+
| PROVSERVICE | MyISAM | 10 | Dynamic | 2025198 | 108 | 219489492 | 281474976710655 | 190608384 | 0 | 2043540 | 2009-10-23 16:34:42 | 2009-10-23 16:35:08 | 2009-10-23 16:37:45 | utf8_general_ci | NULL | | |
+-------------+--------+---------+------------+---------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+---------------------+---------------------+-----------------+----------+----------------+---------+
1 row in set (0.00 sec)
It is not clear what caused this, but this is how it was fixed – eventually! The main issue that resulted from the failure was that the database would not restart and there were 1000's of relay log files in the $DATADIR
Everything should be working now!
backup_dbs_anyprov.sh
#!/bin/sh
#script to take consistent backup of ANYPROV database
date=`/bin/date +"%d-%m-%y-%H"`
umask 066
( /usr/local/bin/mysqldump -uroot –p** --databases ANYPROV --lock-all-tables | gzip) > /var/mysql-backups/anyprov-$date-`hostname -s`.sql.gz 2> /var/mysql-backups/anyprov-$date-`hostname -s`.sql.err.log
B. Take a no-data DDL dump of selected tables from a single database :
backup_dbs_anyprov_nodata.sh
#!/bin/sh
#script to take no-data backup of ANYPROV database selected tables
date=`/bin/date +"%d-%m-%y-%H"`
umask 066
( /usr/local/bin/mysqldump -uroot –p*** --no-data ANYPROV PROVSERVICEVW REPORTGEN | gzip) > /var/mysql-backups/anyprovnd-$date-`hostname -s`.sql.gz 2> /var/mysql-backups/anyprovnd-$date-`hostname -s`.sql.err.log
C. Take a data dump of selected tables from a single database :
backup_anyprov_tables.sh
#!/bin/sh
#script to take consistent backup of ANYPROV database
date=`/bin/date +"%d-%m-%y-%H"`
umask 066
( /usr/local/bin/mysqldump -uroot –p*** ANYPROV SERVICETEMPLATE SERVICEFEATURETEMPLATE RESPONSEDEFINITION | gzip) > /var/mysql-backups/anyprovtables-$date-`hostname -s`.sql.gz 2> /var/mysql-backups/anyprovtables-$date-`hostname -s`.sql.err.log
To make life easier, this profile needs to be created on every MySQL server we log on.
Connect as root and create the following file with correct locations.
cd /
touch /mysql_profile
chmod 600 /mysql_profile
vi /mysql_profile
export ERRORLOG=/local/db/mysql
export DATADIR=/local/db/mysql
export MYCNF=/etc
export SCRIPTDIR=
export BACKUPDIR=/var/mysql-backups
# some useful aliases
alias a='tail -500 $ERRORLOG/mk-stxx-1'
alias h='fc -l'
alias j=jobs
alias k='ls -lh'
alias g='egrep -i'
alias l='ls -alhrt'
alias df='df -h'
alias d='ls -lhrt | grep ^d'
alias p='ps -ef'
. /opt/tiscali/snmp/scripts/mysql/mySQL-conf.conf
alias m='mysql -u$user -p$pswd'
alias pass='cat $SCRIPTDIR/mySQL-conf.conf'
# Prompt
PS1=" `hostname` \${PWD} >"
export PS1
Then when logging onto a box, just run this profile and use the settings to get around easily.
sudo bash
. /mysql_profile
SINGLE USER:
To see which grants a user has do
show grants for sdas;
+-----------------------------------------------------------------------------------------------------+
| Grants for sdas@% |
+-----------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'sdas'@'%' IDENTIFIED BY PASSWORD '*B79ACB61D4237708B6280EC0A5A1092E54A02849' |
| GRANT SELECT ON `ANYPROV`.* TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`RESPONSE` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`FEATURE` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`CUSTOMERVALUE` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`SERVICE` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`PRODUCT` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`PROVPRODUCTATTR` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`PROCESSENGINES` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`PROVFEATURE` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`REPORTGEN` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`PROVPRODUCT` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`FLOWEVENT` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`ORDERS` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`PROVSERVICEATTR` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`CUSTOMER` TO 'sdas'@'%' |
| GRANT SELECT, INSERT, UPDATE ON `ANYPROV`.`PROVSERVICE` TO 'sdas'@'%' |
+-----------------------------------------------------------------------------------------------------+
17 rows in set (0.00 sec)
SHOW GRANTS FOR 'sdas'@'localhost';
To revoke the delete access do :
REVOKE DELETE ON `ANYPROV`.`RESPONSE` FROM sdas;
REVOKE DELETE ON `ANYPROV`.`SERVICE` FROM sdas;
REVOKE DELETE ON `ANYPROV`.`PRODUCT` FROM sdas;
REVOKE DELETE ON `ANYPROV`.`PROVPRODUCT` FROM sdas;
REVOKE DELETE ON `ANYPROV`.`ORDERS` FROM sdas;
REVOKE DELETE ON `ANYPROV`.`PROVSERVICE` FROM sdas;
MULTIPLE USER:
If requested to remove Delete privs from multiple users, you can use the following scripts to help
1) SELECT CONCAT('SHOW GRANTS FOR \'', user ,'\'@\'', host, '\';') FROM mysql.user WHERE user in ('sdas','sbhandari');
+----------------------------------------------------------+
| CONCAT('SHOW GRANTS FOR \'', user ,'\'@\'', host, '\';') |
+----------------------------------------------------------+
| SHOW GRANTS FOR 'sbhandari'@'%'; |
| SHOW GRANTS FOR 'sdas'@'%'; |
| SHOW GRANTS FOR 'sbhandari'@'localhost'; |
| SHOW GRANTS FOR 'sdas'@'localhost'; |
+----------------------------------------------------------+
4 rows in set (0.00 sec)
Exit
2) cut and paste the SHOW GRANTS into a file /tmp/show_grants.sql
3) run it to generate the output statements, delete …
mysql -uroot –p*** -se "source /tmp/show_grants.sql" > /tmp/show_all_grants.sql
4) pull out all the delete statements only
grep -i DELETE /tmp/show_all_grants.sql > /tmp/revoke_deletes.sql
5)
vi /tmp/revoke_deletes.sql
Then change the TO -> FROM
%s/TO/FROM
Also change @'%' -> ;
%s/@'%'/;
Also remove single quote
%s/'//g
Also change
%s/GRANT SELECT, INSERT, UPDATE,/REVOKE
SHOULD NOW LOOK LIKE:
REVOKE DELETE ON `ANYPROV`.`PROVSERVICE` FROM sdas;
5) then run it
Mysql> source /tmp/revoke_deletes.sql
/usr/local/bin/mysqldump -uroot --p*** --databases prequal --lock-all-tables | gzip) > /home/backup/rebuild_prequal-`hostname -s`.sql.gz 2> /home/backup/rebuild_prequal-`hostname -s`.sql.err.log
I kept an eye on the database, and it queued all of the inserts until the backup finished, but allowed the selects to go through, an example of processlist is attached. Maybe we should consider this for some of the smaller databases and slaves
Apart from the start/stop commands the below commands show the different commands that can also be used to start and stop mysql daemon especially if you have encountered the following errors when you were using the 'normal' start/stop commands:
ERROR! MySQL manager or server PID file could not be found!
Starting MySQL.................................. ERROR
1: On the server Search for mysqld_safe
cd /
find . -name mysqld_safe -print
(ie found : ./usr/local/mysql_5.0.24/mysql-standard-5.0.24a-freebsd6.0-i386/bin/mysqld_safe )
2: To Start mysql daemon:
cd /usr/local/mysql_5.0.24/mysql-standard-5.0.24a-freebsd6.0-i386/bin
./mysqld_safe --user=mysql &
3: To Stop mysql daemon:
cd /usr/local/mysql_5.0.24/mysql-standard-5.0.24a-freebsd6.0-i386/bin
./mysqladmin -uroot -pXXXX shutdown
090326 12:30:54 [ERROR] Got error 134 when reading table './ANYPROV/ORDERS'
Reference from Mysql Bug Database
http://lists.mysql.org/mysql/51228
perror 134
and get this result:
Unknown error 134
Record was already deleted (or record file crashed)
mysql> check table ORDERS;
+----------------+-------+----------+-------------------------------+
| Table | Op | Msg_type | Msg_text |
+----------------+-------+----------+-------------------------------+
| ANYPROV.ORDERS | check | error | Found 1443503 keys of 1443502 |
| ANYPROV.ORDERS | check | error | Corrupt |
+----------------+-------+----------+-------------------------------+
2 rows in set (2.88 sec)
Mysql > repair table ORDERS;
00 08 * * * mysql -uroot -p*** -se 'delete from tacacs.accounting where date < date_sub(now(), interval 90 day);' > /tmp/delete_accounting.log 2>&1
You can use this script to purge master logs older than the day of the current log. It checks the slaves are not using the older logs first.
Some prechecks:
1) create a repl_client user on each slave - i.e
GRANT REPLICATION CLIENT ON *.* TO 'repl_client'@'mk-dbxx-1' IDENTIFIED BY 'r3pl';
2) Put the master and slave user, pwd and hosts in the section below.
3) Set savedays to number of days of logs to keep if more than today+1.
4) Make sure perl DBD and CALC modules are installed.
perl –v ,
instmodsh (and enter l to list modules)
5) The purge statement will not work if the "show master logs;" doesn't match up with the actual log files on disk. This can happen if bin logs have been manually deleted, but the index file still thinks they are there. So to check it do
a) show master logs;
b) ls -l $DATADIR
c) if they are not in sync then remove all older ones
eg if 14 is the first one actually on the disk then run "purge master logs to "mysql-bin.000014";"
Crontab entry:
#HLakhan 25 Mar 09 - Purge master logs over a day old after checking slave
00 17 * * * /home/backup/purge.pl > /tmp/purge.log 2>&1
Script
#!/usr/bin/perl
#
###########################################################################################################
# Script Name : purge.pl
# Usage : ./purge.pl
# Description :
#
# A Perl script for a Master with one or more Slaves. It allows different user/passwords for each slave.
# This one finds the oldest log in use by a slave.
# It purges the logs up to, but not including, X days before the date of that log, according to $savedays.
# That is, if the oldest log in use is dated 2008-11-14 and you set $savedays = 1,
# it will purge all logs dated up to and including the last log from 2008-11-12, not purging those from 2008-11-13.
#
# It uses the Perl module Date::Calc, available at cpan.org, to find the previous day's date. Also DBD mysql.
# I have commented the lines to leave out if you do not want install that module to save the previous day's logs.
# It will not purge logs from the same date as the oldest log in use.
# If run as a cron job, the print statements should cause an email to be sent to the cron job user.
#
# You need the SUPER privilege for this operation.
#
# The script does two sanity checks, exiting if either fails:
# 1. Check that all logs in use by slave(s) exist on the master. If not, it prints the log file name(s), and slave host using it.
# 2. Check that the oldest log on the server is older than the date you are purging to.
#
# Also, the purge statement will not work if the "show master logs;" doesn't match up with the actual log files on disk.
# This can happen if bin logs have been manually deleted, but the index file still thinks they are there.
# So to check it do
# a) show master logs;
# b) ls -l $DATADIR
# c) if they are not in sync then remove all older ones
# eg if 14 is the first one actually on the disk then run "purge master logs to "mysql-bin.000014";"
#
#############################################################################################################
# Script Name : purge.pl
# Usage : ./purge.pl
# Prerequisites : 1) create a repl_client user on each slave - i.e
# GRANT REPLICATION CLIENT ON *.* TO 'repl_client'@'mk-dbmaster-1.portal.uk.intranet' IDENTIFIED BY 'r3pl_cl13nt';
# 2) Put the master and slave user, pwd and hosts in the section below.
# 3) Set savedays to number of days of logs to keep if more than today+1.
# 4) Make sure perl DBD and CALC modules are installed. I.e perl –v , instmodsh (and enter l to list modules)
#
# Author Version Date Comment
# -----------------------------------------------------------------------------------------------------------
# Fred McIntyre 1 15 Nov 08 Initial version from the web
# Harjit Lakhan 2 23 Mar 10 Made some changes to get it working
#
use strict;
use DBI;
# For saving previous day's logs
use Date::Calc qw(Add_Delta_Days);
# Master connection
# master user requires Super_priv
my $mstr_dbuser = 'root';
my $mstr_dbpassword = '**';
my $mstr_dbdsn = ('dbi:mysql:host=localhost');
#to specify the socket if its non-standard use following syntax
#my $mstr_dbdsn = ('dbi:mysql:host=localhost;mysql_socket=/tmp/mysql-master.sock');
# Change if appropriate.
my $log_dir = '/var/lib/mysql';
# Name for binary logs, from my.cnf: log-bin=
my $log_bin = 'mysql-bin';
# For saving previous day's logs
# Days prior to currently used log file to not purge
my $savedays = 1;
# Slave connection(s)
# slave user requires Repl_client_priv
#for more than one slave just comma separate eg ('repl_client','repl_client')
my @slv_dbusers = ('repl_client');
#for more than one slave just comma separate eg ('pwd','pwd2')
my @slv_dbpasswords = ('r3pl_cl13nt');
#for more than one slave just comma separate eg ('dbi..1','dbi...2')
my @slv_dbdsns = ('dbi:mysql:host=mk-dbslave-1.portal.uk.intranet');
#to specify the socket if its non-standard use following syntax
#my @slv_dbdsns = ('dbi:mysql:host=;mysql_socket=/tmp/mysql-slave.sock');
### No changes needed below here. ##
$log_dir .= '/' unless $log_dir =~ m|/$|;
# Find the date of the oldest log in use by a slave. Use file mtime.
my $time = time;
# Add an hour to make sure $time is later than newest possible log mtime.
$time += 3600;
my ($no_connect,$not_exist);
for (my $i = 0; $i < @slv_dbdsns; $i++) {
my $slv_dbdsn = $slv_dbdsns[$i];
my $slv_dbuser = $slv_dbusers[$i];
my $slv_dbpassword = $slv_dbpasswords[$i];
my $slv_dbh = DBI->connect($slv_dbdsn,$slv_dbuser,$slv_dbpassword);
print 'MySQL slave connect '.$slv_dbdsns[$i].$slv_dbusers[$i]."\n";
#print 'MySQL slave connect '.$slv_dbdsns[$i].$slv_dbusers[$i].$slv_dbpasswords[$i]."\n";
unless ($slv_dbh) {
$no_connect .= 'MySQL binary log purge: error connecting to slave database '.
$slv_dbdsns[$i].' - '.$DBI::errstr."\n";
next;
}
# Use field name to get current log name.
my $sth = $slv_dbh->prepare('show slave status');
$sth->execute;
my $rows = $sth->fetchall_arrayref({});
$sth->finish;
$slv_dbh->disconnect;
if (-f $log_dir.$rows->[0]->{Master_Log_File}) {
# Get mtime of this log file.
my $this_time = (stat($log_dir.$rows->[0]->{Master_Log_File}))[9];
$time = $this_time if $this_time < $time;
} else {
$not_exist .= 'MySQL binary log purge: major error - oldest log in use, by slave host '.
$slv_dbdsns[$i].', does not exist on master: '.
$log_dir.$rows->[0]->{Master_Log_File}."\n";
}
}
if ($no_connect) {
print $no_connect."\n";
exit;
}
if ($not_exist) {
print $not_exist."\n";
exit;
}
my ($day,$mon,$year) = (localtime($time))[3..5];
$year += 1900;
$mon++;
# For saving previous day's logs
$savedays *= -1 unless $savedays < 0;
($year,$mon,$day) = Add_Delta_Days($year,$mon,$day,$savedays);
$mon = '0'.$mon if $mon < 10;
$day = '0'.$day if $day < 10;
# Check if oldest existing log is on or before purge date
# Purge works okay if not, but best to warn in case it indicates problems
my $firstlog = `head -n 1 $log_dir$log_bin.index`;
chomp $firstlog;
my $logtime = (stat($log_dir.$firstlog))[9];
my ($Lsec,$Lmin,$Lhour,$Lday,$Lmon,$Lyear) = (localtime($logtime))[0..5];
$Lyear += 1900;
$Lmon++;
$Lmon = '0'.$Lmon if $Lmon < 10;
$Lday = '0'.$Lday if $Lday < 10;
$Lhour = '0'.$Lhour if $Lhour < 10;
$Lmin = '0'.$Lmin if $Lmin < 10;
$Lsec = '0'.$Lsec if $Lsec < 10;
if ($Lyear.$Lmon.$Lday.$Lhour.$Lmin.$Lsec >= $year.$mon.$day.'000000') {
print 'MySQL binary log purge: oldest log on master is later than, or equal to, '.
$year.'-'.$mon.'-'.$day.' 00:00:00'."\n";
exit;
}
my $mstr_dbh = DBI->connect($mstr_dbdsn,$mstr_dbuser,$mstr_dbpassword);
#my $mstr_dbh = DBI->connect('dbi:mysql::'.$mstr_dbhost,$mstr_dbuser,$mstr_dbpassword);
#print 'MySQL master connect '.$mstr_dbdsn.$mstr_dbuser.$mstr_dbpassword."\n";
unless ($mstr_dbh) {
print 'MySQL binary log purge: error connecting to master database - '.$DBI::errstr."\n";
exit;
}
###comment out while testing
$mstr_dbh->do('purge master logs before "'.$year.'-'.$mon.'-'.$day.' 00:00:00"');
$mstr_dbh->disconnect;
print 'MySQL binary log purge: purged before '.$year.'-'.$mon.'-'.$day."\n";
## end ##