使用 python 管理 mysql 开发工具箱 - 1

  1. 云栖社区>
  2. 博客>
  3. 正文

使用 python 管理 mysql 开发工具箱 - 1

程序猿肖邦 2016-12-26 12:48:00 浏览738
展开阅读全文

Mysql 是一个比较优秀的开源的数据库,很多公司都在使用。作为运维人员,经常做着一些重复性的工作,比如创建数据库实例,数据库备份等,完全都可以使用 python 编写一个工具来实现。

一、模块 ConfigParser 学习

ConfigParser 模块可以解析类似 MySQL 配置文件 my.cnf 和 windows 下的 ini 配置文件。使用 ConfigParser 模块解析这类配置文件非常方便,而且可以增加、设置或删除配置文件的 section option 等,如果你使用 C 语言解析过配置文件,你就能明白 ConfigParser 模块的高效。

1. 使用前需要注意的

ConfigParser 模块在 python 2.6 版本中不支持没有 value 的 option 解析,解决的方法有 2 种:

(a) 升级 python 2.6 到 python 2.7。
(b) 拷贝一个 python 2.7 标准库的 ConfigParser.py 文件到 2.6 的标准库覆盖原来的 ConfigParser.py 文件。

推荐使用第一种方式,直接,一劳永逸;不过第二种方式比较简单。我用的是 CentOS 6,默认 python2.6.6,我升级 python 版本为 2.7.10。

参考我的一篇博文:http://www.cnblogs.com/liwei0526vip/p/6219998.html

2. ConfigParser 基本使用

导入模块并实例化

import ConfigParser
conf = ConfigParser.ConfigParser(allow_no_value=True)

读配置文件 read/readfp

In [3]: conf.read('/root/my.cnf')
Out[3]: ['/root/my.cnf']
# 返回配置文件列表,其实可以读取多个配置文件的列表,实际应用中没有意义,一般都只读取一个配置文件
# readfp() 函数可以读取 open() 函数返回的文件句柄 fp

section 相关操作

# 配置文件中有哪些 section
In [4]: conf.sections()
Out[4]: ['client', 'mysqld', 'mysqldump', 'mysql', 'myisamchk', 'mysqlhotcopy']
# 添加 section [user]
In [5]: conf.add_section('user')
In [6]: conf.sections()
Out[6]: ['client', 'mysqld', 'mysqldump', 'mysql', 'myisamchk', 'mysqlhotcopy', 'user']
# 删除 section [client] 注意:会把该section下的option全部删除
In [10]: conf.remove_section('client')
Out[10]: True
# 判断是否有section [user]
In [15]: conf.has_section('user')
Out[15]: True

options 相关操作

In [16]: conf.options('mysqld')             # 列出options
In [17]: conf.set('mysqld','key','value')   # 添加options [key] 同样可以修改options
In [18]: conf.remove_option('mysqld','key') # 删除options
In [23]: conf.has_option('mysqld', 'key')   # 判断是否有[key]

获取 get 值 value

In [24]: conf.get('mysqld', 'port')
Out[24]: '3306'                             # 返回value字符串格式
In [25]: conf.getint('mysqld', 'port')
Out[25]: 3306                               # 返回value整数格式
In [26]: conf.getfloat('mysqld', 'xxxx')    # 返回value浮点数格式
In [27]: conf.getboolean('mysqld', 'xxxx')  # 返回value布尔型格式
In [28]: conf.items('mysqld')
Out[28]: 
[('port', '3306'),
 ('query_cache_size', '16M'),
 ... ... ... ...
 ('server-id', '1')]

写入配置文件

# 上述的设置是对内存中数据进行操作,并非实际写入配置文件,通过write写入配置文件
In [29]: fp = open('/tmp/my.cnf', 'w')
In [30]: conf.write(fp)
In [31]: fp.close()

3. 高级功能项

ConfigParser 模块中定义了3个类对配置文件进行操作。分别是 RawConfigParser、ConfigParser、SafeConfigParser。RawCnfigParser 是最基础的INI文件读取类,ConfigParser、SafeConfigParser支持对%(value)s变量的解析

>>> 设定配置文件 test.conf

[test] 
url = http://%(host)s:%(port)s/Portal 
host = localhost 
port = 8080

