Mysql作为目前世界上使用最广泛的免费数据库,相信所有从事系统运维的工程师都一定接触过。但在实际的生产环境中,由单台Mysql作为独立的数据库是完全不能满足实际需求的,无论是在安全性,高可用性以及高并发等各个方面。

因此,一般来说都是通过 主从复制(Master-Slave)的方式来同步数据,再通过读写分离(MySQL-Proxy)来提升数据库的并发负载能力 这样的方案来进行部署与实施的。


下面是我在实际工作过程中所整理的笔记,在此分享出来,以供大家参考。

一、MySQL的安装与配置


具体的安装过程,建议参考我的这一篇文章:http://heylinux.com/archives/993.html
值得一提的是,我的安装过程都是源码包编译安装的,并且所有的配置与数据等都统一规划到了/opt/mysql目录中,因此在一台服务器上安装完成以后,可以将整个mysql目录打包,然后传到其它服务器上解包,便可立即使用。


二、MySQL读写分离


场景描述:
数据库Master主服务器:192.168.10.130
数据库Slave从服务器:192.168.10.131
MySQL-Proxy调度服务器:192.168.10.132

以下操作,均是在192.168.10.132即MySQL-Proxy调度服务器 上进行的。

3.1 MySQL的安装与配置
具体的安装过程与上文相同。

3.2 检查系统所需软件包
通过 rpm -qa | grep name 的方式验证以下软件包是否已全部安装。
gcc* gcc-c++* autoconf* automake* zlib* libxml* ncurses-devel* libmcrypt* libtool* flex* pkgconfig*
libevent* glib*

若缺少相关的软件包,可通过yum -y install方式在线安装,或直接从系统安装光盘中找到并通过rpm -ivh方式安装。

3.3 编译安装lua
MySQL-Proxy的读写分离主要是通过rw-splitting.lua脚本实现的,因此需要安装lua。

lua可通过以下方式获得
从http://www.lua.org/download.html下载源码包

从rpm.pbone.net搜索相关的rpm包
download.fedora.redhat.com/pub/fedora/epel/5/i386/lua-5.1.4-4.el5.i386.rpm
download.fedora.redhat.com/pub/fedora/epel/5/x86_64/lua-5.1.4-4.el5.x86_64.rpm

这里我们建议采用源码包进行安装
cd /opt/install
wget http://www.lua.org/ftp/lua-5.1.4.tar.gz
tar zvfx lua-5.1.4.tar.gz
cd lua-5.1.4

vi src/Makefile
在 CFLAGS= -O2 -Wall $(MYCFLAGS) 这一行记录里加上-fPIC,更改为 CFLAGS= -O2 -Wall -fPIC $(MYCFLAGS) 来避免编译过程中出现错误。

make linux
make install

cp etc/lua.pc /usr/lib/pkgconfig/
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/lib/pkgconfig

3.4 安装配置MySQL-Proxy
MySQL-Proxy可通过以下网址获得:
http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/

推荐采用已经编译好的二进制版本,因为采用源码包进行编译时,最新版的MySQL-Proxy对automake,glib以及libevent的版本都有很高的要求,而这些软件包都是系统的基础套件,不建议强行进行更新。
并且这些已经编译好的二进制版本在解压后都在统一的目录内,因此建议选择以下版本:
32位RHEL5平台:
http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/mysql-proxy-0.8.1-linux-rhel5-x86-32bit.tar.gz
64位RHEL5平台:
http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/mysql-proxy-0.8.1-linux-rhel5-x86-64bit.tar.gz

测试平台为RHEL5 32位,因此选择32位的软件包
wget http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/mysql-proxy-0.8.1-linux-rhel5-x86-32bit.tar.gz

tar xzvf mysql-proxy-0.8.1-linux-rhel5-x86-32bit.tar.gz
mv mysql-proxy-0.8.1-linux-rhel5-x86-32bit /opt/mysql-proxy

创建mysql-proxy服务管理脚本
mkdir /opt/mysql-proxy/init.d/

vim mysql-proxy

01    #!/bin/sh    

02    #    

03    # mysql-proxy This script starts and stops the mysql-proxy daemon    

04    #    

05    # chkconfig: - 78 30    

06    # processname: mysql-proxy    

07    # description: mysql-proxy is a proxy daemon to mysql    

08    

09    # Source function library.    

10    . /etc/rc.d/init.d/functions    

11    

12    #PROXY_PATH=/usr/local/bin    

13    PROXY_PATH=/opt/mysql-proxy/bin    

