好大的风

2010年07月1日

2009年4月1日发表于新浪开发者博客:关于HTTP 协议中的 KeepAlive

Filed under: 你要去哪里 — 好大的风 @ 11:01 上午

这篇文章已经写完将近一年了,最近从历史邮件里面翻出来,和大家分享一下。
其中使用PHP实现持久的HTTP连接,让我费了很多心思。
曾经想过使用C语言编写一个PHP的扩展来实现,后来发现pfsockopen这个函数,让我豁然开朗,避免重新发明一个轮子,呵呵。

一,KeepAlive的概念:

参见 http://en.wikipedia.org/wiki/HTTP_persistent_connection

二,KeepAlive的客户端实现:

使用了PHP支持的 pfsockopen 来实现,参见:http://cn.php.net/pfsockopen

KeepAlive必要的Header有:

Connection: Keep-Alive
Content-Length: xxx

三,性能对比测试:

几种对比实现方式:

1,使用fsockopen来实现,读取body内容后,关闭连接,参见测试程序中的ohttp_get实现。
2,使用pfsockopen来实现,读取body内容后,不关闭连接,参见测试程序中的phttp_get实现。
3,php实现的file_get_contents
4,第三方测试工具ab

前三种测试在测试程序中都包含了。

测试用例 一:

前三种php实现的客户端单进程单线程请求lighttpd服务器一个16字节的静态文件。顺序请求10000次。
客户端与服务器部署在不同服务器,通过内网请求。

测试结果:

第一次:

[root@localhost ~]# /opt/bin/php tp.php
phttp_get: 5.3641529083252
ohttp_get: 8.1628580093384
file_get_contents: 12.217950105667

第二次:

[root@localhost ~]# /opt/bin/php tp.php
phttp_get: 5.033059835434
ohttp_get: 9.589075088501
file_get_contents: 12.775387048721

第三次:

[root@localhost ~]# /opt/bin/php tp.php
phttp_get: 5.0181269645691
ohttp_get: 8.2286441326141
file_get_contents: 11.089616060257

测试用例 二:

使用第三方工具ab来进行测试,-k参数开打开keepalive支持,不做并发测试,顺序请求10000次。
客户端与服务器部署在不同服务器,通过内网请求。

以下测试结果部分省略:

未打开keepalive:

[root@localhost ~]# ab -n 10000 -c 1 “http://10.69.2.206:8080/sms/ns2/save_msg.txt”

Finished 10000 requests

