Mysql主从监控python脚本

脚本通过监控mysql slave status中的Slave_IO_Running,Slave_SQL_Running,Seconds_Behind_Master三个状态参数来判断replication的健康状况,通过邮件发送报警信息 –2015.7.24优化– 实际发现每天都有几次Seconds_Behind_Master的值大于0引发报警的情况,所以将脚本修改为如果连续两次Seconds_Behind_Master大于0才报警,如下:

#!/usr/bin/python
# -*- coding: cp936 -*-

import time
import datetime
import string
import MySQLdb
import smtplib
from email.Header import Header
from email.MIMEText import MIMEText
import urllib


def sendMail(ip, ioStatus, sqlStatus, lastErrNo, lastError, masterLogFile, masterLogFilePos, relayLogFile, relayLogFilePos, relayMasterLogFile, secondsBehindMaster):

    mailto_list = ["abc@xyz.com"]
    mail_host = "mail.xyz.com"
    mail_user = "mon@xyz.com"
    mail_pass = "passwd"
    mail_from = "<mon@xyz.com>"
    mail_postfix = "xyz.com"
    body = ip + " mysql slave status:\n\n IO Thread Status: " + ioStatus + "\n SQL Thread Status: " + sqlStatus + "\n LastErrorNo: " + str(lastErrNo)
    body = body + "\n LastError: " + str(lastError) + "\n Master Log File: " + str(masterLogFile) + "\n Read Master Log File Pos: " + str(masterLogFilePos)
    body = body + "\n Relay Log File: " + relayLogFile + "\n Relay Log File Pos: " + str(relayLogFilePos) +  "\n Relay Master Log File: " + relayMasterLogFile
    body = body + "\n Seconds_Behind_Master: " + str(secondsBehindMaster)

    mail_subject = "(" +ip + ") MYSQL REPLICATION ERROR!!!"
    me = mail_from+"<"+mail_user+">"
    msg = MIMEText(body,_subtype='plain',_charset='gb2312')
    msg['Subject'] = mail_subject
    msg['From'] = mail_from
    msg['To'] = ";".join(mailto_list)
    try:
        s = smtplib.SMTP()
        s.connect(mail_host)
        s.login(mail_user,mail_pass)
        s.sendmail(me, mailto_list, msg.as_string())
        s.close()
        return True
    except Exception, e:
        print str(e)
        return False

def check(ip, username, pwd):
    conn = MySQLdb.connect(host=ip, user=username, passwd=pwd,use_unicode=True,charset='utf8')
    cur = conn.cursor()
    cur.execute("show slave status")
    conn.commit()
    rows = cur.fetchall()

    ioStatus = ""
    sqlStatus = ""
    lastErrNo = 0
    secondsBehindMaster = 0

    logfile = "/var/log/mysql_db_replication_mon_" + str(ip) + ".log"
    if os.path.isfile(logfile):
        #print logfile + ' is exists.'
        cmd = "sed -n '$p' " + logfile + "|awk -F ':' '{print $6}'"
        secondsBehindMaster_last = int(os.popen(cmd).read())
    else:
        print logfile + ' is not exists, I created it.'
        initf = open(logfile,'w')
        initf.close()
        secondsBehindMaster_last = 0

    if len(rows) > 0:
        ioStatus = rows[0][10]
        sqlStatus = rows[0][11]
        lastErrNo = rows[0][18]
        secondsBehindMaster = rows[0][32]
        today = (datetime.date.today().isoformat())
        timestamp = datetime.datetime.strftime(datetime.datetime.today(),"%y%m%d%H%M%S")

    if "Yes" == ioStatus and "Yes" == sqlStatus:
        print >> open(logfile,'a+'), timestamp, ip + ": ioStatus:" + ioStatus + "; sqlStatus:" + sqlStatus + "; lastErrNo:" + str(lastErrNo) + "; Seconds_Behind_Master:" + str(secondsBehindMaster)
        open(logfile,'a+').close()
        if 0 == secondsBehindMaster or 0 == secondsBehindMaster_last:
            cur.close()
            conn.close()
            return

    if "No" == ioStatus and lastErrNo == 23:
        cur.execute("stop slave")
        conn.commit()
        cur.execute("start slave")
        conn.commit()
    cur.close()
    conn.close()
    lastError=rows[0][19]
    masterLogFile=rows[0][5]
    masterLogFilePos=rows[0][6]
    relayLogFile=rows[0][7]
    relayLogFilePos=rows[0][8]
    relayMasterLogFile=rows[0][9]

    try:
        sendMail(ip, ioStatus, sqlStatus, lastErrNo, lastError, masterLogFile, masterLogFilePos, relayLogFile, relayLogFilePos, relayMasterLogFile, secondsBehindMaster)
    except Exception, e:
        print str(e)
        pass

def main():
    SLAVE_ARRAY = ["192.168.0.1", "192.168.0.2", "192.168.0.3"]
    for slave in SLAVE_ARRAY:
        check(slave, "repuser", "reppasswd")

if __name__ == "__main__":
    main()

 

recover data from binary log

转自: http://www.0550go.com/database/mysql/recover-data-from-binary-log.html

要使用mysqlbinlog 恢复数据首先必须启用过binglog,通常我们会在配置文件中添加如下选项:

vi /etc/my.cnf
log-bin=mysql-bin

当然不是等到数据损坏的时候再启用binglog,那时候已经为时已晚!

从binlog恢复数据演练:

1、创建数据库:

mysql> create database test;
Query OK, 1 row affected (0.05 sec)
mysql> use test;
Database changed

2、创建表:

mysql> create table user(id int auto_increment not null primary key, age int(3),name varchar(20));
Query OK, 0 rows affected (0.25 sec)

3、相关操作:

mysql> insert into user(age,name)values(34,'Jack');
Query OK, 1 row affected (0.01 sec)
mysql> insert into user(age,name)values(34,'bob'); 
Query OK, 1 row affected (0.11 sec)
mysql> insert into user(age,name)values(34,'max');  
Query OK, 1 row affected (0.01 sec)

mysql> flush logs;
Query OK, 0 rows affected (0.25 sec)
插入3条记录,并刷新一次logs
mysql> insert into user(age,name)values(34,'zhoufurong');
Query OK, 1 row affected (0.03 sec)
mysql> insert into user(age,name)values(34,'jerry');     
Query OK, 1 row affected (0.04 sec)
mysql> insert into user(age,name)values(34,'mashou');
Query OK, 1 row affected (0.01 sec)
mysql> insert into user(age,name)values(34,'mead');  
Query OK, 1 row affected (0.07 sec)
mysql> delete from user where id between 5 and 7;
Query OK, 3 rows affected (0.05 sec)
mysql> insert into user(age,name)values(40, 'aaa');
Query OK, 1 row affected (0.06 sec)
mysql> insert into user(age,name)values(40, 'bbb');
Query OK, 1 row affected (0.02 sec)
mysql> flush logs;
Query OK, 0 rows affected (0.17 sec)
插入4条记录,并删除记录5-7,然后又插入2条记录

mysql> insert into user(age,name)values(40, 'ccc');
Query OK, 1 row affected (0.06 sec)
mysql> insert into user(age,name)values(40, 'ddd');
Query OK, 1 row affected (0.04 sec)
mysql> drop table user;
Query OK, 0 rows affected (0.14 sec)
mysql> show tables;
Empty set (0.00 sec)
插入2条记录,删除整个表

4、恢复数据:

总共插入了11条记录,中途删除了2条记录,最后丢弃了整张表,首先要做的我们要查询每步操作的起始pos和停目pos,因为我们要恢复所有的数据,所以中间要跳过delete操作和drop操作

首先,我们要查看binlog具体信息,通过下面命令:

mysqlbinlog mysql-bin.000001 > 1.txt
# cat 1.txt 
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at 4
#150416 16:15:41 server id 1  end_log_pos 107   Start: binlog v 4, server v 5.5.34-log created 150416 16:15:41 at startup
ROLLBACK/*!*/;
BINLOG '
rW8vVQ8BAAAAZwAAAGsAAAAAAAQANS41LjM0LWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAACtby9VEzgNAAgAEgAEBAQEEgAAVAAEGggAAAAICAgCAA==
'/*!*/;
# at 107
#150416 16:16:02 server id 1  end_log_pos 190   Query   thread_id=1     exec_time=0     error_code=0
SET TIMESTAMP=1429172162/*!*/;
SET @@session.pseudo_thread_id=1/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=1437073440/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=33/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
create database test
/*!*/;
# at 190
#150416 16:16:45 server id 1  end_log_pos 343   Query   thread_id=1     exec_time=0     error_code=0
use `test`/*!*/;
SET TIMESTAMP=1429172205/*!*/;
create table user(id int auto_increment not null primary key, age int(3),name varchar(20))
/*!*/;
# at 343
#150416 16:18:14 server id 1  end_log_pos 411   Query   thread_id=1     exec_time=0     error_code=0
SET TIMESTAMP=1429172294/*!*/;
BEGIN
/*!*/;
# at 411
# at 458
#150416 16:18:14 server id 1  end_log_pos 458   Table_map: `test`.`user` mapped to number 33
#150416 16:18:14 server id 1  end_log_pos 501   Write_rows: table id 33 flags: STMT_END_F

BINLOG '
RnAvVRMBAAAALwAAAMoBAAAAACEAAAAAAAEABHRlc3QABHVzZXIAAwMDDwI8AAY=
RnAvVRcBAAAAKwAAAPUBAAAAACEAAAAAAAEAA//4AQAAACIAAAAESmFjaw==
'/*!*/;
# at 501
#150416 16:18:14 server id 1  end_log_pos 528   Xid = 10
COMMIT/*!*/;
# at 528
#150416 16:18:24 server id 1  end_log_pos 596   Query   thread_id=1     exec_time=0     error_code=0
SET TIMESTAMP=1429172304/*!*/;
BEGIN
/*!*/;
# at 596
# at 643
#150416 16:18:24 server id 1  end_log_pos 643   Table_map: `test`.`user` mapped to number 33
#150416 16:18:24 server id 1  end_log_pos 685   Write_rows: table id 33 flags: STMT_END_F