14    

15    prog="mysql-proxy"    

16    

17    # Source networking configuration.    

18    . /etc/sysconfig/network    

19    

20    # Check that networking is up.    

21    [ ${NETWORKING} = "no" ] && exit 0    

22    

23    # Set default mysql-proxy configuration.    

24    #PROXY_OPTIONS="--daemon"    

25    PROXY_OPTIONS="--admin-username=root --admin-password=password --proxy-read-only-backend-addresses=192.168.10.131:3306 --proxy-backend-addresses=192.168.10.130:3306  --admin-lua-script=/opt/mysql-proxy/lib/mysql-proxy/lua/admin.lua --proxy-lua-script=/opt/mysql-proxy/scripts/rw-splitting.lua"    

26    PROXY_PID=/opt/mysql-proxy/run/mysql-proxy.pid    

27    

28    # Source mysql-proxy configuration.    

29    if [ -f /etc/sysconfig/mysql-proxy ]; then    

30            . /etc/sysconfig/mysql-proxy    

31    fi    

32    

33    PATH=$PATH:/usr/bin:/usr/local/bin:$PROXY_PATH    

34    

35    # By default it's all good    

36    RETVAL=0    

37    

38    # See how we were called.    

39    case "$1" in    

40      start)    

41            # Start daemon.    

42            echo -n $"Starting $prog: "    

43            $NICELEVEL $PROXY_PATH/mysql-proxy $PROXY_OPTIONS --daemon --pid-file=$PROXY_PID --user=mysql --log-level=warning --log-file=/opt/mysql-proxy/log/mysql-proxy.log    

44            RETVAL=$?    

45            echo    

46            if [ $RETVAL = 0 ]; then    

47                    touch /var/lock/subsys/mysql-proxy    

48            fi    

49           ;;    

50      stop)    

51            # Stop daemons.    

52            echo -n $"Stopping $prog: "    

53            killproc $prog    

54            RETVAL=$?    

55            echo    

56            if [ $RETVAL = 0 ]; then    

57                    rm -f /var/lock/subsys/mysql-proxy    

58                    rm -f $PROXY_PID    

59            fi    

60           ;;    

61      restart)    

62            $0 stop    

63            sleep 3    

64            $0 start    

65           ;;    

66      condrestart)    

67           [ -e /var/lock/subsys/mysql-proxy ] && $0 restart    

68          ;;    

69      status)    

70            status mysql-proxy    

71            RETVAL=$?    

72           ;;    

73      *)    

74            echo "Usage: $0 {start|stop|restart|status|condrestart}"    

75            RETVAL=1    

76           ;;    

77    esac    

78    

79    exit $RETVAL    

脚本参数详解:
==============================================
PROXY_PATH=/opt/mysql-proxy/bin //定义mysql-proxy服务二进制文件路径

PROXY_OPTIONS="--admin-username=root \ //定义内部管理服务器账号
--admin-password=password \ //定义内部管理服务器密码
--proxy-read-only-backend-addresses=192.168.10.131:3306 \ //定义后端只读从服务器地址
--proxy-backend-addresses=192.168.10.130:3306 \ //定义后端主服务器地址
--admin-lua-script=/opt/mysql-proxy/lib/mysql-proxy/lua/admin.lua \ //定义lua管理脚本路径
--proxy-lua-script=/opt/mysql-proxy/scripts/rw-splitting.lua" \ //定义lua读写分离脚本路径

PROXY_PID=/opt/mysql-proxy/run/mysql-proxy.pid //定义mysql-proxy PID文件路径

$NICELEVEL $PROXY_PATH/mysql-proxy $PROXY_OPTIONS \
--daemon \ //定义以守护进程模式启动
--keepalive \ //使进程在异常关闭后能够自动恢复
--pid-file=$PROXY_PID \ //定义mysql-proxy PID文件路径
--user=mysql \ //以mysql用户身份启动服务
--log-level=warning \ //定义log日志级别,由高到低分别有(error|warning|info|message|debug)
--log-file=/opt/mysql-proxy/log/mysql-proxy.log //定义log日志文件路径
==============================================

cp mysql-proxy /opt/mysql-proxy/init.d/
chmod +x /opt/mysql-proxy/init.d/mysql-proxy

mkdir /opt/mysql-proxy/run
mkdir /opt/mysql-proxy/log

mkdir /opt/mysql-proxy/scripts

配置并使用rw-splitting.lua读写分离脚本
最新的脚本我们可以从最新的mysql-proxy源码包中获取
cd /opt/install