>>> 代码中使用ConfigParser解析:

import ConfigParser 
conf = ConfigParser.ConfigParser() 
conf.read("test.conf") 
print conf.get("test", "url") 
conf.set("test", "url2", "%(host)s:%(port)s") 
print conf.get("test", "url2")

>>> 终端输出内容:

http://localhost:8080/Portal
localhost:8080

SafeConfigParser 可以实现 ConfigParser 同样的功能。

 

二、生成 MySQL 配置文件模板

通过借助于 ConfigParser 模块,写了一个 MySQLDConfig 的工具类,使用这个类可以对 MySQL 配置文件进行一些列的操作:生成模板、添加选项、修改选项等,非常方便,作为后续开发工具箱的一个重要工具库。代码 mysql.py 如下:

 1 #!/usr/bin/python
 2 #encoding: utf-8
 3 
 4 from ConfigParser import ConfigParser
 5 import sys,os
 6 
 7 # 定义MySQLConfig类 继承ConfigParser类
 8 class MySQLDConfig(ConfigParser):
 9     def __init__(self, config, **kw):
10         ConfigParser.__init__(self, allow_no_value=True)
11         # must support new style: object++ modify source code
12         #super(ConfigParser, self).__init__(allow_no_value=True)
13         # 所有选项都会汇聚到该字典中,统一写入配置文件
14         self.mysqld_vars = {}
15         self.config = config
16         if os.path.exists(self.config):
17             self.read(self.config)
18             self.get_mysqld_vars()
19         else:
20             self.get_defaults_mysqld_vars()
21         self.set_mysqld_vars(kw)
22 
23     # 统一的接口:将指定的kw写入setattr和全局字典
24     def set_mysqld_vars(self, kw):
25         for k,v in kw.items():
26             setattr(self, k, v)
27             self.mysqld_vars[k] = v
28 
29     # 读取配置文件[mysqld]段的所有选项并set
30     def get_mysqld_vars(self):
31         rst = {}
32         options = self.options('mysqld')
33         for o in options:
34             rst[o] = self.get('mysqld', o)
35         self.set_mysqld_vars(rst)
36 
37     # 设置默认的mysql选项, 如果没有配置文件,则使用默认配置项
38     def get_defaults_mysqld_vars(self):
39         defaults = {
40             'port':'3306',
41             'socket':'/var/lib/mysql/mysql.sock',
42             'key_buffer_size':'256M',
43             'max_allowed_packet':'1M',
44             'table_open_cache':'256',
45             'sort_buffer_size':'1M',
46             'read_buffer_size':'1M',
47             'read_rnd_buffer_size':'4M',
48             'myisam_sort_buffer_size':'64M',
49             'thread_cache_size':'8',
50             'query_cache_size':'16M',
51             'thread_concurrency':'8',
52             'user': 'mysql',
53         }
54         self.set_mysqld_vars(defaults)
55 
56     # 特殊的接口 单独去设置/添加变量 比如skip-salve-start等不能通过变量传递的选项
57     def set_vars(self, k, v):
58         self.mysqld_vars[k] = v
59 
60     # 将所有的选项保存到配置文件,包括:新添加和更新的
61     def save(self):
62         if not self.has_section('mysqld'):
63             self.add_section('mysqld')
64         for k, v in self.mysqld_vars.items():
65             self.set('mysqld', k, v)
66         with open(self.config, 'w') as fd:
67             self.write(fd)
68 
69 if __name__ == '__main__':
70     mc = MySQLDConfig('/root/my1.cnf', max_connections=200, port=3307)
71     #print mc.get('mysqld','port')
72     #print mc.max_connections
73     #print mc.port
74     #print mc.socket
75     
76     #mc.set_vars('skip-slave-start', None)
77     mc.save()
78     print mc.port

 

三、创建 MySQL 实例

借助于上一章节编写的 MySQLDConfig 类,来创建 MySQL 实例,大概如下几个步骤:

  1. 生成配置文件
  2. 安装初始化 MySQL 实例
  3. 设置属主属组
  4. 启动 MySQL 运行

此时的代码结构:

├── library
│   ├── __init__.py
│   └── mysql.py
└── mysqlmanager
    └── myman.py
2 directories, 3 files

