`

php+MySQL Proxy快速实现读写分离以及负载均衡,故障转移

阅读更多

简单的说,MySQL Proxy就是一个连接池,负责将前台应用的连接请求转发给后台的数据库,并且通过使用lua脚本 ,可以实现复杂的连接控制和过滤,从而实现读写分离和负载平衡。对于应用来说,MySQL Proxy是完全透明的,应用则只需要连接到MySQL Proxy的监听端口即可。当然,这样proxy机器可能成为单点失效,但完全可以使用多个proxy机器做为冗余,在应用服务器的连接池配置中配置到多个proxy的连接参数即可。


典型配置案例:
1、最基础的应用,代理单个数据库服务器,这样就可以通过4040端口访问mysql数据库。
mysql-proxy –proxy-backend-addresses=192.168.18.110:3306
2、连接多个数据库服务器,假如A and B. 第一个客户端连接到 A,第二个连接到 B。
如果有一台停止服务时,代理会自动检测到,有新连接时会自动连接到一台正常的数据库。
mysql-proxy \
–proxy-backend-addresses=narcissus:3306 \
–proxy-backend-addresses=nostromo:3306
3、数据库读写分离,192.168.18.110负责写入,192.168.18.107负责读取数据,当然也可以再增加读取数据的服务器。
mysql-proxy \
–proxy-backend-addresses=192.168.18.110:3306 \
–proxy-read-only-backend-addresses=192.168.18.107:3306
这种方式常被理解为可以读写分离了,但是实际不是的。mysql-proxy不能区分哪些是发往从服务器的,还需要自己用脚本控制,见第四种方式。
4、 Lua 脚本能很好的控制连接和分布, 以及查询及返回的结果集.
使用Lua脚本时,必须使用 –proxy-lua-script 指定脚本的名称。
直到产生连接的时候才会读取脚本,也就是修改脚本后不用重新启动服务。
mysql-proxy –proxy-lua-script=rw-splitting.lua –proxy-backend-addresses=192.168.18.110:3306 –proxy-read-only-backend-addresses=192.168.18.107:3306
注意的问题:
1、proxy的读写分离机制是先把最初的几条查询发到master上建立连接,
当发送到master上的查询数超过连接池的最小值时开始把查询
2、LAST_INSERT_ID不能发送到主服务器上, 226 行修改为下面的就可以了
elseif not is_insert_id and token.token_name == “TK_FUNCTION” then
3、使用默认的rw-splitting.lua时,会提示找不到proxy-command,我把mysql-proxy的路径设置为系统路径,然后在 share目录下运行就一切Ok了,在运行中输入cmd,然后cd C:\tools\mysql-proxy\share。
4、字符乱码
通过proxy连上数据库之后,查到的字符串始终是乱码,即便手工执行了set names ‘utf8′也没有效果。
解决办法,mysql server必须设置
1.[mysqld]
2.skip-character-set-client-handshake
3. init-connect=’SET NAMES utf8′
4. default-character-set=utf8

Java代码 复制代码 收藏代码
  1. public function isReadOperation($sql) {   
  2.     return preg_match('/^\s*(SELECT|SHOW|DESC|PRAGMA)\s+/i',$sql);   
  3. }  
public function isReadOperation($sql) {
	return preg_match('/^\s*(SELECT|SHOW|DESC|PRAGMA)\s+/i',$sql);
}

php读写分离类

Php代码 复制代码 收藏代码
  1. <?php   
  2. /****************************************  
  3. *** mysql-rw-php version 0.1 @ 2009-4-16  
  4. *** code by hqlulu#gmail.com  
  5. *** http://www.aslibra.com  
  6. *** http://code.google.com/p/mysql-rw-php/  
  7. *** code modify from class_mysql.php (uchome)  
  8. ****************************************/  
  9.   
  10. class mysql_rw_php {   
  11.   
  12.     //查询个数   
  13.     var $querynum = 0;   
  14.     //当前操作的数据库连接   
  15.     var $link = null;   
  16.     //字符集   
  17.     var $charset;   
  18.     //当前数据库   
  19.     var $cur_db = '';   
  20.   
  21.     //是否存在有效的只读数据库连接   
  22.     var $ro_exist = false;   
  23.     //只读数据库连接   
  24.     var $link_ro = null;   
  25.     //读写数据库连接   
  26.     var $link_rw = null;   
  27.   
  28.     function mysql_rw_php(){   
  29.     }   
  30.   
  31.     function connect($dbhost$dbuser$dbpw$dbname = ''$pconnect = 0, $halt = TRUE) {   
  32.         if($pconnect) {   
  33.             if(!$this->link = @mysql_pconnect($dbhost$dbuser$dbpw)) {   
  34.                 $halt && $this->halt('Can not connect to MySQL server');   
  35.             }   
  36.         } else {   
  37.             if(!$this->link = @mysql_connect($dbhost$dbuser$dbpw)) {   
  38.                 $halt && $this->halt('Can not connect to MySQL server');   
  39.             }   
  40.         }   
  41.            
  42.         //只读连接失败   
  43.         if(!$this->link && !$haltreturn false;   
  44.            
  45.         //未初始化rw时,第一个连接作为rw   
  46.         if($this->link_rw == null)   
  47.             $this->link_rw = $this->link;   
  48.   
  49.         if($this->version() > '4.1') {   
  50.             if($this->charset) {   
  51.                 @mysql_query("SET character_set_connection=$this->charset, character_set_results=$this->charset, character_set_client=binary"$this->link);   
  52.             }   
  53.             if($this->version() > '5.0.1') {   
  54.                 @mysql_query("SET sql_mode=''"$this->link);   
  55.             }   
  56.         }   
  57.         if($dbname) {   
  58.             $this->select_db($dbname);   
  59.         }   
  60.     }   
  61.   
  62.     //连接一个只读的mysql数据库   
  63.     function connect_ro($dbhost$dbuser$dbpw$dbname = ''$pconnect = 0){   
  64.         if($this->link_rw == null)   
  65.             $this->link_rw = $this->link;   
  66.         $this->link = null;   
  67.         //不产生halt错误   
  68.         $this->connect($dbhost$dbuser$dbpw$dbname$pconnect, false);   
  69.         if($this->link){   
  70.             //连接成功   
  71.             //echo "link ro sussess!<br>";   
  72.             $this->ro_exist = true;   
  73.             $this->link_ro = $this->link;   
  74.             if($this->cur_db){   
  75.                 //如果已经选择过数据库则需要操作一次   
  76.                 @mysql_select_db($this->cur_db, $this->link_ro);   
  77.             }   
  78.         }else{   
  79.             //连接失败   
  80.             //echo "link ro failed!<br>";   
  81.             $this->link = &$this->link_rw;   
  82.         }   
  83.     }   
  84.   
  85.     //设置一系列只读数据库并且连接其中一个   
  86.     function set_ro_list($ro_list){   
  87.         if(is_array($ro_list)){   
  88.             //随机选择其中一个   
  89.             $link_ro = $ro_list[array_rand($ro_list)];   
  90.             $this->connect_ro($link_ro['dbhost'], $link_ro['dbuser'], $link_ro['dbpw']);   
  91.         }   
  92.     }   
  93.   
  94.     function select_db($dbname) {   
  95.         //同时操作两个数据库连接   
  96.         $this->cur_db = $dbname;   
  97.         if($this->ro_exist){   
  98.             @mysql_select_db($dbname$this->link_ro);   
  99.         }   
  100.         return @mysql_select_db($dbname$this->link_rw);   
  101.     }   
  102.   
  103.     function fetch_array($query$result_type = MYSQL_ASSOC) {   
  104.         return mysql_fetch_array($query$result_type);   
  105.     }   
  106.   
  107.     function fetch_one_array($sql$type = '') {   
  108.         $qr = $this->query($sql$type);   
  109.         return $this->fetch_array($qr);   
  110.     }   
  111.   
  112.     function query($sql$type = '') {   
  113.         $this->link = &$this->link_rw;   
  114.         //判断是否select语句   
  115.         if($this->ro_exist && preg_match ("/^(\s*)select/i"$sql)){   
  116.             $this->link = &$this->link_ro;   
  117.         }   
  118.         $func = $type == 'UNBUFFERED' && @function_exists('mysql_unbuffered_query') ?   
  119.             'mysql_unbuffered_query' : 'mysql_query';   
  120.         if(!($query = $func($sql$this->link)) && $type != 'SILENT') {   
  121.             $this->halt('MySQL Query Error'$sql);   
  122.         }   
  123.         $this->querynum++;   
  124.         return $query;   
  125.     }   
  126.   
  127.     function affected_rows() {   
  128.         return mysql_affected_rows($this->link);   
  129.     }   
  130.   
  131.     function error() {   
  132.         return (($this->link) ? mysql_error($this->link) : mysql_error());   
  133.     }   
  134.   
  135.     function errno() {   
  136.         return intval(($this->link) ? mysql_errno($this->link) : mysql_errno());   
  137.     }   
  138.   
  139.     function result($query$row) {   
  140.         $query = @mysql_result($query$row);   
  141.         return $query;   
  142.     }   
  143.   
  144.     function num_rows($query) {   
  145.         $query = mysql_num_rows($query);   
  146.         return $query;   
  147.     }   
  148.   
  149.     function num_fields($query) {   
  150.         return mysql_num_fields($query);   
  151.     }   
  152.   
  153.     function free_result($query) {   
  154.         return mysql_free_result($query);   
  155.     }   
  156.   
  157.     function insert_id() {   
  158.         return ($id = mysql_insert_id($this->link)) >= 0 ? $id : $this->result($this->query("SELECT last_insert_id()"), 0);   
  159.     }   
  160.   
  161.     function fetch_row($query) {   
  162.         $query = mysql_fetch_row($query);   
  163.         return $query;   
  164.     }   
  165.   
  166.     function fetch_fields($query) {   
  167.         return mysql_fetch_field($query);   
  168.     }   
  169.   
  170.     function version() {   
  171.         return mysql_get_server_info($this->link);   
  172.     }   
  173.   
  174.     function close() {   
  175.         return mysql_close($this->link);   
  176.     }   
  177.   
  178.     function halt($message = ''$sql = '') {   
  179.         $dberror = $this->error();   
  180.         $dberrno = $this->errno();   
  181.         echo "<div style=\"position:absolute;font-size:11px;font-family:verdana,arial;background:#EBEBEB;padding:0.5em;\">   
  182.                 <b>MySQL Error</b><br>   
  183.                 <b>Message</b>: $message<br>   
  184.                 <b>SQL</b>: $sql<br>   
  185.                 <b>Error</b>: $dberror<br>   
  186.                 <b>Errno.</b>: $dberrno<br>   
  187.                 </div>";   
  188.         exit();   
  189.     }   
  190. }   
  191.   
  192. ?>  
<?php
/****************************************
*** mysql-rw-php version 0.1 @ 2009-4-16
*** code by hqlulu#gmail.com
*** http://www.aslibra.com
*** http://code.google.com/p/mysql-rw-php/
*** code modify from class_mysql.php (uchome)
****************************************/

class mysql_rw_php {

	//查询个数
	var $querynum = 0;
	//当前操作的数据库连接
	var $link = null;
	//字符集
	var $charset;
	//当前数据库
	var $cur_db = '';

	//是否存在有效的只读数据库连接
	var $ro_exist = false;
	//只读数据库连接
	var $link_ro = null;
	//读写数据库连接
	var $link_rw = null;

	function mysql_rw_php(){
	}

	function connect($dbhost, $dbuser, $dbpw, $dbname = '', $pconnect = 0, $halt = TRUE) {
		if($pconnect) {
			if(!$this->link = @mysql_pconnect($dbhost, $dbuser, $dbpw)) {
				$halt && $this->halt('Can not connect to MySQL server');
			}
		} else {
			if(!$this->link = @mysql_connect($dbhost, $dbuser, $dbpw)) {
				$halt && $this->halt('Can not connect to MySQL server');
			}
		}
		
		//只读连接失败
		if(!$this->link && !$halt) return false;
		
		//未初始化rw时,第一个连接作为rw
		if($this->link_rw == null)
			$this->link_rw = $this->link;

		if($this->version() > '4.1') {
			if($this->charset) {
				@mysql_query("SET character_set_connection=$this->charset, character_set_results=$this->charset, character_set_client=binary", $this->link);
			}
			if($this->version() > '5.0.1') {
				@mysql_query("SET sql_mode=''", $this->link);
			}
		}
		if($dbname) {
			$this->select_db($dbname);
		}
	}

	//连接一个只读的mysql数据库
	function connect_ro($dbhost, $dbuser, $dbpw, $dbname = '', $pconnect = 0){
		if($this->link_rw == null)
			$this->link_rw = $this->link;
		$this->link = null;
		//不产生halt错误
		$this->connect($dbhost, $dbuser, $dbpw, $dbname, $pconnect, false);
		if($this->link){
			//连接成功
			//echo "link ro sussess!<br>";
			$this->ro_exist = true;
			$this->link_ro = $this->link;
			if($this->cur_db){
				//如果已经选择过数据库则需要操作一次
				@mysql_select_db($this->cur_db, $this->link_ro);
			}
		}else{
			//连接失败
			//echo "link ro failed!<br>";
			$this->link = &$this->link_rw;
		}
	}

	//设置一系列只读数据库并且连接其中一个
	function set_ro_list($ro_list){
		if(is_array($ro_list)){
			//随机选择其中一个
			$link_ro = $ro_list[array_rand($ro_list)];
			$this->connect_ro($link_ro['dbhost'], $link_ro['dbuser'], $link_ro['dbpw']);
		}
	}

	function select_db($dbname) {
		//同时操作两个数据库连接
		$this->cur_db = $dbname;
		if($this->ro_exist){
			@mysql_select_db($dbname, $this->link_ro);
		}
		return @mysql_select_db($dbname, $this->link_rw);
	}

	function fetch_array($query, $result_type = MYSQL_ASSOC) {
		return mysql_fetch_array($query, $result_type);
	}

	function fetch_one_array($sql, $type = '') {
		$qr = $this->query($sql, $type);
		return $this->fetch_array($qr);
	}

	function query($sql, $type = '') {
		$this->link = &$this->link_rw;
		//判断是否select语句
		if($this->ro_exist && preg_match ("/^(\s*)select/i", $sql)){
			$this->link = &$this->link_ro;
		}
		$func = $type == 'UNBUFFERED' && @function_exists('mysql_unbuffered_query') ?
			'mysql_unbuffered_query' : 'mysql_query';
		if(!($query = $func($sql, $this->link)) && $type != 'SILENT') {
			$this->halt('MySQL Query Error', $sql);
		}
		$this->querynum++;
		return $query;
	}

	function affected_rows() {
		return mysql_affected_rows($this->link);
	}

	function error() {
		return (($this->link) ? mysql_error($this->link) : mysql_error());
	}

	function errno() {
		return intval(($this->link) ? mysql_errno($this->link) : mysql_errno());
	}

	function result($query, $row) {
		$query = @mysql_result($query, $row);
		return $query;
	}

	function num_rows($query) {
		$query = mysql_num_rows($query);
		return $query;
	}

	function num_fields($query) {
		return mysql_num_fields($query);
	}

	function free_result($query) {
		return mysql_free_result($query);
	}

	function insert_id() {
		return ($id = mysql_insert_id($this->link)) >= 0 ? $id : $this->result($this->query("SELECT last_insert_id()"), 0);
	}

	function fetch_row($query) {
		$query = mysql_fetch_row($query);
		return $query;
	}

	function fetch_fields($query) {
		return mysql_fetch_field($query);
	}

	function version() {
		return mysql_get_server_info($this->link);
	}

	function close() {
		return mysql_close($this->link);
	}

	function halt($message = '', $sql = '') {
		$dberror = $this->error();
		$dberrno = $this->errno();
		echo "<div style=\"position:absolute;font-size:11px;font-family:verdana,arial;background:#EBEBEB;padding:0.5em;\">
				<b>MySQL Error</b><br>
				<b>Message</b>: $message<br>
				<b>SQL</b>: $sql<br>
				<b>Error</b>: $dberror<br>
				<b>Errno.</b>: $dberrno<br>
				</div>";
		exit();
	}
}

?>

 调用方法

Java代码 复制代码 收藏代码
  1. <?php   
  2. /****************************************  
  3. *** mysql-rw-php version 0.1 @ 2009-4-16  
  4. *** code by hqlulu#gmail.com  
  5. *** http://www.aslibra.com  
  6. *** http://code.google.com/p/mysql-rw-php/  
  7. *** code modify from class_mysql.php (uchome)  
  8. ****************************************/  
  9.   
  10. require_once('mysql_rw_php.class.php');   
  11.   
  12. //rw info   
  13. $db_rw = array(   
  14.     'dbhost'=>'www.aslibra.com',   
  15.     'dbuser'=>'aslibra',   
  16.     'dbpw'=>'www.aslibra.com',   
  17.     'dbname'=>'test'  
  18. );   
  19.   
  20. $db_ro = array(   
  21.     array(   
  22.         'dbhost'=>'www.aslibra.com:4306',   
  23.         'dbuser'=>'aslibra',   
  24.         'dbpw'=>'www.aslibra.com'  
  25.     )   
  26. );   
  27.   
  28. $DB = new mysql_rw_php;   
  29.   
  30. //connect Master   
  31. $DB->connect($db_rw[dbhost], $db_rw[dbuser], $db_rw[dbpw], $db_rw[dbname]);   
  32.   
  33. //Method 1: connect one server   
  34. $DB->connect_ro($db_ro[0][dbhost], $db_ro[0][dbuser], $db_ro[0][dbpw]);   
  35.   
  36. //Method 2: connect one server from a list by rand   
  37. $DB->set_ro_list($db_ro);   
  38.   
  39. //send to rw   
  40. $sql = "insert into a set a='test'";   
  41. $DB->query($sql);   
  42.   
  43. //send to ro   
  44. $sql = "select * from a";   
  45. $qr = $DB->query($sql);   
  46. while($row = $DB->fetch_array($qr)){   
  47.     echo $row[a];   
  48. }   
  49. ?>  
<?php
/****************************************
*** mysql-rw-php version 0.1 @ 2009-4-16
*** code by hqlulu#gmail.com
*** http://www.aslibra.com
*** http://code.google.com/p/mysql-rw-php/
*** code modify from class_mysql.php (uchome)
****************************************/

require_once('mysql_rw_php.class.php');

//rw info
$db_rw = array(
	'dbhost'=>'www.aslibra.com',
	'dbuser'=>'aslibra',
	'dbpw'=>'www.aslibra.com',
	'dbname'=>'test'
);

$db_ro = array(
	array(
		'dbhost'=>'www.aslibra.com:4306',
		'dbuser'=>'aslibra',
		'dbpw'=>'www.aslibra.com'
	)
);

$DB = new mysql_rw_php;

//connect Master
$DB->connect($db_rw[dbhost], $db_rw[dbuser], $db_rw[dbpw], $db_rw[dbname]);

//Method 1: connect one server
$DB->connect_ro($db_ro[0][dbhost], $db_ro[0][dbuser], $db_ro[0][dbpw]);

//Method 2: connect one server from a list by rand
$DB->set_ro_list($db_ro);

//send to rw
$sql = "insert into a set a='test'";
$DB->query($sql);

//send to ro
$sql = "select * from a";
$qr = $DB->query($sql);
while($row = $DB->fetch_array($qr)){
	echo $row[a];
}
?>
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics