实现MySQL复制及读写分离

实验拓扑:

wKiom1OlLJfy18EcAAEuo42x8Wo992.jpg

实验要求:

1、完成node1与node2的MySQL主从复制,并且实现node2从服务器只读

2、实现node1 node2半同步复制

3、完成node5与node1基于SSL的远程复制

4、实现node5只复制部分数据库既库过滤功能

5、实现MySQL Proxy转发不同读写请求至不同服务器(MySQL读写分离)


实验平台:

RHEL5.8

MySQL5.5


1、node1 node2实现主从复制

修改master  my.cnf配置文件,主要修改以下内容

[mysqld]
server-id=111    --------  每服务器server-id需不一致
innodb_file_per_table=1  ------- 使得innodb每表表空间
datadir=/mydata/data  ---- 修改数据存放路径
log-bin=/mylog/log/mybinlog  --- 开启二进制日志,日志与数据不要存放在同一磁盘上,注意这里指的是具体文件而非路径
binlog_format=row  ---建议二进制日志存放格式为行格式
../mysql/scripts/mysql_install_db --user=mysql --datadir=/mydata/data  初始化数据库后启动服务即可
grant replication slave,replication client on *.* to repliuser identified by "redhat";--授权一个仅有复制权限的用户


修改slave1  my.cnf配置文件,主要修改以下内容

[mysqld]
server-id=112    --------  每服务器server-id需不一致
innodb_file_per_table=1  
datadir=/mydata/data  
relay_log =/mylog/log/relay.log ---开启并指定二进制日志,从服务器可不必开启二进制日志以减少磁盘IO
read_only=1  ---- 设置从服务器为只读,该权限对具有管理员权限的无效

初始化数据库并启动从服务器

从服务器指向主服务器地址及开始复制的二进制日志及位置

change master to MASTER_HOST='10.32.9.51',MASTER_USER='repliuser',MASTER_PASSWORD='redhat',MASTER_LOG_FILE='mybinlog.000004',MASTER_LOG_POS=107;
MASTER_LOG_FILE = 'master_log_name'  ----- 指定要开始复制的二进制日志名
MASTER_LOG_POS = master_log_pos ---- 指定要开始复制的二进制日志位置(POSITION)

之后

start slave;-----启动从服务器复制的IO线程与SQL线程 


验证

wKiom1OlQWzzYLI2AABcwSSDCNI264.jpg

wKioL1OlQOvyhyOFAADgRxnRrVU606.jpg



node2 从服务器实现只读

node2实现只读的方法主要有:1、开启全局变量read_only  2、申请全局锁flash tables with read lock

测试:主服务器授权一个可以写创建及删除表的普通用户,用普通用户连接连接slave1并创建表

wKioL1OlQ_ngUwqqAACB9JagFIo403.jpg

wKioL1OlRD6zd8wuAAItY8VpFKo781.jpg




2、实现node1 node2半同步复制

半同步复制为google公司提供的源代码,主要为了防止出现从服务器的数据库落后于主服务器的问题,同步复制需要所有从服务器SQL线程写完数据返回给主服务器告知已经完成,所谓半同步主要指当出现多个从节点时,选取其中离主服务器带宽最大的一台,主服务器只要确认该服务器完成同步即可。


主服务器配置

INSTALL  PLUGIN  rpl_semi_sync_master  SONAME  'semisync_master.so' --安装主服务器的半同步扩展插件


设置服务器变量

rpl_semi_sync_master_enabled=ON  开启半同步复制功能  
rpl_semi_sync_master_timeout=1000  等待从服务器超时时间毫秒
rpl_semi_sync_master_trace_level
rpl_semi_sync_master_wait_no_slave  如果没有从节点是否等待其上线


从服务器配置

INSTALL  PLUGIN  rpl_semi_sync_slave  SONAME  'semisync_slave.so' --安装主服务器的半同步扩展插件


设置从服务器变量

rpl_semi_sync_slave_enabled = ON 开启


重新启动从服务器的IO线程即可


主服务器上验证半同步客户端的个数

wKioL1OlSJ7xJarqAAJ_rxMH3S4280.jpg



3、完成node5与node1基于SSL的远程复制

3.1 配置iptables规则,做DNAT将node3外网地址的3306端口转发至node1 3306端口