BINLOG '
UHAvVRMBAAAALwAAAIMCAAAAACEAAAAAAAEABHRlc3QABHVzZXIAAwMDDwI8AAY=
UHAvVRcBAAAAKgAAAK0CAAAAACEAAAAAAAEAA//4AgAAACIAAAADYm9i
'/*!*/;
# at 685
#150416 16:18:24 server id 1  end_log_pos 712   Xid = 11
COMMIT/*!*/;
# at 712
#150416 16:18:31 server id 1  end_log_pos 780   Query   thread_id=1     exec_time=0     error_code=0
SET TIMESTAMP=1429172311/*!*/;
BEGIN
/*!*/;
# at 780
# at 827
#150416 16:18:31 server id 1  end_log_pos 827   Table_map: `test`.`user` mapped to number 33
#150416 16:18:31 server id 1  end_log_pos 869   Write_rows: table id 33 flags: STMT_END_F

BINLOG '
V3AvVRMBAAAALwAAADsDAAAAACEAAAAAAAEABHRlc3QABHVzZXIAAwMDDwI8AAY=
V3AvVRcBAAAAKgAAAGUDAAAAACEAAAAAAAEAA//4AwAAACIAAAADbWF4
'/*!*/;
# at 869
#150416 16:18:31 server id 1  end_log_pos 896   Xid = 12
COMMIT/*!*/;
# at 896
#150416 16:18:36 server id 1  end_log_pos 939   Rotate to mysql-bin.000002  pos: 4
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

或者:

mysqlbinlog mysql-bin.000001 | more

当数据量不是很大的情况下,你可以这样操作:

mysql> show binlog events in 'mysql-bin.000001';
+------------------+-----+-------------+-----------+-------------+--------------------------------------------------------------------------------------------------------+
| Log_name         | Pos | Event_type  | Server_id | End_log_pos | Info                                                                                                   |
+------------------+-----+-------------+-----------+-------------+--------------------------------------------------------------------------------------------------------+
| mysql-bin.000001 |   4 | Format_desc |         1 |         107 | Server ver: 5.5.34-log, Binlog ver: 4                                                                  |
| mysql-bin.000001 | 107 | Query       |         1 |         190 | create database test                                                                                   |
| mysql-bin.000001 | 190 | Query       |         1 |         343 | use `test`; create table user(id int auto_increment not null primary key, age int(3),name varchar(20)) |
| mysql-bin.000001 | 343 | Query       |         1 |         411 | BEGIN                                                                                                  |
| mysql-bin.000001 | 411 | Table_map   |         1 |         458 | table_id: 33 (test.user)                                                                               |
| mysql-bin.000001 | 458 | Write_rows  |         1 |         501 | table_id: 33 flags: STMT_END_F                                                                         |
| mysql-bin.000001 | 501 | Xid         |         1 |         528 | COMMIT /* xid=10 */                                                                                    |
| mysql-bin.000001 | 528 | Query       |         1 |         596 | BEGIN                                                                                                  |
| mysql-bin.000001 | 596 | Table_map   |         1 |         643 | table_id: 33 (test.user)                                                                               |
| mysql-bin.000001 | 643 | Write_rows  |         1 |         685 | table_id: 33 flags: STMT_END_F                                                                         |
| mysql-bin.000001 | 685 | Xid         |         1 |         712 | COMMIT /* xid=11 */                                                                                    |
| mysql-bin.000001 | 712 | Query       |         1 |         780 | BEGIN                                                                                                  |
| mysql-bin.000001 | 780 | Table_map   |         1 |         827 | table_id: 33 (test.user)                                                                               |
| mysql-bin.000001 | 827 | Write_rows  |         1 |         869 | table_id: 33 flags: STMT_END_F                                                                         |
| mysql-bin.000001 | 869 | Xid         |         1 |         896 | COMMIT /* xid=12 */                                                                                    |
| mysql-bin.000001 | 896 | Rotate      |         1 |         939 | mysql-bin.000002;pos=4                                                                                 |
+------------------+-----+-------------+-----------+-------------+--------------------------------------------------------------------------------------------------------+

从这个日志可以看到,mysql-bin.000001 里面是创建数据库,创建表,插入三条记录操作,并无删除记录操作!因为只删除了表,所以可以忽略库的恢复!

# mysqlbinlog --database=test mysql-bin.000001 --start-position=190 --stop-position=896 | mysql -uroot -p123456

查看数据库,表回来了!

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

mysql> select * from user;
+----+------+------+
| id | age  | name |
+----+------+------+
|  1 |   34 | Jack |
|  2 |   34 | bob  |
|  3 |   34 | max  |
+----+------+------+
3 rows in set (0.00 sec)

查看mysql-bin.000002日志:

mysql> show binlog events in 'mysql-bin.000002';
+------------------+------+-------------+-----------+-------------+---------------------------------------+
| Log_name         | Pos  | Event_type  | Server_id | End_log_pos | Info                                  |
+------------------+------+-------------+-----------+-------------+---------------------------------------+
| mysql-bin.000002 |    4 | Format_desc |         1 |         107 | Server ver: 5.5.34-log, Binlog ver: 4 |
| mysql-bin.000002 |  107 | Query       |         1 |         175 | BEGIN                                 |
| mysql-bin.000002 |  175 | Table_map   |         1 |         222 | table_id: 33 (test.user)              |
| mysql-bin.000002 |  222 | Write_rows  |         1 |         271 | table_id: 33 flags: STMT_END_F        |
| mysql-bin.000002 |  271 | Xid         |         1 |         298 | COMMIT /* xid=14 */                   |
| mysql-bin.000002 |  298 | Query       |         1 |         366 | BEGIN                                 |
| mysql-bin.000002 |  366 | Table_map   |         1 |         413 | table_id: 33 (test.user)              |
| mysql-bin.000002 |  413 | Write_rows  |         1 |         457 | table_id: 33 flags: STMT_END_F        |
| mysql-bin.000002 |  457 | Xid         |         1 |         484 | COMMIT /* xid=15 */                   |
| mysql-bin.000002 |  484 | Query       |         1 |         552 | BEGIN                                 |
| mysql-bin.000002 |  552 | Table_map   |         1 |         599 | table_id: 33 (test.user)              |
| mysql-bin.000002 |  599 | Write_rows  |         1 |         644 | table_id: 33 flags: STMT_END_F        |
| mysql-bin.000002 |  644 | Xid         |         1 |         671 | COMMIT /* xid=16 */                   |
| mysql-bin.000002 |  671 | Query       |         1 |         739 | BEGIN                                 |
| mysql-bin.000002 |  739 | Table_map   |         1 |         786 | table_id: 33 (test.user)              |
| mysql-bin.000002 |  786 | Write_rows  |         1 |         829 | table_id: 33 flags: STMT_END_F        |
| mysql-bin.000002 |  829 | Xid         |         1 |         856 | COMMIT /* xid=17 */                   |
| mysql-bin.000002 |  856 | Query       |         1 |         924 | BEGIN                                 |
| mysql-bin.000002 |  924 | Table_map   |         1 |         971 | table_id: 33 (test.user)              |
| mysql-bin.000002 |  971 | Delete_rows |         1 |        1045 | table_id: 33 flags: STMT_END_F        |
| mysql-bin.000002 | 1045 | Xid         |         1 |        1072 | COMMIT /* xid=18 */                   |
| mysql-bin.000002 | 1072 | Query       |         1 |        1140 | BEGIN                                 |
| mysql-bin.000002 | 1140 | Table_map   |         1 |        1187 | table_id: 33 (test.user)              |
| mysql-bin.000002 | 1187 | Write_rows  |         1 |        1229 | table_id: 33 flags: STMT_END_F        |
| mysql-bin.000002 | 1229 | Xid         |         1 |        1256 | COMMIT /* xid=19 */                   |
| mysql-bin.000002 | 1256 | Query       |         1 |        1324 | BEGIN                                 |
| mysql-bin.000002 | 1324 | Table_map   |         1 |        1371 | table_id: 33 (test.user)              |
| mysql-bin.000002 | 1371 | Write_rows  |         1 |        1413 | table_id: 33 flags: STMT_END_F        |
| mysql-bin.000002 | 1413 | Xid         |         1 |        1440 | COMMIT /* xid=20 */                   |
| mysql-bin.000002 | 1440 | Rotate      |         1 |        1483 | mysql-bin.000003;pos=4                |
+------------------+------+-------------+-----------+-------------+---------------------------------------+
30 rows in set (0.00 sec)

mysql-bin.000002日志,在这个日志中,有删除记录操作,所以要想全部恢复回来数据,一条不差,就得跳过delte_rows这步:

# mysqlbinlog --database=test  mysql-bin.000002 --start-position=4 --stop-position=971 | mysql -uroot -p123456 

mysql> select * from user;
+----+------+------------+
| id | age  | name       |
+----+------+------------+
|  1 |   34 | Jack       |
|  2 |   34 | bob        |
|  3 |   34 | max        |
|  4 |   34 | zhoufurong |
|  5 |   34 | jerry      |
|  6 |   34 | mashou     |
|  7 |   34 | mead       |
+----+------+------------+
7 rows in set (0.00 sec)

# mysqlbinlog --database=test  mysql-bin.000002 --start-position=1045 --stop-position=1483 | mysql -uroot -p123456
mysql> select * from user;
+----+------+------------+
| id | age  | name       |
+----+------+------------+
|  1 |   34 | Jack       |
|  2 |   34 | bob        |
|  3 |   34 | max        |
|  4 |   34 | zhoufurong |
|  5 |   34 | jerry      |
|  6 |   34 | mashou     |
|  7 |   34 | mead       |
|  8 |   40 | aaa        |
|  9 |   40 | bbb        |
+----+------+------------+
9 rows in set (0.00 sec)

查看mysql-bin.000003:

mysql> show binlog events in 'mysql-bin.000003';
+------------------+-----+-------------+-----------+-------------+---------------------------------------------------------+
| Log_name         | Pos | Event_type  | Server_id | End_log_pos | Info                                                    |
+------------------+-----+-------------+-----------+-------------+---------------------------------------------------------+
| mysql-bin.000003 |   4 | Format_desc |         1 |         107 | Server ver: 5.5.34-log, Binlog ver: 4                   |
| mysql-bin.000003 | 107 | Query       |         1 |         175 | BEGIN                                                   |
| mysql-bin.000003 | 175 | Table_map   |         1 |         222 | table_id: 33 (test.user)                                |
| mysql-bin.000003 | 222 | Write_rows  |         1 |         264 | table_id: 33 flags: STMT_END_F                          |
| mysql-bin.000003 | 264 | Xid         |         1 |         291 | COMMIT /* xid=22 */                                     |
| mysql-bin.000003 | 291 | Query       |         1 |         359 | BEGIN                                                   |
| mysql-bin.000003 | 359 | Table_map   |         1 |         406 | table_id: 33 (test.user)                                |
| mysql-bin.000003 | 406 | Write_rows  |         1 |         448 | table_id: 33 flags: STMT_END_F                          |
| mysql-bin.000003 | 448 | Xid         |         1 |         475 | COMMIT /* xid=23 */                                     |
| mysql-bin.000003 | 475 | Query       |         1 |         581 | use `test`; DROP TABLE `user` /* generated by server */ |
+------------------+-----+-------------+-----------+-------------+---------------------------------------------------------+
10 rows in set (0.00 sec)

mysql-bin.000003 有一个drop table操作,所以在恢复这个文件是跳过drop操作就好:

# mysqlbinlog --database=test  mysql-bin.000003 --start-position=4 --stop-position=475 | mysql -uroot -p123456
mysql> select * from user;
+----+------+------------+
| id | age  | name       |
+----+------+------------+
|  1 |   34 | Jack       |
|  2 |   34 | bob        |
|  3 |   34 | max        |
|  4 |   34 | zhoufurong |
|  5 |   34 | jerry      |
|  6 |   34 | mashou     |
|  7 |   34 | mead       |
|  8 |   40 | aaa        |
|  9 |   40 | bbb        |
| 10 |   40 | ccc        |
| 11 |   40 | ddd        |
+----+------+------------+
11 rows in set (0.00 sec)

 

数据全部取回!

注意:在进行恢复前,一定要将flush logs产生一个新的log文件,这样做是恢复操作将写入一个新的binlog,不会写入老的日志文件中,如若不然记录会接着老的日志往下写,到时查询起来就会比较凌乱了!

当日志非常多时,上面的恢复可能不太适合了,那么binlog可以按时间进行恢复:

/mysql/bin/mysqlbinlog --database=bbs --start-datetime="2013-01-22 05:00:00" --stop-datetime="2013-01-22 09:00:00" /mysql/data/mysql-bin.000001 | /mysql/bin/mysql -u root -p123456 -f

注释:
–database=bbs // 需要恢复的数据库
–start-datetime=”2013-01-22 05:00:00″ // 恢复数据起始时间
–stop-datetime=”2013-01-22 09:00:00″ // 恢复数据截至时间
/mysql/data/mysql-bin.000001 // 引用的日志文件,多个日志就多增加几条
/mysql/bin/mysql -u root -p123456 // 登录Mysql帐号密码
-f // 忽略错误,否则遇到相同条目恢复就会终止

需要注意的问题:

mysqlbinlog命令只能恢复binglog日志里有的数据
一般我们都使用expire-logs-days来限制日志保存的天数,以防止日志文件不断增大撑爆硬盘。
假设 expire-logs-days = 7
那么7天之前的数据如果被误删除将无法通过此命令恢复,比如discuz论坛,你要是误删除了某个版块,而这个版块创建已经有半年,显然版块以及版块内的所有帖子,无法使用近期的binglog日志恢复。
遇到这种情况,要想恢复数据,必须配合每天定时备份的数据来进行
假设数据是每天早上5点定时打包备份,早上9点误删了某个版块
首先恢复打包备份的数据:

service mysql stop
cd /mysql/data
tar zxvf /mysql/dbbackup/bbs20130122.tar.gz
service mysql start
/mysql/bin/mysqlbinlog --database=bbs --start-datetime="2013-01-22 5:00:00" --stop-datetime="2013-01-22 9:00:00" /mysql/data/mysql-bin.000001 | /mysql/bin/mysql -u root -p123456 -f

 

 

 

 

高性能Mysql主从架构的复制原理及配置详解

 

1 复制概述

Mysql内建的复制功能是构建大型,高性能应用程序的基础。将Mysql的数据分布到多个系统上去,这种分布的机制,是通过将Mysql的某一台主机的数据复制到其它主机(slaves)上,并重新执行一遍来实现的。复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。主服务器将更新写入二进制日志文件,并维护文件的一个索引以跟踪日志循环。这些日志可以记录发送到从服务器的更新。当一个从服务器连接主服务器时,它通知主服务器从服务器在日志中读取的最后一次成功更新的位置。从服务器接收从那时起发生的任何更新,然后封锁并等待主服务器通知新的更新。

请注意当你进行复制时,所有对复制中的表的更新必须在主服务器上进行。否则,你必须要小心,以避免用户对主服务器上的表进行的更新与对从服务器上的表所进行的更新之间的冲突。

1.1 mysql支持的复制类型:

(1):基于语句的复制:  在主服务器上执行的SQL语句,在从服务器上执行同样的语句。MySQL默认采用基于语句的复制,效率比较高。
一旦发现没法精确复制时,   会自动选着基于行的复制。
(2):基于行的复制:把改变的内容复制过去,而不是把命令在从服务器上执行一遍. 从mysql5.0开始支持
(3):混合类型的复制: 默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制。

 1.2 . 复制解决的问题

         MySQL复制技术有以下一些特点:
(1)    数据分布 (Data distribution )
(2)    负载平衡(load balancing)
(3)    备份(Backups)
(4)    高可用性和容错行 High availability and failover

  1.3 复制如何工作

        整体上来说,复制有3个步骤:

(1)    master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events);
(2)    slave将master的binary log events拷贝到它的中继日志(relay log);
(3)    slave重做中继日志中的事件,将改变反映它自己的数据。

下图描述了复制的过程:

                                  

          该过程的第一部分就是master记录二进制日志。在每个事务更新数据完成之前,master在二日志记录这些改变。MySQL将事务串行的写入二进制日志,即使事务中的语句都是交叉执行的。在事件写入二进制日志完成后,master通知存储引擎提交事务。
下一步就是slave将master的binary log拷贝到它自己的中继日志。首先,slave开始一个工作线程——I/O线程。I/O线程在master上打开一个普通的连接,然后开始binlog dump process。Binlog dump process从master的二进制日志中读取事件,如果已经跟上master,它会睡眠并等待master产生新的事件。I/O线程将这些事件写入中继日志。
SQL slave thread(SQL从线程)处理该过程的最后一步。SQL线程从中继日志读取事件,并重放其中的事件而更新slave的数据,使其与master中的数据一致。只要该线程与I/O线程保持一致,中继日志通常会位于OS的缓存中,所以中继日志的开销很小。
此外,在master中也有一个工作线程:和其它MySQL的连接一样,slave在master中打开一个连接也会使得master开始一个线程。复制过程有一个很重要的限制——复制在slave上是串行化的,也就是说master上的并行更新操作不能在slave上并行操作。

 2 .复制配置

有两台MySQL数据库服务器Master和slave,Master为主服务器,slave为从服务器,初始状态时,Master和slave中的数据信息相同,当Master中的数据发生变化时,slave也跟着发生相应的变化,使得master和slave的数据信息同步,达到备份的目的。

要点:
负责在主、从服务器传输各种修改动作的媒介是主服务器的二进制变更日志,这个日志记载着需要传输给从服务器的各种修改动作。因此,主服务器必须激活二进制日志功能。从服务器必须具备足以让它连接主服务器并请求主服务器把二进制变更日志传输给它的权限。

环境:
Master和slave的MySQL数据库版本同为5.0.18
操作系统:unbuntu 11.10
IP地址:10.100.0.100

2.1、创建复制帐号

1、在Master的数据库中建立一个备份帐户:每个slave使用标准的MySQL用户名和密码连接master。进行复制操作的用户会授予REPLICATION SLAVE权限。用户名的密码都会存储在文本文件master.info中

命令如下:
mysql > GRANT REPLICATION SLAVE,RELOAD,SUPER ON *.*
TO backup@’10.100.0.200’
IDENTIFIED BY ‘1234’;

建立一个帐户backup,并且只能允许从10.100.0.200这个地址上来登陆,密码是1234。

(如果因为mysql版本新旧密码算法不同,可以设置:set password for ‘backup’@’10.100.0.200’=old_password(‘1234’))

2.2、拷贝数据

(假如是你完全新安装mysql主从服务器,这个一步就不需要。因为新安装的master和slave有相同的数据)

关停Master服务器,将Master中的数据拷贝到B服务器中,使得Master和slave中的数据同步,并且确保在全部设置操作结束前,禁止在Master和slave服务器中进行写操作,使得两数据库中的数据一定要相同!

2.3、配置master

 

接下来对master进行配置,包括打开二进制日志,指定唯一的servr ID。例如,在配置文件加入如下值:

server-id=1
log-bin=mysql-bin

server-id:为主服务器A的ID值
log-bin:二进制变更日值

重启master,运行SHOW MASTER STATUS,输出如下:

 

2.4、配置slave

Slave的配置与master类似,你同样需要重启slave的MySQL。如下:
log_bin           = mysql-bin
server_id         = 2
relay_log         = mysql-relay-bin
log_slave_updates = 1
read_only         = 1
server_id是必须的,而且唯一。slave没有必要开启二进制日志,但是在一些情况下,必须设置,例如,如果slave为其它slave的master,必须设置bin_log。在这里,我们开启了二进制日志,而且显示的命名(默认名称为hostname,但是,如果hostname改变则会出现问题)。
relay_log配置中继日志,log_slave_updates表示slave将复制事件写进自己的二进制日志(后面会看到它的用处)。
有些人开启了slave的二进制日志,却没有设置log_slave_updates,然后查看slave的数据是否改变,这是一种错误的配置。所以,尽量使用read_only,它防止改变数据(除了特殊的线程)。但是,read_only并是很实用,特别是那些需要在slave上创建表的应用。

 

 

2.5、启动slave

接下来就是让slave连接master,并开始重做master二进制日志中的事件。你不应该用配置文件进行该操作,而应该使用CHANGE MASTER TO语句,该语句可以完全取代对配置文件的修改,而且它可以为slave指定不同的master,而不需要停止服务器。如下:

mysql> CHANGE MASTER TO MASTER_HOST=’server1′,

    -> MASTER_USER=’repl’,

    -> MASTER_PASSWORD=’p4ssword’,

    -> MASTER_LOG_FILE=’mysql-bin.000001′,

    -> MASTER_LOG_POS=0;

MASTER_LOG_POS的值为0,因为它是日志的开始位置。

你可以用SHOW SLAVE STATUS语句查看slave的设置是否正确:

mysql> SHOW SLAVE STATUS\G

 

*************************** 1. row ***************************

Slave_IO_State:

Master_Host: server1

Master_User: repl

Master_Port: 3306

Connect_Retry: 60

Master_Log_File: mysql-bin.000001

Read_Master_Log_Pos: 4

Relay_Log_File: mysql-relay-bin.000001

Relay_Log_Pos: 4

Relay_Master_Log_File: mysql-bin.000001

Slave_IO_Running: No

Slave_SQL_Running: No

…omitted…

Seconds_Behind_Master: NULL

 

Slave_IO_State, Slave_IO_Running, 和Slave_SQL_Running是No

表明slave还没有开始复制过程。日志的位置为4而不是0,这是因为0只是日志文件的开始位置,并不是日志位置。实际上,MySQL知道的第一个事件的位置是4。

为了开始复制,你可以运行:

mysql> START SLAVE;

运行SHOW SLAVE STATUS查看输出结果:

mysql> SHOW SLAVE STATUS\G

*************************** 1. row ***************************

Slave_IO_State: Waiting for master to send event

Master_Host: server1

Master_User: repl

Master_Port: 3306

Connect_Retry: 60

Master_Log_File: mysql-bin.000001

Read_Master_Log_Pos: 164

Relay_Log_File: mysql-relay-bin.000001

Relay_Log_Pos: 164

Relay_Master_Log_File: mysql-bin.000001

Slave_IO_Running: Yes

Slave_SQL_Running: Yes

…omitted…

Seconds_Behind_Master: 0

在这里主要是看:
                   Slave_IO_Running=Yes
Slave_SQL_Running=Yes

slave的I/O和SQL线程都已经开始运行,而且Seconds_Behind_Master不再是NULL。日志的位置增加了,意味着一些事件被获取并执行了。如果你在master上进行修改,你可以在slave上看到各种日志文件的位置的变化,同样,你也可以看到数据库中数据的变化。

你可查看master和slave上线程的状态。在master上,你可以看到slave的I/O线程创建的连接:

在master上输入show processlist\G;
mysql> show processlist \G
*************************** 1. row ***************************
     Id: 1
   User: root
   Host: localhost:2096
     db: test
Command: Query
   Time: 0
 State: NULL
   Info: show processlist
*************************** 2. row ***************************
     Id: 2
   User: repl
   Host: localhost:2144
     db: NULL
Command: Binlog Dump
   Time: 1838
 State: Has sent all binlog to slave; waiting for binlog to be updated
   Info: NULL
2 rows in set (0.00 sec)

行2为处理slave的I/O线程的连接。

在slave服务器上运行该语句:

mysql> show processlist \G
*************************** 1. row ***************************
     Id: 1
   User: system user
   Host:
     db: NULL
Command: Connect
   Time: 2291
 State: Waiting for master to send event
   Info: NULL
*************************** 2. row ***************************
     Id: 2
   User: system user
   Host:
     db: NULL
Command: Connect
   Time: 1852
 State: Has read all relay log; waiting for the slave I/O thread to update it
   Info: NULL
*************************** 3. row ***************************
     Id: 5
   User: root
   Host: localhost:2152
     db: test
Command: Query
   Time: 0
 State: NULL
   Info: show processlist
3 rows in set (0.00 sec)

行1为I/O线程状态,行2为SQL线程状态。

2.5、添加新slave服务器

假如master已经运行很久了,想对新安装的slave进行数据同步,甚至它没有master的数据。
此时,有几种方法可以使slave从另一个服务开始,例如,从master拷贝数据,从另一个slave克隆,从最近的备份开始一个slave。Slave与master同步时,需要三样东西:
(1)master的某个时刻的数据快照;
(2)master当前的日志文件、以及生成快照时的字节偏移。这两个值可以叫做日志文件坐标(log file coordinate),因为它们确定了一个二进制日志的位置,你可以用SHOW MASTER STATUS命令找到日志文件的坐标;
(3)master的二进制日志文件。

可以通过以下几中方法来克隆一个slave:
(1)    冷拷贝(cold copy)
停止master,将master的文件拷贝到slave;然后重启master。缺点很明显。
(2)    热拷贝(warm copy)
如果你仅使用MyISAM表,你可以使用mysqlhotcopy拷贝,即使服务器正在运行。
(3)    使用mysqldump
使用mysqldump来得到一个数据快照可分为以下几步:
<1>锁表:如果你还没有锁表,你应该对表加锁,防止其它连接修改数据库,否则,你得到的数据可以是不一致的。如下:
mysql> FLUSH TABLES WITH READ LOCK;
<2>在另一个连接用mysqldump创建一个你想进行复制的数据库的转储:
shell> mysqldump –all-databases –lock-all-tables >dbdump.db
<3>对表释放锁。
mysql> UNLOCK TABLES;

 

3、深入了解复制

已经讨论了关于复制的一些基本东西,下面深入讨论一下复制。

3.1、基于语句的复制(Statement-Based Replication)

     MySQL 5.0及之前的版本仅支持基于语句的复制(也叫做逻辑复制,logical replication),这在数据库并不常见。master记录下改变数据的查询,然后,slave从中继日志中读取事件,并执行它,这些SQL语句与master执行的语句一样。
这种方式的优点就是实现简单。此外,基于语句的复制的二进制日志可以很好的进行压缩,而且日志的数据量也较小,占用带宽少——例如,一个更新GB的数据的查询仅需要几十个字节的二进制日志。而mysqlbinlog对于基于语句的日志处理十分方便。但是,基于语句的复制并不是像它看起来那么简单,因为一些查询语句依赖于master的特定条件,例如,master与slave可能有不同的时间。所以,MySQL的二进制日志的格式不仅仅是查询语句,还包括一些元数据信息,例如,当前的时间戳。即使如此,还是有一些语句,比如,CURRENT USER函数,不能正确的进行复制。此外,存储过程和触发器也是一个问题。
另外一个问题就是基于语句的复制必须是串行化的。这要求大量特殊的代码,配置,例如InnoDB的next-key锁等。并不是所有的存储引擎都支持基于语句的复制。

3.2、基于记录的复制(Row-Based Replication)

      MySQL增加基于记录的复制,在二进制日志中记录下实际数据的改变,这与其它一些DBMS的实现方式类似。这种方式有优点,也有缺点。优点就是可以对任何语句都能正确工作,一些语句的效率更高。主要的缺点就是二进制日志可能会很大,而且不直观,所以,你不能使用mysqlbinlog来查看二进制日志。
对于一些语句,基于记录的复制能够更有效的工作,如:
mysql> INSERT INTO summary_table(col1, col2, sum_col3)
-> SELECT col1, col2, sum(col3)
-> FROM enormous_table
-> GROUP BY col1, col2;
假设,只有三种唯一的col1和col2的组合,但是,该查询会扫描原表的许多行,却仅返回三条记录。此时,基于记录的复制效率更高。
另一方面,下面的语句,基于语句的复制更有效:
mysql> UPDATE enormous_table SET col1 = 0;
此时使用基于记录的复制代价会非常高。由于两种方式不能对所有情况都能很好的处理,所以,MySQL 5.1支持在基于语句的复制和基于记录的复制之前动态交换。你可以通过设置session变量binlog_format来进行控制。

3.3、复制相关的文件

除了二进制日志和中继日志文件外,还有其它一些与复制相关的文件。如下:

(1)mysql-bin.index

服务器一旦开启二进制日志,会产生一个与二日志文件同名,但是以.index结尾的文件。它用于跟踪磁盘上存在哪些二进制日志文件。MySQL用它来定位二进制日志文件。它的内容如下(我的机器上):

 (2)mysql-relay-bin.index

该文件的功能与mysql-bin.index类似,但是它是针对中继日志,而不是二进制日志。内容如下:
.\mysql-02-relay-bin.000017
.\mysql-02-relay-bin.000018

(3)master.info

保存master的相关信息。不要删除它,否则,slave重启后不能连接master。内容如下(我的机器上):

I/O线程更新master.info文件,内容如下(我的机器上):

.\mysql-02-relay-bin.000019
254
mysql-01-bin.000010
286
0
52813

 (4)relay-log.info

包含slave中当前二进制日志和中继日志的信息。

 

 

 

3.4、发送复制事件到其它slave

当设置log_slave_updates时,你可以让slave扮演其它slave的master。此时,slave把SQL线程执行的事件写进行自己的二进制日志(binary log),然后,它的slave可以获取这些事件并执行它。如下:

 

 

 

3.5、复制过滤(Replication Filters)

复制过滤可以让你只复制服务器中的一部分数据,有两种复制过滤:在master上过滤二进制日志中的事件;在slave上过滤中继日志中的事件。如下:

 

 

 

4、复制的常用拓扑结构

复制的体系结构有以下一些基本原则:
(1)    每个slave只能有一个master;
(2)    每个slave只能有一个唯一的服务器ID;
(3)    每个master可以有很多slave;
(4)    如果你设置log_slave_updates,slave可以是其它slave的master,从而扩散master的更新。

 

MySQL不支持多主服务器复制(Multimaster Replication)——即一个slave可以有多个master。但是,通过一些简单的组合,我们却可以建立灵活而强大的复制体系结构。

 

4.1、单一master和多slave

由一个master和一个slave组成复制系统是最简单的情况。Slave之间并不相互通信,只能与master进行通信。

在实际应用场景中,MySQL复制90%以上都是一个Master复制到一个或者多个Slave的架构模式,主要用于读压力比较大的应用的数据库端廉价扩展解决方案。因为只要Master和Slave的压力不是太大(尤其是Slave端压力)的话,异步复制的延时一般都很少很少。尤其是自从Slave端的复制方式改成两个线程处理之后,更是减小了Slave端的延时问题。而带来的效益是,对于数据实时性要求不是特别Critical的应用,只需要通过廉价的pcserver来扩展Slave的数量,将读压力分散到多台Slave的机器上面,即可通过分散单台数据库服务器的读压力来解决数据库端的读性能瓶颈,毕竟在大多数数据库应用系统中的读压力还是要比写压力大很多。这在很大程度上解决了目前很多中小型网站的数据库压力瓶颈问题,甚至有些大型网站也在使用类似方案解决数据库瓶颈。

