python创建MySQL多实例-1

python创建MySQL多实例-1

前言

  • 什么是多实例

多实例就是允许在同一台机器上创建另外一套不同配置文件的数据库,他们之间是相互独立的,主要有以下特点,

  • 1》 不能同时使用一个端口
  • 2》 不可以使用同一个socket文件
  • 3》 也不可以使用同样的pid
  • 4》 配置文件也必须是不一样的
  • 5》 存入数据的data目录也不能一样
  • 6》 日志文件位置也不可以一样

上面这些只是我个人的一些理解,具体的可能参考网上的一些文章
https://www.percona.com/blog/2014/08/26/mysqld_multi-how-to-run-multiple-instances-of-mysql/

https://freedo.ga/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&cad=rja&uact=8&ved=0ahUKEwjOr4-hj7LTAhXpiFQKHSKNDD4QFggrMAE&url=https%3A%2F%2Fdev.mysql.com%2Fdoc%2Fmysql%2Fen%2Fmultiple-servers.html&usg=AFQjCNGkZSDJbsHCVbm0n8cubpUha2SPsA

https://dev.mysql.com/doc/refman/en/multiple-windows-services.html

需求

通过python脚本来实现创建多个实例

使用方法

程序的目录结构如下

# tree mysql_tools/
mysql_tools/
├── library
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── mysql.py
│   └── mysql.pyc
└── manager
    ├── __init__.py
    └── myman.py

2 directories, 6 files
# python myman.py  -h
Usage: myman.py [options]

Options:
  -h, --help            show this help message and exit
  -n NAME, --name=NAME  
  -p PORT, --port=PORT  
  -c COMMAND, --command=COMMAND

如创建一个my02.cnf的配置文件,端口为3302

python myman.py -n my02 -p 3302 -c create 

实例目录结构

# tree -L 3 /data/mysql/
/data/mysql/
├── conf
│   ├── my01.cnf
│   └── my02.cnf
└── data
    ├── my01
    │   ├── ibdata1
    │   ├── ib_logfile0
    │   ├── ib_logfile1
    │   ├── my01-err.log
    │   ├── my01.pid
    │   ├── my01.sock
    │   ├── mysql
    │   └── test
    └── my02
        ├── ibdata1
        ├── ib_logfile0
        ├── ib_logfile1
        ├── my02-err.log
        ├── my02.pid
        ├── my02.sock
        ├── mysql
        └── test

8 directories, 14 files

源码如下

  • ./library/mysql.py
#!/usr/bin/env python
#-*- coding:utf8 -*-

from ConfigParser import ConfigParser
import os

class MySQLDConfig(ConfigParser):
	def __init__(self, cfg, **kwargs):
		ConfigParser.__init__(self, allow_no_value=True)
		# super(ConfigParser, self).__init__(allow_no_value=True) # ConfigParser的你类是以传统方式定义的,所以这里不可以用super来重写方法
		self.cfg = cfg
		self.mysqld_vars = {}
		
		if os.path.exists(self.cfg):
			self.config = cfg
			self.read(self.config)
			self.get_mysqld_vars()
		else:
			self.get_default_mysqld_vars()
			
		self.set_mysqld_vars(kwargs)
		
	def set_mysqld_vars(self, kwargs):
		for k,v in kwargs.items():
			setattr(self, k, v)
			' add every k-v pairs to dictonary. '
			self.mysqld_vars[k] = v
			
	def get_mysqld_vars(self):
		result = {}
		'return a list'
		options = self.options('mysqld')
		for opt in options:
			result[opt] = self.get('mysqld', opt)
		self.set_mysqld_vars(result)
	
	def get_default_mysqld_vars(self):
		defaults = {
			'user': 'mysql',
			'port': '3307',
			'socket': ' /var/lib/mysql/mysql.sock',
		    # 'skip-locking': None,
		    'key_buffer_size': '256M',
		    'max_allowed_packet': '1M',
		    'table_open_cache': '256',
		    'sort_buffer_size': '1M',
		    'read_buffer_size': '1M',
		    'read_rnd_buffer_size': '4M',
		    'myisam_sort_buffer_size': '64M',
		    'thread_cache_size': '8',
		    'query_cache_size': ' 16M',
			}
		' set default dict k-v to instance property. '
		self.set_mysqld_vars(defaults)
		
	def set_vars(self, k, v):
		self.mysqld_vars[k] = v
		
	' set propertyis for non-supported name, e.g. "skip-slave-start" '
	def save_default_cfg(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.cfg, 'wb') as fd:
		    self.write(fd)