具体的主代码 myman.py 如下:

  1 #!/usr/bin/env python
  2 #encoding: utf-8
  3 
  4 import os, sys, time
  5 from subprocess import Popen, PIPE
  6 import shlex
  7 from optparse import OptionParser
  8 DIRNAME = os.path.dirname(__file__)
  9 OPSTOOLS = os.path.abspath(os.path.join(DIRNAME, '..'))
 10 sys.path.append(OPSTOOLS)
 11 from library.mysql import MySQLDConfig
 12 
 13 # MySQL实例的数据目录(会以name为子目录)
 14 MYSQL_DATA_DIR = '/var/mysqlmanager/data'
 15 # MySQL实例的配置文件目录
 16 MYSQL_CONF_DIR = '/var/mysqlmanager/conf'
 17 
 18 # 设置选项参数
 19 def opt():
 20     parser = OptionParser()
 21     parser.add_option(
 22         '-n', '--name',      # 指定实例的name
 23         dest    = 'name',
 24         action  = 'store',
 25         default = 'myinstance'
 26     )
 27     parser.add_option(
 28         '-p', '--port',      # 指定端口
 29         dest    = 'port',
 30         action  = 'store',
 31         default = '3306'
 32     )
 33     parser.add_option(
 34         '-c', '--command',   # create check and so on...
 35         dest    = 'command',
 36         action  = 'store',
 37         default = 'check'
 38     )
 39     options, args = parser.parse_args()
 40     return options, args
 41 
 42 # 初始化目录(如果不存在则创建)
 43 def _init():
 44     if not os.path.exists(MYSQL_DATA_DIR):
 45         os.makedirs(MYSQL_DATA_DIR)
 46     if not os.path.exists(MYSQL_CONF_DIR):
 47         os.makedirs(MYSQL_CONF_DIR)
 48 
 49 # 使用glob模块读取配置文件,返回配置文件列表(绝对路径)
 50 def readConfs():
 51     import glob
 52     confs = glob.glob(MYSQL_CONF_DIR + '/*.cnf')
 53     return confs
 54 
 55 # 检查指定的配置文件中端口号是否与指定端口相同
 56 def checkPort(conf_file, port):
 57     mc = MySQLDConfig(conf_file)
 58     if mc.mysqld_vars['port'] == port:
 59         return True
 60     else:
 61         return False
 62 
 63 # 有些配置项的值由具体示例而定,将这些不定项设置为字典
 64 def _genDict(name, port):
 65     return {
 66         'pid-file': os.path.join(MYSQL_DATA_DIR, name,'%s.pid' % name),
 67         'socket': '/tmp/%s.sock' % name,
 68         'port': port,
 69         'datadir': os.path.join(MYSQL_DATA_DIR, name),
 70         'log-error': os.path.join(MYSQL_DATA_DIR, name, '%s.log' % name),
 71     }
 72 
 73 # 通过配置文件名称->配置文件的绝对路径
 74 def getCNF(name):
 75     cnf = os.path.join(MYSQL_CONF_DIR, '%s.cnf' % name)
 76     return cnf
 77 
 78 # MySQL执行安装过程
 79 def mysql_install(name):
 80     cnf = getCNF(name)
 81     cmd = 'mysql_install_db --defaults-file=%s' % cnf
 82     p = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE)
 83     p.communicate()
 84     p.returncode
 85 
 86 # 为保险起见,设置MySQL实例数据目录的属主数组
 87 def setOwner(datadir):
 88     os.system('chown mysql:mysql %s -R' % datadir)
 89 
 90 # 运行MySQL实例
 91 def mysql_run(name):
 92     cnf = getCNF(name)
 93     cmd = 'mysqld_safe --defaults-file=%s &' % cnf
 94     p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True)
 95     #time.sleep(2)
 96     p.returncode
 97 
 98 # 创建MySQL实例主函数, 由create子命令触发调用
 99 def createInstance(name, port):
100     exists_confs = readConfs()
101 
102     # 检查给定的端口号和名称是否已经使用
103     for conf in exists_confs:
104         if conf.split('/')[-1][:-4] == name:
105             print >> sys.stderr, 'Instance: %s is exists' % name
106             sys.exit(-1)
107         if checkPort(conf, port):
108             print >> sys.stderr, 'Port: %s is exists' % port
109             sys.exit(-1)
110 
111     # 1. 生成配置文件
112     cnf = getCNF(name)
113     if not os.path.exists(cnf):
114         c = _genDict(name, port)
115         mc = MySQLDConfig(cnf, **c)
116         mc.save()
117     datadir = os.path.join(MYSQL_DATA_DIR, name)
118     if not os.path.exists(datadir):
119         # 2. 安装初始化MySQL实例
120         mysql_install(name)
121         # 3. 设置属主属组
122         setOwner(datadir)
123         # 4. 启动MySQL运行
124         mysql_run(name)
125         
126 
127 if __name__ == '__main__':
128     _init()
129     options, args = opt()
130     instance_name = options.name
131     instance_port = options.port
132     instance_cmd  = options.command
133     if instance_cmd == 'create': 
134         createInstance(instance_name, instance_port)

>>>> 扩展:实现几个简单使用功能

  1. 启动 MySQL 实例
  2. 关闭 MySQL 实例
  3. 重启 MySQL 实例

 

四、MySQLdb 模块的使用

1. 安装 MySQL-python

直接使用 yum 工具安装:

[root@mysql ~]# yum install MySQL-python -y

2. 设置库路径

使用 rpm -ql MySQL-python 命令可以查看到默认安装 MySQLdb 库路径是:/usr/lib64/python2.6/site-packages 。如果没有升级 python 版本,可以正常使用,直接 import 即可。但是我升级了 python 2.7.10 ,默认 python 2.7 不会去旧版本路径下找库的,因此无法使用。解决办法有几种:

  1. 设置环境变量 PYTHONPATH
  2. 在 sys.path 追加路径
  3. 拷贝 MySQLdb 库目录到 Python2.7 路径下
  4. 添加 .pth 文件,添加路径

我这里使用的方式是第 4 种:

[root@mysql site-packages]# pwd
/usr/local/python27/lib/python2.7/site-packages
[root@mysql site-packages]# cat python26sitepackages.pth    # 添加 xxx.pth 文件,文件中设置第三方库路径
/usr/lib64/python2.6/site-packages
# *.pth文件名称随意,文件后缀一定要是.pth

3. 基本使用

# 导入MySQLdb模块
In [1]: import MySQLdb

# 连接数据库
In [2]: conn = MySQLdb.Connect(host='127.0.0.1', port=3306, user='root', passwd='')

# 获取游标句柄
In [3]: cur = conn.cursor()

# 执行SQL语句
In [4]: cur.execute('show databases;')
Out[4]: 3L

# 获取SQL执行结果
In [5]: cur.fetchall()
Out[5]: (('information_schema',), ('mysql',), ('test',))

 

五、MySQL 配置文件检查(与内存)

MySQL 可以通过配置文件和数据库命令行方式去修改参数变量,经常会有这种情况,临时通过命令行手动修改了选项参数,比如最大连接数不够了,通过 set global max_connections=200 来临时设置了参数。但是下次重启 MySQL 又会从配置文件读取参数。现在可以通过开发的工具箱来读取配置文件和内存中的参数是否一致,如果不一致,考虑以内存中的值为准,写入配置文件。部分代码如下:

 1 # 根据配置文件信息连接MySQL数据库
 2 def connMysql(name):
 3     cnf = getCNF(name)
 4     if os.path.exists(cnf):
 5         mc = MySQLDConfig(cnf)
 6         port = int(mc.mysqld_vars['port'])
 7         host = '127.0.0.1'
 8         user = 'root'
 9         conn = MySQLdb.connect(host=host , port=port, user=user,)
10         cur = conn.cursor()
11         return cur
12 
13 # 获取内存中全局变量, 输出是一个字典.
14 def getMyVariables(cur):
15     sql = 'show global variables'
16     cur.execute(sql)
17     data = cur.fetchall()
18     return dict(data)
19 
20 # 对比配置文件和内存中变量, 找出不一致相
21 # 遗留问题: 单位不一致和目录最后'/'符号问题 ?????????????????????????????????
22 def diffMyVariables(name):
23     cur = connMysql(name)
24     vars = getMyVariables(cur)
25     cnf = getCNF(name)
26     mc = MySQLDConfig(cnf)
27     for k, v in mc.mysqld_vars.items():
28         k = k.replace('-', '_')
29         if k in vars and v != vars[k]:
30             print k, v, vars[k]
31 
32 if __name__ == '__main__':
33     _init()
34     options, args = opt()
35     instance_name = options.name
36     instance_port = options.port
37     instance_cmd  = options.command
38     if instance_cmd == 'create': 
39         createInstance(instance_name, instance_port)
40     elif instance_cmd == 'check':
41         diffMyVariables(instance_name)