如下:

如果写操作较少,而读操作很时,可以采取这种结构。你可以将读操作分布到其它的slave,从而减小master的压力。但是,当slave增加到一定数量时,slave对master的负载以及网络带宽都会成为一个严重的问题。
这种结构虽然简单,但是,它却非常灵活,足够满足大多数应用需求。一些建议:
(1)    不同的slave扮演不同的作用(例如使用不同的索引,或者不同的存储引擎);
(2)    用一个slave作为备用master,只进行复制;
(3)    用一个远程的slave,用于灾难恢复;

大家应该都比较清楚,从一个Master节点可以复制出多个Slave节点,可能有人会想,那一个Slave节点是否可以从多个Master节点上面进行复制呢?至少在目前来看,MySQL是做不到的,以后是否会支持就不清楚了。

MySQL不支持一个Slave节点从多个Master节点来进行复制的架构,主要是为了避免冲突的问题,防止多个数据源之间的数据出现冲突,而造成最后数据的不一致性。不过听说已经有人开发了相关的patch,让MySQL支持一个Slave节点从多个Master结点作为数据源来进行复制,这也正是MySQL开源的性质所带来的好处。

 

 

4.2、主动模式的Master-Master(Master-Master in Active-Active Mode)

Master-Master复制的两台服务器,既是master,又是另一台服务器的slave。这样,任何一方所做的变更,都会通过复制应用到另外一方的数据库中。

可能有些读者朋友会有一个担心,这样搭建复制环境之后,难道不会造成两台MySQL之间的循环复制么?实际上MySQL自己早就想到了这一点,所以在MySQL的BinaryLog中记录了当前MySQL的server-id,而且这个参数也是我们搭建MySQLReplication的时候必须明确指定,而且Master和Slave的server-id参数值比需要不一致才能使MySQLReplication搭建成功。一旦有了server-id的值之后,MySQL就很容易判断某个变更是从哪一个MySQLServer最初产生的,所以就很容易避免出现循环复制的情况。而且,如果我们不打开记录Slave的BinaryLog的选项(–log-slave-update)的时候,MySQL根本就不会记录复制过程中的变更到BinaryLog中,就更不用担心可能会出现循环复制的情形了。

如图:

 

主动的Master-Master复制有一些特殊的用处。例如,地理上分布的两个部分都需要自己的可写的数据副本。这种结构最大的问题就是更新冲突。假设一个表只有一行(一列)的数据,其值为1,如果两个服务器分别同时执行如下语句:
在第一个服务器上执行:
mysql> UPDATE tbl SET col=col + 1;
在第二个服务器上执行:
mysql> UPDATE tbl SET col=col * 2;
那么结果是多少呢?一台服务器是4,另一个服务器是3,但是,这并不会产生错误。
实际上,MySQL并不支持其它一些DBMS支持的多主服务器复制(Multimaster Replication),这是MySQL的复制功能很大的一个限制(多主服务器的难点在于解决更新冲突),但是,如果你实在有这种需求,你可以采用MySQL Cluster,以及将Cluster和Replication结合起来,可以建立强大的高性能的数据库平台。但是,可以通过其它一些方式来模拟这种多主服务器的复制。

 

4.3、主动-被动模式的Master-Master(Master-Master in Active-Passive Mode)

这是master-master结构变化而来的,它避免了M-M的缺点,实际上,这是一种具有容错和高可用性的系统。它的不同点在于其中一个服务只能进行只读操作。如图:

4.4 级联复制架构 Master –Slaves – Slaves

在有些应用场景中,可能读写压力差别比较大,读压力特别的大,一个Master可能需要上10台甚至更多的Slave才能够支撑注读的压力。这时候,Master就会比较吃力了,因为仅仅连上来的SlaveIO线程就比较多了,这样写的压力稍微大一点的时候,Master端因为复制就会消耗较多的资源,很容易造成复制的延时。

遇到这种情况如何解决呢?这时候我们就可以利用MySQL可以在Slave端记录复制所产生变更的BinaryLog信息的功能,也就是打开—log-slave-update选项。然后,通过二级(或者是更多级别)复制来减少Master端因为复制所带来的压力。也就是说,我们首先通过少数几台MySQL从Master来进行复制,这几台机器我们姑且称之为第一级Slave集群,然后其他的Slave再从第一级Slave集群来进行复制。从第一级Slave进行复制的Slave,我称之为第二级Slave集群。如果有需要,我们可以继续往下增加更多层次的复制。这样,我们很容易就控制了每一台MySQL上面所附属Slave的数量。这种架构我称之为Master-Slaves-Slaves架构

这种多层级联复制的架构,很容易就解决了Master端因为附属Slave太多而成为瓶颈的风险。下图展示了多层级联复制的Replication架构。

当然,如果条件允许,我更倾向于建议大家通过拆分成多个Replication集群来解决

上述瓶颈问题。毕竟Slave并没有减少写的量,所有Slave实际上仍然还是应用了所有的数据变更操作,没有减少任何写IO。相反,Slave越多,整个集群的写IO总量也就会越多,我们没有非常明显的感觉,仅仅只是因为分散到了多台机器上面,所以不是很容易表现出来。

此外,增加复制的级联层次,同一个变更传到最底层的Slave所需要经过的MySQL也会更多,同样可能造成延时较长的风险。

而如果我们通过分拆集群的方式来解决的话,可能就会要好很多了,当然,分拆集群也需要更复杂的技术和更复杂的应用系统架构。

 4.5、带从服务器的Master-Master结构(Master-Master with Slaves)

这种结构的优点就是提供了冗余。在地理上分布的复制结构,它不存在单一节点故障问题,而且还可以将读密集型的请求放到slave上。

 

 

级联复制在一定程度上面确实解决了Master因为所附属的Slave过多而成为瓶颈的问题,但是他并不能解决人工维护和出现异常需要切换后可能存在重新搭建Replication的问题。这样就很自然的引申出了DualMaster与级联复制结合的Replication架构,我称之为Master-Master-Slaves架构

和Master-Slaves-Slaves架构相比,区别仅仅只是将第一级Slave集群换成了一台单独的Master,作为备用Master,然后再从这个备用的Master进行复制到一个Slave集群。

这种DualMaster与级联复制结合的架构,最大的好处就是既可以避免主Master的写入操作不会受到Slave集群的复制所带来的影响,同时主Master需要切换的时候也基本上不会出现重搭Replication的情况。但是,这个架构也有一个弊端,那就是备用的Master有可能成为瓶颈,因为如果后面的Slave集群比较大的话,备用Master可能会因为过多的SlaveIO线程请求而成为瓶颈。当然,该备用Master不提供任何的读服务的时候,瓶颈出现的可能性并不是特别高,如果出现瓶颈,也可以在备用Master后面再次进行级联复制,架设多层Slave集群。当然,级联复制的级别越多,Slave集群可能出现的数据延时也会更为明显,所以考虑使用多层级联复制之前,也需要评估数据延时对应用系统的影响。

转自:http://blog.csdn.net/hguisu/article/details/7325124

Mysql数据库主从心得整理

管理mysql主从有2年多了,管理过200多组mysql主从,几乎涉及到各个版本的主从,本博文属于总结性的,有一部分是摘自网络,大部分是根据自己管理的心得和经验所写,整理了一下,分享给各位同行,希望对大家有帮助,互相交流。

一、mysql主从的原理

1、Replication 线程

Mysql的 Replication 是一个异步的复制过程(mysql5.1.7以上版本分为异步复制和半同步两种模式),从一个 Mysql instace(我们称之为 Master)复制到另一个 Mysql instance(我们称之 Slave)。在 Master 与 Slave 之间的实现整个复制过程主要由三个线程来完成,其中两个线程(Sql线程和IO线程)在 Slave 端,另外一个线程(IO线程)在 Master 端。

要实现 MySQL 的 Replication ,首先必须打开 Master 端的Binary Log(mysql-bin.xxxxxx)功能,否则无法实现。因为整个复制过程实际上就是Slave从Master端获取该日志然后再在自己身上完全 顺序的执行日志中所记录的各种操作。打开 MySQL 的 Binary Log 可以通过在启动 MySQL Server 的过程中使用 “—log-bin” 参数选项,或者在 my.cnf 配置文件中的 mysqld 参数组([mysqld]标识后的参数部分)增加 “log-bin” 参数项。

2、MySQL 复制的基本过程如下:
2.1Slave 上面的IO线程连接上 Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容;

2.2. Master 接收到来自 Slave 的 IO 线程的请求后,通过负责复制的 IO 线程根据请求信息读取指定日志指定位置之后的日志信息,返回给 Slave 端的 IO 线程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息在 Master 端的 Binary Log 文件的名称以及在 Binary Log 中的位置;

2.3. Slave 的 IO 线程接收到信息后,将接收到的日志内容依次写入到 Slave 端的Relay Log文件(mysql-relay-bin.xxxxxx)的最末端,并将读取到的Master端的bin-log的文件名和位置记录到master- info文件中,以便在下一次读取的时候能够清楚的高速Master“我需要从某个bin-log的哪个位置开始往后的日志内容,请发给我”

2.4. Slave 的 SQL 线程检测到 Relay Log 中新增加了内容后,会马上解析该 Log 文件中的内容成为在 Master 端真实执行时候的那些可执行的 Query 语句,并在自身执行这些 Query。这样,实际上就是在 Master 端和 Slave 端执行了同样的 Query,所以两端的数据是完全一样的。

 

3、Mysql复制的几种模式

3.1.从 MySQL 5.1.12 开始,可以用以下三种模式来实现:
– 基于SQL语句的复制(statement-based replication, SBR),
– 基于行的复制(row-based replication, RBR),
– 混合模式复制(mixed-based replication, MBR)。
相应地,binlog的格式也有三种:STATEMENT,ROW,MIXED。 MBR 模式中,SBR 模式是默认的。
在运行时可以动态改动 binlog的格式,除了以下几种情况:
1.存储流程或者触发器中间
2.启用了NDB
3.当前会话试用 RBR 模式,并且已打开了临时表

