设为首页 收藏本站
查看: 1420|回复: 0

[经验分享] Python 自动化管理Mysql数据库

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2014-6-5 09:02:58 | 显示全部楼层 |阅读模式
  MySQL数据库一款非常优秀的开源数据库,很多人都在使用。我也不例外,数据库实例创建、数据库状态检测、数据库备份等,天天做着重复、枯燥的工作。为了减轻工作量,使用Python写了一个自动管理Mysql数据库的工具。
  • 具体功能:

    Mysql管理工具的主要功能:
        > 数据库实例创建

        > 数据库备份

        > 数据库配置检测

        > 数据库主从同步
        > 配置文件重新载入


2. 工具代码结构:
    wKiom1OPABrDW_5ZAADXifau2Gw283.jpg
     library: 将共用的功能封装成一个库。

        mysqlmanager:myman.py脚本实现基本管理Mysql的功能。

3. 工具代码展示
   library/mysql.py:  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#!/usr/local/bin/python2.7
#-*- coding:utf-8 -*-

from ConfigParser import ConfigParser
import os
import MySQLdb

def getMyVariables(cur):
    '''查询数据库配置信息'''
    cur.execute('show global variables;')
    data = cur.fetchall()
    return dict(data)

class MySQLDConfig(ConfigParser):
    '''将所有公用的功能封装成一个class'''
    def __init__(self, config, **kw):
        '''Python版本必须2.7以上,2.6版本没有allow_no_value 属性'''
        ConfigParser.__init__(self, allow_no_value=True)
        self.config = config
        self.mysqld_vars = {}
        if os.path.exists(self.config):
            self.read(self.config)
            self.get_mysqld_vars()
        else:
            self.set_mysqld_defaults_var()
        self.set_mysqld_vars(kw)

    def set_mysqld_vars(self, kw):
        '''获取配置文件信息,覆盖默认配置'''
        for k, v in kw.items():
            self.mysqld_vars[k] = v

    def get_mysqld_vars(self):
        '''获取现有配置文件信息'''
        options = self.options('mysqld')
        rst = {}
        for o in options:
            rst[o] = self.get('mysqld', o)
        self.set_mysqld_vars(rst)

    def set_mysqld_defaults_var(self):
        '''如果配置文件不存在,设置默认配置'''
        defaults = {
            "user":"mysql",
            "pid-file": "/var/run/mysqld/mysqld.pid",
            "socket": "/var/lib/mysql/mysql.sock",
            "port": "3306",
            "basedir": "/usr",
            "datadir": "/tmp/mysql",
            "tmpdir": "/tmp",
            "skip-external-locking": None,
            "bind-address": "127.0.0.1",
            "key_buffer": "16M",
            "max_allowed_packet": "16M",
            "thread_stack": "192K",
            "thread_cache_size": "8",
            "myisam-recover": "BACKUP",
            "query_cache_limit": "1M",
            "query_cache_size": "16M",
            "log_error": "/var/log/mysqld.log",
            "expire_logs_days": "10",
            "max_binlog_size": "100M"
        }
        self.set_mysqld_vars(defaults)

    def save(self):
        '''将配置信息保存至配置文件'''
        if not self.has_section('mysqld'):
            self.add_section('mysqld')
        for k, v in self.mysqld_vars.items():
            self.set('mysqld', k, v)
        with open(self.config, 'w') as fd:
            self.write(fd)

if __name__  == "__main__":
    mc = MySQLDConfig('/root/david/mysqlmanager/cnfs/my.cnf', max_connection=200, user='mysql')
    mc.set_var('skip-slave-start', None)
    mc.save()



  library/utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/local/bin/python2.7
#-*-coding:utf-8 -*-
'''
    格式时间的转换,数据库配置文件的单位(*.cnf)和数据库global(mysql>show global variables;)配置的单位不一致,需要转换
'''

