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 实例,大概如下几个步骤:
- 生成配置文件
- 安装初始化 MySQL 实例
- 设置属主属组
- 启动 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)
>>>> 扩展:实现几个简单使用功能
- 启动 MySQL 实例
- 关闭 MySQL 实例
- 重启 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 不会去旧版本路径下找库的,因此无法使用。解决办法有几种:
- 设置环境变量 PYTHONPATH
- 在 sys.path 追加路径
- 拷贝 MySQLdb 库目录到 Python2.7 路径下
- 添加 .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 statusG;
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》
---------- 本文结束 ----------