iptables -t nat -A PREROUTING -d 1.1.1.1 -p tcp --dport 3306 -j DNAT --to-destination 10.32.9.51:3306

测试

wKiom1OlTPHxPGseAAD5hH3qGYQ537.jpg


3.2 配置mysql slave2

[mysqld]
server-id=115   --------  每服务器server-id需不一致
innodb_file_per_table=1  
datadir=/mydata/data

初始化并启动slave2服务器


3.3使node1成为CA并为node5签署数字证书

这里使用我自己写的脚本,直接运行既可以成为CA

#!/bin/bash
#
#
echo -e "now setting CA work directory /etc/pki/CA\n\n"
sed -i s@"../../CA"@"/etc/pki/CA"@ /etc/pki/tls/openssl.cnf
sleep 1
echo -e "done!!\n"
#The work directory should be /etc/pki/CA
DIR=/etc/pki/CA
echo -e "now testing the necessary directory or file. if not creat,the next will creat auto\n"
#test the work directory and file
[ ! -d $DIR ] && mkdirp -p $DIR &> /dev/null;echo -e "$DIR is creating!! done!\n"
sleep 1
[ ! -e $DIR/certs ] && touch $DIR/certs;echo -e "$DIR/certs is creating!! done!\n"
sleep 1
[ ! -d $DIR/crl ] && mkdirp -p $DIR/crl &> /dev/null;echo -e "$DIR/crl is creating!! done!\n"
sleep 1
[ ! -e $DIR/index.txt ] && touch $DIR/index.txt;echo -e "$DIR/index.txt is creating!! done!\n"
sleep 1
[ ! -d $DIR/newcerts ] && mkdir -p $DIR/newcerts;echo -e "$DIR/newcerts is creating!! done!\n"
sleep 1
[ ! -e $DIR/serial ] && touch $DIR/serial ; echo "01" > $DIR/serial;echo -e "$DIR/serial is creating!! done!\n"
sleep 1
[ ! -e $DIR/crlnumber ] && touch $DIR/crlnumber;echo  -e "$DIR/crlnumber is creating!! done!\n"
sleep 1
[ ! -d $DIR/private ] && mkdir -p $DIR/private;echo -e "$DIR/private is creating!! done!\n"
sleep 1
echo -e "The next please input cacert info\n\n"
read -p "please input your country(2 letters) like CN|JP.....:" A
read -p "please input your province:" B
read -p "please input your city:" C
read -p "please input your company:" D
read -p "please input your Unit name:" E
read -p "please input your name or your server's hostname:" F
read -p "please input your email address:" G
openssl genrsa -out $DIR/private/cakey.pem 2048 &> /dev/null
openssl rsa -in $DIR/private/cakey.pem -pubout  > /tmp/pub.key &> /dev/null
echo "$A
$B
$C
$D
$E
$F
$G" | openssl req -new -x509 -key $DIR/private/cakey.pem -out $DIR/cacert.pem -days 365 &> /dev/null
rm -rf /tmp/pub.key
echo -e "\n\n"
echo -e "the cacert is in $DIR/cacert.pem,and the cacert info is::\n\n "
sleep 5
openssl x509 -text -in $DIR/cacert.pem

node5 配置

openssl genrsa -out FILENAME   ------ 生成私钥
openssl req -new -key 私钥名 -out 证书请求文件  ----  生成证书签署请求,私有证书机构 部门 组织单位都要与CA保持一致,否则不给签
openssl ca -in 证书请求文件 -out 证书位置  ------   该条指令在CA上执行,需要node5发送证书至CA,这样就签署了并生成证书,再使用SCP发回node5即可在node5上使用。


node1在配置文件中开启ssl功能并指定相关文件路径后启服务进程

ssl             ------------ 开启ssl功能
ssl_ca=/opt/cacert.pem   ---------ca证书位置
ssl_cert=/opt/cacert.pem  ---------公钥位置
ssl_key=/opt/cakey.pem    -----私钥位置

为slave2创造需要ssl连接才能进行复制的权限账号

grant replication slave,replication client on *.* to node5 identified by "redhat" require ssl


node5配置:

change master to MASTER_HOST='1.1.1.1',MASTER_USER='node5',MASTER_PASSWORD='hadoop',MASTER_LOG_FILE='mybinlog.000004',MASTER_LOG_POS=107, MASTER_SSL=1,MASTER_SSL_CA='/opt/cacert.pem', MASTER_SSL_CERT = '/opt/privite.crt',MASTER_SSL_KEY='/opt/privite';
MASTER_SSL=1表示开启SSL功能
MASTER_SSL_CA 指定CA证书位置
MASTER_SSL_CERT 指定从节点证书的位置
MASTER_SSL_KEY  指定从节点私钥的位置

验证:

方法1

wKioL1Ol1AvAT1PKAAO6IItmMNE281.jpg

方法2

mysql -unode5 -phadoop -h1.1.1.1 --ssl-ca=/opt/cacert.pem  --ssl-cert=/opt/privite.crt --ssl-key=/opt/privite  测试是否可以使用SSL加密登陆主服务器



4、实现node5只复制部分数据库既库过滤功能

复制时实现数据过滤的方法有两种,均是通过修改配置文件参数

master:设置binlog_do_db表示复制哪些数据到二进制日志   binlog_ignore_db忽略哪些数据库
slave: replicate_do_db  replicate_ignore_db  
        replicate_do_table  replicate_ignore_table  (需要指明是哪个库的表)
        replicate_wild_do_table   replicate_wild_ignore_table 支持通配的写法

测试:

主服务器创建aa数据库,数据库中创建cc dd表

wKiom1OmSHyQyxlLAAIDJMIJMaI697.jpg


观察从服务器的复制情况及设置

wKioL1OmSGTSxk3FAAQLAz7mUOs684.jpg



5、实现MySQL Proxy转发不同读写请求至不同服务器(MySQL读写分离)

5.1官网下载通用二进制安装mysql-proxy

5.2为mysql-proxy提供SysV启动脚本

#!/bin/bash
#
# mysql-proxy This script starts and stops the mysql-proxy daemon
#
# chkconfig: - 78 30
# processname: mysql-proxy
# description: mysql-proxy is a proxy daemon for mysql
# Source function library.
. /etc/rc.d/init.d/functions
prog="/usr/local/mysql-proxy/bin/mysql-proxy"
# Source networking configuration.
if [ -f /etc/sysconfig/network ]; then
    . /etc/sysconfig/network
fi
# Check that networking is up.
[ ${NETWORKING} = "no" ] && exit 0
# Set default mysql-proxy configuration.
ADMIN_USER="admin"
ADMIN_PASSWD="admin"
ADMIN_LUA_SCRIPT="/usr/local/mysql-proxy/share/doc/mysql-proxy/admin.lua"
PROXY_OPTIONS="--daemon"
PROXY_PID=/var/run/mysql-proxy.pid
PROXY_USER="mysql-proxy"
# Source mysql-proxy configuration.
if [ -f /etc/sysconfig/mysql-proxy ]; then
    . /etc/sysconfig/mysql-proxy
fi
RETVAL=0
start() {
    echo -n $"Starting $prog: "
    daemon $prog $PROXY_OPTIONS --pid-file=$PROXY_PID --proxy-address="$PROXY_ADDRESS" --user=$PROXY_USER --admin-username="$ADMIN_USER" --admin-lua-script="$ADMIN_LUA_SCRIPT" --admin-password="$ADMIN_PASSWORD"
    RETVAL=$?
    echo
    if [ $RETVAL -eq 0 ]; then
        touch /var/lock/subsys/mysql-proxy
    fi
}
stop() {
    echo -n $"Stopping $prog: "
    killproc -p $PROXY_PID -d 3 $prog
    RETVAL=$?
    echo
    if [ $RETVAL -eq 0 ]; then
        rm -f /var/lock/subsys/mysql-proxy
        rm -f $PROXY_PID
    fi
}
# See how we were called.
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        start
        ;;
    condrestart|try-restart)
        if status -p $PROXY_PIDFILE $prog >&/dev/null; then
            stop
            start
        fi
        ;;
    status)
        status -p $PROXY_PID $prog
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|reload|status|condrestart|try-restart}"
        RETVAL=1
        ;;
esac
exit $RETVAL

5.3 为服务脚本提供配置文件/etc/sysconfig/mysql-proxy