unit = {'t':2**40,'g':2**30,'m':2**20,'k':2**10,'b':1}

def convertUnit(s):
    s = s.lower()
    lastchar = s[-1]
    num = int(s[:-1])
    if lastchar in unit:
        return num*unit[lastchar]
    else:
        return int(s)

def scaleUnit(d):
    for k,v in unit.items():
        num = d / v
        if (0 < num < 2**10):
            return num,k



  mysqlmanager/myman.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
#!/usr/local/bin/python2.7
#-*- coding:utf-8 -*-

from os import path
from optparse import OptionParser
from subprocess import PIPE, Popen
import MySQLdb
import glob
import os
import sys
import time
import datetime
import re


DIRNAME = path.dirname(__file__)
OPSTOOLS_DIR = path.abspath(path.join(DIRNAME, '..'))
sys.path.append(OPSTOOLS_DIR)
from library.mysql import MySQLDConfig, getMyVariables

REPLICATION_USER = 'repl'
REPLICATION_PASS = '123qwe'
MYSQL_DATA_DIR = '/home/david/data'
MYSQL_CONF_DIR = '/home/david/cnfs'
MYSQL_BACK_DIR = '/home/david/backup'

def opts():
    parser = OptionParser(usage="usage: %prog [options] arg1 arg2")
    parser.add_option("-c","--cmd",
        dest="cmd",
        action="store",
        default="check",
        help="Check the configuration file and database configuration parameters are different.[%options]"
    )
    parser.add_option("-n","--name",
        dest="name",
        action="store",
        default="mysqlinstance",
        help="Create Examples."
    )
    parser.add_option("-p","--port",
        dest="port",
        action="store",
        default="3306",
        help="Examples of port."
    )
    return parser.parse_args()

def checkPort(d, p):
    '''实例端口检测'''
    for m in d:
        if p == m.mysqld_vars['port']:
            return True
    return False

def setReplMaster(cur):
    '''设置slave数据库同步用户的授权'''
    sql = "GRANT REPLICATION SLAVE ON *.* TO %s@'localhost' IDENTIFIED BY '%s'" % (REPLICATION_USER, REPLICATION_PASS)
    cur.execute(sql)

def connMySQLd(mc):
    '''连接数据库'''
    host = '127.0.0.1'
    user = 'root'
    port = int(mc.mysqld_vars['port'])
    conn = MySQLdb.connect(host, port=port, user=user)
    cur = conn.cursor()
    return cur

def run_mysql(cnf):
    '''运行数据库'''
    cmd = "mysqld_safe --defaults-file=%s &" % cnf
    p = Popen(cmd, stdout=PIPE, shell=True)
    time.sleep(5)
    return p.returncode

def setOwner(p, user):
    '''设置目录权限'''
    os.system("chown -R %s:%s %s" % (user, user, p))

def mysql_install_db(cnf):
    '''数据库初始化'''
    p = Popen("mysql_install_db --defaults-file=%s" % cnf, stdout=PIPE, shell=True)
    #p = Popen("mysql_install_db --user=mysql --datadir=%s " % MYSQL_DATA_DIR, stdout=PIPE, shell=True)
    stdout, stderr = p.communicate()
    return p.returncode

def _genDict(name, port):
    '''设置文件存储目录及监听端口'''
    return {
        'pid-file': path.join(MYSQL_DATA_DIR, name, "%s.pid" % name),
        'socket': '/tmp/%s.sock' % name,
        'port': port,
        'datadir': path.join(MYSQL_DATA_DIR, name)+'/',
        'log_error': path.join(MYSQL_DATA_DIR, name)
    }

def readConfs():
    '''读取配置文件,如果配置文件不存在,使用默认配置生成配置文件'''
    confs = glob.glob(path.join(MYSQL_CONF_DIR, '*.cnf'))
    return [MySQLDConfig(c) for c in confs]