如果binlog采用了 MIXED 模式,那么在以下几种情况下会自动将binlog的模式由 SBR 模式改成 RBR 模式:
1.当DML语句更新一个NDB表时
2.当函数中包含 UUID() 时
3.2个及以上包含 AUTO_INCREMENT 字段的表被更新时
4.行任何 INSERT DELAYED 语句时
5.用 UDF 时
6.视图中必须要求运用 RBR 时,例如建立视图是运用了 UUID() 函数

3.2.设定主从复制模式:

log-bin=mysql-bin
#binlog_format="STATEMENT"
#binlog_format="ROW"
binlog_format="MIXED"
也可以在运行时动态修改binlog的格式。例如
mysql> SET SESSION binlog_format = 'STATEMENT';
mysql> SET SESSION binlog_format = 'ROW';
mysql> SET SESSION binlog_format = 'MIXED';
mysql> SET GLOBAL binlog_format = 'STATEMENT';
mysql> SET GLOBAL binlog_format = 'ROW';
mysql> SET GLOBAL binlog_format = 'MIXED';

3.3.两种模式各自的优缺点:

SBR 的优点:
历史悠久,技能成熟
binlog文件较小
binlog中包含了所有数据库修改信息,可以据此来审核数据库的安全等情况
binlog可以用于实时的还原,而不仅仅用于复制
主从版本可以不一样,从服务器版本可以比主服务器版本高

SBR 的缺点:
不是所有的UPDATE语句都能被复制,尤其是包含不确定操作的时候。
调用具有不确定因素的 UDF 时复制也可能出疑问
运用以下函数的语句也不能被复制:
* LOAD_FILE()
* UUID()
* USER()
* FOUND_ROWS()
* SYSDATE() (除非启动时启用了 –sysdate-is-now 选项)
INSERT … SELECT 会产生比 RBR 更多的行级锁
复制须要执行 全表扫描(WHERE 语句中没有运用到索引)的 UPDATE 时,须要比 RBR 请求更多的行级锁
对于有 AUTO_INCREMENT 字段的 InnoDB表而言,INSERT 语句会阻塞其他 INSERT 语句
对于一些复杂的语句,在从服务器上的耗资源情况会更严重,而 RBR 模式下,只会对那个发生变化的记录产生影响
存储函数(不是存储流程 )在被调用的同时也会执行一次 NOW() 函数,这个可以说是坏事也可能是好事
确定了的 UDF 也须要在从服务器上执行
数据表必须几乎和主服务器保持一致才行,否则可能会导致复制出错
执行复杂语句如果出错的话,会消耗更多资源

RBR 的优点:
任何情况都可以被复制,这对复制来说是最安全可靠的
和其他大多数数据库系统的复制技能一样
多数情况下,从服务器上的表如果有主键的话,复制就会快了很多
复制以下几种语句时的行锁更少:
* INSERT … SELECT
* 包含 AUTO_INCREMENT 字段的 INSERT
* 没有附带条件或者并没有修改很多记录的 UPDATE 或 DELETE 语句
执行 INSERT,UPDATE,DELETE 语句时锁更少
从服务器上采用多线程来执行复制成为可能

RBR 的缺点:
binlog 大了很多
复杂的回滚时 binlog 中会包含大量的数据
主服务器上执行 UPDATE 语句时,所有发生变化的记录都会写到 binlog 中,而 SBR 只会写一次,这会导致频繁发生 binlog 的并发写疑问
UDF 产生的大 BLOB 值会导致复制变慢
不能从 binlog 中看到都复制了写什么语句(加密过的)
当在非事务表上执行一段堆积的SQL语句时,最好采用 SBR 模式,否则很容易导致主从服务器的数据不一致情况发生
另外,针对系统库 mysql 里面的表发生变化时的处理准则如下:
如果是采用 INSERT,UPDATE,DELETE 直接操作表的情况,则日志格式根据 binlog_format 的设定而记录
如果是采用 GRANT,REVOKE,SET PASSWORD 等管理语句来做的话,那么无论如何 都采用 SBR 模式记录。
注:采用 RBR 模式后,能处理很多原先出现的主键重复问题。实例:
对于insert into db_allot_ids select * from db_allot_ids 这个语句:
在BINLOG_FORMAT=STATEMENT 模式下:
BINLOG日志信息为:

BEGIN
/*!*/;
# at 173
#090612 16:05:42 server id 1 end_log_pos 288 Query thread_id=4 exec_time=0 error_code=0
SET TIMESTAMP=1244793942/*!*/;
insert into db_allot_ids select * from db_allot_ids
/*!*/;

 

在BINLOG_FORMAT=ROW 模式下:

BINLOG日志信息为:

BINLOG '
hA0yShMBAAAAMwAAAOAAAAAAAA8AAAAAAAAAA1NOUwAMZGJfYWxsb3RfaWRzAAIBAwAA
hA0yShcBAAAANQAAABUBAAAQAA8AAAAAAAEAAv/8AQEAAAD8AQEAAAD8AQEAAAD8AQEAAAA=
'/*!*/;

 

4、Mysql主从的优缺点

MySQL的主从同步是一个很成熟的架构,优点为:①在从服务器可以执行查询工作(即我们常说的读功能),降低主服 务器压力;②在从主服务器进行备份,避免备份期间影响主服务器服务;③当主服务器出现问题时,可以切换到从服务器。所以我在项目部署和实施中经常会采用这种方案;鉴于生产环境下的mysql的严谨性。

实际上,在老版本中,MySQL 的复制实现在 Slave 端并不是由 SQL 线程和 IO 线程这两个线程共同协作而完成的,而是由单独的一个线程来完成所有的工作。但是 MySQL 的工程师们很快发现,这样做存在很大的风险和性能问题,主要如下:

首先,如果通过一个单一的线程来独立实现这个工作的话,就使复制 Master 端的,Binary Log日志,以及解析这些日志,然后再在自身执行的这个过程成为一个串行的过程,性能自然会受到较大的限制,这种架构下的 Replication 的延迟自然就比较长了。

其次,Slave 端的这个复制线程从 Master 端获取 Binary Log 过来之后,需要接着解析这些内容,还原成 Master 端所执行的原始 Query,然后在自身执行。在这个过程中,Master端很可能又已经产生了大量的变化并生成了大量的 Binary Log 信息。如果在这个阶段 Master 端的存储系统出现了无法修复的故障,那么在这个阶段所产生的所有变更都将永远的丢失,无法再找回来。这种潜在风险在Slave 端压力比较大的时候尤其突出,因为如果 Slave 压力比较大,解析日志以及应用这些日志所花费的时间自然就会更长一些,可能丢失的数据也就会更多。

所以,在后期的改造中,新版本的 MySQL 为了尽量减小这个风险,并提高复制的性能,将 Slave 端的复制改为两个线程来完成,也就是前面所提到的 SQL 线程和 IO 线程。最早提出这个改进方案的是Yahoo!的一位工程师“Jeremy Zawodny”。通过这样的改造,这样既在很大程度上解决了性能问题,缩短了异步的延时时间,同时也减少了潜在的数据丢失量。

当然,即使是换成了现在这样两个线程来协作处理之后,同样也还是存在 Slave 数据延时以及数据丢失的可能性的,毕竟这个复制是异步的。只要数据的更改不是在一个事务中,这些问题都是存在的。

如果要完全避免这些问题,就只能用 MySQL 的 Cluster 来解决了。不过 MySQL的 Cluster 知道笔者写这部分内容的时候,仍然还是一个内存数据库的解决方案,也就是需要将所有数据包括索引全部都 Load 到内存中,这样就对内存的要求就非常大的大,对于一般的大众化应用来说可实施性并不是太大。MySQL 现在正在不断改进其 Cluster 的实现,其中非常大的一个改动就是允许数据不用全部 Load 到内存中,而仅仅只是索引全部 Load 到内存中,我想信在完成该项改造之后的 MySQL Cluster 将会更加受人欢迎,可实施性也会更大。

5、Mysql的半同步模式(Semisynchronous Replication)

我们知道在5.5之前,MySQL的复制其实是异步操作,而不是同步,也就意味着允许主从之间的数据存在一定的延迟,mysql当初这样设计的目的可能也是基于可用性的考虑,为了保证master不受slave的影响,并且异步复制使得master处于一种性能最优的状态:写完binlog后即可提交而不需要等待slave的操作完成。这样存在一个隐患,当你使用slave作为备份时,如果master挂掉,那么会存在部分已提交的事务未能成功传输到slave的可能,这就意味着数据丢失!

在MySQL5.5版本中,引入了半同步复制模式(Semi-synchronous Replication)能够成功(只是相对的)避免上述数据丢失的隐患。在这种模式下:master会等到binlog成功传送并写入至少一个slave的relay log之后才会提交,否则一直等待,直到timeout(默认10s)。当出现timeout的时候,master会自动切换半同步为异步,直到至少有一个slave成功收到并发送Acknowledge,master会再切换回半同步模式。结合这个新功能,我们可以做到,在允许损失一定的事务吞吐量的前提下来保证同步数据的绝对安全,因为当你设置timeout为一个足够大的值的情况下,任何提交的数据都会安全抵达slave。

mysql5.5 版本支持半同步复制功能(Semisynchronous Replication),但还不是原生的支持,是通过plugin来支持的,并且默认是没有安装这个插件的。不论是二进制发布的,还是自己源代码编译的,都会默认生成这个插件,一个是针对master 的一个是针对slave的,在使用之前需要先安装这俩plugins。

二、Mysql主从复制的过滤
复制的过滤主要有2种方式:
1、在主服务器在把事件从进二制日志中过滤掉,相关的参数是:binlog_do_db和binlog_ignore_db。
2、在从服务器上把事件从中继日志中过滤掉,相关的参数是replicate_*。
复制只能扩展读取,不能扩展写入,对数据进行分区可以进行扩展写入。
复制的优化:
在mysql复制环境中,有8个参数可以让我们控制,需要复制或需要忽略不进行复制的DB或table分别为:
下面二项需要在Master上设置:
Binlog_Do_DB:设定哪些数据库需要记录Binlog
Binlog_Ignore_DB:设定哪里数据库不需要记录Binlog
优点是Master端的Binlog记录所带来的Io量减少,网络IO减少,还会让slave端的IO线程,SQL线程减少,从而大幅提高复制性能,
缺点是mysql判断是否需要复制某个事件不是根据产生该事件的查询所在的DB,而是根据执行查询时刻所在的默认数据库(也就是登录时指定的库名或运行”use database”中指定的DB),只有当前默认DB和配置中所设定的DB完全吻合时IO线程才会将该事件读取给slave的IO线程.所以,如果在默认DB和设定须要复制的DB不一样的情况下改变了须要复制的DB中某个Table中的数据,该事件是不会被复制到Slave中去的,这样就会造成Slave端的数据和Master的数据不一致.同样,在默认的数据库下更改了不须要复制的数据库中的数据,则会被复制到slave端,当slave端并没有该数据库时,则会造成复制出错而停止。