# Options for mysql-proxy 
ADMIN_USER="admin"
ADMIN_PASSWORD="admin"
ADMIN_ADDRESS="1.1.1.1"
ADMIN_LUA_SCRIPT="/usr/local/mysql-proxy/share/doc/mysql-proxy/admin.lua"
PROXY_ADDRESS="1.1.1.1"
PROXY_USER="mysql-proxy"
PROXY_OPTIONS="--daemon --log-level=info --log-use-syslog"
其中最后一行,需要按实际场景进行修改,例如:
PROXY_OPTIONS="--daemon --log-level=info --log-use-syslog --plugins=proxy --plugins=admin --proxy-backend-addresses=10.32.9.51:3306 --proxy-read-only-backend-addresses=10.32.9.52:3306 --proxy-lua-script=/usr/local/mysql-proxy/share/doc/mysql-proxy/rw-splitting.lua"
其中的proxy-backend-addresses选项和proxy-read-only-backend-addresses选项均可重复使用多次,以实现指定多个读写服务器或只读服务器。

5.4为mysql-proxy提供管理lua脚本,admin.lua文件,将其保存至/usr/local/mysql-proxy/share/doc/mysql-proxy/目录中

--[[ $%BEGINLICENSE%$
 Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as
 published by the Free Software Foundation; version 2 of the
 License.
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 02110-1301  USA
 $%ENDLICENSE%$ --]]
function set_error(errmsg) 
proxy.response = {
type = proxy.MYSQLD_PACKET_ERR,
errmsg = errmsg or "error"
}
end
function read_query(packet)
if packet:byte() ~= proxy.COM_QUERY then
set_error("[admin] we only handle text-based queries (COM_QUERY)")
return proxy.PROXY_SEND_RESULT
end
local query = packet:sub(2)
local rows = { }
local fields = { }
if query:lower() == "select * from backends" then
fields = { 
{ name = "backend_ndx", 
  type = proxy.MYSQL_TYPE_LONG },
{ name = "address",
  type = proxy.MYSQL_TYPE_STRING },
{ name = "state",
  type = proxy.MYSQL_TYPE_STRING },
{ name = "type",
  type = proxy.MYSQL_TYPE_STRING },
{ name = "uuid",
  type = proxy.MYSQL_TYPE_STRING },
{ name = "connected_clients", 
  type = proxy.MYSQL_TYPE_LONG },
}


for i = 1, #proxy.global.backends do
local states = {
"unknown",
"up",
"down"
}
local types = {
"unknown",
"rw",
"ro"
}
local b = proxy.global.backends[i]
rows[#rows + 1] = {
i,
b.dst.name,          -- configured backend address
states[b.state + 1], -- the C-id is pushed down starting at 0
types[b.type + 1],   -- the C-id is pushed down starting at 0
b.uuid,              -- the MySQL Server's UUID if it is managed
b.connected_clients  -- currently connected clients
}
end
elseif query:lower() == "select * from help" then
fields = { 
{ name = "command", 
  type = proxy.MYSQL_TYPE_STRING },
{ name = "description", 
  type = proxy.MYSQL_TYPE_STRING },
}
rows[#rows + 1] = { "SELECT * FROM help", "shows this help" }
rows[#rows + 1] = { "SELECT * FROM backends", "lists the backends and their state" }
else
set_error("use 'SELECT * FROM help' to see the supported commands")
return proxy.PROXY_SEND_RESULT
end
proxy.response = {
type = proxy.MYSQLD_PACKET_OK,
resultset = {
fields = fields,
rows = rows
}
}
return proxy.PROXY_SEND_RESULT
end


测试管理功能

wKiom1OmdLXysK92AAEUQfi0vro835.jpg

测试读操作是否分担发往主从服务器

利用一简单脚本实现批量查询

#!/bin/bash
#
while :;do
        mysql -ukeyia -predhat -h1.1.1.1 -e "select * from mysql.user;"
done

管理方式登陆mysql-proxy,可以发现两个backend都已经up

wKioL1OmftSjHadIAAIHMAiwEwg562.jpg

通过tcpdump抓包可以观察写操作仅发给主服务器

tcpdump -i eth0  -p tcp port 3306 

你可能感兴趣的:(mysql,复制,读写)