Concurrency Level: 1
Time taken for tests: 10.410467 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 2480000 bytes
HTML transferred: 160000 bytes
Requests per second: 960.57 [#/sec] (mean)
Time per request: 1.041 [ms] (mean)
Time per request: 1.041 [ms] (mean, across all concurrent requests)
Transfer rate: 232.55 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 30.0 0 3002
Processing: 0 0 0.4 0 9
Waiting: 0 0 0.3 0 9
Total: 0 0 30.0 0 3003

打开keepalive:

[root@localhost ~]# ab -k -n 10000 -c 1 “http://10.69.2.206:8080/sms/ns2/save_msg.txt”

Finished 10000 requests

Concurrency Level: 1
Time taken for tests: 4.148619 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Keep-Alive requests: 9412
Total transferred: 2527060 bytes
HTML transferred: 160000 bytes
Requests per second: 2410.44 [#/sec] (mean)
Time per request: 0.415 [ms] (mean)
Time per request: 0.415 [ms] (mean, across all concurrent requests)
Transfer rate: 594.66 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 5
Processing: 0 0 2.1 0 203
Waiting: 0 0 2.1 0 203
Total: 0 0 2.1 0 203

四,在实际中的应用

以上实现的phttp_get和mysql memcache的 中的“保持连接”概念类似,这种技术一般来说,只适用于fastcgi模式的web服务器。
对于本机之间的http通信,在测试过程中发现phttp_get的优势有限,基本合乎逻辑。
对于本身处理时间比较长的服务,phttp_get的优势也不明显。
综上,phttp_get适用于fastcgi模式的web应用调用远程http服务,且此http服务器响应时间比较短的情况。

五,服务端需要注意的事项

1,http服务器必须支持HTTP/1.1协议
2,php应用必须返回Content-Length:的header,具体实现参见:

http://cn.php.net/manual/en/function.ob-get-length.php

需要在代码中加入:

ob_start();
$size=ob_get_length();
header(”Content-Length: $size”);
ob_end_flush();

最后附上测试代码:

<?php

//$url=http://10.69.2.206:8080/sms/ns2/save_msg.txt

function ohttp_get($host,$port,$query,&$body)
{
$fp=pfsockopen($host,$port,$errno,$errstr,1);
if(!$fp)
{
var_dump($errno,$errstr);
return -1;
}
$out = “GET ${query} HTTP/1.1\r\n”;
$out.= “Host: ${host}\r\n”;
$out.= “Connection: close\r\n”;
$out.= “\r\n”;
fwrite($fp,$out);
$line=trim(fgets($fp));
$header.=$line;
list($proto,$rcode,$result)=explode(” “,$line);
$len=-1;
while( ($line=trim(fgets($fp))) != “” )
{
$header.=$line;
if(strstr($line,”Content-Length:”))
{
list($cl,$len)=explode(” “,$line);
}
if(strstr($line,”Connection: close”))
{
$close=true;
}
}
if($len < 0)
{
echo “ohttp_get must cope with Content-Length header!\n”;
return -1;
}
$body=fread($fp,$len);
if($close)
fclose($fp);
return $rcode;
}
function phttp_get($host,$port,$query,&$body)
{
$fp=pfsockopen($host,$port,$errno,$errstr,1);
if(!$fp)
{
var_dump($errno,$errstr);
return -1;
}
$out = “GET ${query} HTTP/1.1\r\n”;
$out.= “Host: ${host}\r\n”;
$out.= “Connection: Keep-Alive\r\n”;
$out.= “\r\n”;
fwrite($fp,$out);
$line=trim(fgets($fp));
$header.=$line;
list($proto,$rcode,$result)=explode(” “,$line);
$len=-1;
while( ($line=trim(fgets($fp))) != “” )
{
$header.=$line;
if(strstr($line,”Content-Length:”))
{
list($cl,$len)=explode(” “,$line);
}
if(strstr($line,”Connection: close”))
{
$close=true;
}
}
if($len < 0)
{
echo “phttp_get must cope with Content-Length header!\n”;
return -1;
}
$body=fread($fp,$len);
if($close)
fclose($fp);
return $rcode;
}

$time1=microtime(true);
for($i=0;$i<10000;$i++)
{
$host=”10.69.2.206″;
$port=8080;
$query=”/sms/ns2/save_msg.txt”;
$body=””;
$r=ohttp_get($host,$port,$query,$body);
if($r != 200)
{
echo “return code : $r\n”;
}
}
$time2=microtime(true);
for($i=0;$i<10000;$i++)
{
$url=”http://10.69.2.206:8080/sms/ns2/save_msg.txt”;
$host=”10.69.2.206″;
$port=8080;
$query=”/sms/ns2/save_msg.txt”;
$body=””;
$r=phttp_get($host,$port,$query,$body);
if($r != 200)
{
echo “return code : $r\n”;
}
}
$time3=microtime(true);
for($i=0;$i array( ‘timeout’ => 1 )
)
);
$body=file_get_contents($url, 0, $ctx);
$r=200;
if($r != 200)
{
echo “return code : $r\n”;
}
}
$time4=microtime(true);

echo “phttp_get: “.($time3-$time2).”\n”;
echo “ohttp_get: “.($time2-$time1).”\n”;
echo “file_get_contents: “.($time4-$time3).”\n”;

?>

2009年6月2日发表于新浪开发者博客:说说一个数据库的查询方法

Filed under: 你要去哪里 — 好大的风 @ 10:57 上午

最近做了博客关注系统的方案设计,主要的数据表有两个:
数据内容表:

CREATE TABLE `story` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(10) unsigned NOT NULL,
`type` tinyint(3) unsigned NOT NULL,
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`data` varbinary(8192) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `i_ut` (`uid`,`type`,`time`)
) ENGINE=InnoDB;

以及关注关系表:

CREATE TABLE `attention` (
`uid` int(10) unsigned NOT NULL,
`aid` int(10) unsigned NOT NULL,
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`uid`,`aid`),
KEY `i_a` (`aid`)
) ENGINE=InnoDB;

我们的要查询出uid为1001的用户A关注的所有人最近发表的内容,结果集大概在10000条,我们只取前20条。
简单直接的查询方法可以是:

select id,data from story,attention
where story.uid=attention.aid and attention.uid=1001 order by time desc,id desc limit 20;

但是这个查询语句效率很低,换用一种貌似比较复杂的查询:

select story.* from
story,
(select id from story,attention
where story.uid=attention.aid and attention.uid=1001 order by time desc,id desc
) relation
where story.id=relation.id limit 20;

这个查询效率就很好了,通过对比发现,至少比上一个查询快几十倍。

我认为这样的差异主要取决于数据库中“查询优化”的实现,可能MySQL在这方面做的有限,需要我们精确的告诉他一个比较优化的查询方案。

2009年6月4日发表在新浪开发者博客的文章: 讨论一下CRC32的碰撞

Filed under: 你要去哪里 — 好大的风 @ 10:55 上午

数据库的索引字段的size真是寸土寸金,也是兵家必争之地,我尽力将这些字段的size降到最低,所以想了很多办法,包括对一些字符串类的字段取hash然后再存储。但是这样会担心有碰撞的情况发生,也就是说两个字符串本来不一样,但hash计算的结果却一样了,CRC32的计算结果是一个32位整数,理论上说,碰撞的几率大概是四十亿分之一,但我对近200万的邮件地址进行了计算,发现很不乐观:发现了大概800个发生碰撞的邮件地址,以下是部分计算结果,为了保护用户隐私,邮件地址部分做了处理:

| 12670487 | li**an15333838900@163.com |
| 12670487 | sh**lyzyh@163.net |
| 44119406 | lx**at326@163.com |
| 44119406 | sh**leyfeng305@live.cn |
| 55678026 | xi**eng@9010sina.com |
| 55678026 | zh**gtong0508@yahoo.com.cn |
| 87492793 | lu**811123@sina.com.cn |
| 87492793 | wo**203@126.com |
| 104224050 | lr**72310@sina.com |
| 104224050 | xx**dy@sina.com |
| 111133190 | wa**ingalice@163.com |
| 111133190 | zh**grongrong05624@163.com |
| 121282991 | sz**lb@163.com |
| 121282991 | zh**gpengpeng1991@163.com |
| 122865341 | wr**wrln@126.com |
| 122865341 | zk**979@126.com |
| 153868011 | li**ng7510@163.com |
| 153868011 | sc**u@163.com |
| 158081872 | qq**46904866.com |
| 158081872 | ti**r1009@sina.com |
| 173541473 | ly**y@hotmail.com |
| 173541473 | wa**.ok@163.com |
| 179173510 | zo**zhiyong@163.com |
| 179173510 | zp**99077@126.com |
| 186547905 | li**eqiao0408@sina.com |
| 186547905 | mo**cashu452@gmail.com |
| 195555720 | li**izhi1991@sina.com |
| 195555720 | ta**ao584201314@sina.com |
| 198793857 | qu**ng880521@126.com |
| 198793857 | sm**5201314@yahoo.cn |
| 201351361 | pi**n_2000@163.com |
| 201351361 | xi**mao9521@hotmail.com |
| 206841534 | qi**nianwenzhai3@sina.com |
| 206841534 | xh**72@163.com |

看来CRC32还是不能做这个用,如果用MD5的计算结果来保存,又显得很大了,MD5计算出来是128位的整数,占用16个字节。
呵呵,后来想想,还是用MD5的一半吧,占用8个字节,估计还是可以的,稍后再做实验。

简单说说PHP的APC模块

Filed under: 你要去哪里 — 好大的风 @ 10:47 上午

根据我粗浅的理解,APC模块有两部分功能:

1,缓存PHP源码编译后结果,这样每次执行PHP程序的时候,就无需重新读取和编译了,现在很多解释型高级语言都有这个功能了,JAVA貌似做的更进一步,能在程序执行的过程中,根据程序执行的环境和频率来进行相应的优化。缓存编译结果对于WEB应用程序很有意义,我之前看过一些比较大型的程序,每个http请求对应的PHP程序,会include几十个甚至上百个其他的PHP程序,使用strace对httpd进程探测一下,就会发现有大量的open、read,stat等系统调用。启用apc后,将会大大减少上述系统调用。如果将apc.stat设置为0,还可以进一步减少stat的调用,但此时如果更新PHP源码,是不会立即生效的,需要重启WEB服务器或者调用apc_clear_cache来清理一下APC的缓存。在我的使用过程中,没发现什么问题,所以我认为,APC不需要改动任何代码,就可以显著提升性能,不用白不用。

2,单机的数据缓存,简单来说,就像是一个Memcached一样,可以保存、读取、删除数据,操作也和Memcache类似。但APC的缓存是本地的,不能多个服务器共享。因为无需网络通信,所以效率比Memcache更高。当需要缓存的数据量比较小的时候,APC具有一定的优势。

APC模块的官方技术文档参见:

http://cn2.php.net/manual/en/book.apc.php

2010年06月28日

漫长的夏日正在一点一点的划过

Filed under: 你要去哪里 — 好大的风 @ 10:16 上午

漫长的夏日正在一点一点的划过。

最近晚上还算是比较凉快,不会酷热难眠,白天在小屋看书的时候,有一个电扇陪伴,也是很不错的。

要表扬一下我现在这个电扇,居然还带遥控器,有自然风和睡眠风可选,还可以最长定时13小时,购买时的价钱也就100多块钱。我觉得买电扇一定得有自然风的选择,不然一直吹着不仅浪费电能,而且时间稍长就容易感冒,实在不爽。顺便再说一下空调,我现在尽量不开空调,屋子里这个空调太不舒服,忽冷忽热的,睡觉都在担心冻着,我猜变频空调应该能解决这个问题吧,以后家里再买新空调,一定要买变频的。据我的理解,变频的主要目的在与调整电机转速,来达到温和的调节温度的目的吧。