下面六项需要在slave上设置:
Replicate_Do_DB:设定需要复制的数据库,多个DB用逗号分隔
Replicate_Ignore_DB:设定可以忽略的数据库.
Replicate_Do_Table:设定需要复制的Table
Replicate_Ignore_Table:设定可以忽略的Table
Replicate_Wild_Do_Table:功能同Replicate_Do_Table,但可以带通配符来进行设置。
Replicate_Wild_Ignore_Table:功能同Replicate_Do_Table,功能同Replicate_Ignore_Table,可以带通配符。
优点是在slave端设置复制过滤机制,可以保证不会出现因为默认的数据库问题而造成Slave和Master数据不一致或复制出错的问题.
缺点是性能方面比在Master端差一些.原因在于:不管是否须要复制,事件都会被IO线程读取到Slave端,这样不仅增加了网络IO量,也给Slave端的IO线程增加了Relay Log的写入量。
注:在实际的生产应用中发现,在mysql5.0以前的版本,mysql的这个过滤设置几乎是形同虚设,不起作用:不管你在主库或是从库上设置了忽略某个数据库或是表,他依然会进行同步,所以在做5.0以前版本的主从同步时,一定保持主从数据库的一致性,主上有的库或是表从上一定要有,否则在同步的过程会出错。

三、Mysql主从同步的配置

主库IP:192.168.1.2
从库IP:192.168.1.3
添加一个用于主从同步的用户:
GRANT REPLICATION SLAVE ON *.* TO ‘repl’@’%’ IDENTIFIED BY ‘1q2w3e4r’;
如果监控mysql主从的话,请加上一个super权限:
GRANT SUPER, REPLICATION SLAVE ON *.* TO ‘repl’@’%’ IDENTIFIED BY ’1q2w3e4r’;

1、主库的配置
1.1.mysql5.0以下版本的配置

修改主库mysql配置配置文件,在[mysqld]段添加以下内容:

server-id = 1
log-bin=/home/mysql/logs/binlog/bin-log
max_binlog_size = 500M
binlog_cache_size = 128K
binlog-do-db = adb
binlog-ignore-db = mysql
log-slave-updates

 

1.2. mysql5.0以上版本的配置

修改主库mysql配置配置文件,在[mysqld]段添加以下内容:

server-id = 1
log-bin=/home/mysql/logs/binlog/bin-log
max_binlog_size = 500M
binlog_cache_size = 128K
binlog-do-db = adb
binlog-ignore-db = mysql
log-slave-updates
expire_logs_day=2
binlog_format="MIXED"

 

1.3.各个参数的含义和相关注意项:

server-id = 1 #服务器标志号,注意在配置文件中不能出现多个这样的标识,如果出现多个的话mysql以第一个为准,一组主从中此标识号不能重复。
log-bin=/home/mysql/logs/binlog/bin-log #开启bin-log,并指定文件目录和文件名前缀。
max_binlog_size = 500M #每个bin-log最大大小,当此大小等于500M时会自动生成一个新的日志文件。一条记录不会写在2个日志文件中,所以有时日志文件会超过此大小。
binlog_cache_size = 128K #日志缓存大小
binlog-do-db = adb #需要同步的数据库名字,如果是多个,就以此格式在写一行即可。
binlog-ignore-db = mysql  #不需要同步的数据库名字,如果是多个,就以此格式在写一行即可。
log-slave-updates  #当Slave从Master数据库读取日志时更新新写入日志中,如果只启动log-bin 而没有启动log-slave-updates则Slave只记录针对自己数据库操作的更新。
expire_logs_day=2 #设置bin-log日志文件保存的天数,此参数mysql5.0以下版本不支持。
binlog_format=”MIXED”   #设置bin-log日志文件格式为:MIXED,可以防止主键重复。

2、从库的配置
2.1.mysql5.1.7以前版本
修改从库mysql配置配置文件,在[mysqld]段添加以下内容:

server-id=2
master-host=192.168.1.2
master-user=repl
master-password=1q2w3e4r
master-port=3306
master-connect-retry=30
slave-skip-errors=1062
replicate-do-db = adb
replicate-ignore-db = mysql
slave-skip-errors=1007,1008,1053,1062,1213,1158,1159
master-info-file = /home/mysql/logs/master.info
relay-log = /home/mysql/logs/relay-bin
relay-log-index = /home/mysql/logs/relay-bin.index
relay-log-info-file = /home/mysql/logs/relay-log.info

 

如果修改了连接主库相关信息,重启之前一定要删除master.info文件,否则重启之后由于连接信息改变从库而不会自动连接主库,造成同步失败。此文件是保存连接主库信息的。

2.2.mysql5.1.7以后版本

Mysql5.1.7版本在丛库上面的配置很少,主要是采用了新的同步信息记录方式,他不在支持在配置文件中配置连接主库的相关信息,而是把连接等相关信息记录在master-info-file = /home/mysql/logs/master.info文件中,如果入库变了,直接在mysql命令行执行连接信息的改变即可生效,比较灵活了,而不用去重启mysql。修改从库mysql配置配置文件,在[mysqld]段添加以下内容:
slave-skip-errors=1007,1008,1053,1062,1213,1158,1159

2.3. 各个参数的含义和相关注意项
这里只讲一下2个参数,其他全部是从库连接主库的信息和中间日志relay-log的设置。
master-connect-retry=30 #这个选项控制重试间隔,默认为60秒。
slave-skip-errors=1007,1008,1053,1062,1213,1158,1159 #这个是在同步过程中忽略掉的错误,这些错误不会影响数据的完整性,有事经常出现的错误,一般设置忽略。其中1062为主键重复错误。

3、实现主从同步
3.1.实现数据库的统一
检查主从数据库的配置文件,查看是否已正确配置。首次实现 同步要备份主库上需要同步的数据库,然后完整的导入到从库中。注:mysql5.0之前的版本涉及到mysql本身复制过滤存在问题,需要把所有的数据库都备份导入到丛库,保持。

3.2.查看并记录主库bin-log信息
进入主库mysql中,执行:show master status;显示信息如下:
mysql> show master status;
+————-+———-+————–+——————+
| File        | Position | Binlog_do_db | Binlog_ignore_db |
+————-+———-+————–+——————+
| bin-log.003 | 4        | adb          | mysql            |
+————-+———-+————–+——————+
1 row in set (0.00 sec)
记录File 和Position信息;
3.3.在从库上执行同步语句
进入mysql,执行以下语句:

slave stop;
change master to
master_host='192.168.1.2',
master_user='repl',
master_password='1q2w3e4r',
master_port=3306,
master_log_file='bin-log.003',
master_log_pos=4;
slave start;

 

3.4.查看主从同步状态

进入mysql,执行show slave status\G;显示如下(mysql版本不同查询的结果不同,但是重要的指标还是一样的):
Mysql5.0之前的版本如下:

123459386

Mysql5.5之前的版本如下:

8

Mysql5.5的版本如下:
9

重要的指标为:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Master_Log_File: bin-log.003
Relay_Master_Log_File: bin-log.003
Read_Master_Log_Pos: 4
Exec_master_log_pos: 4
Seconds_Behind_Master: 0(5.0之前版本没有这个选项)
以上选项是两两对应的,只要结果是一致的,就说明主从同步成功。

3.5.同步中的常见的错误和处理
1、现象:在从库上面show slave status\G;出现下列情况,
Slave_IO_Running: Yes
Slave_SQL_Running: No
Seconds_Behind_Master: NULL
原因:
a.程序可能在slave上进行了写操作;
b.也可能是slave机器重起后,事务回滚造成的;
c.有可能是在同步过程中遇到某种错误,这个会在查看从库中状态时看到错误提示,最少见的就是主键重复1062的错误。
解决方法:
进入master
mysql> show master status;
+———————-+———-+————–+——————+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+———————-+———-+————–+——————+
| mysql-bin.000040 | 324 |adb | mysql|
+———————-+———-+————–+——————+
然后到slave服务器上执行手动同步

 

slave stop;
change master to
master_host='10.14.0.140',
master_user='repl',
master_password='1q2w3e4r',
master_port=3306,
master_log_file='mysql-bin.000040',
master_log_pos=324;
slave start;
show slave status\G;

 

2、现象:从数据库无法同步,show slave status显示:

Slave_IO_Running: No
Slave_SQL_Running: Yes
Seconds_Behind_Master: NULL

 

解决:首先查看数据库的err日志,查看是什么错误提示,看从库连接主库的IP、用户、密码等相关信息是否有误,如果有误,重新执行同步;如果确认无误,重启主数据库。

mysql> show master status;
+——————+———-+————–+——————+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+——————+———-+————–+——————+
| mysql-bin.000001 | 98 | adb| mysql|
+——————+———-+————–+——————+
进入从库mysql,执行:

slave stop;
change master to Master_Log_File='mysql-bin.000001',Master_Log_Pos=98;
slave start;

或是这样:

slave stop;
change master to Master_Log_File='mysql-bin.000001',Master_Log_Pos=98;
slave start;

 

这个现象主要是master数据库存在问题,由于连接主库信息错误、主库数据库挂掉如果说常见错等原因引起的,我在实际的操作中先重启master后重启slave即可解决这问题,出现此问题,必须要要重启master数据库。

四、mysql主主和主主集群

1、mysql主主的实现
在实际的生产应用中,为了在主库出现崩溃或是主服务器出现严重故障时快速的恢复业务,会直接切换到从库上,当主库故障处理完成后让他直接作为丛库来运行,此时主主就是一个不错的选择。