wget http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/mysql-proxy-0.8.1.tar.gz

tar xzvf mysql-proxy-0.8.1.tar.gz
cd mysql-proxy-0.8.1
cp lib/rw-splitting.lua /opt/mysql-proxy/scripts

修改读写分离脚本rw-splitting.lua
修改默认连接,进行快速测试,不修改的话要达到连接数为4时才启用读写分离
vim /opt/mysql-proxy/scripts/rw-splitting.lua
=============================
-- connection pool
if not proxy.global.config.rwsplit then
proxy.global.config.rwsplit = {
min_idle_connections = 1, //默认为4
max_idle_connections = 1, //默认为8
is_debug = false
}
end
=============================

修改完成后,启动mysql-proxy
/opt/mysql-proxy/init.d/mysql-proxy start

3.5 测试读写分离效果
创建用于读写分离的数据库连接用户
登陆主数据库服务器192.168.10.130,通过命令行登录管理MySQL服务器
/opt/mysql/bin/mysql -uroot -p'new-password'
mysql> GRANT ALL ON *.* TO 'proxy1'@'192.168.10.132' IDENTIFIED BY 'password';
由于我们配置了主从复制功能,因此从数据库服务器192.168.10.131上已经同步了此操作。

为了清晰的看到读写分离的效果,需要暂时关闭MySQL主从复制功能
登陆从数据库服务器192.168.10.131,通过命令行登录管理MySQL服务器
/opt/mysql/bin/mysql -uroot -p'new-password'

关闭Slave同步进程
mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)

连接MySQL-Proxy
/opt/mysql/bin/mysql -uproxy1 -p'password' -P4040 -h192.168.10.132

登陆成功后,在first_db数据的first_tb表中插入两条记录
mysql> use first_db;
Database changed
mysql> insert into first_tb values (007,’first’);
Query Ok, 1 row affected (0.00 sec)
mysql> insert into first_tb values (110,’second’);
Query Ok, 1 row affected (0.00 sec)

查询记录
mysql> select * from first_tb;
=============================
+------+------+
| id | name |
+------+------+
| 1 | myself |
+------+------+
1 rows in set (0.00 sec)
=============================
通过读操作并没有看到新记录

mysql> quit
退出MySQL-Proxy

下面,分别登陆到主从数据库服务器,对比记录信息
首先,检查主数据库服务器
mysql> select * from first_tb;
=============================
+------+------+
| id | name |
+------+------+
| 1 | myself |
+------+------+
| 007 | first |
+------+------+
| 110 | second |
+------+------+
3 rows in set (0.00 sec)
=============================
两条新记录都已经存在

然后,检查从数据库服务器
mysql> select * from first_tb;
=============================
+------+------+
| id | name |
+------+------+
| 1 | myself |
+------+------+
1 rows in set (0.00 sec)
=============================
没有新记录存在

由此验证,我们已经实现了MySQL读写分离,目前所有的写操作都全部在Master主服务器上,用来避免数据的不同步;
另外,所有的读操作都分摊给了其它各个Slave从服务器上,用来分担数据库压力。

经验分享:
1.当MySQL主从复制在 show slave status\G 时出现Slave_IO_Running或Slave_SQL_Running 的值不为YES时,需要首先通过 stop slave 来停止从服务器,然后再执行一次本文 2.1与2.2 章节中的步骤即可恢复,但如果想尽可能的同步更多的数据,可以在Slave上将master_log_pos节点的值在之前同步失效的值的基础上增大一些,然后反复测试,直到同步OK。因为MySQL主从复制的原理其实就是从服务器读取主服务器的binlog,然后根据binlog的记录来更新数据库。

2.MySQL-Proxy的rw-splitting.lua脚本在网上有很多版本,但是最准确无误的版本仍然是源码包中所附带的lib/rw-splitting.lua脚本,如果有lua脚本编程基础的话,可以在这个脚本的基础上再进行优化;

3.MySQL-Proxy实际上非常不稳定,在高并发或有错误连接的情况下,进程很容易自动关闭,因此打开--keepalive参数让进程自动恢复是个比较好的办法,但还是不能从根本上解决问题,因此通常最稳妥的做法是在每个从服务器上安装一个MySQL-Proxy供自身使用,虽然比较低效但却能保证稳定性;

