mysql-proxy主從服務架構下讀寫分離和負載均衡實現及原理
來自: http://blog.csdn.net//clh604/article/details/8906022
系統環境 ubuntu
假設已經配置好mysql的主從架構
主服務器:192.168.3.189
從服務器:192.168.3.104
我們的目的就是實現讀取操作由192.168.3.104服務器響應,寫的操作由192.168.3.189響應
首先安裝mysql-proxy
1、從mysql官網上下載最新版的mysql-proxy:http://dev.mysql.com/downloads/mysql-proxy/
2、在本地的/opt/software/ (你可以換成自己的目錄)解壓下載的壓縮文件
解壓后能看到 bin、lib、include、libexc、share幾個文件夾
并把bin目錄添加到系統環境變量中去(在/etc/profile 文件最后添加:export PATH=$PATH:/opt/software/mysql-proxy-0.8.3-linux-glibc2.3-x86-32bit/bin)
然后source /etc/profile 讓剛才設置的環境變量生效
3、在命令行下輸入mysql-proxy --help 能看到幫助信息說明安裝成功了
4、啟動mysql-proxy服務
mysql-proxy --keepalive --proxy-read-only-backend-addresses=192.168.3.104:3306 --proxy-backend-addresses=192.168.3.189:3306 --proxy-lua-script=/opt/software/mysql-proxy-0.8.3-linux-glibc2.3-x86-32bit/share/doc/mysql-proxy/rw-splitting.lua --log-file=/opt/software/mysql-proxy-0.8.3-linux-glibc2.3-x86-32bit/var/mysql-proxy.log
--keepalive 網上解釋說有時候mysql-porxy會崩潰,這個參數能讓mysql-proxy崩潰后自動重啟
--proxy-read-only-backend-addresses 配置只讀服務器
--proxy-backend-addresses 配置可讀可寫服務器(主服務器master)
--proxy-lua-script 配置讀寫分離的lua腳本,這個腳本就在mysql-proxy解壓后的share目錄里面
--log-file 日志文件
這樣mysql-proxy就啟動了,如果想讓mysql-proxy在后臺運行只需要添加--daemon 參數就行了,連接mysql-proxy的命令是
mysql -h192.168.3.189 -P4040 -utest -p123456
那么mysql-proxy是如何進行讀寫分離的呢,關鍵就在讀寫分離的lua腳本rw-splitting.lua
為了解釋讀寫分離的原理,打開rw-splitting.lua腳本,該腳本中有幾個關鍵的地方,我們分別來看
1、配置
if not proxy.global.config.rwsplit then proxy.global.config.rwsplit = { min_idle_connections = 1,--設置每個服務器所擁有的最大連接池數量(我也不知道為啥命名為min),實際的最大連接數會比這個值大1,也就是在這個配置條件下,實際上每個服務器所能擁有的最大連接數是2 max_idle_connections = 1,--這個配置有啥做用還不知道 is_debug = true--開啟調試模式 } end
2、connect_server() 每次客戶端連接myql-proxy的時候都會運行一次這個函數,然后返回一個mysql連接給客戶端
首先明確一件是mysql-proxy所擁有最大的連接數是確定的,假設有n臺服務器,min_idle_connections=2,那么所擁有的最大連接數量就是:n*(2+1)
函數首先會判斷主服務器連接數是否達到最大值,如果沒有,就創建一個連接主服務器的連接,并返回;
如果主服務器連接數已滿,遍歷每個從服務器,看從服務器連接是否達到最大值,如果沒有,就創建一個,并返回;
如果所有服務器連接數都達到最大值,就返回主服務器創建的第一個連接
3、read_query() 每次客戶端做sql查詢的時候都會調用這個函數,這個函數把sql傳到某個服務器,得到結果后返回給客戶端
那么如何做讀寫分離呢?在函數內有這么一個判斷
if stmt.token_name == "TK_SQL_SELECT" then
這個語句的作用就是判斷sql語句是不是以SELECT開始的,也就是判斷是否是查詢,如果是查詢的話,接下來會有這么個語句
local backend_ndx = lb.idle_ro()lb.idle_ro() 是通過 local lb = require("proxy.balance") 引入的balance.lua文件
function idle_ro() local max_conns = -1 local max_conns_ndx = 0 for i = 1, #proxy.global.backends do local s = proxy.global.backends[i] local conns = s.pool.users[proxy.connection.client.username] -- pick a slave which has some idling connections if s.type == proxy.BACKEND_TYPE_RO and s.state ~= proxy.BACKEND_STATE_DOWN and conns.cur_idle_connections > 0 then if max_conns == -1 or s.connected_clients < max_conns then max_conns = s.connected_clients max_conns_ndx = i end end end return max_conns_ndx end
這個函數的作用就是選擇使用哪個讀服務器,并返回服務器的index:max_conns_ndx
如何選擇服務器呢? 它通過循環遍歷所有服務器,然后選出一個客戶端連接(s.connected_clients)最少的服務器,這樣在一定程度上實現負載均衡