周六去看了电影《海洋天堂》,本来觉得这个电影在电脑上看也够了,但后来看也没啥别的片子可看了,而且《海洋天堂》里面有桂纶镁啊,呵呵,于是就去了。片子确实不错,不时听到周围抽泣的声音。我没哭,但不代表片子不让人感动。

周日上午继续练车,最近研究了好多车,纠结买车还是不买,纠结买自动挡还是手动挡,解决一步到位还是先买个凑合着用。现在练习的是北京现代的悦动自动挡,觉得自动挡的车真是方便啊,起步太简单了,一起等红灯的车,都不是我的对手,哈哈。三次总计八个小时的练习后,我觉得对车的感觉很有长进,现在在路上一点都不害怕了,当然人多的无护栏的路上,还是得小心翼翼的。新奥托看上去不错,好多同事朋友推荐,但居然没有现车,最新发布的新赛欧貌似也不错的样子,不管三厢还是两厢都挺好。想想有了自己的车,周末就可以扩大一点活动范围了,哈哈。

晚上继续看电影,懒得开动投影机了,就戴着耳机在22寸的显示器上看希区柯克的《惊魂记》,之前看了几个希区柯克的电影,确实名不虚传,虽然大多是黑白的老片子,但情节紧凑,不落俗套。这次的《惊魂记》依旧没有让我失望,貌似在IMDB TOP250的列表中,希区柯克的电影不少,加油加油。

再顺便说说我最近用过的耳机,声海品牌的有MX360,PX100,HD238,最早时候还用过MX500,但不知道是不是买到了假货,没啥感觉。MX360感觉不错,耳塞式的,就是有点大,或是我耳朵有点小,时间长了感觉硌的慌,音质不错,尤其低音。PX100戴上很舒服,不捂不热,声音也不错,物美价廉,HD238算是我听过的最高档的耳机了,声音清澈真实,细节丰富,不愧是HD系列的。后来还想过HD600或者HD650,但后来一看,还需要单独的耳机放大器,于是放弃了这个烧钱的无底洞。

最近工作上还算正常,把之前在SINA做过的东西又复习了一遍,比如各种消息系统啦,好友关注系统啦,好友动态之类的,又重新实现了一遍,我觉得很有收获,每次重新考虑这些问题,都会有新的改进和新的想法,不光是数据库的设计的改进,在PHP程序的设计和开发上,也有各种各样新技术的实践。我很开心。

2010年06月18日

rsyslogd 日志服务 参考

Filed under: 你要去哪里 — 好大的风 @ 11:52 上午

rsyslogd 日志服务 参考

一,简介

rsyslogd是一个加强版的syslogd,具有各种各样的新功能,典型的有:
1,直接将日志写入到数据库。
2,日志队列(内存队列和磁盘队列)。
3,灵活的模板机制,可以得到多种输出格式。
4,插件式结构,多种多样的输入、输出模块。

二,下载、安装

下载地址:
http://www.rsyslog.com/Downloads-index-req-viewdownload-cid-1-orderby-dateD.phtml

安装:
经典的tar zxvf,configure,make,make install过程。

三,配置示例:

以下rsyslog.conf只有5行,但实现了udp日志接收、将日志写入到文件、将日志写入到数据库的功能。

$ModLoad imudp.so  # provides UDP syslog reception
$UDPServerRun 518 # start a UDP syslog server at standard port 514
*.*                                                     /tmp/rsys.log
$ModLoad ommysql
*.*     :ommysql:192.168.120.67,log,mg,123qwe

详细配置示例,参见:

http://www.rsyslog.com/doc-sample.conf.html

四,syslog客户端

rsyslogd可以兼容系统原有的syslog配置文件,可以近乎完美的替换掉原有的syslogd。
但我希望rsyslogd是一个独立的系统,所以我将其配置为侦听518 UDP端口的独立服务器。
然后我使用PHP语言编写了一个简单的syslog客户端,通过UDP协议,直接将日志消息发送给
rsyslogd日志服务,达到最大的灵活性。

以下是syslog客户端代码:

function mgsyslog($level,$msg)
{
 $server=”192.168.120.67″;
 $port=518;
 $facility=1;
 $pid=posix_getpid();
 $process=”PHP[${pid}]“;
 $actualtime = time();
 $month      = date(“M”, $actualtime);
 $day        = substr(“  “.date(“j”, $actualtime), -2);
 $hhmmss     = date(“H:i:s”, $actualtime);
 $timestamp  = $month.” “.$day.” “.$hhmmss;
 $hostname=gethostname();
 $pri    = “<”.($facility*8 + $level).”>”;
 $header = $timestamp.” “.$hostname;
 $message = substr($pri.$header.” “.$process.”: “.$msg, 0, 1024);
 $fp = fsockopen(“udp://”.$server, $port, $errno, $errstr);
 if ($fp)
 {
  fwrite($fp, $message);
  fclose($fp);
  return true;
 }
 return false;
}

具体的syslog协议可以参见RFC3164。

五,总结

PHP的所有日志都可以使用上面定制的syslog客户端发送到集中的日志服务器。
然后可以集中查看、处理。

另外,一个方便的WEB日志查看器可以在下面的地址获得:
http://loganalyzer.adiscon.com/
安装简单方便,用起来也不错。

HAProxy TCP 负载均衡与数据转发 参考

Filed under: 你要去哪里 — 好大的风 @ 9:57 上午

HAProxy TCP 负载均衡与数据转发 参考

配置文件重要参数:

global
        maxconn 65535 #最大连接数,HAProxy是数据转发,每条数据链路占用两个连接。
        uid 99   #运行身份
        gid 99   #运行身份
        #daemon   #daemon方式运行
        #debug   #调试方式运行
        #quiet   #静默方式运行
       

defaults
        timeout connect 86400s #连接超时,超过设置时间,HAProxy会主动断开双方连接。
        timeout server  86400s
        timeout client  86400s
       
       
listen  db_3601 0.0.0.0:13601 #设置转发实例
        mode tcp    #tcp模式,只转发,不分析
        option tcpka   
        option srvtcpka
        option clitcpka
        balance roundrobin  #均衡算法
        server db_47_3601 192.168.120.47:3601 check inter 1000 rise 1 fall 2 #目标服务器定义
        server db_48_3601 192.168.120.48:3601 check inter 1000 rise 1 fall 2 #目标服务器定义
命令行重要参数:

        -D goes daemon   #设置为daemon方式运行
        -p writes pids of all children to this file  #将pid写入到文件中
        -sf/-st [pid ]* finishes/terminates old pids. Must be last arguments. #替换原有进程
        -f <cfgfile> #指定配置文件
       

如何在更新配置文件后,快速重启服务:

通过-st指定原有进程的pid:
haproxy -f /apps/haproxy/db.cfg -p /apps/haproxy/db.pid -D -st 26611

简单数据转发示例:

listen  db_3608 0.0.0.0:13608
        mode tcp
        option tcpka
        option srvtcpka
        option clitcpka
        balance roundrobin
        server db_47_3608 192.168.120.47:3608

简单负载均衡示例:

listen  http_8080 0.0.0.0:8080
        mode tcp
        option tcpka
        option srvtcpka
        option clitcpka
        balance roundrobin
        server http_69_80 192.168.120.69:80
        server http_67_80 192.168.120.67:80

简单TCP健康检查示例:

        server http_67_80 192.168.120.67:80 check inter 1000 rise 1 fall 2
其中:
check为健康检查指令。
inter 1000表示检察时间间隔为1000毫秒。
rise 1表示检查成功一次即将其标识为可用。
fall 2表示检查失败两次即将其标识为不可用。

简单数据库健康检查实例:

  option mysql-check
        server http_67_80 192.168.120.67:80 check inter 1000 rise 1 fall 2
简单主备模式数据转发:

listen  http_8080 0.0.0.0:8080
        mode tcp
        option tcpka
        option srvtcpka
        option clitcpka
        balance roundrobin
        server http_69_80 192.168.120.69:80 check
        server http_67_80 192.168.120.67:80 backup check #待实践

附:balance使用参考:

简单数据转发:

balance 8080 192.168.120.69:80

简单负载均衡:

balance 8080 192.168.120.69:80 192.168.120.67:80

调试用途:

balance 11211 192.168.120.69:80 -p -d -f
编译好的64位Linux版本的HAProxy和Balance可以在下面的地址快速下载:

http://bbs.xmgu2008.org/tools/balance
http://bbs.xmgu2008.org/tools/haproxy

2010年04月23日

网易域名邮箱

Filed under: 你要去哪里 — 好大的风 @ 2:15 下午

今天试用了一下网易域名邮箱,感觉挺好。

挺好的原因主要是几方面:

1,注册简单。

免费注册,需要验证手机,整个流程顺畅。注册完成后修改自己域名的MX记录即可。

2,管理界面简洁。

管理功能简单易用,包括MX记录验证,邮箱公告编辑,用户添加和管理。

3,收发信顺畅。

之前自己架设邮件服务器,给公司的邮箱发邮件时,会被当成垃圾邮件给拒绝,现在通过网易域名邮箱发信,十分迅速,基本上5秒钟之内就可以到达。

我以后不再自己折腾邮件服务器了。

我觉得域名邮箱对于小型公司还是很有用的,申请一个域名(花费100元以下)即可拥有可靠的、便捷的企业邮箱系统。省心也省钱。

2010年04月15日

说说我最近研究的一个新的改装版Memcached

Filed under: 你要去哪里 — 好大的风 @ 1:00 下午

说说我最近研究的一个新的改装版Memcached:

官方网站:http://www.gear6.com/
相关介绍:http://www.gear6.com/memcached-product/memcached

我看到的主要特性:
1, 有一个漂亮精美的WEB界面,可以查看Memcached服务的各种状态。
2, 安装很方便,一个RPM包所有需要的东西都有了,包括libevent等。
3, 支持非内存存储,比如可以使用文件或者BLOCK 设备来当作存储,可以实现持久化。
4, 支持复制,这样在一定程度上为HA提供了基础。
5, 据说内存利用率更好,基本没有碎片的问题。
6, 免费下载使用,收费提供技术支持。

他们网站上说:
Dynamic content providers choose enhanced Memcached from Gear6 instead of DIY Memcached for replication, lower TCO, less servers, cache depth, pro tools, 24/7 support and a faster web experience.

我在某种程度上比较抵触DIY,尤其在一些重要基础设施上。

另外,最新的O’Reilly MySQL Conference正在如火如荼的进行中,各种Slide也可以看了,有些比较有意思,大家可以找来看看。

网站:http://en.oreilly.com/mysql2010

2010年03月19日

使用xtrabackup在线备份数据库并增加辅库-快速参考

Filed under: 你要去哪里 — 好大的风 @ 5:48 下午

xtrabackup quickstart

使用xtrabackup在线备份数据库。
使用上面的备份文件增加一个Slave数据库。
现有环境:

现有一个数据库环境,信息如下:

Master:代号A
IP地址:192.168.115.149
端口:3340

Slave:代号B
IP地址:192.168.115.150
端口:3340
配置文件:/apps/mysql_3340/my.cnf
socket路径:/tmp/mysql_3340.sock
通过本地socket访问用户名:root
通过本地socket访问密码:无

新搭建数据库:代号C

以下实践都在B数据库所在的机器上操作。

xtrabackup相关程序下载地址:

http://www.percona.com/percona-builds/XtraBackup/XtraBackup-1.0/binary/xtrabackup-1.0.tar.gz

下载后解压,将三个可执行文件放到/usr/local/sbin/即可。

当前目录:/apps/backup/

使用innobackupex脚本对数据库进行备份,相关参数可以通过–help参数了解。
以下程序输出经过筛选,略去不必要信息:
[root@slave150 backup]# innobackupex-1.5.1 –defaults-file=/apps/mysql_3340/my.cnf –slave-info –socket=/tmp/mysql_3340.sock 3340

InnoDB Backup Utility v1.5.1-xtrabackup; Copyright 2003, 2009 Innobase Oy.
All Rights Reserved.
…版权信息…

innobackupex-1.5.1: Using mysql  Ver 14.14 Distrib 5.1.43, for unknown-linux-gnu (x86_64) using readline 5.1
innobackupex-1.5.1: Using mysql server version Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.

innobackupex-1.5.1: Created backup directory /apps/backup/3340/2010-03-19_16-51-33
100319 16:51:33  innobackupex-1.5.1: Starting mysql with options: –unbuffered –socket=/tmp/mysql_3340.sock –
100319 16:51:33  innobackupex-1.5.1: Connected to database with mysql child process (pid=25246)
100319 16:51:37  innobackupex-1.5.1: Connection to database server closed

100319 16:51:37  innobackupex-1.5.1: Starting ibbackup with command: xtrabackup  –defaults-file=”/apps/mysql_3340/my.cnf” –backup –suspend-at-end –target-dir=/apps/backup/3340/2010-03-19_16-51-33
innobackupex-1.5.1: Waiting for ibbackup (pid=25252) to suspend
innobackupex-1.5.1: Suspend file ‘/apps/backup/3340/2010-03-19_16-51-33/xtrabackup_suspended’

xtrabackup  Ver 1.0 Rev 113 for 5.0.84 unknown-linux-gnu (x86_64)
xtrabackup: uses posix_fadvise().
xtrabackup: cd to /apps/mysql_3340
xtrabackup: Target instance is assumed as followings.
xtrabackup:   innodb_data_home_dir = ./
xtrabackup:   innodb_data_file_path = ibdata1:10M:autoextend
xtrabackup:   innodb_log_group_home_dir = ./
xtrabackup:   innodb_log_files_in_group = 2
xtrabackup:   innodb_log_file_size = 5242880
xtrabackup: use O_DIRECT
>> log scanned up to (23 3600029795)
Copying ./ibdata1
     to /apps/backup/3340/2010-03-19_16-51-33/ibdata1
>> log scanned up to (23 3600060713)
>> log scanned up to (23 3600087337)
>> log scanned up to (23 3600121153)
        …done
Copying ./xjqy2/configs.ibd
     to /apps/backup/3340/2010-03-19_16-51-33/xjqy2/configs.ibd
Copying ./game/answer_count.ibd
     to /apps/backup/3340/2010-03-19_16-51-33/game/answer_count.ibd
        …done

100319 16:56:03  innobackupex-1.5.1: Continuing after ibbackup has suspended
100319 16:56:03  innobackupex-1.5.1: Starting mysql with options: –unbuffered –socket=/tmp/mysql_3340.sock –
100319 16:56:03  innobackupex-1.5.1: Connected to database with mysql child process (pid=25269)
>> log scanned up to (23 3602333556)
100319 16:56:07  innobackupex-1.5.1: Starting to lock all tables…
>> log scanned up to (23 3602373264)
>> log scanned up to (23 3602458529)
>> log scanned up to (23 3602458529)
100319 16:56:19  innobackupex-1.5.1: All tables locked and flushed to disk

100319 16:56:19  innobackupex-1.5.1: Starting to backup .frm, .MRG, .MYD, .MYI,
innobackupex-1.5.1: .TRG, .TRN, .ARM, .ARZ and .opt files in
innobackupex-1.5.1: subdirectories of ‘/apps/mysql_3340′
innobackupex-1.5.1: Backing up files ‘/apps/mysql_3340/mysql/*.{frm,MYD,MYI,MRG,TRG,TRN,ARM,ARZ,opt,par}’ (65 files)
innobackupex-1.5.1: Backing up files ‘/apps/mysql_3340/xjqy2/*.{frm,MYD,MYI,MRG,TRG,TRN,ARM,ARZ,opt,par}’ (29 files)
innobackupex-1.5.1: Backing up files ‘/apps/mysql_3340/game/*.{frm,MYD,MYI,MRG,TRG,TRN,ARM,ARZ,opt,par}’ (23 files)
100319 16:56:19  innobackupex-1.5.1: Finished backing up .frm, .MRG, .MYD, .MYI, .TRG, .TRN, .ARM, .ARZ and .opt files

innobackupex-1.5.1: Resuming ibbackup

xtrabackup: The latest check point (for incremental): ’23:3602295070′
>> log scanned up to (23 3602458752)
xtrabackup: Stopping log copying thread.
xtrabackup: Transaction log of lsn (23 3599800027) to (23 3602458752) was copied.
100319 16:56:22  innobackupex-1.5.1: All tables unlocked
100319 16:56:22  innobackupex-1.5.1: Connection to database server closed

innobackupex-1.5.1: Backup created in directory ‘/apps/backup/3340/2010-03-19_16-51-33′
innobackupex-1.5.1: MySQL binlog position: filename ‘mysql-bin.000005′, position 989572152
innobackupex-1.5.1: MySQL slave binlog position: master host ’192.168.115.149′, filename ‘mysql-bin.000006′, position 690623257
100319 16:56:22  innobackupex-1.5.1: completed OK!

此时在备份的目录下会有一个xtrabackup_slave_info文件,是设置Slave的命令:

[root@slave150 2010-03-19_16-51-33]# cat xtrabackup_slave_info
CHANGE MASTER TO MASTER_LOG_FILE=’mysql-bin.000006′, MASTER_LOG_POS=690623257

恢复备份文件:
[root@slave150 2010-03-19_16-51-33]# innobackupex-1.5.1 –apply-log –defaults-file=/apps/backup/3340/2010-03-19_16-51-33/backup-my.cnf /apps/backup/3340/2010-03-19_16-51-33/

100319 16:57:48  innobackupex-1.5.1: Starting ibbackup with command: xtrabackup  –defaults-file=”/apps/backup/3340/2010-03-19_16-51-33/backup-my.cnf” –prepare –target-dir=/apps/backup/3340/2010-03-19_16-51-33

xtrabackup  Ver 1.0 Rev 113 for 5.0.84 unknown-linux-gnu (x86_64)
xtrabackup: cd to /apps/backup/3340/2010-03-19_16-51-33
xtrabackup: This target seems to be not prepared yet.
xtrabackup: xtrabackup_logfile detected: size=2998272, start_lsn=(23 3599800027)
xtrabackup: Temporary instance for recovery is set as followings.
xtrabackup:   innodb_data_home_dir = ./
xtrabackup:   innodb_data_file_path = ibdata1:10M:autoextend
xtrabackup:   innodb_log_group_home_dir = ./
xtrabackup:   innodb_log_files_in_group = 1
xtrabackup:   innodb_log_file_size = 2998272
xtrabackup: Starting InnoDB instance for recovery.
xtrabackup: Using 104857600 bytes for buffer pool (set by –use-memory parameter)
InnoDB: Log scan progressed past the checkpoint lsn 23 3599800027
100319 16:57:48  InnoDB: Database was not shut down normally!
InnoDB: Starting crash recovery.
InnoDB: Reading tablespace information from the .ibd files…
InnoDB: Doing recovery: scanned up to log sequence number 23 3602458752 (99 %)
100319 16:57:49  InnoDB: Starting an apply batch of log records to the database…
InnoDB: Progress in percents: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
InnoDB: Apply batch completed
下面两行是说,B数据库作为Slave时,所同步的A库日志的位置。
如果新的C库作为A的Slave,那么应该配置这个日志位置。
这个位置也是和上面的xtrabackup_slave_info是一致的。
InnoDB: In a MySQL replication slave the last master binlog file
InnoDB: position 0 690623257, file name mysql-bin.000006

下面两行是说,B数据库自己的binlog位置。如果新的C库作为B的Slave,那么应该配置这个日志位置。
InnoDB: Last MySQL binlog file position 0 989572152, file name ./mysql-bin.000005
100319 16:57:52  InnoDB: Started; log sequence number 23 3602458752

[notice (again)]
  If you use binary log and don’t use any hack of group commit,
  the binary log position seems to be:
InnoDB: Last MySQL binlog file position 0 989572152, file name ./mysql-bin.000005

xtrabackup: starting shutdown with innodb_fast_shutdown = 1
100319 16:57:52  InnoDB: Starting shutdown…
100319 16:57:54  InnoDB: Shutdown completed; log sequence number 23 3602458752

100319 16:57:54  innobackupex-1.5.1: Restarting xtrabackup with command: xtrabackup  –defaults-file=”/apps/backup/3340/2010-03-19_16-51-33/backup-my.cnf” –prepare –target-dir=/apps/backup/3340/2010-03-19_16-51-33
for creating ib_logfile*

xtrabackup  Ver 1.0 Rev 113 for 5.0.84 unknown-linux-gnu (x86_64)
xtrabackup: cd to /apps/backup/3340/2010-03-19_16-51-33
xtrabackup: This target seems to be already prepared.
xtrabackup: notice: xtrabackup_logfile was already used to ‘–prepare’.
xtrabackup: Temporary instance for recovery is set as followings.
xtrabackup:   innodb_data_home_dir = ./
xtrabackup:   innodb_data_file_path = ibdata1:10M:autoextend
xtrabackup:   innodb_log_group_home_dir = ./
xtrabackup:   innodb_log_files_in_group = 2
xtrabackup:   innodb_log_file_size = 5242880
xtrabackup: Starting InnoDB instance for recovery.
xtrabackup: Using 104857600 bytes for buffer pool (set by –use-memory parameter)
100319 16:57:54  InnoDB: Log file ./ib_logfile0 did not exist: new to be created
InnoDB: Setting log file ./ib_logfile0 size to 5 MB
InnoDB: Database physically writes the file full: wait…
100319 16:57:54  InnoDB: Log file ./ib_logfile1 did not exist: new to be created
InnoDB: Setting log file ./ib_logfile1 size to 5 MB
InnoDB: Database physically writes the file full: wait…
InnoDB: The log sequence number in ibdata files does not match
InnoDB: the log sequence number in the ib_logfiles!
100319 16:57:54  InnoDB: Database was not shut down normally!
InnoDB: Starting crash recovery.
InnoDB: Reading tablespace information from the .ibd files…
InnoDB: In a MySQL replication slave the last master binlog file
InnoDB: position 0 690623257, file name mysql-bin.000006
InnoDB: Last MySQL binlog file position 0 989572152, file name ./mysql-bin.000005
100319 16:57:54  InnoDB: Started; log sequence number 23 3602459148

[notice (again)]
  If you use binary log and don’t use any hack of group commit,
  the binary log position seems to be:
InnoDB: Last MySQL binlog file position 0 989572152, file name ./mysql-bin.000005

xtrabackup: starting shutdown with innodb_fast_shutdown = 1
100319 16:57:54  InnoDB: Starting shutdown…
100319 16:57:55  InnoDB: Shutdown completed; log sequence number 23 3602459148
100319 16:57:55  innobackupex-1.5.1: completed OK!

至此,数据文件已经可用。
将准备好的数据移动到合适的位置,做好新的配置文件,即可启动数据库。
新的配置文件与原有Slave配置文件基本一致,需要注意修改几个参数:
server-id必须修改,否则会与原有主库产生冲突。
如果在同一台机器上进行测试,端口和socket文件必须修改。

启动后,通过mysql客户端连接新的数据库,执行xtrabackup_slave_info中的命令,执行时应该加上master_host等参数,然后执行start slave即可。

« Newer PostsOlder Posts »

Powered by WordPress