if __name__ == '__main__':
	cfg_file = '/tmp/my.cnf2'
	mc = MySQLDConfig(cfg_file, max_connection=200, port=3307)
	# mysqld_socket = mc.get('mysqld', 'socket')
	# print mysqld_socket
	mc.set_vars('skip-slave-start', None)
	mc.save_default_cfg()
	print mc.mysqld_vars['skip-slave-start']
	print mc.max_connection
	print mc.port
	print mc.socket
  • ./library/mysql.py
#!/usr/bin/env python
#*-* coding: utf-8 -*-

from optparse import OptionParser
import sys
import os
from subprocess import Popen,PIPE
import shlex
import time
import shutil

current_dir = os.path.dirname(__file__)
parent_dir = os.path.join(current_dir, '../')
sys.path.append( os.path.abspath(parent_dir) )

from library.mysql import MySQLDConfig

MYSQL_DATA_DIR = '/data/mysql/data'
MYSQL_CONF_DIR = '/data/mysql/conf'

def opt():
    parser = OptionParser()
    parser.add_option('-n', '--name',
                      dest='name',
                      action='store',
                      default='myinstance'
                      )
    parser.add_option('-p', '--port',
                      dest='port',
                      action='store',
                      default='3306'
                      )
    parser.add_option('-c', '--command',
                      dest='command',
                      action='store',
                      default='check'
                      )
    options, args = parser.parse_args()

    return options, args

def _init():
    if not os.path.exists(MYSQL_DATA_DIR):
        # os.mkdir(MYSQL_DATA_DIR)
        shutil.os.makedirs(MYSQL_DATA_DIR)
    if not os.path.exists(MYSQL_CONF_DIR):
        # os.mkdir(MYSQL_CONF_DIR)
        shutil.os.makedirs(MYSQL_CONF_DIR)

def readConfs():
    from glob import glob
    confs = glob(MYSQL_CONF_DIR + '/*.cnf')
    return confs

def checkPort(cfg_file, port):
    mc = MySQLDConfig(cfg_file)
    if mc.mysqld_vars['port'] == port:
        return True
    else:
        return False

def _genDict(name, port):
    return {'pid-file': os.path.join(MYSQL_DATA_DIR, name, '%s.pid' % name),
            'socket': os.path.join(MYSQL_DATA_DIR, name, '%s.sock' % name),
            'port': port,
            'datadir': os.path.join(MYSQL_DATA_DIR, name),
            'log-error': os.path.join(MYSQL_DATA_DIR, name, '%s-err.log' % name)
            }

def getCnf(name):
    return os.path.join(MYSQL_CONF_DIR, '%s.cnf' % name)

def createInstance(name, port):
    confs = readConfs()

    for conf in confs:
        if conf.split('/')[-1].split('.')[0] == name:
            print >> sys.stderr, 'Instance: %s is already exists' % name
            sys.exit(-1)
        if checkPort(conf, port):
            print >> sys.stderr, 'Port: %s is already exists' % port
            sys.exit(-1)
    cnf = getCnf(name)
    if not os.path.exists(cnf):
        kwargs = _genDict(name, port)
        mc = MySQLDConfig(cnf, **kwargs)
        mc.save_default_cfg()
        
    datadir = os.path.join(MYSQL_DATA_DIR, name)
    if not os.path.exists(datadir):
        mysql_install(name)
        setOwner(datadir)
        mysqlRun(name)

def mysql_install(name):
    cnf = getCnf(name)
    cmd = 'mysql_install_db --defaults-file=%s' % cnf
    p = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE)
    p.communicate()
    p.returncode
    
def setOwner(datadir):
    os.system('chown mysql:mysql %s' % datadir)
    
def mysqlRun(name):
    cnf = getCnf(name)
    cmd = 'mysqld_safe --defaults-file=%s &' % cnf
    # 此行不知为什么会有问题,猜想可能与后台进程符号'&'有关
    # p = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE)
    p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True)
    # 如果是以后台进程启动的,那么就会hang住,因为后台进程并不会返回结果,所以p.communicate()就会一起处于等待状态
    # p.communicate()
    time.sleep(2)
    p.returncode
    
if __name__ == '__main__':
    _init()
    options, args = opt()
    instance_name = options.name
    instance_port = options.port
    instance_cmd = options.command
    createInstance(instance_name, instance_port)
    # print options, args
posted @ 2017-04-20 12:01  ZhangRuoxu  阅读(...)  评论(...编辑  收藏