命令执行:python myman.py -n my01 -p 3306 -c check

 

六、MySQL 配置文件调整(同步)

通过 check 命令找到了配置文件和内存中参数的不同,然后就可以通过 adjust 命令来调整一下配置文件,保持配置文件和内存的同步。实现代码如下:

 1 # 根据命令行参数调整配置文件
 2 def setMyVariables(name, k, v):
 3     cnf = getCNF(name)
 4     mc = MySQLDConfig(cnf)
 5     mc.set_vars(k, v)
 6     mc.save()
 7 
 8 if __name__ == '__main__':
 9     _init()
10     options, args = opt()
11     instance_name = options.name
12     instance_port = options.port
13     instance_cmd  = options.command
14     if instance_cmd == 'create': 
15         createInstance(instance_name, instance_port)
16     elif instance_cmd == 'check':
17         diffMyVariables(instance_name)
18     elif instance_cmd == 'adjust':
19         try:
20             k = args[0]
21         except IndexError:
22             print 'Usage: -c adjust max_connections 300. At least 1 arg.'
23         try:
24             v = args[1]
25         except IndexError:
26             v = None
27         setMyVariables(instance_name, k, v)

命令执行:python myman.py -n my01 -p 3306 -c adjust max_connections 200

 

七、数据库 MySQL 主从复制介绍

1. MySQL 主节点设置步骤

  • 打开bin-log:log-bin = mysql-bin
  • 设置server-id:server-id = 1
  • 创建主从账号:grant replication slave on *.* to 'slave'@'%' identified by '123456';

2. MySQL 从节点设置步骤

  • 设置server-id:server-id = 2(写入配置文件)
  • 设置master-host:master-host = 192.168.0.8(写入配置文件)
  • 设置master-port:master-port = 3306(写入配置文件)
  • 设置master-user:master-user = slave(写入配置文件)
  • 设置master-password:master-pass = 123456(写入配置文件)

注意:以上 master 开头的选项可以写入配置文件,但不建议写入配置文件,不安全。可以使用如下命令代替:

mysql> change master to master_host = '192.168.0.8', master_port = 3306, master_user = 'repl', master_password = '123456'

3. 先后运行 Mster 和 Slave 实例

先运行 master,后运行 slave。在从节点上运行命令查看:show slave status\G;

4. 其它选项

skip-slave-start:从库节点 slave 运行时,不会直接启动 slave 的主从复制
replicate-ignore-db:忽略某个数据库的同步复制,比如 mysql 库,忽略,常容易出错

 

八、代码实现主库和从库创建

以下是继续使用 create 子命令来实现主从库的创建。

 1 REPLICATION_USER = 'repl'
 2 REPLICATION_PASS = '123456'
 3 
 4 # 设置主从用户时执行创建用户的SQL语句
 5 def runSQL(name):
 6     sql = "grant replication slave on *.* to %s@'%%' identified by '%s'" % (REPLICATION_USER, REPLICATION_PASS)
 7     cur = connMysql(name)
 8     cur.execute(sql)
 9 