4.一主多从的架构并不是最好的架构,通常比较优的做法是通过程序代码和中间件等方面,来规划,比如设置对表数据的自增id值差异增长等方式来实现两个或多个主服务器,但一定要注意保证好这些主服务器数据的完整性,否则效果会比多个一主多从的架构还要差;

5.MySQL-Cluster 的稳定性也不是太好;

6.Amoeba for MySQL 是一款优秀的中间件软件,同样可以实现读写分离,负载均衡等功能,并且稳定性要大大超过MySQL-Proxy,建议大家用来替代MySQL-Proxy,甚至MySQL-Cluster。

 

详解Mysql Proxy Lua读写分离设置

 

原址如下:

http://mobile.51cto.com/iphone-287937.htm

Mysql Proxy Lua读写分离设置是本文要介绍的内容,主要是来了解Mysql Proxy的Mysql 分离设置,为了未来MySQL读写分离的需要, 先行对MySQL官方的Mysql Proxy产品进行了初步测试. 以下是测试过程,二进制版Mysql Proxy可以去下载。

1、设置说明

Master服务器: 192.168.41.196  Slave服务器: 192.168.41.197  Proxy服务器: 192.168.41.203

2、安装Mysql Proxy

在Proxy服务器上安装即可. 如果源码方式安装, 需提前安装pkg-config,libevent,glibc,lua等依赖包, 非常麻烦, 建议直接使用二进制版.

# cd /u01/software/mysql  # tar -zxvf Mysql Proxy-0.8.1-linux-rhel5-x86-32bit.tar.gz -C /usr/local  # cd /usr/local  # ln -s Mysql Proxy-0.8.1-linux-rhel5-x86-32bit Mysql Proxy  # vi + ~/.bash_profile  export PATH=$PATH:/usr/local/Mysql Proxy/bin/  # . ~/.bash_profile

3、Mysql Proxy选项说明

# Mysql Proxy help-all

管理功能选项:

admin-address=host:port 指定一个mysqo-proxy的管理端口, 缺省是4041;  admin-username=<string> username to allow to log in  admin-password=<string> password to allow to log in  admin-lua-script=<filename> script to execute by the admin plugin

代理功能选项:

-P, proxy-address=<host:port> 是Mysql Proxy 服务器端的监听端口, 缺省是4040;  -r, proxy-read-only-backend-addresses=<host:port> 只读Slave的地址和端口, 缺省为不设置;  -b, proxy-backend-addresses=<host:port> 远程Master地址和端口, 可设置多个做failover和load balance, 缺省是127.0.0.1:3306;  proxy-skip-profiling 关闭查询分析功能, 缺省是打开的;  proxy-fix-bug-25371 修正 mysql的libmysql版本大于5.1.12的一个#25371号bug;  -s, proxy-lua-script=<file> 指定一个Lua脚本来控制Mysql Proxy的运行和设置, 这个脚本在每次新建连接和脚本发生修改的的时候将重新调用;

其他选项:

defaults-file=<file>配置文件, 可以把Mysql Proxy的参数信息置入一个配置文件里;  daemon Mysql Proxy以守护进程方式运行  pid-file=file 设置Mysql Proxy的存储PID文件的路径  keepalive try to restart the proxy if it crashed, 保持连接启动进程会有2个, 一号进程用来监视二号进程, 如果二号进程死掉自动重启proxy.

4、数据库准备工作

(1)安装半同步补丁(建议)

读写分离不能回避的问题之一就是延迟, 可以考虑Google提供的SemiSyncReplication补丁.

(2)给用户授权

在Master/Slave建立一个测试用户, 因为以后客户端发送的SQL都是通过Mysql Proxy服务器来转发, 所以要确保可以从Mysql Proxy服务器上登录MySQL主从库.

mysql> grant all privileges on *.* to 'u_test'@'192.168.41.203' identified by 'xxx' with grant option;

(3)在Master建立测试表

mysql> create table db_test.t_test (col varchar(10));  mysql> insert into db_test.t_test values ('testA');  mysql> select * from db_test.t_test;  +-+  | col   |  +-+  | testA |  +-+

5、Mysql Proxy启动

(1)修改读写分离lua脚本

默认最小4个最大8个以上的客户端连接才会实现读写分离, 现改为最小1个最大2个:

# vi +40 /usr/local/Mysql Proxy/share/doc/Mysql Proxy/rw-splitting.lua   connection pool  if not proxy.global.config.rwsplit then          proxy.global.config.rwsplit = {                  min_idle_connections = 1,                  max_idle_connections = 2,                  is_debug = true           }         end

