實驗拓撲:
實驗要求:
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線程
驗證
node2 從服務器實現只讀
node2實現只讀的方法主要有:1、開啓全局變量read_only 2、申請全局鎖flash tables with read lock
測試:主服務器授權一個可以寫創建及刪除表的普通用戶,用普通用戶連接連接slave1並創建表
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線程即可
主服務器上驗證半同步客戶端的個數
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
測試
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
方法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表
觀察從服務器的複製情況及設置
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
測試管理功能
測試讀操作是否分擔發往主從服務器
利用一簡單腳本實現批量查詢
#!/bin/bash # while :;do mysql -ukeyia -predhat -h1.1.1.1 -e "select * from mysql.user;" done
管理方式登陸mysql-proxy,可以發現兩個backend都已經up
通過tcpdump抓包可以觀察寫操作僅發給主服務器
tcpdump -i eth0 -p tcp port 3306