<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>一个故事@MySQL DBA &#187; 技术细节</title>
	<atom:link href="http://www.orczhou.com/index.php/category/code-detail/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.orczhou.com</link>
	<description>一个故事@MySQL DBA</description>
	<lastBuildDate>Tue, 20 Dec 2011 15:51:20 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>使用mod_proxy_html完善Apache反向代理</title>
		<link>http://www.orczhou.com/index.php/2011/12/apache-reverse-proxy-with-mod-proxy-html/</link>
		<comments>http://www.orczhou.com/index.php/2011/12/apache-reverse-proxy-with-mod-proxy-html/#comments</comments>
		<pubDate>Fri, 09 Dec 2011 14:37:02 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[技术细节]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[Reverse Proxy]]></category>
		<category><![CDATA[反向代理]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=3335</guid>
		<description><![CDATA[<p>在使用和配置过程中，还是遇到了一些波折，这里分享、记录一下。</p>
<div class="myt1">1. 模块mod_proxy_html可以做些什么</div>
<p>使用Apache内置的mod_proxy的<a href="http://httpd.apache.org/docs/current/mod/mod_proxy.html#proxypass">Proxypass</a>可以帮助我们实现基本的<a href="http://httpd.apache.org/docs/2.2/urlmapping.html#proxy">反向代理</a>功能。但是，代理返回的结果页面包含的链接并不会被重写，所以如果被代理的Server返回的是绝对地址，用户则无法正确访问这些链接。例如：</p>
<p>我们有反向代理服务器http://myproxy.taobao.org/，而内网有需要代理的服务器http://internalserver.taobao.org/，通过基本的反向代理指令<a href="http://httpd.apache.org/docs/current/mod/mod_proxy.html#proxypass">Proxypass</a>：</p>
<blockquote><p>ProxyPass  ^/proxy_internal/(.*)$ http://internalserver.taobao.org/$1</p></blockquote>
<p>可以完成基本的反向代理任务，但是如果internalserver.taobao.org返回的HTML代码中有如下链接，则客户端则无法正常链接到目标页面了：</p>
<blockquote><p>&#60;a href="/test/index.html">Sample Link&#60;/a></p></blockquote>
<p>因为，客户端点击该链接时会定向到http://myproxy.taobao.org/test/index.html，而资源实际是在http://internalserver.taobao.org/test/index.html上。</p>
<p>Apache自带的mod_proxy<a href="http://httpd.apache.org/docs/2.2/urlmapping.html#proxy">无法解决这个问题</a>，开源的第三方模块<a href="http://apache.webthing.com/mod_proxy_html/">mod_proxy_html</a>为此而生。经过代理服务器的HTML可以通过该模块做一次替换，从而变成可以正常访问的URL（上例中会把/oninternal/index.html替换成/proxy_internal/test/index.html）。<br />
[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2011/12/apache-reverse-proxy-with-mod-proxy-html/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>在使用和配置过程中，还是遇到了一些波折，这里分享、记录一下。</p>
<div class="myt1">1. 模块mod_proxy_html可以做些什么</div>
<p>使用Apache内置的mod_proxy的<a href="http://httpd.apache.org/docs/current/mod/mod_proxy.html#proxypass">Proxypass</a>可以帮助我们实现基本的<a href="http://httpd.apache.org/docs/2.2/urlmapping.html#proxy">反向代理</a>功能。但是，代理返回的结果页面包含的链接并不会被重写，所以如果被代理的Server返回的是绝对地址，用户则无法正确访问这些链接。例如：</p>
<p>我们有反向代理服务器http://myproxy.taobao.org/，而内网有需要代理的服务器http://internalserver.taobao.org/，通过基本的反向代理指令<a href="http://httpd.apache.org/docs/current/mod/mod_proxy.html#proxypass">Proxypass</a>：</p>
<blockquote><p>ProxyPass  ^/proxy_internal/(.*)$ http://internalserver.taobao.org/$1</p></blockquote>
<p>可以完成基本的反向代理任务，但是如果internalserver.taobao.org返回的HTML代码中有如下链接，则客户端则无法正常链接到目标页面了：</p>
<blockquote><p>&lt;a href="/test/index.html">Sample Link&lt;/a></p></blockquote>
<p>因为，客户端点击该链接时会定向到http://myproxy.taobao.org/test/index.html，而资源实际是在http://internalserver.taobao.org/test/index.html上。</p>
<p>Apache自带的mod_proxy<a href="http://httpd.apache.org/docs/2.2/urlmapping.html#proxy">无法解决这个问题</a>，开源的第三方模块<a href="http://apache.webthing.com/mod_proxy_html/">mod_proxy_html</a>为此而生。经过代理服务器的HTML可以通过该模块做一次替换，从而变成可以正常访问的URL（上例中会把/oninternal/index.html替换成/proxy_internal/test/index.html）。<br />
<span id="more-3335"></span></p>
<div class="myt1">2. 编译安装mod_proxy_html模块</div>
<p>我是在RHEL5.4上安装的，并不容易。<strong>理想的安装步骤</strong>（为了支持多字符集需要同时安装mod_xml2enc）：</p>
<p>1. 检查是否安装了libxml2。</p>
<p>2. 下载mod_proxy_html源码：<a href="http://apache.webthing.com/mod_proxy_html/">地址</a>；下载mod_xml2enc的源码：地址<a href="http://apache.webthing.com/mod_xml2enc/">mod_xml2enc</a></p>
<p>3. 编译mod_proxy_html和mod_xml2enc：</p>
<blockquote><p>
sudo /usr/sbin/apxs -c -I/usr/include/libxml2 -I. -I /usr/lib -i mod_proxy_html.c<br />
sudo /usr/sbin/apxs -c -I/usr/include/libxml2 -I. -i mod_xml2enc.c<br />
chmod 755 /usr/lib64/httpd/modules/mod_proxy_html.so
</p></blockquote>
<p>如果你这么顺利就编译好了模块，那你很幸运。</p>
<div class="myt2">编译中的问题</div>
<blockquote><p>$ /usr/sbin/apxs -c -I/usr/include/libxml2 -I. -I /usr/lib -i mod_proxy_html.c<br />
"my" variable $libdir masks earlier declaration in same scope at /usr/sbin/apxs line 36.<br />
/apr-1/build/libtool --silent --mode=compile gcc -prefer-pic -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -fno-strict-aliasing  -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -pthread -I/usr/include/httpd  -I/usr/include/apr-1   -I/usr/include/apr-1  -I/usr/include/libxml2 -I. -I/usr/lib  -c -o mod_proxy_html.lo mod_proxy_html.c &#038;& touch mod_proxy_html.slo<br />
sh: /apr-1/build/libtool: No such file or directory<br />
apxs:Error: Command failed with rc=8323072</p></blockquote>
<p>仔细看是：<strong>/apr-1/build/libtool: No such file or directory</strong>。对libtool和apr-1不很熟悉，看出是因为没有正确找到libtool的路径导致的，所以修改了一下脚本apxs，强制指定libtool的位置，虽然这不是“优雅的”解决办法：</p>
<div class="mycode">
$locate libtool   #找到你的libtool<br />
......<br />
$vi /usr/sbin/apxs<br />
...<br />
my $libtool = `$apr_config --apr-libtool`;<br />
chomp($libtool);<br />
$libtool = "/usr/lib64/apr-1/build/libtool";<br />
...
</div>
<p>It works!  如果有个有更好的办法，或者知道根本原因的，请告诉我，谢过先。</p>
<div class="myt1">3. 配置该模块</div>
<p>强烈建议参考mod_proxy_html源码中的proxy_html.conf，这是一个示例配置文件，非常有指导意义。我这里的配置如下：</p>
<p>1. 加载必要的模块：</p>
<blockquote><p>LoadFile /usr/lib64/libxml2.so<br />
LoadModule xml2enc_module modules/mod_xml2enc.so<br />
LoadModule proxy_html_module modules/mod_proxy_html.so</p></blockquote>
<p>2. 指定需要修改的链接标签：</p>
<blockquote><p>SetOutputFilter proxy-html<br />
ProxyHTMLExtended On<br />
ProxyHTMLLinks  a           href<br />
ProxyHTMLLinks  area        href<br />
ProxyHTMLLinks  link        href<br />
ProxyHTMLLinks  img         src longdesc usemap<br />
ProxyHTMLLinks  object      classid codebase data usemap<br />
ProxyHTMLLinks  q           cite<br />
ProxyHTMLLinks  blockquote  cite<br />
ProxyHTMLLinks  ins         cite<br />
ProxyHTMLLinks  del         cite<br />
ProxyHTMLLinks  form        action<br />
ProxyHTMLLinks  input       src usemap<br />
ProxyHTMLLinks  head        profile<br />
ProxyHTMLLinks  base        href<br />
ProxyHTMLLinks  script      src for<br />
ProxyHTMLEvents onclick ondblclick onmousedown onmouseup \<br />
        onmouseover onmousemove onmouseout onkeypress \<br />
        onkeydown onkeyup onfocus onblur onload \<br />
        onunload onsubmit onreset onselect onchange</p></blockquote>
<p>3. 指定的URL替换规则：</p>
<pre>
<blockquote>
ProxyPassMatch ^/proxy_dir/(.*)$ http://yourdomain/$1
&lt;LocationMatch "/proxy_dir/">
        ProxyPassReverse /
        ProxyHTMLURLMap  / /proxy_dir/
        ProxyHTMLURLMap  http://some_(reg_exp)_url_(is_supported)you_wanna_replace/  /proxy_dir/$1/$2/ R
        RequestHeader    unset  Accept-Encoding
&lt;/LocationMatch></blockquote>
</pre>
<p>OK，That's all.</p>
<p><script type="text/javascript" src="http://www.xiami.com/widget/player-single?uid=318706&#038;sid=1769953757&#038;mode=js"></script></p>
<p>广告：<a href="http://www.orczhou.com/index.php/projects/we-are-hunting-mysql-hacker/">我们寻找靠谱的人</a> | <a href="http://www.orczhou.com/index.php/wish-list/">感谢作者</a></p>
<p>参考：</p>
<p>1. <a href="http://apache.webthing.com/mod_proxy_html/">mod_proxy_html主页</a></p>
<p>2. <a href="http://www.apachetutor.org/admin/reverseproxies">Running a Reverse Proxy in Apache</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2011/12/apache-reverse-proxy-with-mod-proxy-html/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TCP/IP重传超时--RTO</title>
		<link>http://www.orczhou.com/index.php/2011/10/tcpip-protocol-start-rto/</link>
		<comments>http://www.orczhou.com/index.php/2011/10/tcpip-protocol-start-rto/#comments</comments>
		<pubDate>Thu, 27 Oct 2011 12:19:27 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[技术细节]]></category>
		<category><![CDATA[retransmission timeout]]></category>
		<category><![CDATA[RTO]]></category>
		<category><![CDATA[TCP]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=3162</guid>
		<description><![CDATA[<p>概述：本文讨论主机在发送一个TCP数据包后，如果迟迟没有收到ACK，主机多久后会重传这个数据包。主机从发出数据包到第一次TCP重传开始，<a href="http://zh.wikipedia.org/wiki/RFC#RFC.E6.96.87.E4.BB.B6.E7.9A.84.E7.94.A2.E7.94.9F">RFC</a>中这段时间间隔称为retransmission timeout，缩写做RTO。本文会先看看RFC中如何定义RTO，然后看看Linux中如何实现。<strong>本文旨在分享</strong>：当遇到了TCP层问题改如何去查找、阅读文档，该如何去在Linux源码中寻求答案。</p>
<div class="myt1">1. 起源</div>
<p>在分析MySQL Semi-sync故障时，我们用Tcpdump+Wireshark（感谢淘宝雕梁）抓住当时的网络包传送细节，观察到了一次TCP重传最终导致了Semi-sync超时：</p>
<pre>
<div class="mycode">第一次传输
13:55:11.893291 master => slave	Binlog pos:319890197
重传：
13:55:12.094596	master => slave	Binlog pos:319890197
</div>
</pre>
<p>看到两次传送间隔约201毫秒，即第一次传输201毫秒后，还没有收到ACK响应，TCP认为传输超时，开始重传。</p>
<p>疑问：host和host之间的RTT大约是0.5毫秒，为什么第一次重传需要等200毫秒？（我希望是&#60;20ms）socket程序可以配置吗RTO吗？TCP有参数可配置RTO吗？</p>
<div class="myt1">2. Google/书籍/RFC</div>
<p>翻开TCP/IP详解找到关于TCP Retransmission章节，较详细的介绍TCP的超时机制，书中是个概述，于是又找到RFC1122。</p>
<p>RFC1122的4.2.2.15和4.2.3.1都介绍了Retransmission Timeout的处理（说来惭愧，这是第一次阅读TCP相关RFC）。</p>
<p>在<a href="http://www.ietf.org/rfc.html">RFC中搜索</a>Retransmission发现RFC 793 1122 2988 6298都有对重传算法、和初次重传超时的描述。于是开始阅读这个四个RFC，耗时约2小时，了解了大致的重传超时算法。[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2011/10/tcpip-protocol-start-rto/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>概述：本文讨论主机在发送一个TCP数据包后，如果迟迟没有收到ACK，主机多久后会重传这个数据包。主机从发出数据包到第一次TCP重传开始，<a href="http://zh.wikipedia.org/wiki/RFC#RFC.E6.96.87.E4.BB.B6.E7.9A.84.E7.94.A2.E7.94.9F">RFC</a>中这段时间间隔称为retransmission timeout，缩写做RTO。本文会先看看RFC中如何定义RTO，然后看看Linux中如何实现。<strong>本文旨在分享</strong>：当遇到了TCP层问题改如何去查找、阅读文档，该如何去在Linux源码中寻求答案。</p>
<div class="myt1">1. 起源</div>
<p>在分析MySQL Semi-sync故障时，我们用Tcpdump+Wireshark（感谢淘宝雕梁）抓住当时的网络包传送细节，观察到了一次TCP重传最终导致了Semi-sync超时：</p>
<pre>
<div class="mycode">第一次传输
13:55:11.893291 master => slave	Binlog pos:319890197
重传：
13:55:12.094596	master => slave	Binlog pos:319890197
</div>
</pre>
<p>看到两次传送间隔约201毫秒，即第一次传输201毫秒后，还没有收到ACK响应，TCP认为传输超时，开始重传。</p>
<p>疑问：host和host之间的RTT大约是0.5毫秒，为什么第一次重传需要等200毫秒？（我希望是<20ms）socket程序可以配置吗RTO吗？TCP有参数可配置RTO吗？</p>
<div class="myt1">2. Google/书籍/RFC</div>
<p>翻开TCP/IP详解找到关于TCP Retransmission章节，较详细的介绍TCP的超时机制，书中是个概述，于是又找到RFC1122。</p>
<p>RFC1122的4.2.2.15和4.2.3.1都介绍了Retransmission Timeout的处理（说来惭愧，这是第一次阅读TCP相关RFC）。</p>
<p>在<a href="http://www.ietf.org/rfc.html">RFC中搜索</a>Retransmission发现RFC 793 1122 2988 6298都有对重传算法、和初次重传超时的描述。于是开始阅读这个四个RFC，耗时约2小时，了解了大致的重传超时算法。<span id="more-3162"></span></p>
<div class="myt1">3. RFC中如何计算RTO(Retransmission Timeout)</div>
<div class="myt2">3.1 RFC-793如何计算RTO</div>
<p>概述：先根据该socket的RTT计算出SRTT（Smoothed Round Trip Time），然后根据一个最大、最小超时时间确定当前RTO。说明：srtt可以理解为“平滑化”的RTT，即在保持计算简单的情况尽量考虑历史RTT。</p>
<p>详细计算：SRTT = ( ALPHA * SRTT ) + ((1-ALPHA) * RTT)</p>
<p>基于SRTT，我们再来计算RTO：RTO = min[UBOUND,max[LBOUND,(BETA*SRTT)]]</p>
<p>UBOUND是RTO上线，ALPHA是平滑因子（smoothing factor， e.g., .8 to .9），BETA是一个延迟方差因子（BETA is a delay variance factor (e.g., 1.3 to 2.0)）。</p>
<p>仔细看这两个公式大概就能理解了RTO的计算了。</p>
<p>这里对上面两个公式做一个简单的注释：公式1中计算SRTT，ALPHA越接近于0，则表示SRTT越相信这一次的RTT；越接近于1，则表示SRTT越相信上次统计的RTT。公式二给RTO分别设置了一个上限和下限。</p>
<div class="myt2">3.2 RTO重传间隔是指数增加的</div>
<p>上面我们介绍的是初次重传时的RTO，如果重传后还没收到另一端的响应，下一次重传RTO则会指数增加，例如第一次重传RTO是1，之后分别2，4，8，16...。</p>
<div class="myt2">3.3 RFC-2988和RFC-6298中的RTO计算</div>
<p>在RFC-2988和RFC-6298中又重新改进了RTO的计算方法，Linux中的实现即使参考RFC-2988。算法核心公式：</p>
<pre>
<div class="mycode">初始：
SRTT <- R
RTTVAR <- R/2
RTO <- SRTT + max (G, K*RTTVAR)
where K = 4.

根据RTT计算SRTT：
RTTVAR <- (1 - beta) * RTTVAR + beta * |SRTT - R'|
SRTT <- (1 - alpha) * SRTT + alpha * R'

最后RTO：
RTO <- SRTT + max (G, K*RTTVAR)
</div>
</pre>
<div class="myt1">4. Linux中的RTO(Retransmission Timeout)</div>
<p>这里说的是RHEL5.4的2.6.18内核，RFC-2988实现参考net/ipv4/tcp_input.c中的tcp_rtt_estimator和tcp_set_rto。可以看到，在Linux中alpha=1/8，RTO最小为TCP_RTO_MIN。因为我们的系统中RTT总是很小，所以RTO取值总是能够取到TCP_RTO_MIN。</p>
<p>在看看TCP_RTO_MIN在Linux中的定义：</p>
<pre>
<div class="mycode">123#define TCP_RTO_MAX     ((unsigned)(120*HZ))
124#define TCP_RTO_MIN     ((unsigned)(HZ/5))
</div>
</pre>
<p>(这里简单的介绍介绍一下HZ，HZ可以理解为1s，所以120*HZ就是120秒，HZ/5就是200ms。详细的：HZ表示CPU一秒种发出多少次时间中断--IRQ-0，Linux中通常用HZ来做时间片的计算，<a href="http://blog.csdn.net/bdc995/article/details/4144031">参考</a>)</p>
<div class="myt1">5. 其他：Linux中可配置重传参数</div>
<p>/proc/sys/net/ipv4/tcp_retries1  (integer; default: 3)</p>
<p>TCP尝试了3次（tcp_retries1默认3）重传后，还没有收到ACK的话，则后续每次重传都需要network layer先更新路由。</p>
<p>/proc/sys/net/ipv4/tcp_retries2  (integer; default: 15)</p>
<p>TCP默认最多做15次重传。根据RTO(retransmission timeout)不同，最后一次重传间隔大概是13到30分钟左右。如果15次重传都做完了，TCP/IP就会告诉应用层说：“搞不定了，包怎么都传不过去！”</p>
<div class="myt1">6. 最后</div>
<p>回答前面的问题：即使RTT很小（0.8ms），但是因为RTO有下限，最小必须是200ms，所以这是RTT再小也白搭；RTO最小值是内核编译是决定的，socket程序中无法修改，Linux TCP也没有任何参数可以改变这个值。</p>
<p>好了，不容易。</p>
<p><script type="text/javascript" src="http://www.xiami.com/widget/player-single?uid=318706&#038;sid=382457&#038;mode=js"></script></p>
<p>广告：<a href="http://www.orczhou.com/index.php/projects/we-are-hunting-mysql-hacker/">我们寻找靠谱的人</a> | <a href="http://www.orczhou.com/index.php/wish-list/">感谢作者</a></p>
<p>参考文献</p>
<p>1. RFC 1122 ... (<a href="http://www.ietf.org/rfc.html">在哪儿查找RFC</a>) TCP协议相关的RFC：<br />
RFC 675 - Specification of Internet Transmission Control Program, December 1974 Version<br />
RFC 793 - TCP v4<br />
RFC 1122 - includes some error corrections for TCP<br />
RFC 1323 - TCP-Extensions<br />
RFC 1379 - Extending TCP for Transactions—Concepts<br />
RFC 1948 - Defending Against Sequence Number Attacks<br />
RFC 2018 - TCP Selective Acknowledgment Options<br />
RFC 2988 - Computing TCP's Retransmission Timer<br />
RFC 4614 - A Roadmap for TCP Specification Documents<br />
RFC 5681 - TCP Congestion Control</p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2011/10/tcpip-protocol-start-rto/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>也说快速关闭MySQL/InnoDB</title>
		<link>http://www.orczhou.com/index.php/2010/12/more-about-mysql-innodb-shutdown/</link>
		<comments>http://www.orczhou.com/index.php/2010/12/more-about-mysql-innodb-shutdown/#comments</comments>
		<pubDate>Wed, 01 Dec 2010 19:09:54 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[技术细节]]></category>
		<category><![CDATA[InnoDB]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=2166</guid>
		<description><![CDATA[<p>如果用的引擎是InnoDB，每次敲下mysqladmin -uroot -p shutdown关闭数据库的时候，总是很难预测这个命令会执行多久，实际经验表明，短则五秒，长则三十分钟一小时都有可能。也分享一下我的经验吧。</p>
<div class="myt1">1. 为什么InnoDB关闭会慢？</div>
<p>事实上，并不是每次关闭InnoDB都很慢的。Why？InnoDB较之MyISAM，一个重要特性是InnoDB会在内存中开辟一个<a href="http://www.orczhou.com/index.php/2009/08/innodb-dirty-page-redo-log-2/">Buffer Pool</a>来存储最近访问的数据块/索引块，使得下次再次访问这个块时速度能够很快。当InnoDB对需要修改数据块的时候，会先记录修改日志，然后直接对Buffer_Pool中的数据块的操作。记录日志是顺序写，对数据块的操作是内存操作，这让InnoDB在很多场景下有这很好的速度优势。[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/12/more-about-mysql-innodb-shutdown/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>如果用的引擎是InnoDB，每次敲下mysqladmin -uroot -p shutdown关闭数据库的时候，总是很难预测这个命令会执行多久，实际经验表明，短则五秒，长则三十分钟一小时都有可能。也分享一下我的经验吧。</p>
<div class="myt1">1. 为什么InnoDB关闭会慢？</div>
<p>事实上，并不是每次关闭InnoDB都很慢的。Why？InnoDB较之MyISAM，一个重要特性是InnoDB会在内存中开辟一个<a href="http://www.orczhou.com/index.php/2009/08/innodb-dirty-page-redo-log-2/">Buffer Pool</a>来存储最近访问的数据块/索引块，使得下次再次访问这个块时速度能够很快。当InnoDB对需要修改数据块的时候，会先记录修改日志，然后直接对Buffer_Pool中的数据块的操作。记录日志是顺序写，对数据块的操作是内存操作，这让InnoDB在很多场景下有这很好的速度优势。<span id="more-2166"></span></p>
<p>上面对内存块修改完成后，InnoDB就向客户端返回了。可这时实际磁盘上的数据块，还并没有被更新，我们把这样的page称为<a href="http://www.orczhou.com/index.php/2009/08/innodb-dirty-page-redo-log-2/">Dirty Page</a>。在InnoDB的后台有一个专门的线程来做将内存数据块Flush到磁盘的工作。这个Flush操作就是主要影响InnoDB关闭时间的因素。在关闭MySQL/InnoDB时，所有的Dirty_Page都需要Flush，<strong>所以Dirty_Page越多，要Flush的数据块也就越多，意味着InnoDB关闭时间越长</strong>。</p>
<p>我们可以通过下面的命令来观察Dirty Page的数量：</p>
<div class="mycode">mysqladmin -uroot ext -i 1 |grep "Innodb_buffer_pool_pages_dirty" </div>
<div class="myt1">2. 参数innodb_max_dirty_pages_pct</div>
<p>Buffer_Pool中Dirty_Page所占的数量，直接影响InnoDB的关闭时间。参数innodb_max_dirty_pages_pct可以直接控制了Dirty_Page在Buffer_Pool中所占的比率，而且幸运的是innodb_max_dirty_pages_pct是可以动态改变的。</p>
<p>所以，在关闭InnoDB之前先将innodb_max_dirty_pages_pct调小，强制数据块Flush一段时间，则能够大大缩短MySQL关闭的时间。</p>
<div class="mycode">set global innodb_max_dirty_pages_pct=0;</div>
<p>一般执行了上面的命令之后，Dirty_Page的Flush仍需要一段时间，所以要稍等一会儿，再关闭MySQL才有效果。</p>
<div class="myt1">3. 关闭前做些什么</div>
<p>有时候，就算你改变innodb_max_dirty_pages_pct=0，仍然不能保证InnoDB快速关闭。还有一些注意事项。</p>
<p>设置数据库为只读：如果数据库一直是活跃的，有连接在向里面写数据，那么Dirty Page则还可能不断的产生。</p>
<p>如果是备库，在innodb_max_dirty_pages_pct设置成0的同时，最好先stop slave：这个很关键，而且对关闭时间影响也会很大。第一，主动stop slave后，MySQL在关闭时，需要停止的线程其实是更少了的。第二，如果slave的SQL线程还在执行的话，Buffer Pool则还在活动，Dirty Page也可能还会不断的增多。</p>
<p>一般，如果注意到了上面三点：</p>
<div class="myt2">set global innodb_max_dirty_pages_pct=0</div>
<div class="myt2">set global read_only=1</div>
<div class="myt2">stop slave</div>
<p>关闭数据库，就会很快了。如果你把上面三步做完，然后观察Dirty Page的数量，当数量很少时，再执行命令关库，这样总能保证以较快的速度完成关库命令。</p>
<div class="myt1">4. 一个注意事项</div>
<p>这里需要注意的是，你不需等到Dirty Page的数量到零，才开始关闭MySQL。因为有时候，即使已经没有活动的会话时，InnoDB的Insert Buffer的合并仍然会产生一些Dirty Page，所以这时你可能会发现，等了很久很久很久Dirty Page的数量仍然大于零。</p>
<p>其实这时，你已经可以快速的关闭数据库了。我怎么判断这种情况呢？这时我们可以通过InnoDB的LSN来判断，下面是SHOW InnoDB Status里面获取的信息：</p>
<pre>
<div class="mycode">Log sequence number 814 3121743145
Log flushed up to   814 3121092043
Last checkpoint at  814 2826361389
</div>
</pre>
<p>这里看到，当前的LSN是814 3121743145，最后一个检查点在814 2826361389，也就是说两者相差了3121743145-2826361389=295381756，那么意味着InnoDB还有很多Dirty Page需要Flush。</p>
<p>下面是另一个库的LSN信息：</p>
<pre>
<div class="mycode">Log sequence number 0 1519256161
Log flushed up to   0 1519256161
Last checkpoint at  0 1519256161</div>
</pre>
<p>可以看到，这里的Dirty Page都已经Flush了，那么关闭InnoDB也就很快了。</p>
<p>一般，并不需要等到最后检查点和当前LSN相等才关闭，两者只要相差不多（<1000）关闭起来就很快了。</p>
<div class="myt1">5. 最后再罗嗦一下</div>
<p>我这个人写博客很罗嗦的，各位看官在忍耐一下吧。</p>
<p>其实，像上面这样折腾，整个关库的过程有可能并不比你直接执行mysqladmin -uroot shutdown快，但是执行上面的步骤，会让你清楚关库到底与多久，可以让你的关库命令能够在一个可以预期的时间内完成。简单的说，会让关库的时间心里有个底。</p>
<p>当你希望一切都心里有底的时候，那你就需要注意上面提到的一些细节。</p>
<div class="myt1">参考文档</div>
<p>1. <a href="http://www.mysqlperformanceblog.com/2009/04/15/how-to-decrease-innodb-shutdown-times/">How to decrease InnoDB shutdown times</a></p>
<p>2. <a href="http://dbahacker.com/mysql/innodb-fast-shutdown">测试快速关闭innodb的方法</a></p>
<p>3. MySQL/InnoDB Manual</p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/12/more-about-mysql-innodb-shutdown/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>使用getopts处理Shell脚本参数</title>
		<link>http://www.orczhou.com/index.php/2010/04/linux-shell-getopt/</link>
		<comments>http://www.orczhou.com/index.php/2010/04/linux-shell-getopt/#comments</comments>
		<pubDate>Wed, 21 Apr 2010 11:48:21 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[技术细节]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[shell]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=1822</guid>
		<description><![CDATA[<p>编写一个shell脚本，做一些事；改进这个脚本，更好做这件事；再改进这个脚本，帮自己做些其他的事情；再改进这个脚本帮助其他人做一些事......</p>
<p>简单的脚本处理，一般使用变量$0 $1 $2 ...就可以依次获得全部参数，还可以通过$#获得这个脚本一共有多少个参数。如果你需要处理的情况（或者分支）更多的时候，这个方法就不凑效了，这时候，就可以考虑使用getopts了（man getopts）。[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/04/linux-shell-getopt/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>编写一个shell脚本，做一些事；改进这个脚本，更好做这件事；再改进这个脚本，帮自己做些其他的事情；再改进这个脚本帮助其他人做一些事......</p>
<p>简单的脚本处理，一般使用变量$0 $1 $2 ...就可以依次获得全部参数，还可以通过$#获得这个脚本一共有多少个参数。如果你需要处理的情况（或者分支）更多的时候，这个方法就不凑效了，这时候，就可以考虑使用getopts了（man getopts）。<span id="more-1822"></span></p>
<p>这里将通过一个示例来介绍getopts的用法。</p>
<p>下面的代码，可以通过"./sample -d 5"的方式获取参数：</p>
<pre>
<div class="mycode">vi sample.sh
#!/bin/sh
day=7  #default value
while getopts ":d:" opt; do
  case $opt in
    d)
      day=$OPTARG   #get the value
      ;;
    ?)
      echo "How to use: $0 [-d DAY]" >&#038;2
      exit 1
      ;;
    &#58;)
      echo "Option -$OPTARG requires an argument." >&#038;2
      exit 1
      ;;
  esac
done
echo $day
</div>
</pre>
<p>上面例子中需要解释的是下面的部分：</p>
<pre>
<div class="mycode">while getopts ":d:" opt; do</div>
</pre>
<p>这里，第一个冒号表示忽略错误（例如出现了不认识的参数），并在脚本中通过:&#58;)来处理这样的错误；字母d则表示，接受参数-d；d后面的冒号表示参数d接收值，即“-d 7”这样的形式；（这里opt变量，可以在while循环中引用当前找到的参数，试试输出$opt试试）</p>
<p>如果是要有很多参数，那么写法可能是：</p>
<pre>
<div class="mycode">while getopts ":ixarm:uneh" opt; do</div>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/04/linux-shell-getopt/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux iostat监测IO状态</title>
		<link>http://www.orczhou.com/index.php/2010/03/iostat-detail/</link>
		<comments>http://www.orczhou.com/index.php/2010/03/iostat-detail/#comments</comments>
		<pubDate>Mon, 01 Mar 2010 05:13:28 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[技术细节]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=1228</guid>
		<description><![CDATA[<p>Linux系统出现了性能问题，一般我们可以通过top、iostat、free、vmstat等命令来查看初步定位问题。其中iostat可以给我们提供丰富的IO状态数据。</p>
<p><font color="red">1. 基本使用</font></p>
<pre>
<div class="mycode">$iostat -d -k 1 10</div>
</pre>
<p>参数 -d 表示，显示设备（磁盘）使用状态；-k某些使用block为单位的列强制使用Kilobytes为单位；1 10表示，数据显示每隔1秒刷新一次，共显示10次。[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/03/iostat-detail/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>Linux系统出现了性能问题，一般我们可以通过top、iostat、free、vmstat等命令来查看初步定位问题。其中iostat可以给我们提供丰富的IO状态数据。</p>
<p><font color="red">1. 基本使用</font></p>
<pre>
<div class="mycode">$iostat -d -k 1 10</div>
</pre>
<p>参数 -d 表示，显示设备（磁盘）使用状态；-k某些使用block为单位的列强制使用Kilobytes为单位；1 10表示，数据显示每隔1秒刷新一次，共显示10次。<span id="more-1228"></span></p>
<pre>
<div class="mycode">$iostat -d -k 1 10
Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
sda              39.29        21.14         1.44  441339807   29990031
sda1              0.00         0.00         0.00       1623        523
sda2              1.32         1.43         4.54   29834273   94827104
sda3              6.30         0.85        24.95   17816289  520725244
sda5              0.85         0.46         3.40    9543503   70970116
sda6              0.00         0.00         0.00        550        236
sda7              0.00         0.00         0.00        406          0
sda8              0.00         0.00         0.00        406          0
sda9              0.00         0.00         0.00        406          0
sda10            60.68        18.35        71.43  383002263 1490928140

Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
sda             327.55      5159.18       102.04       5056        100
sda1              0.00         0.00         0.00          0          0
</div>
</pre>
<p><font color="blue">tps</font>：该设备每秒的传输次数（Indicate  the  number  of  transfers  per  second that were issued to the device.）。“一次传输”意思是“一次I/O请求”。多个逻辑请求可能会被合并为“一次I/O请求”。“一次传输”请求的大小是未知的。</p>
<p><font color="blue">kB_read/s</font>：每秒从设备（drive expressed）读取的数据量；<font color="blue">kB_wrtn/s</font>：每秒向设备（drive expressed）写入的数据量；<font color="blue">kB_read</font>：读取的总数据量；<font color="blue">kB_wrtn</font>：写入的总数量数据量；这些单位都为Kilobytes。</p>
<p>上面的例子中，我们可以看到磁盘sda以及它的各个分区的统计数据，当时统计的磁盘总TPS是39.29，下面是各个分区的TPS。（因为是瞬间值，所以总TPS并不严格等于各个分区TPS的总和）</p>
<p><font color="red">2. -x 参数</font></p>
<p>使用-x参数我们可以获得更多统计信息。</p>
<pre>
<div class="mycode">iostat -d -x -k 1 10
Device:    rrqm/s wrqm/s   r/s   w/s  rsec/s  wsec/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await  svctm  %util
sda          1.56  28.31  7.80 31.49   42.51    2.92    21.26     1.46     1.16     0.03    0.79   2.62  10.28
Device:    rrqm/s wrqm/s   r/s   w/s  rsec/s  wsec/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await  svctm  %util
sda          2.00  20.00 381.00  7.00 12320.00  216.00  6160.00   108.00    32.31     1.75    4.50   2.17  84.20
</div>
</pre>
<p><font color="blue">rrqm/s</font>：每秒这个设备相关的读取请求有多少被Merge了（当系统调用需要读取数据的时候，VFS将请求发到各个FS，如果FS发现不同的读取请求读取的是相同Block的数据，FS会将这个请求合并Merge）；wrqm/s：每秒这个设备相关的写入请求有多少被Merge了。</p>
<p><font color="blue">rsec/s</font>：每秒读取的扇区数；<font color="blue">wsec/</font>：每秒写入的扇区数。<font color="blue">r/s</font>：The number of read requests that were issued to the device per second；<font color="blue">w/s</font>：The number of write requests that were issued to the device per second；</p>
<p><font color="blue">await</font>：每一个IO请求的处理的平均时间（单位是<del datetime="2011-03-01T08:00:29+00:00">微秒</del>毫秒）。这里可以理解为IO的响应时间，一般地系统IO响应时间应该低于5ms，如果大于10ms就比较大了。</p>
<p><font color="blue">%util</font>：在统计时间内所有处理IO时间，除以总共统计时间。例如，如果统计间隔1秒，该设备有0.8秒在处理IO，而0.2秒闲置，那么该设备的%util = 0.8/1 = 80%，所以该参数暗示了设备的繁忙程度。一般地，如果该参数是100%表示设备已经接近满负荷运行了（当然如果是多磁盘，即使%util是100%，因为磁盘的并发能力，所以磁盘使用未必就到了瓶颈）。</p>
<p><font color="red">3. -c 参数</font></p>
<p>iostat还可以用来获取cpu部分状态值：</p>
<pre>
<div class="mycode"><strong>iostat -c 1 10</strong>
avg-cpu:  %user   %nice    %sys %iowait   %idle
           1.98    0.00    0.35   11.45   86.22
avg-cpu:  %user   %nice    %sys %iowait   %idle
           1.62    0.00    0.25   34.46   63.67</div>
</pre>
<p><font color="red">4. 常见用法</font></p>
<pre>
<div class="mycode">$iostat -d -k 1 10        #查看TPS和吞吐量信息
iostat -d -x -k 1 10      #查看设备使用率（%util）、响应时间（await）
iostat -c 1 10            #查看cpu状态
</div>
</pre>
<p><font color="red">5. 实例分析</font></p>
<pre>
<div class="mycode">$$iostat -d -k 1 |grep sda10
Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
sda10            60.72        18.95        71.53  395637647 1493241908
sda10           299.02      4266.67       129.41       4352        132
sda10           483.84      4589.90      4117.17       4544       4076
sda10           218.00      3360.00       100.00       3360        100
sda10           546.00      8784.00       124.00       8784        124
sda10           827.00     13232.00       136.00      13232        136</div>
</pre>
<p>上面看到，磁盘每秒传输次数平均约400；每秒磁盘读取约5MB，写入约1MB。</p>
<pre>
<div class="mycode">iostat -d -x -k 1
Device:    rrqm/s wrqm/s   r/s   w/s  rsec/s  wsec/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await  svctm  %util
sda          1.56  28.31  7.84 31.50   43.65    3.16    21.82     1.58     1.19     0.03    0.80   2.61  10.29
sda          1.98  24.75 419.80  6.93 13465.35  253.47  6732.67   126.73    32.15     2.00    4.70   2.00  85.25
sda          3.06  41.84 444.90 54.08 14204.08 2048.98  7102.04  1024.49    32.57     2.10    4.21   1.85  92.24</div>
</pre>
<p>可以看到磁盘的平均响应时间<5ms，磁盘使用率>80。磁盘响应正常，但是已经很繁忙了。</p>
<p>参考文献：</p>
<ol>
<li>Linux man iostat</li>
<li><a href="http://www.xaprb.com/blog/2010/01/09/how-linux-iostat-computes-its-results/">How Linux iostat computes its results</a></li>
<li><a href="http://www.mjmwired.net/kernel/Documentation/iostats.txt">Linux iostat</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/03/iostat-detail/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Linux本地磁盘（硬盘）介绍</title>
		<link>http://www.orczhou.com/index.php/2010/02/linux-disk-hardware/</link>
		<comments>http://www.orczhou.com/index.php/2010/02/linux-disk-hardware/#comments</comments>
		<pubDate>Wed, 24 Feb 2010 12:05:27 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[技术细节]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=1243</guid>
		<description><![CDATA[<p>本文试图从硬件结构、逻辑结构两方面来介绍Linux中本地磁盘的基本知识。</p>
<p><font color="red">1. 硬件结构</font></p>
<p>一块磁盘由多个盘面组成，每个盘面有一个对应的<strong>磁头（heads）</strong>，我们可以在该盘面上存储我们需要的信息。</p>
<p>每一个盘面，被分成多个圆环，我们称之为<strong>柱面（cylinders）</strong>。每一个柱面又被分割为多个扇形区域，简称<strong>扇区（sectors）</strong>，参见下图：</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/4381155033/" title="磁盘扇区 by orczhou, on Flickr"><img src="http://farm3.static.flickr.com/2725/4381155033_048953a69d_o.png" width="392" height="275" alt="磁盘扇区" /></a>（图片来源<a href="http://www.eygle.com/">www.eygle.com/</a>）</p>
<p>两个红色环形之间是一个柱面；图中黄色标记部分是一个扇区。[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/02/linux-disk-hardware/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>本文试图从硬件结构、逻辑结构两方面来介绍Linux中本地磁盘的基本知识。</p>
<p><font color="red">1. 硬件结构</font></p>
<p>一块磁盘由多个盘面组成，每个盘面有一个对应的<strong>磁头（heads）</strong>，我们可以在该盘面上存储我们需要的信息。</p>
<p>每一个盘面，被分成多个圆环，我们称之为<strong>柱面（cylinders）</strong>。每一个柱面又被分割为多个扇形区域，简称<strong>扇区（sectors）</strong>，参见下图：</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/4381155033/" title="磁盘扇区 by orczhou, on Flickr"><img src="http://farm3.static.flickr.com/2725/4381155033_048953a69d_o.png" width="392" height="275" alt="磁盘扇区" /></a>（图片来源<a href="http://www.eygle.com/">www.eygle.com/</a>）</p>
<p>两个红色环形之间是一个柱面；图中黄色标记部分是一个扇区。<span id="more-1243"></span></p>
<p><font color="red">2. fdisk -l 查看磁盘情况</font></p>
<pre>
<div class="mycode">#fdisk -l
Disk /dev/sda: 73.2 GB, 73274490880 bytes
255 heads, 63 sectors/track, 8908 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1          16      128488+  83  Linux
/dev/sda2              17        1928    15358140   83  Linux
/dev/sda3            1929        2189     2096482+  82  Linux swap / Solaris
/dev/sda4            2190        8908    53970367+   5  Extended
/dev/sda5            2190        2450     2096451   82  Linux swap / Solaris
/dev/sda6            2451        2711     2096451   82  Linux swap / Solaris
/dev/sda7            2712        2972     2096451   82  Linux swap / Solaris
/dev/sda8            2973        8908    47680888+  83  Linux
</div>
</pre>
<p>可以看到，上面的磁盘/dev/sda有255个磁头，8908个柱面，每个柱面轨道上有63个扇区。因为每个扇区大小为512bytes，所以总大小约为：8908 * 63 * 512 * 255 = 73270794240(和上面的73274490880 相差3696640，不知道是什么地方多出来的)。</p>
<p><font color="red">3. 磁盘分区、文件系统、挂载点</font></p>
<p>我们继续看上面的例子，磁盘/dev/sda共分为8个分区：/dev/sda1，/dev/sda2，......，/dev/sda8，Start和End分别为各个分区的起始和结束柱面。例如分区/dev/sda2从柱面17开始到柱面1928结束，则大小约为（1928-17）* 63 * 512 * 255 = 14.6G。</p>
<p>可以通过df -h来查看分区/dev/sda2的挂载目录：</p>
<pre>
<div class="mycode">#df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/sda2              15G   13G  1.1G  92% /
/dev/sda1             122M   20M   96M  17% /boot
tmpfs                 2.0G  669M  1.4G  33% /dev/shm
/dev/sda8              45G   26G   17G  62% /u01
</div>
</pre>
<p>可以看到，分区/dev/sda2挂载根目录 / 上。然后，通过文件/etc/fstab来查看该分区的文件系统：</p>
<pre>
<div class="mycode">#more /etc/fstab
LABEL=/                 /                       ext3    defaults        1 1
LABEL=/boot1            /boot                   ext3    defaults        1 2
devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
tmpfs                   /dev/shm                tmpfs   defaults        0 0
proc                    /proc                   proc    defaults        0 0
sysfs                   /sys                    sysfs   defaults        0 0
LABEL=/u01              /u01                    ext3    defaults        1 2
LABEL=SWAP-sda5         swap                    swap    defaults        0 0
LABEL=SWAP-sda6         swap                    swap    defaults        0 0
LABEL=SWAP-sda7         swap                    swap    defaults        0 0
LABEL=SWAP-sda3         swap                    swap    defaults        0 0
</div>
</pre>
<p>看到根目录 / 使用了Linux ext3文件系统。</p>
<p>至此，我们看到磁盘/dev/sda共分为八个分区。其中第二个分区/dev/sda2从柱面17到柱面1928结束，大小约为15G，该分区挂载在根目录 / 上，使用了ext3的文件系统。</p>
<p><font color="red">4. 其他磁盘分区</font></p>
<p>在上面的fdisk -l 列表中我们一共可以看到八个磁盘分区。上面已经分析了/dev/sda2，我们再看来看看其他的分区。</p>
<ul>
<li>在Linux中第一个分区/dev/sda1，为引导扇区，挂载在/boot上</li>
<li>第二个分区挂载在根目录 / 上，大小15G，为ext3文件系统</li>
<li>第三个分区为交换分区，从柱面1929到2189，大小约：（2189 - 1929）* 63 * 512 * 255 = 2G。</li>
<li>第四个分区为扩展分区，后面四个分区均是该分区的一部分。该分区从柱面2190到8908结束。</li>
<li>分区五、六、七为扩展分区的子分区，均为交换分区，单个大小均为2G。所以全部的交换分区大小共2G + 2G*3 = 8G。</li>
<li>第八个分区为扩展分区的最后一个子分区，挂载在/u01上，大小约为 （8908 - 2973）* 63 * 512 * 255 = 45G。</li>
</ul>
<p>以上为示例主机上的全部磁盘分区，以及挂载情况。</p>
<p><font color="red">5. 小结</font></p>
<p>有了以上信息，我们就基本清楚了硬件结构以及对应的逻辑结构，需要使用的命令其实也很少：</p>
<div class="mycode">
#fdisk -l<br />
#df -h<br />
#more /etc/fstab
</div>
<p><font color="red">6. 补充</font></p>
<pre>
<div class="mycode"># This file is edited by fstab-sync - see 'man fstab-sync' for details
LABEL=/1                /                       ext3    defaults        1 1
LABEL=/boot1            /boot                   ext3    defaults        1 2
......
</div>
</pre>
<p>关于/etc/fstab的简单补充：</p>
<ul>
<li>第一列是：一般是设备（分区），如也有是LABEL=×××的，这是ext2 ext3特有的卷标，可以通过e2label来查看各个设备的卷标。</li>
<li>第二列是：挂载点</li>
<li>第三列是：文件系统</li>
<li>第四列是：文件系统挂载时候的参数（例如NFS有时候需要用户名密码等）</li>
<li>第五列是：标志是否需要备份1为需要，0为不需要</li>
<li>第六列是：启动时，是否需要fsck检查磁盘1为先检查，2为后检查，0为不检查</li>
</ul>
<p>在fstab文件还有部分第一列是none的，这些都是不需要物理磁盘的文件系统。典型的如/proc文件系统：</p>
<pre>
<div class="mycode">none                    /dev/shm                tmpfs   defaults        0 0
none                    /proc                   proc    defaults        0 0
none                    /sys                    sysfs   defaults        0 0
</div>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/02/linux-disk-hardware/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Linux下C语言连接MySQL</title>
		<link>http://www.orczhou.com/index.php/2010/01/linux-c-mysql-api/</link>
		<comments>http://www.orczhou.com/index.php/2010/01/linux-c-mysql-api/#comments</comments>
		<pubDate>Tue, 26 Jan 2010 11:21:30 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[技术细节]]></category>
		<category><![CDATA[Linux Programming]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=1323</guid>
		<description><![CDATA[<p>相对于Window下的设置，Linux要简单的很多。</p>
<p><font color="red">1. 示例程序</font></p>
<pre>
<div class="mycode">$ vi mysql-c-api.c
#include &#60;stdio.h&#62;
#include "mysql/mysql.h"
int main(){
        MYSQL mysql;
        mysq[......]</div></pre><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/01/linux-c-mysql-api/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>相对于Window下的设置，Linux要简单的很多。</p>
<p><font color="red">1. 示例程序</font></p>
<pre>
<div class="mycode">$ vi mysql-c-api.c
#include &lt;stdio.h&gt;
#include "mysql/mysql.h"
int main(){
        MYSQL mysql;
        mysql_init(&#038;mysql);
        mysql_options(&#038;mysql,MYSQL_OPT_COMPRESS,0);
        mysql_options(&#038;mysql,MYSQL_INIT_COMMAND,"SET autocommit=0");
        if (!mysql_real_connect(&#038;mysql,"10.2.3.4","user","passwd","database",0,NULL,0))
        {
                fprintf(stderr, "Failed to connect to database: Error: %sn",
                mysql_error(&#038;mysql));
        }
        return 0;
}
</div>
</pre>
<p><font color="red">2. 编译命令</font></p>
<p>在MySQL编译后的二进制文件中，有一个很有用的二进制文件“<a href="http://dev.mysql.com/doc/refman/5.0/en/mysql-config.html">mysql_config</a>”（一般和mysql命令在同一个目录下），我们可以通过这个程序找到需要的头文件和连接库：</p>
<pre>
<div class="mycode">$ gcc -o a.out $(mysql_config --cflags) <strong>mysql-c-api.c</strong> $(mysql_config --libs)
$ ./a.out
</div>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/01/linux-c-mysql-api/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Sphinx之配置篇</title>
		<link>http://www.orczhou.com/index.php/2010/01/sphinx-how-to-2/</link>
		<comments>http://www.orczhou.com/index.php/2010/01/sphinx-how-to-2/#comments</comments>
		<pubDate>Thu, 07 Jan 2010 12:33:31 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[技术细节]]></category>
		<category><![CDATA[sphinx]]></category>

		<guid isPermaLink="false">http://orczhou.com/?p=619</guid>
		<description><![CDATA[<p style="text-indent:0">
<strong>作者：</strong>周振兴 <a href="http://www.flickr.com/photos/26825745@N06/3933896112/" title="mail by orczhou, on Flickr"><img src="http://farm3.static.flickr.com/2614/3933896112_8ebfd42177_o.png" width="163" height="22" alt="mail" /></a><br />
<strong>摘要：</strong>MySQL提供了LIKE子句可以很好的实现对数据库中数据的模糊搜索，使用LIKE很多时候可以帮助我们解决一些站内搜索的问题。但是随着MySQL中数据量逐渐增加，LIKE模糊查询的效率将成为MySQL数据库的主要压力。这时候<a href="http://orczhou.com/index.php/2009/08/sphinx-1/">尝试使用sphinx</a>，可以很好的帮你解决这个问题。本文将继续介绍具体如何部署sphinx。在阅读本文前，建议先浏览<a href="http://orczhou.com/index.php/2009/08/sphinx-1/">sphinx使用介绍(前篇)</a>，或者跳过本文直接<a href="http://www.sphinxsearch.com/docs/">RTFM</a>。
</p>
<p>[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/01/sphinx-how-to-2/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p style="text-indent:0">
<strong>作者：</strong>周振兴 <a href="http://www.flickr.com/photos/26825745@N06/3933896112/" title="mail by orczhou, on Flickr"><img src="http://farm3.static.flickr.com/2614/3933896112_8ebfd42177_o.png" width="163" height="22" alt="mail" /></a><br />
<strong>摘要：</strong>MySQL提供了LIKE子句可以很好的实现对数据库中数据的模糊搜索，使用LIKE很多时候可以帮助我们解决一些站内搜索的问题。但是随着MySQL中数据量逐渐增加，LIKE模糊查询的效率将成为MySQL数据库的主要压力。这时候<a href="http://orczhou.com/index.php/2009/08/sphinx-1/">尝试使用sphinx</a>，可以很好的帮你解决这个问题。本文将继续介绍具体如何部署sphinx。在阅读本文前，建议先浏览<a href="http://orczhou.com/index.php/2009/08/sphinx-1/">sphinx使用介绍(前篇)</a>，或者跳过本文直接<a href="http://www.sphinxsearch.com/docs/">RTFM</a>。
</p>
<p><span id="more-619"></span></p>
<p style="text-indent:0">
+++++++++++++++++++++++++++++++++<br />
<strong>正文：</strong><br />
+++++++++++++++++++++++++++++++++
</p>
<p style="text-indent:0"><font size=4><b>一、下载，安装</b></font></p>
<p>下载<a href="http://www.sphinxsearch.com/downloads.html">sphinx-0.9.8.1.tar.gz</a>(在撰写本文时，0.9.8.1是最新版)</p>
<p>安装过程够简洁，在GNU/Linux 2.6.24  Ubuntu 8.04.3 LTS:</p>
<blockquote><pre>
tar zxvf sphinx-0.9.8.1.tar.gz
cd sphinx-0.9.8.1
./configure    #这里你可以加上你的安装路径如：./configure --prefix=/usr/local/sphinx
make
sudo make install</pre>
</blockquote>
<p style="text-indent:0"><font size=4><b>二、配置sphinx的数据源</b></font></p>
<p>sphinx主要有两个进程indexer和searchd，在他们运行前，我们需要配置好sphinx.conf文件，告诉他们如何工作。(默认配置文件的路径/usr/local/etc/)</p>
<p>首先，我需要配置一个数据源(source)，告诉indexer到哪儿去获取原始数据。例如，我们将配置一个名为mars的数据源：</p>
<blockquote><pre>
cd /usr/local/etc/
cp sphinx.conf.dist sphinx.conf
vi sphinx.conf
source mars
{
	type = mysql
	sql_host = localhost
	sql_user = sphinxuser
	sql_pass = sI9sWb2uFYxz
	sql_db = discuzdb
	sql_sock = /var/run/mysqld/mysqld.sock
	sql_port = 3306
	……
</pre>
</blockquote>
<p>这里我配置了一个名为mars的数据源。数据类型是mysql，表示indexer将从mysql中获取我要的数据(除了mysql，sphinx还支持很多种类的数据源，如文本，xml文档等)。这里还配置了访问该数据库的主机名、用户、密码、数据库名、数据库端口、以及连接该数据库的socket文件。(注意：配置文件中有一些多余的source需要删除，如source src1throttled : src1)</p>
<p>现在indexer知道去那个数据库取数据了，但是具体取哪个数据表中的哪些列的数据呢？好，我们继续。编写需要索引的数据列的SQL，例如我需要索引字段为cdb_thread.threadtitle，那么sphinx需要用到的SQL是:</p>
<blockquote><pre>
	sql_query = SELECT tid AS id, threadtitle FROM cdb_threads;
}
</pre>
</blockquote>
<p>我们注意到，我们的query中一个时id字段，第二个才是我们需要索引的内容。sphinx有这样的规则，你的Query中获取的第一个字段必须是一个唯一的数字（使用自增的字段是很方便的），后面才是你需要索引的内容（另外，也可以你想排序的字段，这里暂不介绍）。</p>
<p>到这，我们就完成了一个数据源的配置，这里的我们的数据源名字是mars（如果需要，我们还可以配置更多数据源，例如Juliet等）。</p>
<p style="text-indent:0"><font size=4><b>三、配置sphinx的索引任务</b></font></p>
<p>现在有了：数据源以及需要索引的内容。接下来，我们需要在配置文件中，指定一个某个索引任务去索引前面的内容。这个索引任务应该包含：数据源用哪一个、数据数据放在什么目录下、以及其他的一些索引算法的细节参数。</p>
<blockquote><pre>
index explorer{
	source = mars              #还记得前面建立的source吧？
	path    = /var/data/sphinx/catalog    #任务的工作空间在哪，sphinx需要一些空间建立索引
	morphology              = stem_en      #搜索的一些“词法”  我也不知道这是什么！:-(

	min_word_len            = 3   
	min_prefix_len          = 0   
	min_infix_len           = 3   
}
</pre>
</blockquote>
<p style="text-indent:0"><font size=4><b>四、sphinx搜索配置</b></font></p>
<p>最后，还剩一点点工作。那就是配置searchd，你甚至保持默认就可以了。searchd中定义了一些搜索行为，例如当搜索结果集太大的时候，最多返回多少个（默认1000），等等。</p>
<p>搞定配置文件，98%的事情就算完成了。</p>
<p style="text-indent:0"><font size=4><b>五、启动、使用Sphinx</b></font></p>
<p>所有工作都已经就绪了，现在是倒计时启动的时间了。</p>
<p>执行indexer，对数据进行一次索引。</p>
<blockquote><p>
sudo /usr/local/bin/indexer [&#45;&#45;rotate] &#45;&#45;config /usr/local/etc/sphinx.conf explorer<br />
//这里的explorer就是前面配置文件中一个索引任务
</p></blockquote>
<p>启动搜索相应进程（searchd）：</p>
<blockquote><p>
sudo mkdir -p /var/data/sphinx<br />
sudo /usr/local/bin/searchd &#45;&#45;config /usr/local/etc/sphinx.conf &#038;
</p></blockquote>
<p>好了，搞定。我们来测试一下吧：（早就迫不及待了吧）</p>
<blockquote><p>
例如查找字符串 “ENG”<br />
sudo usr/local/bin/search &#45;&#45;config /usr/local/etc/sphinx.conf ENG<br />
看看你的搜索结果
</p></blockquote>
<p>如果搜索结果正常，到这，我们就完成全部的sphinx配置和启动工作。在接下来的一篇文章中我将介绍如果使用php向sphinx发送搜索请求，并处理返回结果。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/01/sphinx-how-to-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>php中session过期时间设置</title>
		<link>http://www.orczhou.com/index.php/2009/12/php-session/</link>
		<comments>http://www.orczhou.com/index.php/2009/12/php-session/#comments</comments>
		<pubDate>Wed, 02 Dec 2009 11:07:21 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[技术细节]]></category>

		<guid isPermaLink="false">http://orczhou.com/?p=1033</guid>
		<description><![CDATA[<p>网上很多人给出了<a href="http://www.google.cn/search?hl=zh-CN&#038;source=hp&#038;q=php+session+%E8%BF%87%E6%9C%9F&#038;btnG=Google+%E6%90%9C%E7%B4%A2&#038;aq=f&#038;oq=">解答</a>：修改php配置文件中的session.gc_maxlifetime。如果想了解更多session回收机制，继续阅读。（本文环境php5.2）</p>
<p>概述：每一次php请求，会有1/100的概率（默认值）触发“session回收”。如果“session回收”发生，那就会检查/tmp/sess_*的文件，如果最后的修改时间到现在超过了1440秒（gc_maxlifetime的值），就将其删除，意味着这些session过期失效。[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2009/12/php-session/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>网上很多人给出了<a href="http://www.google.cn/search?hl=zh-CN&#038;source=hp&#038;q=php+session+%E8%BF%87%E6%9C%9F&#038;btnG=Google+%E6%90%9C%E7%B4%A2&#038;aq=f&#038;oq=">解答</a>：修改php配置文件中的session.gc_maxlifetime。如果想了解更多session回收机制，继续阅读。（本文环境php5.2）</p>
<p>概述：每一次php请求，会有1/100的概率（默认值）触发“session回收”。如果“session回收”发生，那就会检查/tmp/sess_*的文件，如果最后的修改时间到现在超过了1440秒（gc_maxlifetime的值），就将其删除，意味着这些session过期失效。<span id="more-1033"></span></p>
<p><font color="red">1. session在server端（一般是Apache with PHP module）如何存在的？</font> </p>
<p>默认的，php会将session保存在/tmp目录下，文件名为这个样子：sess_01aab840166fd1dc253e3b4a3f0b8381。每一个文件对应了一个session（会话）。</p>
<div class="mycode">
more /tmp/sess_01aab840166fd1dc253e3b4a3f0b8381<br />
username|s:9:"jiangfeng";admin|s:1:"0";<br />
#变量名|类型:长度:值
</div>
<p>删除这里的session文件，就表示对应的session失效了。</p>
<p><font color="red">2. session在client端（一般是浏览器）如何存在的？</font> </p>
<p>session在浏览器端，只需要保存session ID（由server端生成的唯一ID）就可以了。有两种保存方式：在cookie中、在url里面。如果cookie中保存session ID，就可以看到浏览器的cookie中有一个PHPSESID变量。如果是URL传递的，就可以看到形如:<br />
index.php?PHPSESID=01aab840166fd1dc253e3b4a3f0b8381的URL。（在server端通过session.use_cookies来控制使用哪一种方式）</p>
<p><font color="red">3. 在server端，php如何判断session文件是否过期？</font> </p>
<p>如果"最后的修改时间"到"现在"超过了gc_maxlifetime（默认是1440）秒，这个session文件就被认为是过期了，在下一次session回收的时候，如果这个文件仍然没有被更改过，这个session文件就会被删除（session就过期了）。</p>
<p>简单的说，如果我登录到某网站，如果在1440秒（默认值）内没有操作过，那么对应的session就认为是过期了。</p>
<p>所以，修改php.ini文件中的gc_maxlifetime变量就可以延长session的过期时间了：（例如，我们把过期时间修改为86400秒）</p>
<div class="mycode">
session.gc_maxlifetime = 86400
</div>
<p>然后，重启你的web服务（一般是apache）就可以了。</p>
<p>注意：php5里面session过期使用了回收机制。这里设置时间为86400秒，如果session在86400秒内没有被修改过，那么在下一次“回收”时才真的被删除。</p>
<p><font color="red">3. session“回收”何时发生？</font> </p>
<p>默认情况下，每一次php请求，就会有1/100的概率发生回收，所以可能简单的理解为“每100次php请求就有一次回收发生”。这个概率是通过以下参数控制的</p>
<div class="mycode">
#概率是gc_probability/gc_divisor<br />
session.gc_probability = 1<br />
session.gc_divisor     = 100
</div>
<p>注意1：假设这种情况gc_maxlifetime=120，如果某个session文件最后修改时间是120秒之前，那么在下一次回收（1/100的概率）发生前，这个session仍然是有效的。</p>
<p>注意2：如果你的session使用session.save_path中使用别的地方保存session，session回收机制有可能不会自动处理过期session文件。这时需要定时手动（或者crontab）的删除过期的session：cd /path/to/sessions; find -cmin +24 | xargs rm</p>
<p><font color="red">4. 一些特殊情况</font> </p>
<p>因为回收机制会检查文件的“最后修改时间”，所以如果某个会话是活跃的，但是session的内容没有改变过，那么对应的session文件也就没有改变过，回收机制会认为这是一个长时间没有活跃的session而将其删除。这是我们不愿看到的，可以通过增加如下的简单代码解决这个问题：</p>
<pre>
<div class="mycode">&lt;?php
if(!isset($_SESSION['last_access'])||(time()-$_SESSION['last_access'])>60)
	$_SESSION['last_access'] = time();
?&gt;
</div>
</pre>
<p>代码会每隔60秒，尝试修改修改一次session。</p>
<p>总结：如果想修改session过期时间，修改变量gc_maxlifetime就可以了。php5的session采用被动的回收机制（garbage collection）。过期的session文件不会自己消失，而是通过触发“回收”来处理过期的session。</p>
<p>参考文献：</p>
<ol>
<li><a href="http://cn.php.net/manual/en/book.session.php">php Manual</a></li>
<li>php.ini文件</li>
<li><a href="http://www.openphp.cn/blog.php?blog_id=10">PHP会话(Session)使用入门</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2009/12/php-session/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>mysqld随Linux开机自动启动</title>
		<link>http://www.orczhou.com/index.php/2009/11/mysqldauto/</link>
		<comments>http://www.orczhou.com/index.php/2009/11/mysqldauto/#comments</comments>
		<pubDate>Thu, 26 Nov 2009 03:11:41 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[技术细节]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://orczhou.com/?p=1004</guid>
		<description><![CDATA[<p>在MySQL的管理过程中，偶尔会遇到一些PC Server宕机或者重启，这时我需要在主机启动后再将MySQL服务启动。一般情况下，这项工作都是简单的。但是，当面临上百台或者更多的MySQL主机的时候，这种“偶尔”可能会很多，这种“偶尔”还会在半夜或者凌晨发生，如果每次都手动操作，这是很繁琐的。更重要的是，如果因此而打断了凌晨的美梦是不值得的。</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/4132795867/" title="LinuxMySQL-2 by orczhou, on Flickr"><img src="http://farm3.static.flickr.com/2660/4132795867_0e8c895ca4_o.png" width="341" height="268" alt="LinuxMySQL-2" /></a></p>
<p>[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2009/11/mysqldauto/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>在MySQL的管理过程中，偶尔会遇到一些PC Server宕机或者重启，这时我需要在主机启动后再将MySQL服务启动。一般情况下，这项工作都是简单的。但是，当面临上百台或者更多的MySQL主机的时候，这种“偶尔”可能会很多，这种“偶尔”还会在半夜或者凌晨发生，如果每次都手动操作，这是很繁琐的。更重要的是，如果因此而打断了凌晨的美梦是不值得的。</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/4132795867/" title="LinuxMySQL-2 by orczhou, on Flickr"><img src="http://farm3.static.flickr.com/2660/4132795867_0e8c895ca4_o.png" width="341" height="268" alt="LinuxMySQL-2" /></a></p>
<p><span id="more-1004"></span></p>
<p>要实现随开启自动启动mysqld，我们需要搞定如下几个问题：</p>
<p><font color="red">1. Linux开机自动启动脚本放在哪儿？</font> </p>
<p>一般的，作为服务器使用的Linux一般会以“完全多用户模式(Multi-User Mode with Networking)”级别来启动，这种情况下Linux在启动时会运行/etc/rc.d/rc3.d/下的全部脚本。例如我们在这个目录下会看到脚本"/etc/rc.d/rc3.d/S90crond"，意味着开机启动时会运行S90crond脚本。</p>
<p><font color="red">2. Linux如何运行这些脚本？</font> </p>
<p>既然已经知道自动启动脚本该放在哪儿了，一切就好办。我们只需要将一个启动MySQL的脚本放过去就好了。下面是我们的一个简单的启动脚本v0.1 mysqldauto</p>
<div class="mycode">
$vi mysqldauto<br />
#!/bin/sh<br />
# Version: 0.1 by orczhou@gmail.com<br />
/opt/mysql/bin/mysqld_safe --user=mysql &#038;      #这里需要修改为你的mysqld_safe目录<br />
$chmod +x mysqldauto<br />
$mv mysqldauto /etc/rc.d/init.d/<br />
$ln -s /etc/rc.d/init.d/mysqldauto /etc/rc.d/rc3.d/S99mysqld
</div>
<p>这样我们就把创建的mysqldauto脚本放到了/etc/rc.d/rc3.d/下面（注意这里使用了link的方式），mysqld可以自动启动了。</p>
<p>这有两个问题需要解释：</p>
<p>* <font color="red"> * </font>* <font color="red">*</font> * 为什么不直接在目录/etc/rc.d/rc3.d/下创建文件，而要创建一个软连接？这并不是必须的。但是这样做是有很多好处的（后面会解释），不过这样做至少会看起来更加专业。</p>
<p>* <font color="red"> * </font>* <font color="red">*</font> * 为什么文件名要用S99mysqld？这是规则，在rc3.d下面的脚本如果以字母S开头，那么执行时Linux会给它传递一个start参数（如果以字母K开头，则会传递stop参数）；S后面接个数字，表示了这个脚本的启动顺序，如果目录rc3.d下还有S98*那么它会在S99之前运行。（看到这儿，你可以猜测一下前面我们为什么要创建软连接了）</p>
<p>好了，自此你的mysqld就已经可以随Linux开机自动启动了。</p>
<p><font color="red">3. 怎样做更专业些？</font> </p>
<p>上面这样做，已经可以解决问题了，不过写法多少有些“山寨”，下面我们看看怎样改造一下。</p>
<p>* <font color="red"> * </font>* <font color="red">*</font> * 改造1：处理参数start。既然前面提到以字母S开头的脚本会自动传递一个参数start，K则会传递stop。那么我再做如下修改：</p>
<pre>
<div class="mycode">
$vi mysqldauto
#!/bin/sh
# Version: 0.2 by orczhou@gmail.com
MYSQLHOME=/opt/mysql                         #这里需要修改为你的mysql安装目录
if [ $# -ge 1 ];then
        mysqldProc=`ps -ef|grep -E "mysqld.+safe"|grep -v "grep"|wc -l`
        if [ $1 = "stop" ] ;then
                if [ $mysqldProc -eq 1 ];then
                        $MYSQLHOME/bin/mysqladmin -uroot shutdown
	fi
        elif [ $1 = "start" ];then
                if [ $mysqldProc -eq 0 ];then
                        $MYSQLHOME/bin/mysqld_safe --user=mysql &#038;
	        fi
        fi
fi
</div>
</pre>
<p>做了如此改造后，我们脚本需要接收两个参数start stop了。如果你想让你mysqld在关闭主机的时候自动关闭，那么stop参数就可以起作用了：</p>
<div class="mycode">
$ln -s /etc/rc.d/init.d/mysqldauto /etc/rc.d/rc0.d/K20mysqld
</div>
<p>这里做软连接好处就体现出来了，启动和关闭只需要用一个脚本就可以了。</p>
<p>* <font color="red"> * </font>* <font color="red">*</font> * 改造2：当你面临几十上百台主机的时候，MySQL的启动参数可能会不一样，例如备库启动时可能需要mysqld_safe --user=mysql --read_olny=1 &#038;，这种情况怎么办呢？这里提供一个解决思路。</p>
<p>在主机上运行一个脚本，探测当前mysqld的启动参数，然后写到一个指定的文件里。最后，在你的启动脚本中，读取这个文件里面的启动参数来启动mysqld。It works。</p>
<p>最后，祝你睡个好觉，做个好梦。</p>
<p>（全文完）</p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2009/11/mysqldauto/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

