`

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];
}
?>
分享到:
评论

相关推荐

    mysql高并发解决方案

    11. **负载均衡**:配合Nginx或LVS等负载均衡器,动态分配请求到不同的服务器,确保资源充分利用且无单点故障。 12. **数据库连接池**:使用连接池管理数据库连接,避免频繁创建和关闭连接带来的开销。 通过以上...

    大规模网站架构介绍.pptx

    数据库的读写分离是提高性能的常见手段,MySQL Proxy可实现这一功能,包括负载均衡、故障切换、查询分析和读写分离。数据库分片(Sharding)进一步提升了处理能力,分为水平分区和垂直分区。水平分区是根据特定字段...

    经典大规模网站架构.ppt

    数据库的读写分离是一种常见的优化手段,通过使用MySQL Proxy等工具,可以实现负载均衡、故障切换和查询分析,同时将读操作和写操作分散到不同的数据库服务器,减轻主数据库的压力。读写分离还可以进一步细分为水平...

    大规模网站架构.ppt

    读写分离是一种常见做法,通过MySQL Proxy等工具,可以实现负载均衡、故障切换和查询分析。此外,读写分离还可以通过R/W Splitting来进一步提高性能,将读操作和写操作分发到不同的数据库实例上。 数据库Sharding是...

    网站架构设计 教你进入架构设计

    数据库的读写分离是提高性能的一个常见做法,如MySQL Proxy可以实现这一功能,提供负载均衡、故障切换和查询分析。此外,Sharding是一种有效的数据分区方法,分为水平分区和垂直分区。水平分区通常应用于web 2.0网站...

    大规模网站架构

    大规模网站架构是一门复杂的学问,涉及多个方面,包括但不限于高性能、高可用性、可伸缩性、负载均衡、故障转移、读写分离、数据库分片等。以下是针对给出的内容点进行的详细知识点介绍。 首先,架构师在设计大规模...

    大规模网站架构培训资料

    数据库读写分离是一种常见的优化手段,例如通过MySQL Proxy来实现,它能够进行负载均衡、故障切换和查询分析,以及读写分离。数据库分片(Sharding)是另一种扩展策略,分为水平分区和垂直分区。水平分区将数据按照...

    大规模网站架构祥细教程

    **读写分离**通过使用代理如MySQL Proxy,可以实现负载均衡、故障切换和查询分析,以及读写操作的分离,从而提升性能。**分片(Sharding)**是另一种扩展数据库的方法,分为**水平分片**和**垂直分片**。水平分片是...

    大规模网站架构 讲义

    合理地选择技术栈、设计高性能的架构、优化数据库性能、利用消息队列解耦系统以及实现高效的负载均衡等都是构建成功的大规模网站不可或缺的因素。此外,还需要持续关注新技术的发展,并不断优化现有系统,以适应不断...

Global site tag (gtag.js) - Google Analytics