五、mysql主从的监控
在mysql主从的应用中,只要进行了合理设置,基本上不会出现问题,但是对他的监控是必不可少的,以免由于真的出现问题又不知道而造成不必要的数据损失。
1、mysql主从监控的主要思路
Mysql主从的监控,其主要是监控从库上的一些重要参数:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Master_Log_File: bin-log.003
Relay_Master_Log_File: bin-log.003
Read_Master_Log_Pos: 4
Exec_master_log_pos: 4
Seconds_Behind_Master: 0(5.0之前版本没有这个选项)
通过以上的参数可以反映出主库和从库状态是否正常,从库是否落后于主库等。值得一提的是在mysql5.0以前的版本,Slave_IO_Running这个状态指标不可靠,会在主库直接挂掉的情况下不会变成NO,Seconds_Behind_Master参数也不存在。监控以上参数即可监控mysql主从。

2、mysql主从监控的实现
不管mysql是那个版本,其中的从库上的Exec_master_log_pos、Exec_master_log_pos;主库上的 Master上的Log_File, Position,这四个参数可以判断出当前主从的状态。以下是适用于mysql所有版本的主从监控shell脚本:

#/bin/sh
user=repl
passwd=123415
master_ip="192.168.1.2"
log="/data3/check_repl.log"
value()
{
 master=`/usr/local/mysql/bin/mysql -u$user -p$passwd -h$master_ip -e "show master status\G;"|egrep "File|Position"`
 #mysql 4.0
 slave=`/usr/local/mysql/bin/mysql -u$user -p$passwd -h127.0.0.1 -e "show slave status\G;"|egrep "Relay_Master_Log_File|Exec_master_log_pos"`
 #mysql 5.0
 #slave=`mysql -u$user -p$passwd -e "show slave status\G;"|egrep "Relay_Master_Log_File|Exec_Master_Log_Pos"`
 #取主库上的bin-log号及写入的当前日志位置   
 Master_Log=`echo $master |awk '{print $2}'|awk -F "." '{print $2}'`
 Master_Log_Pos=`echo $master |awk '{print $4}'`
 #取从库上当前同步主库的位置
 Relay_Master_Log_File=`echo $slave |awk '{print $2}'|awk -F "." '{print $2}'`
 Exec_Master_Log_Pos=`echo $slave |awk '{print $4}'`
 echo "Master_Log:"$Master_Log>>$log
 echo "Master_Log_Pos:"$Master_Log_Pos>>$log
 echo "Relay_Master_Log_File:"$Relay_Master_Log_File>>$log
 echo "Exec_Master_Log_Pos:"$Exec_Master_Log_Pos>>$log
}
for((i=1;i<=10;i++));
do
 echo "#################################">>$log
 value
 time=`date +"%Y-%m-%d %H:%M:%S"`
 if [ $Master_Log -eq $Relay_Master_Log_File ];then
       A=`expr $Master_Log_Pos - $Exec_Master_Log_Pos`
       if [ $A -lt 0 ];then
             A=`expr 0 - $A`
       fi
       echo $A>>$log
       if [ $A -lt 10000 ];then
             echo "$time Master-Slave is OK.">>$log
             #echo "$i"
             break
       else
             if [ $i ge 3 ];then              
                  echo "$time Warning:Slave-Master lag $A " >>$log
                  echo "$i"
             fi
             sleep 30
             continue
       fi
 else
       sleep 60
       fi
       if [ $i -eq 10 ];then
             echo "$i"
             echo "$time Error:Slave-Master must be check !" >>$log
       fi
done

在mysql5.0以后的版本,mysql主从已经相当的成熟了,可以只监控Slave_IO_Running,Slave_SQL_Running,Seconds_Behind_Master状态就可以了,这里不再做说明。

转自:http://blog.sae.sina.com.cn/archives/4666

安装zabbix2.2.5+php5.5+mysql5.5.9+nginx1.5.1

先安装php,nginx和mysql
php编译参数:

./configure \
–prefix=/usr/local/php5 \
–with-bz2 \
–with-curl \
–enable-ftp \
–enable-sockets \
–enable-bcmath \
–disable-ipv6 \
–with-gd \
–with-jpeg-dir=/usr/local \
–with-png-dir=/usr/local \
–with-freetype-dir=/usr/local \
–enable-gd-native-ttf \
–with-iconv-dir=/usr/local \
–enable-mbstring \
–enable-calendar \
–with-gettext \
–with-libxml-dir=/usr/local \
–with-zlib \
–with-pdo-mysql=mysqlnd \
–with-mysqli=mysqlnd \
–with-mysql=mysqlnd \
–enable-dom \
–enable-xml \
–enable-fpm \
–with-libdir=lib64

 

php装好之后需要修改php.ini为以下值,以满足zabbix安装需要

max_execution_time = 300
memory_limit = 128M
post_max_size = 16M
upload_max_filesize = 2M
max_input_time = 300
date.timezone PRC

nginx编译参数

./configure –prefix=/usr/local/nginx –add-module=/usr/local/src/nginx_upstream_check_module-master –with-http_stub_status_module –with-http_ssl_module –with-http_realip_module –with-http_sub_module –with-http_gzip_static_module –with-http_flv_module –with-http_dav_module –with-http_spdy_module –with-pcre –with-debug

 
mysql安装略过 安装zabbix
注意:zabbix只使用普通用户运行,如果在root环境下运行zabbix,那么zabbix将会主动使用zabbix用户来运行。

groupadd zabbix&&useradd -g zabbix zabbix
cd /usr/local/src/
wget http://jaist.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/2.2.5/zabbix-2.2.5.tar.gz
tar -zxvf zabbix-2.2.5.tar.gz
cd /usr/local/src/zabbix-2.2.5
./configure –prefix=/usr/local/zabbix/ –enable-server –enable-agent –with-mysql –with-net-snmp –with-libcurl –with-libxml2
make&&make install

 

复制前端文件到主目录

cp -r /usr/local/src/zabbix-2.2.5/frontends/php/* /usr/local/nginx/html/zabbix/

 

修改nginx虚拟主机配置

server {
        listen       80;
        server_name  zabbix.abc.com;
        access_log  /usr/local/nginx/logs/zabbix.abc.com.log  main;

        index         index.html index.htm index.php;
        root         /usr/local/nginx/html/zabbix;

        location / {
                try_files $uri $uri/ /index.php?$args;
        }
        location ~ ^(.+.php)(.*)$ {
                fastcgi_split_path_info ^(.+.php)(.*)$;
                include fastcgi.conf;
                fastcgi_pass  127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_param  PATH_INFO          $fastcgi_path_info;
        }
}

 
访问http://zabbix.abc.com开始配置

安装zabbix客户端

groupadd zabbix&&useradd -g zabbix zabbix
cd /usr/loca/src/
wget http://www.zabbix.com/downloads/2.2.5/zabbix_agents_2.2.5.linux2_6.amd64.tar.gz
mkdir /usr/local/zabbix_agent
tar -zxvf zabbix_agents_2.2.5.linux2_6.amd64.tar.gz -C /usr/local/zabbix_agent/
cd /usr/local/zabbix_agent/&&mkdir logs
chown -R zabbix:zabbix/usr/local/zabbix_agent

 

修改客户端配置文件

# vi /usr/local/zabbix_agent/conf/zabbix_agentd.conf

LogFile=/usr/local/zabbix_agent/logs/zabbix_agentd.log
Server=192.168.0.1
ServerActive=192.168.0.1
Hostname=Zabbix server

 

注意:Server和ServerActive都是指定zabbixserver的IP地址,Server是用来允许192.168.0.1来客户端取数据。而serverActive的192.168.0.1的意思是,客户端主动提交数据给服务器端 启动客户端

/usr/local/zabbix_agent/sbin/zabbix_agentd -c /usr/local/zabbix_agent/conf/zabbix_agentd.conf

检查是否正常启动

netstat -nlp|grep 10050

 

写一个客户端启动脚本链接到/etc/init.d/目录下

# more /etc/init.d/zabbix_agentd_srv
 
#!/bin/bash
# chkconfig: 345 20 80
# description: zabbix_agentd auto start-stop script.

case "$1" in

start)
echo "Starting zabbix_agentd ... "
/usr/local/zabbix_agent/sbin/zabbix_agentd -c /usr/local/zabbix_agent/conf/zabbix_agentd.conf
;;

stop)
echo "Stopping zabbix_agentd ... " 
/usr/bin/killall zabbix_agentd 
;;

restart)
$0 stop
sleep 2
$0 start
;;

*)
echo "Usage: $0 {start|stop|restart}" 
exit 1
esac

 

Mysql二进制日志的清理

删除MySQL二进制日志的3种方法

1.RESET MASTER
可以删除列于索引文件中的所有二进制日志,把二进制日志索引文件重新设置为空,并创建一个新的二进制日志文件

2.PURGE MASTER LOGS
语法
PURGE {MASTER | BINARY} LOGS TO ‘log_name
PURGE {MASTER | BINARY} LOGS BEFORE ‘date
用于删除列于在指定的日志或日期之前的日志索引中的所有二进制日志。这些日志也会从记录在日志索引文件中的清单中被删除,这样被给定的日志成为第一个。
例如:
PURGE MASTER LOGS TO ‘mysql-bin.010’;
PURGE MASTER LOGS BEFORE ‘2003-04-02 22:46:26’;
BEFORE变量的date自变量可以为’YYYY-MM-DD hh:mm:ss’格式。MASTER和BINARY是同义词。
如果您有一个活性的从属服务器,该服务器当前正在读取您正在试图删除的日志之一,则本语句不会起作用,而是会失败,并伴随一个错误。不过,如果从属服务器是休止的,并且您碰巧清理了其想要读取的日志之一,则从属服务器启动后不能复制。当从属服务器正在复制时,本语句可以安全运行。您不需要停止它们。
要清理日志,需按照以下步骤:

1.
在每个从属服务器上,使用SHOW SLAVE STATUS来检查它正在读取哪个日志。
2.
使用SHOW MASTER LOGS获得主服务器上的一系列日志。
3.
在所有的从属服务器中判定最早的日志。这个是目标日志。如果所有的从属服务器是更新的,这是清单上的最后一个日志。
4.
制作您将要删除的所有日志的备份。(这个步骤是自选的,但是建议采用。)
5.
清理所有的日志,但是不包括目标日志。

3. expire_logs_days参数
二进制日志自动删除的天数。默认值为0,表示“没有自动删除”。启动时和二进制日志循环时可能删除
expire_logs_days numeric GLOBAL
在my.cnf配置文件【mysqld】段中加入,重启MySQL服务,例:
expire_logs_days=3
删除3天以前的日志

注:当然需要考虑有slave情况下的影响