def getCNF(name):
    '''获取配置文件完整路径'''
    return path.join(MYSQL_CONF_DIR, "%s.cnf" % name)

def runMySQLdump(cmd):
    '''启动Mysql命令'''
    p = Popen(cmd, stdout=PIPE, shell=True)
    stdout, stderr = p.communicate()
    return p.returncode

def getBinlogPOS(f):
    '''获取binlog'''
    with open(f) as fd:
        f, p = findLogPos(l)
        if f and p:
            return f,p

def findLogPos(s):
    rlog = re.compile(r"MASTER_LOG_FILE='(\S+)',", re.IGNORECASE)
    rpos = re.compile(r"MASTER_LOG_POS=(\d+),?", re.IGNORECASE)
    log = rlog.search(s)
    pos = rpos.search(s)
    if log and pos:
        return log.group(1), int(pos.group(1))
    else:
        return (None, None)

def changeMaster(cur, host, port, user, mpass, mf, p):
    sql = '''CHANGE MASTER TO
        MASTER_HOST='%s',
        MASTER_PORT='%s',
        MASTER_USER='%s',
        MASTER_PASSWORD='%s',
        MASTER_LOG_FILE='%s',
        MASTER_LOG_POS=%s;''' % (host, port, user, mpass, mf, p)
        cur.execute(sql)

def createInstance(name, port, dbtype="master", **kw):
    '''创建数据库实例'''
    cnf = path.join(MYSQL_CONF_DIR, "%s.cnf" % name)
    datadir = path.join(MYSQL_DATA_DIR, name)
    exists_cnfs = readConfs()

    if checkPort(exists_cnfs, port):
        print >> sys.stderr, "port exist."
        sys.exit(-1)
    if not path.exists(cnf):
        c = _genDict(name, port)
        c.update(kw)
        mc = MySQLDConfig(cnf, **c)
        mc.save()
    else:
        mc = MySQLDConfig(cnf, **kw)

    if not path.exists(datadir):
        mysql_install_db(cnf)
        setOwner(datadir, mc.mysqld_vars['user'])
        run_mysql(cnf)
        time.sleep(3)
        cur = connMySQLd(mc)
        setReplMaster(cur)

def diffVariables(instance_name):
    '''查询数据库配置文件和数据库配置的差异'''
    cnf = getCNF(instance_name)
    if path.exists(cnf):
        mc = MySQLDConfig(cnf)
        print mc
        cur = connMySQLd(mc)
        vars = getMyVariables(cur)
        for k, v in mc.mysqld_vars.items():
            k = k.replace('-', '_')
            if k in vars and vars[k] != v:
                print k, v, vars[k]

def setVariable(instance_name, variable, value):
    '''重新加载配置'''
    cnf = getCNF(instance_name)
    if path.exists(cnf):
        mc = MySQLDConfig(cnf)
        cur = connMySQLd(mc)
        cur.execute('set global %s = %s' % (variable, value))
        mc.set_var(variable, value)
        mc.save()

def backupMySQL(instance_name):
    '''备份数据库'''
    cnf = getCNF(instance_name)
    if path.exists(cnf):
        mc = MySQLDConfig(cnf)
    now = datetime.datetime.now()
    timestamp = now.strftime('%Y-%m-%d-%H%M%S')
    backup_file = path.join(MYSQL_BACK_DIR, instance_name, timestamp+'.sql')
    _dir = path.dirname(backup_file)
    if not path.exists(_dir):
        os.makedirs(_dir)
    cmd = 'mysqldump -A -x -F --master-data=1 --host=127.0.0.1 --user=root --port=%s > %s' % (mc.mysqld_vars['port'], backup_file)
    runMySQLdump(cmd)