这是因为Mysql Proxy会检测客户端连接, 当连接没有超过min_idle_connections预设值时, 不会进行读写分离, 即查询操作会发生到Master上.

(2)启动Mysql Proxy

建议使用配置文件的形式启动, 注意配置文件必须是660权限, 否则无法启动. 如果有多个Slave的话, proxy-read-only-backend-addresses参数可以配置多个以逗号分隔的IP:Port从库列表.

# killall Mysql Proxy   # vi /etc/Mysql Proxy.cnf  [Mysql Proxy]  admin-username=wangnc admin-password=iamwangnc admin-lua-script=/usr/local/Mysql Proxy/lib/Mysql Proxy/lua/admin.lua  proxy-backend-addresses=192.168.41.196:3351  proxy-read-only-backend-addresses=192.168.41.197:3351  proxy-lua-script=/usr/local/Mysql Proxy/share/doc/Mysql Proxy/rw-splitting.lua  log-file=/var/tmp/Mysql Proxy.log  log-level=debug daemon=true keepalive=true # chmod 660 /etc/Mysql Proxy.cnf  # Mysql Proxy defaults-file=/etc/Mysql Proxy.cnf  # ps -ef | grep Mysql Proxy | grep -v grep  root      1869     1  0 18:16 ?        00:00:00 /usr/local/Mysql Proxy/libexec/Mysql Proxy defaults-file=/etc/Mysql Proxy.cnf  root      1870  1869  0 18:16 ?        00:00:00 /usr/local/Mysql Proxy/libexec/Mysql Proxy defaults-file=/etc/Mysql Proxy.cnf  # tail -50f /var/tmp/Mysql Proxy.log

6、客户端连接测试

(1)先停止Slave的复制进程

mysql> stop slave;

(2)连接Proxy端口, 插入数据

# mysql -uu_test -pxxx -h192.168.41.203 -P4040 -Ddb_test  mysql> insert into db_test.t_test values ('testB');  mysql> select * from db_test.t_test;  +-+  | col   |  +-+  | testA |  | testB |  +-+

(3)多开几个客户端, 连接Proxy端口, 查询数据

# mysql -uu_test -pxxx -h192.168.41.203 -P4040 -Ddb_test  mysql> select * from db_test.t_test;  +-+  | col   |  +-+  | testA |  +-+

如果查询不到上步新插入的数据, 说明连接到了Slave, 读写分离成功. 在同一线程再插入数据并验证:

mysql> insert into db_test.t_test values ('testC');  mysql> select * from db_test.t_test;  +-+  | col   |  +-+  | testA |  +-+

发现insert操作成功, 但是select不出刚插入的数据, 说明同一线程也读写分离成功. 从日志中可以验证:

# tail -50f /var/tmp/Mysql Proxy.log  ...  [read_query] 192.168.41.203:45481    current backend   = 0   client default db = db_test   client username   = u_test   query             = select * from db_test.t_test    sending to backend : 192.168.41.197:3351      is_slave         : true      server default db: db_test      server username  : u_test      in_trans        : false      in_calc_found   : false      COM_QUERY       : true  [read_query] 192.168.41.203:45481    current backend   = 0   client default db = db_test   client username   = u_test   query             = insert into db_test.t_test values ('testC')    sending to backend : 192.168.41.196:3351      is_slave         : false      server default db: db_test      server username  : u_test      in_trans        : false      in_calc_found   : false      COM_QUERY       : true

(4)测试完毕后, 启动Slave的复制进程

mysql> start slave;

7、正式环境说明

1、Mysql Proxy当前还只是个测试版, MySQL官方还不建议用到生产环境中;

2、Mysql Proxy的rw-splitting.lua脚本在网上有很多版本, 但是最准确无误的版本仍然是源码包中所附带的rw-splitting.lua脚本, 如果有lua脚本编程基础的话, 可以在这个脚本的基础上再进行优化;

3、Mysql Proxy实际上非常不稳定, 在高并发或有错误连接的情况下, 进程很容易自动关闭, 因此打开keepalive参数让进程自动恢复是个比较好的办法, 但还是不能从根本上解决问题, 因此通常最稳妥的做法是在每个从服务器上安装一个Mysql Proxy供自身使用, 虽然比较低效但却能保证稳定性;

4、Amoeba for MySQL是一款优秀的中间件软件, 同样可以实现读写分离, 负载均衡等功能, 并且稳定性要大大超过Mysql Proxy, 建议大家用来替代Mysql Proxy, 甚至MySQL-Cluster.