10 # 代替配置文件,在slave上设置master相关信息
11 def changeMaster(name, host, port, user, password):
12     sql = """change master to 
13              master_host = '%s',
14              master_port = %s,
15              master_user = '%s',
16              master_password = '%s'
17           """ % (host, port, user, password)
18 
19     cur = connMysql(name)
20     cur.execute(sql)
21 
22 if __name__ == '__main__':
23     _init()
24     options, args = opt()
25     instance_name = options.name
26     instance_port = options.port
27     instance_cmd  = options.command
28     if instance_cmd == 'create': 
29         if not args:
30             createInstance(instance_name, instance_port)
31         else:
32             dbtype = args[0]
33             serverid = args[1]
34             mysql_options = {'server-id': serverid}
35             if dbtype == 'master':
36                 mysql_options['log-bin'] = 'mysql-bin'
37                 createInstance(instance_name, instance_port, **mysql_options) 
38                 runSQL(instance_name)
39             elif dbtype == 'slave':
40                 # 5.5 版本以上不建议将master开头字段写入配置文件,其实低版本写入配置文件也是不妥当的
41                 #mysql_options['master-host'] = args[2]
42                 #mysql_options['master-port'] = args[3]
43                 #mysql_options['master-user'] = REPLICATION_USER
44                 #mysql_options['master-pass'] = REPLICATION_PASS
45                 mysql_options['replicate-ignore-db'] = 'mysql'    # 设置忽略mysql库
46                 mysql_options['skip-slave-start'] = None          # 设置slave运行时不自动启动主从复制
47                 createInstance(instance_name, instance_port, **mysql_options)  # 按照从选项来创建实例
48                 host = args[2]    # 传入master的主机
49                 port = args[3]    # 传入master的端口
50                 user = REPLICATION_USER
51                 password = REPLICATION_PASS
52                 # 设置master信息
53                 changeMaster(instance_name, host, port, user, password) 
54     elif instance_cmd == 'check':
55         diffMyVariables(instance_name)
56     elif instance_cmd == 'adjust':
57         try:
58             k = args[0]
59         except IndexError:
60             print 'Usage: -c adjust max_connections 300. At least 1 arg.'
61         try:
62             v = args[1]
63         except IndexError:
64             v = None
65         setMyVariables(instance_name, k, v)

创建主库:python myman.py -n master01 -p 3306 -c create master 1
创建从库:python myman.py -n slave01  -p 3307 -c create slave  2 192.168.0.8 3306

 

九、备份 MySQL 数据库

数据库备份很重要,要制定好备份策略。部分代码如下:

 1 # MySQL实例的数据目录(会以name为子目录)
 2 MYSQL_DATA_DIR = '/var/mysqlmanager/data'
 3 # MySQL实例的配置文件目录
 4 MYSQL_CONF_DIR = '/var/mysqlmanager/conf'
 5 # MySQL实例的备份文件目录
 6 MYSQL_BACK_DIR = '/var/mysqlmanager/back'
 7 
 8 # MySQL备份,有待完善,调整下灵活性
 9 def backupMysql(name):
10     import datetime
11     now = datetime.datetime.now()
12     ts = now.strftime('%Y-%m-%d.%H:%M:%S')
13     sqlfile = os.path.join(MYSQL_BACK_DIR, name, '%s.sql' % ts)
14     _dir = os.path.dirname(sqlfile)
15 
16     # 如果目录不存在,则创建
17     if not os.path.exists(_dir):
18         os.makedirs(_dir)
19     cnf = getCNF(name)
20     mc = MySQLDConfig(cnf)
21     port = mc.mysqld_vars['port']
22     # -A:所有数据库    -x:dump时锁表,只读   -F:dump前刷log
23     cmd = "mysqldump -A -x -F --master-data=1 --host=127.0.0.1 --port=%s --user=root > %s 2>/dev/null" % (port, sqlfile)
24     p = Popen(cmd, stdout=PIPE, shell=True)
25     stdin, stdout = p.communicate()
26     p.returncode
27     
28 if __name__ == '__main__':
29     _init()
30     options, args = opt()
31     instance_name = options.name
32     instance_port = options.port
33     instance_cmd  = options.command
34     if instance_cmd == 'create': 
35         if not args:
36             createInstance(instance_name, instance_port)
37     # ...... 省略其它部分
38     elif instance_cmd == 'check':
39         diffMyVariables(instance_name)
40     elif instance_cmd == 'adjust':
41         try:
42             k = args[0]
43         except IndexError:
44             print 'Usage: -c adjust max_connections 300. At least 1 arg.'
45         try:
46             v = args[1]
47         except IndexError:
48             v = None
49         setMyVariables(instance_name, k, v)
50     elif instance_cmd == 'backup':
51         backupMysql(instance_name)

执行命令:python myman.py -n master01 -c backup

 

十、MySQL 备份的恢复还原