def restoreMySQL(instance_name, instance_port, sqlfile, **kw):
    createInstance(instance_name, instance_port, **kw)
    cnf = getCNF(instance_name)
    if path.exists(cnf):
        mc = MySQLDConfig(cnf)
        cur = connMySQLd(mc)
        cmd = "mysql -h 127.0.0.1 -P %s -u root < %s" % (mc.mysqld_vars['port'], sqlfile)
        f, p = getBinlogPOS(sqlfile)
        runMySQLdump(cmd)
        changeMaster(cur,
                     host=kw['master-host'],
                     port=kw['master-port'],
                     user=REPLICATION_USER,
                     mpass=REPLICATION_PASS,
                     mf=f,
                     p=p)

def _init():
    '''查询mysql几个目录是否存在,如果不存在,自动创建'''
    if not path.exists(MYSQL_DATA_DIR):
        os.makedirs(MYSQL_DATA_DIR)
    if not path.exists(MYSQL_CONF_DIR):
        os.makedirs(MYSQL_CONF_DIR)
    if not path.exists(MYSQL_BACK_DIR):
        os.makedirs(MYSQL_BACK_DIR)

def main():
    opt, args = opts()
    instance_name = opt.name
    instance_port = opt.port
    command = opt.cmd
    if command == "create":
        if not args:
            createInstance(instance_name, instance_port)
        else:
            dbtype = args[0]
            serverid = args[1]
            mysqld_options = {'server-id':serverid}
            if dbtype == 'master':
                mysqld_options['log-bin'] = 'mysql-bin'
            elif dbtype == 'slave':
                master_host = args[2]
                master_port = args[3]
                mysqld_options['master-host'] = master_host
                mysqld_options['master-port'] = master_port
                mysqld_options['master-user'] = REPLICATION_USER
                mysqld_options['master-password'] = REPLICATION_PASS
                mysqld_options['skip-slave-start'] = None
                mysqld_options['replicate-ignore-db'] = 'mysql'
                mysqld_options['read-only'] = None
            createInstance(instance_name, instance_port, dbtype=dbtype, **mysqld_options)
    elif command == 'check':
        diffVariables(instance_name)
    elif command == 'adjust':
        variable = args[0]
        value = args[1]
        setVariable(instance_name, variable, value)
    elif command == 'backup':
        backupMySQL(instance_name)
    elif command == 'restore':
        serverid == args[0]
        mhost = args[1]
        mport = args[2]
        sqlfile = args[3]
        mysqld_options = {
            "master-host":mhost,
            "master-port":mport,
            "server-id":serverid,
            "skip-slave-start":None,
        }
        restoreMySQL(instance_name, instance_port, sqlfile, **mysqld_options)

if __name__ == "__main__":
    print main()




4.测试

帮助信息:
wKioL1OPCB7QCQ7iAAGdj56CGo8501.jpg
创建master实例:
wKiom1OPCU_gwpJ6AACHuSb_shY805.jpg
wKiom1OPCcSiaK7oAANn59ZVoes573.jpg
创建slave实例:
wKioL1OPCl2DVGySAACT5pZ7SyU813.jpg wKioL1OPCpjB7UQlAALV8bynwiE615.jpg wKioL1OPC1mgzItYAAMKXu_xs-8742.jpg

检测配置文件和数据库加载配置差异:
wKioL1OPPK2D23rwAAIg_aWL0io634.jpg   由于单位格式不同,所以出现了差异,可以结合library/utils.py 进行单位换算

数据库备份:
wKiom1OPQC7wXIyLAADdH9B_NjM301.jpg wKioL1OPQBHDW8nEAAC0eePWtuk141.jpg

需要注意:
     1. python版本必须2.7及2.7以上版本。
         2. MYSQL_DATA_DIR 目录不能放在/root目录下,如果放在root目录下,初始化数据库的时候会报错(权限问题)。我在这犯过错.

  如果哪里有错误,或者不足的地方。还请大家多多沟通。


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-20169-1-1.html 上篇帖子: 批处理实现每日自动备份和清除数据库文件 下篇帖子: nginx启动python脚本 数据库
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表