mysql-poxy 实现mysql主从架构读写分离

        在高并发系统设计中,后端数据库的性能往往会成为系统的瓶颈,这时候就需要进行合理的设计,以分摊后端数据库的压力,比如在数据层前面构建缓存层、数据文件存放在RAID这样的设备、对数据进行分库分表分区存放、合理利用索引、进行数据的读写分离等。mysql-proxy提供了mysql数据库的读写分离能力,mysql-proxy通过Lua脚本能分析得出用户的sql请求,如果发现在是read请求,则会转化到master-slave模型的slave中,如果是write请求,则会转发到master中,以达到读写分离的目的,在一定程度上能大大增加系统的性能。下面来一步步通过mysql-proxy搭建mysql的读写分离。


一、环境准备

    os:centos6.4

    mysql-slave:version:5.6.13    ip:192.168.1.111

    mysql-master:version:5.6.13    ip:192.168.1.110

    mysql-proxy:version:0.85    ip:192.168.1.112


二、环境搭建

    1、mysql主从复制搭建

        mysql安装具体参考:http://crazytechnology.blog.51cto.com/6906973/1640197

        tips:mysql从5.6起,主从复制中采用了GTID,GTID为每个数据库事务编码一个number,并为

        每个server赋予一个global uuid,通过uuid和事务编号来进行数据的复制和恢复,这种方案更有利

        于在高可用的环境中数据的恢复。在5.6后,mysql主从架构中还支持多线程复制,即可以启动多个

        sql thread对relay log进行处理,默认的线程数建议为要同步的数据库的个数。


        由于是搭建5.6.13的mysql主从架构,因上安装好mysql后。在/etc/my.cnf中主要配置如下:

        master:

        [mysqld]

        port = 3306

        datadir = /data/mysql

        server_id = 1

        socket = /tmp/mysql.sock

        innodb_file_per_table = 1

        log-bin = master-bin

        binlog-format=ROW

        log-slave-updates=true

        gtid-mode=on

        enforce-gtid-consistency=true

        master-info-repository=TABLE

        relay-log-info-repository=TABLE

        sync-master-info=1

        slave-parallel-workers=2

        binlog-checksum=CRC32

        master-verify-checksum=1

        slave-sql-verify-checksum=1

        binlog-rows-query-log_events=1

        report-port=3306

        report-host=master.lee.com

            

            vim /etc/hosts添加一条条目

            192.168.1.110 master.lee.com master


        slave:

        [mysqld]

        datadir = /data/mysql    #存放数据目录

        port = 3306    #启动端口

        server_id = 100    #server_id

        socket = /tmp/mysql.sock

        innodb_file_per_table = 1    #每表一个表空间

        binlog-format=ROW    

        log-slave-updates=true

        gtid-mode=on     #启动gtid

        enforce-gtid-consistency=true

        master-info-repository=TABLE    #master记录bin-log的位置(myql.slave_master_info)

        relay-log-info-repository=TABLE    #slave记录relay-log的地方,默认为file

        sync-master-info=1    #启用后可确保无信息丢失

        slave-parallel-workers=2    #启动的sql thread线程数

        binlog-checksum=CRC32    #二进制校验算法crc32

        master-verify-checksum=1    #启用复制有关的校验功能

        slave-sql-verify-checksum=1    #启用复制有关的校验功能

        binlog-rows-query-log_events=1    #记录相关事件信息,降低排错复杂度

        report-port=3306    #通知给master的端口号

        log-bin=mysql-bin.log    #开启二进制日志功能,可以拥有自己的从服务器

        report-host=slave.lee.com    #通知给master的主机名

        

        在master中创建主从复制用户

        mysql> grant replication slave on *.* to ‘repluser‘@‘192.168.1.%‘ identified by ‘replpass‘;

        在slave中连接master:

        mysql> change master to master_host=‘192.168.1.110‘,master_user=‘repluser‘,master_password

        =‘replpass‘,master_auto_position=1;

        启动myql slave

        mysql> start slave

        

    2、安装mysql-proxy

        tar xf mysql-proxy-0.8.5-linux-glibc2.3-x86-64bit.tar.gz -C /usr/local

        cd /usr/local

        ln -sv mysql-proxy-0.8.5-linux-glibc2.3-x86-64bit/ mysql-proxy

        创建mysql-proxy用户

        useradd -r -s /sbin/nologin mysql-proxy

   


     

      为mysql-proxy提供SysV脚本,将下面代码复制到/etc/init.d/mysql-proxy

        

    #!/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


     chmod +x /etc/init.d/mysql-proxy

        chkconfig --add mysql-proxy

        chkconfig mysql-proxy on



        为mysql-prox提供服务脚本

        vim /etc/sysconfig/mysql-proxy

        

       DMIN_USER="admin"    #管理员用户名

        ADMIN_PASSWORD="admin"    #管理员用户密码

        ADMIN_ADDRESS="192.168.1.112:4041"    #管理mysql-proxy监听的套接字

           #admin lua脚本

        ADMIN_LUA_SCRIPT="/usr/local/mysql-proxy/share/doc/mysql-proxy/admin.lua"

        PROXY_ADDRESS="192.168.1.112:3306"    #mysql-proxy监听的套接字

        PROXY_USER="mysql-proxy"    #启动mysql-proxy服务的用户

          #mysql-proxy启动的选项

        PROXY_OPTIONS="--daemon --log-level=debug --log-use-syslog --plugins=proxy --plugins=admin --proxy-backend-addresses=192.168.1.110:3306 --proxy-read-only-backend-addresses=192.168.1.111:3306 --proxy-lua-script=/usr/local/mysql-proxy/share/doc/mysql-proxy/rw-splitting.lua


        

        #--daemon:以守护进程方式启动

        #--log-level=debug,日志级别,此处为了调试而用

        #--log-use-syslog : 通过系统的syslog记录日志

        #--plugins:插件

        #--proxy-backend-addresses:后台读写数据库服务器(rw),可添加多个

        #--proxy-read-only-backend-addresses:后台只读数据库服务器(ro),可添加多个

        #--proxy-lua-script:mysql读写分离lua脚本



        复制下面代码,到/usr/local/mysql-proxy/share/doc/mysql-proxy/admin.lua文件中

        

    --[[ $%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

    
     启动mysql-proxy

        service mysql-proxy start

        查看是否启动成功

        netstat -tlnp可查看到mysql-proxy监听在3306和4041(管理)端口上。



    3、读写分离测试

        在master中创建一个测试管理用户mproxyadmin

        mysql> grant all on *.* to ‘mproxyadmin‘@‘192.168.1.112‘ identified by ‘mproxypass‘;

        mysql> flush priviledges;

        选择从slave中登录mysql-proxy

        mysql -umproxyadmin -h192.168.1.112 -p;

        登录进去

        创建一个aa数据库

        mysql> create database aa;

        可以看到主从数据库都有已经创建了aa数据库。

        

        在slave中登录mysql-proxy所的admin管理后台

        mysql -uadmin -padmin -h192.168.1.112 --port=4041

        管理后台只有一条sql语句有用,查看backends的状态信息:

        mysql> select * from backends;

        技术分享


        可看到此时的backends有两个,但状态为unknown.,且master为读写模式,slave为主从模式

        当在主中进行相关非查询操作时,master的state会变成up。当进行查询时,slave的状态也会变

        成up,当二者都激活后,mysql-proxy就能进行读写分离了。

        技术分享


        由于mysql-proxy启动时日志级别设计为debug,所以在mysql-proxy所在机子可以轻易地看到日志

        信息,首先在slave上连接上mysq_proxy,在刚才创建的aa数据库中创建一张test表,

        技术分享

        这时在mysql-proxy中看到的日志是:

        技术分享

        由is_slave为false可知道,这个创表语句是在master中执行的。

        下面我们为这个表插入一条数据,然后再select出来

        mysql> insert into test values(1,"test");

        mysql> select * from test;

        技术分享

    这时发现select语句,is_slave为true,也就是查询是在slave中执行的。

    以后每执行查询操作都会在slave中执行,其它的操作都会在master中执行,这就实现了mysql的读写

    分离。

    

        

    

        


        



       

 


            




            

    

        

             

    

本文出自 “向天飞” 博客,请务必保留此出处http://crazytechnology.blog.51cto.com/6906973/1641476

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。