通过 backup 命令备份后得到 SQL 文件,在另一个 MySQL 实例中可以直接导入备份文件。但是如果 master 节点又有数据写入,那么导入SQL后的节点和原来节点的数据是不一致的,缺少了后续的写入数据。在 master 导出 SQL 文件后,记录 bin-log 文件和 log 位置,那么在从节点导入 SQL 文件后,再次根据 bin-log 和 pos 来和 master 进行再次同步,那么,master 和 slave 就保持数据的同步了。具体代码如下:

 1 # 代替配置文件,在slave上设置master相关信息, 注意最后2个缺省参数(是为restore命令而加)
 2 def changeMaster(name, host, port, user, password, logfile=None, logpos=None):
 3     if logfile is None:
 4         sql = """change master to 
 5                  master_host = '%s',
 6                  master_port = %s,
 7                  master_user = '%s',
 8                  master_password = '%s'
 9               """ % (host, port, user, password)
10     else:
11         sql = """change master to 
12                  master_host = '%s',
13                  master_port = %s,
14                  master_user = '%s',
15                  master_password = '%s',
16                  master_log_file = '%s',
17                  master_log_pos = %s
18               """ % (host, port, user, password, logfile, logpos)
19 
20     cur = connMysql(name)
21     cur.execute(sql)
22 
23 # 定义MySQL备份恢复主函数,参数需要指定备份的sql文件 
24 def restoreMysql(name, port, sqlfile, **kw):
25     # 创建从实例 传入选项参数
26     createInstance(name, port, **kw)
27     cnf = getCNF(name)
28     mc = MySQLDConfig(cnf)
29     port = mc.mysqld_vars['port']
30     # 执行导入sql的shell命令
31     cmd = "mysql -h 127.0.0.1 -u root -P %s < %s" % (port, sqlfile)
32     p = Popen(cmd, stdout=PIPE, shell=True)
33     stdin, stdout = p.communicate()
34     p.returncode
35 
36 # 定义内部正则过滤参数的功能函数, 获取:master-log-file 和 master-log-pos
37 def findPos(s):
38     import re
39     rlog = re.compile(r"MASTER_LOG_FILE='(\S+)'")
40     rpos = re.compile(r"MASTER_LOG_POS=(\d+)")
41     log = rlog.search(s)
42     pos = rpos.search(s)
43     if log and pos:
44         return log.group(1), pos.group(1)
45     else:
46         return (None, None)
47 
48 # 读SQL文件,返回log、pos
49 def getPos(sqlfile):
50     with open(sqlfile) as fd:
51         for line in fd:
52             log, pos = findPos(line)
53             if log and pos:
54                 return log, pos
55 
56 if __name__ == '__main__':
57     _init()
58     # ... 省略了中间部分代码 ...
59     elif instance_cmd == 'backup':     # MySQL备份命令
60         backupMysql(instance_name)
61     elif instance_cmd == 'restore':    # MySQL备份的恢复命令
62         server_id = args[0]
63         master_host = args[1]
64         master_port = args[2]
65         sqlfile = args[3]
66         mysqld_options = {'server-id': server_id}
67         mysqld_options['skip-slave-start'] = None
68         mysqld_options['replicate-ignore-db'] = 'mysql'
69         # 恢复固定SQL内容(SQL文件备份的内容)
70         restoreMysql(instance_name, instance_port, sqlfile, **mysqld_options)
71         logfile, logpos = getPos(sqlfile)
72         # 指定log和pos,来配置slave激活master的主从复制(实现增量备份的恢复)
73         changeMaster(instance_name, master_host, master_port, REPLICATION_USER, REPLICATION_PASS, logfile, logpos)

测试实验过程:
>>> python myman.py -n master01 -p 3306 -c create master 1
>>> create database db1; create database db2; create database db3;
>>> python myman.py -n master01 -p 3306 -c backup
>>> create database db4;
>>> python myman.py -n slave01 -p 3307 -c restore 2 192.168.0.8 3306 /var/mysqlmanager/back/master01/2017-01-01.03:08:36.sql

测试结果:slave01 可以同步到 master01 两次的写入数据。

这篇篇幅太长了,另开一篇博文《使用 python 管理 mysql 开发工具箱 - 2

---------- 本文结束 ----------

网友评论

登录后评论
0/500
评论
程序猿肖邦
+ 关注