<?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; MySQL</title>
	<atom:link href="http://www.orczhou.com/index.php/tag/mysql/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>MySQL/InnoDB和Group Commit(2)</title>
		<link>http://www.orczhou.com/index.php/2011/12/time-to-group-commit-2/</link>
		<comments>http://www.orczhou.com/index.php/2011/12/time-to-group-commit-2/#comments</comments>
		<pubDate>Tue, 20 Dec 2011 15:51:20 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Group Commit]]></category>
		<category><![CDATA[InnoDB]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=3397</guid>
		<description><![CDATA[<p>今天发现Percona Release的<a href="http://www.mysqlperformanceblog.com/2011/12/19/percona-server-5-5-18-23-0-now-with-group-commit/">Percona-Server-5-5-18-23-0</a>已经完成了Group Commit工作，而且是用最优雅的方式（移植了MariaDB的实现，而不是workaround），心里难掩激动。</p>
<p>这篇文章接<a href="http://www.orczhou.com/index.php/2010/08/time-to-group-commit-1/">前篇</a>继续介绍一下问题的背景：什么是Group Commit，现在的官方版本Group Commit做到了什么程度？</p>
<div class="myt1">1. 什么是Group Commit</div>
<p>MySQL/InnoDB在做事务的时候使用的日志先行（<a href="http://en.wikipedia.org/wiki/Write-ahead_logging">Write-ahead logging</a>）的方式保证事务的快速和持久，所以每次事务完成都要求日志必须持久化到磁盘，在Linux上对应的操作就是“<a href="http://www.orczhou.com/index.php/2009/08/innodb_flush_method-file-io/">write and fsync</a>”，write速度是很快的，一般对应的写Page Cache，而fsync则要求文件系统把对应文件的哦Page Cache必须持久化到磁盘上，而对于传统磁盘一次写操作大约需要1~3ms（不说BBU），这意味着对于传统磁盘1秒钟最多最多做333～1000个事务，这是不理想的，对硬件的利用率（吞吐量）也是非常低。</p>
<p>所以，这里就有了Group操作的概念，即当好几个线程都要fsync同一个日志文件时，我们将这些fsync合并到一次来做。简单举例：我们让系统每2ms做一次fsync，这样如果这2ms内有100个线程都需要做fsync，那就赚到了，原本100个fsync都独立来做那耗时会非常多的。</p>
<p>你肯定会说，难道这不是很简单的想法吗？是的，这就是原本是很简单、也很自然的想法。</p>
<p>但对MySQL来说却变成了一种奢求（看看这个<a href="http://bugs.mysql.com/bug.php?id=13669">Bug讨论</a>）。</p>
<div class="myt1">2. 为啥MySQL一直没有实现？</div>
<p>MySQL是不是太弱了，这么简单的事情都搞不定？不是的。</p>
<p>MySQL从开源到现在，成功的一个非常重要的原因，就是MySQL的插件式架构。如果MySQL只是MyISAM估计不会有现在的流行程度，插件式的架构让诸如Heikki Tuuri有了发挥空间，在InnoDB和MySQL一起时，MySQL才能算是一个真正的DBMS。再到后来，有Infobright、 FEDERATED、PBXT等等。</p>
<p>插件式的架构给MySQL带来了活力，做出牺牲便是在上层(MySQL)和下层(存储引擎)交互时带来的额外消耗，有时甚至上层和下层需要做一些重复工作。无法做Group Commit就是这其中的牺牲之一。</p>
<p>[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2011/12/time-to-group-commit-2/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>今天发现Percona Release的<a href="http://www.mysqlperformanceblog.com/2011/12/19/percona-server-5-5-18-23-0-now-with-group-commit/">Percona-Server-5-5-18-23-0</a>已经完成了Group Commit工作，而且是用最优雅的方式（移植了MariaDB的实现，而不是workaround），心里难掩激动。</p>
<p>这篇文章接<a href="http://www.orczhou.com/index.php/2010/08/time-to-group-commit-1/">前篇</a>继续介绍一下问题的背景：什么是Group Commit，现在的官方版本Group Commit做到了什么程度？</p>
<div class="myt1">1. 什么是Group Commit</div>
<p>MySQL/InnoDB在做事务的时候使用的日志先行（<a href="http://en.wikipedia.org/wiki/Write-ahead_logging">Write-ahead logging</a>）的方式保证事务的快速和持久，所以每次事务完成都要求日志必须持久化到磁盘，在Linux上对应的操作就是“<a href="http://www.orczhou.com/index.php/2009/08/innodb_flush_method-file-io/">write and fsync</a>”，write速度是很快的，一般对应的写Page Cache，而fsync则要求文件系统把对应文件的哦Page Cache必须持久化到磁盘上，而对于传统磁盘一次写操作大约需要1~3ms（不说BBU），这意味着对于传统磁盘1秒钟最多最多做333～1000个事务，这是不理想的，对硬件的利用率（吞吐量）也是非常低。</p>
<p>所以，这里就有了Group操作的概念，即当好几个线程都要fsync同一个日志文件时，我们将这些fsync合并到一次来做。简单举例：我们让系统每2ms做一次fsync，这样如果这2ms内有100个线程都需要做fsync，那就赚到了，原本100个fsync都独立来做那耗时会非常多的。</p>
<p>你肯定会说，难道这不是很简单的想法吗？是的，这就是原本是很简单、也很自然的想法。</p>
<p>但对MySQL来说却变成了一种奢求（看看这个<a href="http://bugs.mysql.com/bug.php?id=13669">Bug讨论</a>）。</p>
<div class="myt1">2. 为啥MySQL一直没有实现？</div>
<p>MySQL是不是太弱了，这么简单的事情都搞不定？不是的。</p>
<p>MySQL从开源到现在，成功的一个非常重要的原因，就是MySQL的插件式架构。如果MySQL只是MyISAM估计不会有现在的流行程度，插件式的架构让诸如Heikki Tuuri有了发挥空间，在InnoDB和MySQL一起时，MySQL才能算是一个真正的DBMS。再到后来，有Infobright、 FEDERATED、PBXT等等。</p>
<p>插件式的架构给MySQL带来了活力，做出牺牲便是在上层(MySQL)和下层(存储引擎)交互时带来的额外消耗，有时甚至上层和下层需要做一些重复工作。无法做Group Commit就是这其中的牺牲之一。</p>
<p><span id="more-3397"></span></p>
<div class="myt1">3. 现在的官方版本Group Commit做到了什么程度</div>
<p>InnoDB在一次事务中，有两次fsync，分别是prepare阶段和commit阶段，对于这两次操作InnoDB都是<a href="http://www.innodb.com/wp/products/innodb_plugin/plugin-performance/innodb-plugin-1-0-4-group-commit-test-sysbench/">实现了Group操作</a>的。</p>
<p>如果你设置了binlog_sync=1，则又多了一次fsync操作（参考MYSQL_BIN_LOG::flush_and_sync），这次操作在innobase_xa_prepare和innodb_commit之间：</p>
<blockquote><p>binlog_prepare (do nothing)<br />
innodb_prepare<br />
...<br />
binlog_commit<br />
innobase_commit</p></blockquote>
<p><strong>这次fsync，在MySQL中一直都无法做Group</strong>。所以，一直以来当innodb_flush_log_at_trx_commit和binlog_sync都等于1的时候，MySQL的性能就<a href="http://www.mysqlperformanceblog.com/2011/07/13/testing-the-group-commit-fix/">非常、非常糟糕</a>。原因是为了保证InnoDB<strong>内部Commit</strong>的顺序和MySQL日志的顺序一致（<a href="http://www.orczhou.com/index.php/2010/08/time-to-group-commit-1/">参考</a>）。</p>
<div class="myt1">4. 开源社区的努力</div>
<p>难道没人去解决这个问题吗？不是的。有很多人已经做出了努力，直到Kristian Nielsen@MariaDB提出了理论和实际上的最优解决方案。</p>
<p>Mark Callaghan@Facebook关于Group Commit做的工作：<a href="http://www.facebook.com/note.php?note_id=386328905932">FB的基本实现</a> | <a href="http://www.facebook.com/note.php?note_id=438641125932">FB的问题和改进</a> | <a href="http://www.facebook.com/note.php?note_id=10150261692455933">性能</a> | <a href="http://www.facebook.com/note.php?note_id=10150211546215933">对比MariaDB的实现</a>(对比的结果是MariaDB完胜，无论是方案架构还是性能)</p>
<p>Facebooke可以说是通过<a href="http://www.facebook.com/note.php?note_id=386328905932">一个Trick</a>快速实现了Group Commit。而Kristian Nielsen@MariaDB这是从架构上根本的解决了这个问题。</p>
<p>MariaDB关于Group Commit架构和实现： <a href="http://askmonty.org/worklog/Server-Sprint/?tid=116">WL116</a> | <a href="http://askmonty.org/worklog/Server-Sprint/?tid=132">WL132</a> | <a href="http://askmonty.org/worklog/Server-RawIdeaBin/?tid=164">WL164</a></p>
<div class="myt1">5. 未完待续</div>
<p>后面还有很多值得写的，我希望自己能够一直写完这个系列。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2011/12/time-to-group-commit-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Percona-Server/MySQL响应时间统计</title>
		<link>http://www.orczhou.com/index.php/2011/09/thanks-percona-response-time-distribution/</link>
		<comments>http://www.orczhou.com/index.php/2011/09/thanks-percona-response-time-distribution/#comments</comments>
		<pubDate>Mon, 19 Sep 2011 13:02:57 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Percona MySQL]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=3068</guid>
		<description><![CDATA[<p>在Percona的5.1.53和5.5.8版本，开始将RT的统计内置到MySQL Server端。Thanks, Percona.</p>
<p>Percona在提供了<a href="http://www.mysqlperformanceblog.com/2010/08/31/introducing-tcprstat-a-tcp-response-time-tool/">tcprstat工具</a>统计RT时间之后，很快就在Percona Server中集成了响应时间统计的功能。这里介绍一下该功能，各位看官如果在犹豫选择<a href="http://www.percona.com/software/percona-server/">Percona Server</a>还是<a href="http://dev.mysql.com/downloads/mysql/">MySQL Community Server</a>，这里给Percona Server加一个筹码。</p>
<p>"响应时间"（Response time，后面简称RT）在数据库应用中，特别是OLTP的场景，可以说非常重要，不过，MySQL官方版本中一直没有加上这个统计功能。最早，社区开始尝试tcmdump+perl脚本的方式查看RT，后来告警一点用<a href="http://ronaldbradford.com/blog/take-a-look-at-mk-query-digest-2009-10-08/">tcpdump+mk-query-digest</a>，再后来Ignacio Nin和Baron Schwartz完成了tcprstat。</p>
<p>在Percona的5.1.53和5.5.8版本，开始将RT的统计内置到MySQL Server端。</p>
<div class="myt1">1. 配置该功能</div>
<p>这个是功能默认是关闭的，可以通过设置参数query_response_time_stats=1打开这个功能，这是一个动态参数，所以在服务器上可以很方便的打开或者关闭这个功能。可以通过命令SHOW QUERY_RESPONSE_TIME查看响应时间统计。[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2011/09/thanks-percona-response-time-distribution/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>在Percona的5.1.53和5.5.8版本，开始将RT的统计内置到MySQL Server端。Thanks, Percona.</p>
<p>Percona在提供了<a href="http://www.mysqlperformanceblog.com/2010/08/31/introducing-tcprstat-a-tcp-response-time-tool/">tcprstat工具</a>统计RT时间之后，很快就在Percona Server中集成了响应时间统计的功能。这里介绍一下该功能，各位看官如果在犹豫选择<a href="http://www.percona.com/software/percona-server/">Percona Server</a>还是<a href="http://dev.mysql.com/downloads/mysql/">MySQL Community Server</a>，这里给Percona Server加一个筹码。</p>
<p>"响应时间"（Response time，后面简称RT）在数据库应用中，特别是OLTP的场景，可以说非常重要，不过，MySQL官方版本中一直没有加上这个统计功能。最早，社区开始尝试tcmdump+perl脚本的方式查看RT，后来告警一点用<a href="http://ronaldbradford.com/blog/take-a-look-at-mk-query-digest-2009-10-08/">tcpdump+mk-query-digest</a>，再后来Ignacio Nin和Baron Schwartz完成了tcprstat。</p>
<p>在Percona的5.1.53和5.5.8版本，开始将RT的统计内置到MySQL Server端。</p>
<div class="myt1">1. 配置该功能</div>
<p>这个是功能默认是关闭的，可以通过设置参数query_response_time_stats=1打开这个功能，这是一个动态参数，所以在服务器上可以很方便的打开或者关闭这个功能。可以通过命令SHOW QUERY_RESPONSE_TIME查看响应时间统计。<span id="more-3068"></span></p>
<div class="myt2">默认关闭状态：</div>
<pre>
<div class="mycode">SHOW QUERY_RESPONSE_TIME;
+----------------+---+----------------+
|                |   |                |
+----------------+---+----------------+
|       0.000001 | 0 |       0.000000 |
|       0.000010 | 0 |       0.000000 |
|       0.000100 | 0 |       0.000000 |
|       0.001000 | 0 |       0.000000 |
......
|  1000000.00000 | 0 |       0.000000 |
| TOO LONG       | 0 | TOO LONG       |
+----------------+---+----------------+</div>
</pre>
<div class="myt2">打开参数后：</div>
<pre>
<div class="mycode">set global query_response_time_stats=1;
Query OK, 0 rows affected (0.00 sec)

08:23:13>SHOW QUERY_RESPONSE_TIME;
+----------------+-------+----------------+
|                |       |                |
+----------------+-------+----------------+
|       0.000001 |  7982 |       0.000000 |
|       0.000010 | 13927 |       0.014511 |
|       0.000100 | 43811 |       1.553811 |
|       0.001000 |    29 |       0.007936 |
|       0.010000 |     1 |       0.001711 |
|       0.100000 |     0 |       0.000000 |
</div>
</pre>
<p>这里可以看到，响应时间小于0.000001<strong>秒</strong>的SQL有7982个，0.000001 &lt; RT &lt;  0.000010有13927个， 0.000010 &lt; RT &lt; 0.000100有43811  ...... 。这样整个系统的响应时间就清晰了。</p>
<div class="myt1">2. INFORMATION_SCHEMA.QUERY_RESPONSE_TIME</div>
<p>除了用命令SHOW QUERY_RESPONSE_TIME，还可以通过INFORMATION_SCHEMA里面的表QUERY_RESPONSE_TIME来查看MySQL的响应时间。</p>
<pre>
<div class="mycode">08:25:16>SELECT * from INFORMATION_SCHEMA.QUERY_RESPONSE_TIME;
+----------------+--------+----------------+
| time           | count  | total          |
+----------------+--------+----------------+
|       0.000001 |  33824 |       0.000000 |
|       0.000010 |  59222 |       0.060457 |
|       0.000100 | 186003 |       6.659908 |
|       0.001000 |    117 |       0.025163 |
|       0.010000 |      8 |       0.014534 |</div>
</pre>
<p>使用INFORMATION_SCHEMA.QUERY_RESPONSE_TIME，就可以稍微定制一下输出结果了：</p>
<pre>
<div class="mycode">
SELECT time, CONCAT(ROUND(100*count/query_count,2),"%") as percent,count
FROM(
    SELECT
    	c.count,
    	c.time,
    	(
    	    SELECT SUM(a.count)
    	    FROM INFORMATION_SCHEMA.QUERY_RESPONSE_TIME as a
    	    WHERE a.count != 0
    	) as query_count
    FROM INFORMATION_SCHEMA.QUERY_RESPONSE_TIME as c
    WHERE c.count > 0
) d;
+----------------+---------+--------+
| time           | percent | count  |
+----------------+---------+--------+
|       0.000001 | 12.64%  |  81557 |
|       0.000010 | 20.69%  | 133440 |
|       0.000100 | 66.62%  | 429705 |
|       0.001000 | 0.05%   |    339 |
|       0.010000 | 0.00%   |     10 |
|    1000.000000 | 0.00%   |      1 |
+----------------+---------+--------+
</div>
</pre>
<div class="myt1">3. 高端一点的功能：定制化时间区间</div>
<p>默认的时间区间是：</p>
<p>(0; 10 ^ -6], (10 ^ -6; 10 ^ -5], (10 ^ -5; 10 ^ -4], ..., (10 ^ -1; 10 ^1], (10^1; 10^2]...(10^</p>
<p>你可以通过修改参数query_response_time_range_base来缩小时间区间，默认该参数是10，时间区间如上。</p>
<p>例如我们修个set global query_response_time_range_base=2; 则区间如下：</p>
<p>(0; 2 ^ -19], (2 ^ -19; 2 ^ -18], (2 ^ -18; 2 ^ -17], ..., (2 ^ -1; 2 ^1], (2 ^ 1; 2 ^ 2]...(2 ^</p>
<p>第一个区间总是最接近0.000001区间开始(2^19次方最接近)；最后一个区间是到最接近且小于10000000处结束。</p>
<p>具体的：</p>
<div class="mycode">set global query_response_time_range_base=2;<br />
flush QUERY_RESPONSE_TIME;<br />
SHOW QUERY_RESPONSE_TIME;<br />
|       0.000001 |  711 |       0.000000 |<br />
|       0.000003 | 1456 |       0.001456 |<br />
......<br />
|       0.125000 |    0 |       0.000000 |<br />
|       0.250000 |    0 |       0.000000 |<br />
|       0.500000 |    0 |       0.000000 |<br />
|       1.000000 |    0 |       0.000000 |<br />
|       2.000000 |    0 |       0.000000 |<br />
|       4.000000 |    0 |       0.000000 |<br />
|       8.000000 |    0 |       0.000000 |<br />
|      16.000000 |    0 |       0.000000 |<br />
......<br />
|  8388608.00000 |    0 |       0.000000 |<br />
| TOO LONG       |    0 | TOO LONG       |
</div>
<div class="myt1">一些注意事项</div>
<p>在备库上，只有打开了参数<a href="log_slow_slave_statements">log_slow_slave_statements</a>(这个参数也是Percona Server上特有的)时，slave sql线程的SQL才会被统计。</p>
<p>启动变量have_response_time_distribution，不能配置，该参数只表示该Server是否具有上述的RT统计功能。</p>
<p>设置了query_response_time_range_base，必须flush QUERY_RESPONSE_TIME后才生效。这里flush做两件事情，使得QUERY_RESPONSE_TIME生效，另外清空之前的统计结果。</p>
<p>累了，听首歌，扭扭脖子吧:)</p>
<p><script type="text/javascript" src="http://www.xiami.com/widget/player-single?uid=0&#038;sid=3599116&#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://www.mysqlperformanceblog.com/2010/08/31/introducing-tcprstat-a-tcp-response-time-tool/">Introducing tcprstat, a TCP response time tool</a></p>
<p>2. <a href="http://www.percona.com/docs/wiki/tcprstat:start">The tcprstat User's Manual</a></p>
<p>3. <a href="http://www.megalinux.net/using-tcpdump-for-mysql-query-logging/">Using Tcpdump for MySQL query logging</a></p>
<p>4. <a href="http://www.mysqlperformanceblog.com/2011/04/18/how-to-use-tcpdump-on-very-busy-hosts/">How to use tcpdump on very busy hosts</a></p>
<p>5. <a href="http://ronaldbradford.com/blog/take-a-look-at-mk-query-digest-2009-10-08/">Take a look at mk-query-digest</a></p>
<p>6. <a href="http://www.percona.com/docs/wiki/percona-server:features:implementation_details:details_response_time_distribution">Implementation Details: Response Time Distribution</a></p>
<p>7. <a href="http://www.percona.com/docs/wiki/percona-server:features:response_time_distribution">Percona Software Documentation :Response Time Distribution</a></p>
<p>【附录】</p>
<p>既然上面读取INFORMATION_SCHEMA的SQL已经有些复杂了，那就不怕再复杂一些：</p>
<pre>
<div class="mycode">SELECT
    case TRIM(time)
    	when '0.000001' then '<  1us'
    	when '0.000010' then '< 10us'
    	when '0.000100' then '<100us'
    	when '0.001000' then '<  1ms'
    	when '0.010000' then '< 10ms'
    	when '0.100000' then '<100ms'
    	when '1.000000' then '<   1s'
    	when '10.000000' then '<  10s'
    	when '100.000000' then '<100s'
    	else '>100s'
    END as `RT area`,
    CONCAT(ROUND(100*count/query_count,2),"%") as percent,
    count
FROM(
    SELECT
    	c.count,
    	c.time,
    	(
    	    SELECT SUM(a.count)
    	    FROM INFORMATION_SCHEMA.QUERY_RESPONSE_TIME as a
    	    WHERE a.count != 0
    	) as query_count
    FROM INFORMATION_SCHEMA.QUERY_RESPONSE_TIME as c
    WHERE c.count > 0
) d;
+---------+---------+--------+
| RT area | percent | count  |
+---------+---------+--------+
| < 10us  | 41.92%  | 404409 |
| <100us  | 21.34%  | 205881 |
| <  1ms  | 35.20%  | 339574 |
| < 10ms  | 1.30%   |  12586 |
| <100ms  | 0.18%   |   1736 |
| <   1s  | 0.02%   |    160 |
| <  10s  | 0.04%   |    340 |
+---------+---------+--------+
</div>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2011/09/thanks-percona-response-time-distribution/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL Federated引擎实现多主一备</title>
		<link>http://www.orczhou.com/index.php/2011/04/mysql-federated-multiple-master-single-slave/</link>
		<comments>http://www.orczhou.com/index.php/2011/04/mysql-federated-multiple-master-single-slave/#comments</comments>
		<pubDate>Thu, 14 Apr 2011 15:50:39 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Federated]]></category>
		<category><![CDATA[multiple master]]></category>
		<category><![CDATA[single slave]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=2893</guid>
		<description><![CDATA[多主一丛,multiple master, single slave[......]<p class='read-more'><a href='http://www.orczhou.com/index.php/2011/04/mysql-federated-multiple-master-single-slave/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>偶尔我们需要这样做。</p>
<p>在一台主机上如果复制多个主库，可以很简单：在备库主机上启动多个MySQL实例（<a href="http://dev.mysql.com/doc/refman/5.1/en/mysqld-multi.html">mysqld_muilt</a>），每个实例使用一个端口，复制多台主库就可以了。但是如果希望复制过来的数据都在同一个实例中，事情就复杂了。</p>
<div class="myt1">1. 问题以及开源社区的尝试</div>
<p>需求：多个主库都复制到一个备库，<strong>复制过来的数据都在一个实例中</strong>，这样实现对应用层的透明。目前，MySQL本身不提供这样的功能(<a href="http://forge.mysql.com/worklog/task.php?id=1697">考虑过，但一直未实现</a>)。</p>
<p><span style="color:blue">1.1</span> 开源社区也有很多方案和想法，例如Tungsten replicator：可以支持异构数据复制，JAVA实现，数据量较大时，性能较差，而且MySQL版本更新较快，而该软件支持较慢；</p>
<p><span style="color:blue">1.2</span> 再如<a href="http://www.penglixun.com/tech/program/how_to_mysql_multi_master.html">P.Linux的尝试</a>：通过拉取多个主库上的Binlog在单个备库上应用的方式，对MySQL没有入侵，实现也较为简单。</p>
<p><span style="color:blue">1.3</span> <a href="http://www.amazon.com/High-Performance-MySQL-Optimization-Replication/dp/0596101716">High Performance MySQL</a>还可以通过多级复制的方式实现：例如有主库DB1、DB2，都需要复制到S1上，则先配置D2复制D1的全部数据，然后再从S1上复制D2的全部数据就可以了。整理需要注意D2上的<a href="http://dev.mysql.com/doc/refman/5.0/en/replication-options-slave.html#option_mysqld_log-slave-updates">log-slave-update</a>需要打开，为了减少D2的压力，DB上复制过来的表可以全部使用<a href="http://dev.mysql.com/doc/refman/5.1/en/blackhole-storage-engine.html">Blackhole引擎</a>。</p>
<p><span style="color:blue">1.4 </span>另外，还有一些<a href="http://shinguz.blogspot.com/2008/10/mysql-multi-master-single-slave.html">欠成熟的Patch</a>实现。</p>
<div class="myt1">2. MySQL Federated引擎的“软链接”特性</div>
<p><a href="http://dev.mysql.com/doc/refman/5.1/en/federated-storage-engine.html">Federated引擎</a>可以在本地数据库中创建一个远端数据表的“软链接”。这样，访问远端数据表就如同访问本地数据表。（这里远端可以是不同主机上的不同实例）</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/5619434764/" title="se-federated-structure by orczhou, on Flickr"><img src="http://farm6.static.flickr.com/5309/5619434764_bfd3966949.jpg" width="475" height="269" alt="se-federated-structure"></a></p>
<div class="myt1">3. 使用Federated数据表实现多主一备</div>
<p>例如，主库DB1，DB2，都需要复制到S1上，这里DB1，DB2，S1分别在主机H1，H2，H3上。首先，在主机H3(S1所在的主机)上其另外起两个MySQL实例S2、S3，分别复制主库DB1和DB2，然后在实例S1上建立多个Federated表关联S2、S3中的数据表（相当于在S1中建立到S2、S3的软连接）。</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/5618939777/" title="Federated-Muilt-master by orczhou, on Flickr"><img src="http://farm6.static.flickr.com/5063/5618939777_2b3daaa899.jpg" width="500" height="439" alt="Federated-Muilt-master"></a></p>
<p>这时应用，连接到S1，就可以<strong>同时透明的</strong>访问到DB1、DB2中的<strong>实时</strong>数据表了。</p>
<div class="myt1">4. 一些说明</div>
<p><span style="color:blue">4.1</span> 上面描述的方法，也可以直接在S1上建立到DB1、DB2各个表的软连接，但是为了最大程度的减少对主库的影响，最好如上多配一层备库。</p>
<p><span style="color:blue">4.2</span> 最简单的可以直接在备库上配置多个实例，让应用连接多个实例就好了，无需上面这么复制的配置。</p>
<p>参考文献：</p>
<p>1. <a href="http://dev.mysql.com/doc/refman/5.1/en/federated-storage-engine.html">MySQL Manual: The FEDERATED Storage Engine</a></p>
<p>2. <a href="http://forums.mysql.com/read.php?26,238241,256703#msg-256703">Post of MySQL Forums : Multiple masters to single slave</a></p>
<p>3. <a href="http://bbs.chinaunix.net/viewthread.php?tid=1487796">通过tungsten replicator实现mysql多主一从的备份架构</a></p>
<p>4. <a href="http://stackoverflow.com/questions/1576603/is-it-possible-to-do-n-master-1-slave-replication-with-mysql">Is it possible to do N-master => 1-slave replication with MySQL</a></p>
<p>5. <a href="http://shinguz.blogspot.com/2008/10/mysql-multi-master-single-slave.html">MySQL Multi-Master – Single-Slave – Replication </a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2011/04/mysql-federated-multiple-master-single-slave/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>MySQL: Too many connections</title>
		<link>http://www.orczhou.com/index.php/2010/09/mysql-too-many-connections/</link>
		<comments>http://www.orczhou.com/index.php/2010/09/mysql-too-many-connections/#comments</comments>
		<pubDate>Mon, 27 Sep 2010 23:10:41 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=2460</guid>
		<description><![CDATA[<div class="mycode">$mysql -uroot<br />
ERROR 1040 (00000): Too many connections</div>
<p>上面的错误，估计很多人都遇到过，Aurimas Mikalauskas在<a href="http://www.mysqlperformanceblog.com/2010/03/23/too-many-connections-no-problem/">MySQL Performance Blog</a>已经提到了一个解决办法（<strong>生产环境慎用之</strong>）：</p>
<div class="mycode">$gdb -p $(cat data/mysql_sandbox5087.pid) \<br />
-ex "set max_connections=5000" -batch</div>
<p>上面提到的只是一个救火的办法，MySQL提供了另一个办法，可以<span style="color:red">一定程度上</span>避免上述问题。</p>
<p>MySQL提供了参数<a href="http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_max_connections">max_connections</a>控制最大连接数，这包括所有用户的连接数。MySQL还提供了另一个参数<a href="http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_max_user_connections">max_user_connections</a>，用来控制<strong>某个用户</strong>的最大连接数。例如，将该参数设置为50，那么对于<strong>任何一个用户</strong>（包括super权限的用户），最多只能创建50个连接。[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/09/mysql-too-many-connections/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<div class="mycode">$mysql -uroot<br />
ERROR 1040 (00000): Too many connections</div>
<p>上面的错误，估计很多人都遇到过，Aurimas Mikalauskas在<a href="http://www.mysqlperformanceblog.com/2010/03/23/too-many-connections-no-problem/">MySQL Performance Blog</a>已经提到了一个解决办法（<strong>生产环境慎用之</strong>）：</p>
<div class="mycode">$gdb -p $(cat data/mysql_sandbox5087.pid) \<br />
-ex "set max_connections=5000" -batch</div>
<p>上面提到的只是一个救火的办法，MySQL提供了另一个办法，可以<span style="color:red">一定程度上</span>避免上述问题。</p>
<p>MySQL提供了参数<a href="http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_max_connections">max_connections</a>控制最大连接数，这包括所有用户的连接数。MySQL还提供了另一个参数<a href="http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_max_user_connections">max_user_connections</a>，用来控制<strong>某个用户</strong>的最大连接数。例如，将该参数设置为50，那么对于<strong>任何一个用户</strong>（包括super权限的用户），最多只能创建50个连接。<span id="more-2460"></span></p>
<p>于是我们可以制定下面的策略：如果的MySQL有3个用户（不包括root），将<a href="http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_max_user_connections">max_user_connections</a>设置为300，<a href="http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_max_connections">max_connections</a>设置为1000。配置文件：</p>
<div class="mycode">max_connections = 1000<br />
max_user_connections=300
</div>
<p>那么因为只有3个用户，单个用户的最大连接数是300，那么正常最多就只有900个连接，也就总会有100个额外的连接给root来使用。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/09/mysql-too-many-connections/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL/InnoDB和Group Commit(1)</title>
		<link>http://www.orczhou.com/index.php/2010/08/time-to-group-commit-1/</link>
		<comments>http://www.orczhou.com/index.php/2010/08/time-to-group-commit-1/#comments</comments>
		<pubDate>Wed, 11 Aug 2010 15:13:18 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Group Commit]]></category>
		<category><![CDATA[InnoDB]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=2260</guid>
		<description><![CDATA[<p>估计相关的东西一篇文章是讲不清楚的。</p>
<p>这个问题<a href="http://bugs.mysql.com/bug.php?id=13669">由来已久</a>，Kristian Nielsen连续写了四篇文章《Fixing MySQL group commit》(<a href="http://kristiannielsen.livejournal.com/12254.html">part 1</a> &#124; <a href="http://kristiannielsen.livejournal.com/12408.html">part 2</a> &#124; <a href="http://kristiannielsen.livejournal.com/12553.html">part 3</a> &#124; <a href="http://kristiannielsen.livejournal.com/12810.html">part 4</a>  )深入细致的分析了“故事”的前因后果。本文完全没有任何新意，仅做一个小的总结。这里会先介绍一下什么是“Group Commit”，MySQL/InnoDB里面的Group Commit为什么引起如此大的关注，现在是怎么解决问题的。[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/08/time-to-group-commit-1/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>估计相关的东西一篇文章是讲不清楚的。</p>
<p>这个问题<a href="http://bugs.mysql.com/bug.php?id=13669">由来已久</a>，Kristian Nielsen连续写了四篇文章《Fixing MySQL group commit》(<a href="http://kristiannielsen.livejournal.com/12254.html">part 1</a> | <a href="http://kristiannielsen.livejournal.com/12408.html">part 2</a> | <a href="http://kristiannielsen.livejournal.com/12553.html">part 3</a> | <a href="http://kristiannielsen.livejournal.com/12810.html">part 4</a>  )深入细致的分析了“故事”的前因后果。本文完全没有任何新意，仅做一个小的总结。这里会先介绍一下什么是“Group Commit”，MySQL/InnoDB里面的Group Commit为什么引起如此大的关注，现在是怎么解决问题的。<span id="more-2260"></span></p>
<div class="myt1">1. 什么是Group Commit</div>
<p>简单的，InnoDB在每次提交事务时，为了保证数据已经持久化到磁盘（Durable），需要调用一次fsync（或者是fdatasync、或者使用O_DIRECT选项等）来告知文件系统将可能在缓存中的数据刷新到磁盘（更多关于<a href="http://www.orczhou.com/index.php/2009/08/innodb_flush_method-file-io/">fsync</a>）。而fsync操作本身是非常“昂贵”的（关于“昂贵”：消耗较多的IO资源，响应较慢）：传统硬盘（10K转/分钟）大约每秒支撑150个fsync操作，SSD（Intel X25-M）大约每秒支撑1200个fsync操作。所以，如果每次事务提交都单独做fsync操作，那么这里将是系统TPS的一个瓶颈。所以就有了Group Commit的概念。</p>
<p>当多个事务并发时，我们让多个都在等待fsync的事务一起合并为仅调用一次fsync操作。这样的一个简单的优化将大大提高系统的吞吐量，Yoshinori Matsunobu的<a href="http://yoshinorimatsunobu.blogspot.com/2009/08/great-performance-effect-of-fixing.html">实验</a>表明，这将带来五到六倍的性能提升。</p>
<div class="myt1">2. MySQL/InnoDB与Group Commit</div>
<p>MySQL5.0开始，InnoDB就不再支持Group Commit了。在2005年，Peter Zaitsev就将此作为一个<a href="http://bugs.mysql.com/bug.php?id=13669">Bug</a>提交，直到2009年在InnoDB Plugin1.0.4才将此问题修复。</p>
<div class="myt2">2.1 为什么5.0之后MySQL/InnoDB无法支持Group Commit</div>
<p>究其更本原因发现，5.0之后MySQL开始支持“分布式事务和两阶段提交协议”（distributed transactions and Two Phase Commit /2PC），在代码实现时为了保证Binlog中的事务顺序和InnoDB Log（Transaction Log或者叫redo log）事务顺序一致，被动放弃了Group Commit。如果Binlog顺序不一致，那么备库就无法确保和主库有一致的数据。参考上面的Part 2给出代码相关的解释：</p>
<p>InnoDB本身的代码实现是可以支持Group Commit的，我们来看下面的代码：</p>
<div class="myt2">第一阶段：在内存中注册事务commit（还没有持久化，这是比较快的）</div>
<div class="mycode"> trx->flush_log_later = TRUE;<br />
 innobase_commit_low(trx);<br />
 trx->flush_log_later = FALSE;</div>
<div class="myt2">第二阶段：调用write和fsync</div>
<div class="mycode"> trx_commit_complete_for_mysql(trx)</div>
<p>上面的第二个阶段数据持久化是比较慢的，所以当多线程并发完成第一阶段后，可以一起只调用一次fsync来持久化全部数据（fsync应该是以文件描述符为单位的）。</p>
<p>如果打开了二进制日志，那么代码就有些不一样了。MySQL使用了“两阶段提交”（XA/2-phase commit ）来保证存储引擎和二进制日志的一致，在上面的过程之前有多了一步：</p>
<div class="mycode">    innobase_xa_prepare()<br />
    write() and fsync() binary log<br />
    innobase_commit()</div>
<p><span style="color:red;font-weight:bold;">在innobase_xa_prepare函数中需要获取排他锁<a href="http://www.orczhou.com/index.php/2010/06/mysql-innodb-source-code-sync-1/">prepare_commit_mutex</a>，直到上面的innodb_commit结束锁才释放，所以上面的binlog write and fsync也就无法Group操作了（当binlog_sync=1时）</span>。当设置Binlog_sync=1时，InnoDB就无法做Group_commit了，如果prepare_commit_mutex提前释放了，则可能导致InnoDB内部的事务顺序和Binlog内部事务的顺序不一致(这里值得商榷，参考下面的参考文献6)。</p>
<div class="myt2">2.2 Facebook的解决方案</div>
<p>Facebook作为一个SNS（Social network service）网站，在互联网界已经创造了奇迹。在技术领域Facebook也为开源社区（包括MySQL PHP Linux等）做了很多的杰出的贡献，希望我们公司能以此为目标。</p>
<p>开源社区为此也展开了很多讨论，Mark Callaghan（Facebook）提出的"<a href="http://www.facebook.com/note.php?note_id=386328905932">Ticket System</a>"目前看到的最简洁而有效的实现。</p>
<p>Facebook尝试两种方式实现，一，通过一个InnoDB的UT_LIST链表来确保InnoDB和Binlog内部的顺序一致；二，实现一个简单的“Ticket System”，当多个线程都进入innobase_xa_prepare()阶段时，就分配一个"ticket"值，并且分配的ticket值是单调增加的。当线程的binlog准备commit时，必须保证小于当前ticket的binlog都提交了，如果这时有多个事务的binlog则可以同时合并提交。</p>
<p>这里可以看到相关的性能测试情况：<a href="http://www.facebook.com/note.php?note_id=386328905932">Laying the foundatation for group commit</a></p>
<div class="myt2">2.2 InnoDB Plugin</div>
<p>在InnoDB Plugin 1.0.4<strong>声称</strong>解决了这个问题。下面是Plugin手册上的一段原话：</p>
<blockquote><p>
Beginning with InnoDB Plugin 1.0.4, the group commit functionality inside InnoDB works with the Two Phase Commit protocol in MySQL. Re-enabling of the group commit functionality fully ensures that the ordering of commit in the MySQL binlog and the InnoDB logfile is the same as it was before.
</p></blockquote>
<p>相关的<a href="http://www.innodb.com/wp/products/innodb_plugin/plugin-performance/innodb-plugin-1-0-4-group-commit-test-sysbench/">性能测试数据</a>。（sync_binlog = 0时）</p>
<p>很遗憾，我还不知道具体的实现细节，也还没有去检查源代码看看有没有相关实现说明。</p>
<p>Update 2011-07-25: InnoDB Plugin关于Group commit代码在storage/innobase/trx/trx0trx.c中实现，可以尝试阅读。</p>
<p>Update(2011-11-06):当sync_binlog = 0，MySQL并不能Group commit。</p>
<p>Update(2011-12-20):当打开二进制日志时，MySQL如何提交事务(这是一个两阶段提交)：</p>
<blockquote><p>
    binlog_prepare (do nothing)<br />
    innodb_prepare<br />
    ...<br />
    binlog_commit<br />
    innobase_commit
</p></blockquote>
<div class="myt2">参考文献</div>
<p>1. <a href="http://mysqlmusings.blogspot.com/2010/04/binary-log-group-commit-implementation.html">Binary Log Group Commit - An Implementation Proposal</a></p>
<p>2. <a href="http://www.chriscalender.com/?p=99">Ease of Switching to the InnoDB Plugin and the Numerous Benefits</a></p>
<p>3. <a href="http://ronaldbradford.com/blog/eventually-consistent-group-commit-2010-06-01/">Eventually consistent Group Commit </a></p>
<p>4. <a href="http://www.mysqlperformanceblog.com/2009/08/11/innodb-plugin-1-0-4-released-great-job-innobase/">Innodb plugin 1.0.4 released – great job Innobase</a></p>
<p>5. <a href="http://www.percona.com/docs/wiki/percona-xtradb:specs:fix_broken_group_commit">The followings are just my current understanding about broken group commit</a></p>
<p>6. Fixing MySQL group commit <a href="http://kristiannielsen.livejournal.com/12254.html">part 1</a> | <a href="http://kristiannielsen.livejournal.com/12408.html">part 2</a> | <a href="http://kristiannielsen.livejournal.com/12553.html">part 3</a> | <a href="http://kristiannielsen.livejournal.com/12810.html">part 4</a> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/08/time-to-group-commit-1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Manage MySQL With Open Source</title>
		<link>http://www.orczhou.com/index.php/2010/07/manage-mysql-with-open-source/</link>
		<comments>http://www.orczhou.com/index.php/2010/07/manage-mysql-with-open-source/#comments</comments>
		<pubDate>Sun, 11 Jul 2010 19:14:13 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=2204</guid>
		<description><![CDATA[<p>上次在一个“<a href="http://www.taobaodba.com/html/459_db_conference_pic_info.html">数据库技术论坛</a>”上分享MySQL管理方面的一些经验，这里把PPT分享一下：</p>
<div style="width:425px" id="__ss_4556719"><strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/orczhou/manage-mysql-with-open-source" title="Manage MySQL with open source">Manage MySQL with open source</a></strong><object id="__sse4556719" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=managemysqlwithopensource-100620074016-phpapp01&#038;rel=0&#038;stripped_title=manage-mysql-with-open-source" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse4556719" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=managemysqlwithopensource-100620074016-phpapp01&#038;rel=0&#038;stripped_title=manage-mysql-with-open-source" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object></div>
<p>- -EOF- -</p>]]></description>
			<content:encoded><![CDATA[<p>上次在一个“<a href="http://www.taobaodba.com/html/459_db_conference_pic_info.html">数据库技术论坛</a>”上分享MySQL管理方面的一些经验，这里把PPT分享一下：</p>
<div style="width:425px" id="__ss_4556719"><strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/orczhou/manage-mysql-with-open-source" title="Manage MySQL with open source">Manage MySQL with open source</a></strong><object id="__sse4556719" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=managemysqlwithopensource-100620074016-phpapp01&#038;rel=0&#038;stripped_title=manage-mysql-with-open-source" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse4556719" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=managemysqlwithopensource-100620074016-phpapp01&#038;rel=0&#038;stripped_title=manage-mysql-with-open-source" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object></div>
<p>- -EOF- -</p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/07/manage-mysql-with-open-source/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL/InnoDB源代码：线程并发访问控制(续)</title>
		<link>http://www.orczhou.com/index.php/2010/07/mysql-innodb-source-code-sync-2/</link>
		<comments>http://www.orczhou.com/index.php/2010/07/mysql-innodb-source-code-sync-2/#comments</comments>
		<pubDate>Tue, 06 Jul 2010 14:45:55 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[代码细节]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[Source Code]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=2215</guid>
		<description><![CDATA[<p><a href="http://www.orczhou.com/index.php/2010/06/mysql-innodb-source-code-sync-1/">前面</a>做了一个开始，沿着路慢慢走下去。</p>
<div class="myt1">0. 起源</div>
<p>开始之前，这里可以说说这次准备开始研究源代码的一个很大诱因了。前一段时间在生产环境遇到了一个InnoDB报错，这个错误甚至会导致InnoDB Crash：</p>
<div class="mycode">InnoDB: Warning: a long semaphore wait:<br />
--Thread 1222654272 has waited at ./include/btr0btr.ic line 53 for 241.00 seconds the semaphore:<br />
S-lock on RW-latch at 0x2aaab510b818 created in file buf/buf0buf.c line 680</div>
<p>沿着这里的线索 buf/buf0buf.c line 680  找到了：</p>
<div class="mycode">678	mutex_create(&#038;block->mutex, SYNC_BUF_BLOCK);<br />
679<br />
680	rw_lock_create(&#038;block->lock, SYNC_LEVEL_VARYING);<br />
681	ut_ad(rw_lock_validate(&#038;(block->lock)));</div>
<p>继续，就开始看rw_lock_create的实现，然后感觉需要看更多基础的一点的内容，这样就有了前面一片文章，继续研究，就有了现在的这篇文章。[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/07/mysql-innodb-source-code-sync-2/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.orczhou.com/index.php/2010/06/mysql-innodb-source-code-sync-1/">前面</a>做了一个开始，沿着路慢慢走下去。</p>
<div class="myt1">0. 起源</div>
<p>开始之前，这里可以说说这次准备开始研究源代码的一个很大诱因了。前一段时间在生产环境遇到了一个InnoDB报错，这个错误甚至会导致InnoDB Crash：</p>
<div class="mycode">InnoDB: Warning: a long semaphore wait:<br />
--Thread 1222654272 has waited at ./include/btr0btr.ic line 53 for 241.00 seconds the semaphore:<br />
S-lock on RW-latch at 0x2aaab510b818 created in file buf/buf0buf.c line 680</div>
<p>沿着这里的线索 buf/buf0buf.c line 680  找到了：</p>
<div class="mycode">678	mutex_create(&#038;block->mutex, SYNC_BUF_BLOCK);<br />
679<br />
680	rw_lock_create(&#038;block->lock, SYNC_LEVEL_VARYING);<br />
681	ut_ad(rw_lock_validate(&#038;(block->lock)));</div>
<p>继续，就开始看rw_lock_create的实现，然后感觉需要看更多基础的一点的内容，这样就有了前面一片文章，继续研究，就有了现在的这篇文章。<span id="more-2215"></span></p>
<div class="myt1">1. 读写锁</div>
<p>前文介绍了，InnoDB中如何使用mutex_struct结构来保护内存（变量）。我们可以看到，如果使用mutex_struct时，其实实现的是一个排他锁。如果每个变量可以被允许并发共享（只读）访问，写的时候才需要“排他”访问的话，再用mutex_struct的话就不合适了。所以，InnoDB在mutex_struct的基础上，又实现了一个<strong>读写锁</strong>。</p>
<p>本文中"共享锁"即"读锁"，"排他锁"即"写锁"。关于读写锁的基本知识这里就不再介绍了。先说说InnoDB的实现原理，再来看细节。InnoDB使用了一个整数来标记当前锁的状态。</p>
<div class="myt1">2. 读写锁 InnoDB实现机制</div>
<p>InnoDB用了一个很简单的读写锁实现机制，使用一个整型变量lock_word标志当前锁的状态：</p>
<div class="mycode">struct rw_lock_struct {<br />
	volatile lint	<strong>lock_word</strong>; 	/*!< Holds the state of the lock. */</div>
<p>初始化时，lock_word的值为1048576（0x00100000），当每一个线程获得一次读锁时，lock_word值减一。每次获取写锁时，lock_word的值则减少1048576。这样，每次根据lock_word当前值，就可以获取当前锁的状态了。下面是关于lock_word规则的详细说明：</p>
<div class="myt2">1） lock_word初始值为X_LOCK_DECR（#define X_LOCK_DECR	 0×00100000 //1,048,575）</div>
<div class="myt2">2） 每一线程以读（共享）的方式获取该锁，则lock_word减1</div>
<div class="myt2">3） 每次线程以写（排他）的方式获取锁，则lock_word减X_LOCK_DECR</div>
<div class="myt2">4） 如果某线程以写（排他）方式获得锁，该线程仍可以以递归的方式获得该写锁</div>
<div class="myt2">5） 如果线程在等待获取写锁，lock_word减X_LOCK_DECR，<strong>且这样可以阻止线程再获得读锁</strong></div>
<div class="myt2">6） 写锁虽然可以递归获取，但是读锁不能递归获取</div>
<p>根据上面的规律，我们就可以推导出，lock_word取不同区间的值时，当前锁的状态。例如当0&lt;lock_word&lt;X_LOCK_DECR时，当前锁以读的方式被获取，下面是lock_word不同区间值锁的状态表：</p>
<div class="myt2">1） lock_word == X_LOCK_DECR   则锁处于初始状态，即没有任何线程获得该锁</div>
<div class="myt2">2） 0 < lock_word < X_LOCK_DECR  则锁处于读锁状态，读锁持有者个数为X_LOCK_DECR-lock_word；且并没有任何写锁处于等待状态</div>
<div class="myt2">3） lock_word == 0  则锁处于写锁状态</div>
<div class="myt2">4） -X_LOCK_DECR < lock_word < 0  则锁处于读锁状态，且有一个线程在等待写锁。-lock_word就是读锁的个数</div>
<div class="myt2">5） lock_word <= -X_LOCK_DECR  则当前锁处于“递归写锁”（同一个线程多次获得该写锁）状态，写锁的个数是((-lock_word) / X_LOCK_DECR) + 1</div>
<p>这里看到，当线程需要获取该锁（读、写）的时候，根据lock_word值即可以判断当前锁的状态，也就可以知道自己是否可以获得锁。</p>
<div class="myt1">3. 关于递归写锁</div>
<p>lock_word基本上包含了锁的全部状态，但是如果细心的话，你会发现有一个例外：当一个线程A需要获得写锁的时候，lock_word的如果是写锁状态时，线程A还必须知道当前持有该锁的线程ID，如果持有锁的线程就是自己，则线程A可以获得写锁。所以，rw_lock_struct结构中还有另一个成员变量：</p>
<div class="mycode">volatile os_thread_id_t	writer_thread;</div>
<p>当锁以排他方式被持有的时候，该变量记录持有者的线程ID。</p>
<p>如果细心，你还可能发现，这里虽然提到了“递归写锁”，却没有提到“递归读锁”。在InnoDB的读写锁实现时，并不考虑读锁的递归，如果锁被排他方式获取，即使是同一个线程，仍然无法获取读锁。</p>
<div class="myt1">4 .lock_word的一致性</div>
<p>在读写锁的实现中，lock_word的可以“举足轻重”。这就要求每次对lock_word的修改都要做到“一致”。否则，这把锁就坏了。</p>
<div class="myt2">4.1 什么是一致性</div>
<p>如果是单线程，获取读锁我们可能写出下面的代码：</p>
<div class="mycode">if(local_lock_word > 0){<br />
    local_lock_word = local_lock_word -1;<br />
}
</div>
<p>上面的代码，在多线多处理环境是不安全的，会导致lock_word的不一致。线程A需要获取读锁，则CPU在执行上面代码的时候，首先获取local_lock_word的值将其载入寄存器，然后运算，然后返回到内存。如果在“载入寄存器之后运算之前”，lock_word的值发生了改变（例如另一个线程B成功获取了读锁），而这时当前线程A却不知道，线程A仍然把自己的值回写到内存，则会覆盖线程B对lock_word的修改。lock_word的值就不一致了，等锁全部释放后，lock_word的值就可能不是X_LOCK_DECR。</p>
<p>上面这段话可能有点绕，简单的说：多线程并发时，上面简单的赋值操作是不可靠的。回想一下多线程，就不难理解上面这段话了。</p>
<div class="myt2">4.2 InnoDB如何保证lock_wrod的一致性</div>
<p>介绍完了前面那么多原理，这里总算可以看看源代码的一些东西了。InnoDB封装了函数rw_lock_lock_word_decr来实现对lock_wrod的一致操作。InnoDB会根据不同的编译环境，决定使用不同的策略。简单地，InnoDB使用了前面介绍的mutex_t（排他锁）来实现lock_word的一致操作，即在操作之前获取排他锁；另外，InnoDB根据不同的CPU平台还可以使用<a href="http://en.wikipedia.org/wiki/Compare-and-swap">Compare-And-Swap</a>来实现，下面来看看源码：</p>
<p>这里是核心代码，详细参考sync0rw.ic文件</p>
<pre>
<div class="mycode">ibool rw_lock_lock_word_decr(rw_lock_t*	lock, ulint amount)
{
#ifdef INNODB_RW_LOCKS_USE_ATOMICS
	while (local_lock_word > 0) {
		if (<font color="blue">os_compare_and_swap_lint</font>(&#038;lock->lock_word,local_lock_word,local_lock_word - amount)) {
			return(TRUE);
		}
		local_lock_word = lock->lock_word;
	}
	return(FALSE);
#else /* INNODB_RW_LOCKS_USE_ATOMICS */
	ibool success = FALSE;
	<strong><font color="blue">mutex_enter(&#038;(lock->mutex));</font></strong>
	if (lock->lock_word > 0) {
		lock->lock_word -= amount;
		success = TRUE;
	}
	<strong><font color="blue">mutex_exit(&#038;(lock->mutex));</font></strong>
	return(success);
#endif /* INNODB_RW_LOCKS_USE_ATOMICS */
}</div>
</pre>
<p>有了<a href="http://en.wikipedia.org/wiki/Compare-and-swap">Compare-And-Swap</a>或者排他锁(<a href="http://www.orczhou.com/index.php/2010/06/mysql-innodb-source-code-sync-1/">前文</a>的mutex_t)的保证，InnoDB就可以一致的更改锁的状态（lock_word）了。</p>
<div class="myt1">5. 再休息一下</div>
<p>这篇文章算是介绍了一下InnoDB读写锁的实现机制，本来想把具体实现也写了，发现作为一篇博<del datetime="2010-07-06T09:04:48+00:00">客</del>文是有点长了，而且已经非常的晦涩难懂了。所以暂时休息一下，欲知后事如何，且听下回分解。</p>
<p>广告时间：<a href="http://www.orczhou.com/index.php/projects/we-are-hunting-mysql-hacker/">工作机会--MySQL Hacker</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/07/mysql-innodb-source-code-sync-2/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>MySQL/InnoDB源代码：线程并发访问控制</title>
		<link>http://www.orczhou.com/index.php/2010/06/mysql-innodb-source-code-sync-1/</link>
		<comments>http://www.orczhou.com/index.php/2010/06/mysql-innodb-source-code-sync-1/#comments</comments>
		<pubDate>Wed, 30 Jun 2010 03:51:24 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[代码细节]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[Source Code]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=2173</guid>
		<description><![CDATA[<p>作为DBA关心的更多的可能是原理、机制，对于源码一般大家也不是特别关心，也不太用得上。而且对于对于源代码级别的细节也很难用文字表达自己的理解，最终理解必须还是需要自己去看看每一行代码到底是怎样的。也读过《Understanding.MySQL.Internals》的大部分章节，作者也是偏重于从代码的实现目的（原理、机制）来介绍的，国内的《MySQL核心内幕》（个人对于“核心内幕”这个词有莫名的反感）算是更多的从源码开始介绍MySQL了，可是这本书也许是授予篇幅的限制，介绍的东西也并不多。</p>
<p>开始写这篇文章，不能期待自己能写多少，也不能期待自己能研究多少，不过至少走出了自己探索的第一步。文章的宗旨不在于能够多么细致的分析MySQL的源代码，而是希望能给自己，能给他人打开走向源代码的第一道门。</p>
<p>我是BNU数学系毕业的，对C语言知道甚少，C语言的经历大概就是“计算方法”课程中实现的求解各种方程的撇脚程序了。所以虽然我会极力避免错误，但是相信还是会犯一堆错误，希望各位看官能够宽容点，有错误指出来，慢慢改正。</p>
<div class="myt1">0. 开始</div>
<p>InnoDB的代码通过宏定义考虑很多平台的兼容问题，这里分析的主要是类Unix/Linux平台（POSIX标准）的代码段，所以下面的很多代码段也是删除相关兼容性代码的。本文InnoDB源码是Plugin1.0.6版本。[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/06/mysql-innodb-source-code-sync-1/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>作为DBA关心的更多的可能是原理、机制，对于源码一般大家也不是特别关心，也不太用得上。而且对于对于源代码级别的细节也很难用文字表达自己的理解，最终理解必须还是需要自己去看看每一行代码到底是怎样的。也读过《Understanding.MySQL.Internals》的大部分章节，作者也是偏重于从代码的实现目的（原理、机制）来介绍的，国内的《MySQL核心内幕》（个人对于“核心内幕”这个词有莫名的反感）算是更多的从源码开始介绍MySQL了，可是这本书也许是授予篇幅的限制，介绍的东西也并不多。</p>
<p>开始写这篇文章，不能期待自己能写多少，也不能期待自己能研究多少，不过至少走出了自己探索的第一步。文章的宗旨不在于能够多么细致的分析MySQL的源代码，而是希望能给自己，能给他人打开走向源代码的第一道门。</p>
<p>我是BNU数学系毕业的，对C语言知道甚少，C语言的经历大概就是“计算方法”课程中实现的求解各种方程的撇脚程序了。所以虽然我会极力避免错误，但是相信还是会犯一堆错误，希望各位看官能够宽容点，有错误指出来，慢慢改正。</p>
<div class="myt1">0. 开始</div>
<p>InnoDB的代码通过宏定义考虑很多平台的兼容问题，这里分析的主要是类Unix/Linux平台（POSIX标准）的代码段，所以下面的很多代码段也是删除相关兼容性代码的。本文InnoDB源码是Plugin1.0.6版本。<span id="more-2173"></span></p>
<div class="myt1">1. InnoDB多线程并发访问</div>
<p>MySQL是一个多线程的实现的<a href="http://en.wikipedia.org/wiki/Database_management_system">DBMS</a>，多线程编程需要解决的一个重要问题就是线程间并发访问的控制。一般某个线程更改需要更改某个内存块（变量）时，首先要先将数据从内存读取到寄存器，然后计算更改之，最后回写到内存块。如果有<strong>多个线程同时</strong>在修改内存块，而且读取同时（“同时”表示在两个线程修改该值之前读取）发生，那么两个线程将有一个会丢失修改。</p>
<p>所以，希望保持内存数据（变量）“一致”、“准确”时，线程在操作这些变量时，需要先获取对应的锁。利用各个变量对应的“锁机制”来保证数据访问的并发控制。</p>
<div class="myt1">2. 基本原理：Posix thread</div>
<p>多线程并发访问控制中，InnoDB使用标准<a href="http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html">POSIX线程模型</a>中的mutex（condition variable）实现基本的排他访问。在对mutex（condition variable）的封装的基础上，又实现了相应的读写锁。</p>
<div class="myt2">2.1 os_fast_mutex_t VS pthread_mutex_t</div>
<p>pthread_mutex_t封装在os_fast_mutex_t中，condition variable则封装在了os_event_struct中。</p>
<div class="mycode">typedef pthread_mutex_t	os_fast_mutex_t;</div>
<p>对应的mutex操作分别封装在了os_fast_mutex_init / os_fast_mutex_lock / os_fast_mutex_unlock / os_fast_mutex_free中，这里我们看看os_fast_mutex_trylock的封装函数实现：</p>
<pre>
<div class="mycode">ulint os_fast_mutex_trylock(
	os_fast_mutex_t*	fast_mutex)
{
#ifdef __WIN__
	EnterCriticalSection(fast_mutex);
	return(0);
#else
	return((ulint) pthread_mutex_trylock(fast_mutex));
#endif
}</div>
</pre>
<div class="myt2">2.2 os_event_struct VS condition variable</div>
<p>Condition variable较之pthread_mutex_t要复杂一点，对应的封装也更复杂。os_event_struct封装了condition variable全部操作，主要包括初始化，陷入等待，广播唤醒：os_event_create（初始化）/os_event_wait_low（等待）/os_event_set（广播唤醒）。下面是基本数据结构struct os_event_struct：</p>
<pre>
<div class="mycode">struct os_event_struct {
	os_fast_mutex_t	os_mutex;
	ibool		is_set;
	ib_longlong	signal_count;
	pthread_cond_t	cond_var;	/* condition variable is used in
					waiting for the event */
        ......
};</div>
</pre>
<p>这里抽取一个广播唤醒开函数os_event_set看看：</p>
<pre>
<div class="mycode">void os_event_set(
	os_event_t	event)	/*!< in: event to set */
{
	os_fast_mutex_lock(&#038;(event->os_mutex));
	if (event->is_set) {
		/* Do nothing */
	} else {
		event->is_set = TRUE;
		event->signal_count += 1;
		ut_a(0 == <strong>pthread_cond_broadcast</strong>(&#038;(event->cond_var)));
	}
	os_fast_mutex_unlock(&#038;(event->os_mutex));
}</div>
</pre>
<p>可以看到上面函数，没有什么神秘的地方，就是判断了一下是否需要广播，然后调用了pthread_cond_broadcast函数广播。</p>
<p>数据结构os_fast_mutex_t和os_event_t看到，InnoDB并不是那么复杂，但是需要对Linux（或者说POSIX）有一定的理解。</p>
<p>另外，算是最底层的数据结构，事实上，这个封装还没结束，InnoDB其他模块中用的较多的是mutex_struct和rw_lock结构，mutex_t是对os_event_t封装，rw_lock是对mutex_t的封装。</p>
<div class="myt1">3. 前奏：mutex_struct</div>
<div class="myt2">3.1 mutex_struct的一般说明</div>
<pre>
<div class="mycode">struct mutex_struct {
	os_event_t	event;	/* Used by sync0arr.c for the wait queue */
	ulint	lock_word;	/* This ulint is the target of the atomic
				test-and-set instruction in Win32 */
	ulint	waiters;	//如果有线程在等待，设置为1
	UT_LIST_NODE_T(mutex_t)	list;
	const char*	cfile_name;/* File name where mutex created */
	ulint		cline;	/* Line where created */
#ifndef UNIV_HOTBACKUP
	ulong		count_os_wait; /* count of os_wait */
#endif /* !UNIV_HOTBACKUP */
};</div>
</pre>
<p>mutex_struct相关的函数有很多，一般需要使用的有：mutex_create、mutex_enter、mutex_exit；还有一些其他的函数：mutex_spin_wait、mutex_free、mutex_signal_object。</p>
<div class="myt2">3.2 mutex_struct的典型用法</div>
<p>InnoDB中一个典型的mutex_struct使用如下：</p>
<pre>
<div class="mycode">mutex_create(&#038;rw_lock_list_mutex);
mutex_enter(&#038;rw_lock_list_mutex);
...
//这里，对rw_lock_list_mutex所保护的对象，这里可以进行一致的、排他操作
...
mutex_exit(&#038;rw_lock_list_mutex);</div>
</pre>
<p>mutex_create函数相对简单，做了一些初始化的工作。我们继续沿着线索来看看mutex_enter的实现。这个函数实现：</p>
<pre>
<div class="mycode">typedef struct mutex_struct		mutex_t;
void mutex_enter_func(
	mutex_t* mutex, const char*  file_name, ulint  line)
{
	ut_d(mutex->count_using++);
	if (!mutex_test_and_set(mutex)) {
		ut_d(mutex->thread_id = os_thread_get_curr_id());
		return;	/* Succeeded! */
	}
	mutex_spin_wait(mutex, file_name, line);
}</div>
</pre>
<p>代码说明：首先，尝试使用mutex_test_and_set（这是对os_fast_mutex_trylock的封装）获得锁，成功则返回。失败，则调用mutex_spin_wait陷入“spin lock的方式”获取锁。spin lock的方式是指，自己不释放cpu资源，而是自己空循环一段时间后，在重新尝试获取锁。</p>
<div class="myt2">3.3 继续深入：mutex_spin_wait</div>
<p>线索到了mutex_spin_wait了，该函数是InnoDB里面实现了spin lock的主要部分。这里简单介绍一下原理：首先线程检查lock_word，如果被设置0（表示当前并没有变量并没有被锁住），则直接调用mutex_test_and_set（其实是os_fast_mutex_trylock）来尝试获取锁。如果lock_word=1那么，则表示当前的锁已经被某个线程获取，则线程进入空循环，延迟一段时间（spin lock），等待lock_word被设置为0后，立刻调用mutex_test_and_set尝试获取锁。如果lock_word一直1（锁一直在被占用），那么线程将在延迟SYNC_SPIN_ROUNDS个单位时间后，线程将尝试调用mutex_test_and_set一次（碰运气），如果仍失败，这时线程将陷入等待（这时Condition variables开始登上舞台），如果其他线程释放该锁，则会将lock_word被设置为0，并进行一次广播，那么前面陷入等待的线程将会被唤醒，并重新调用mutex_test_and_set尝试获取锁。</p>
<p>下面是mutex_spin_wait的一个简单流程图：</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/4748692857/" title="mutex_spin_wait_v3 by orczhou, on Flickr"><img src="http://farm5.static.flickr.com/4121/4748692857_19d8c453be.jpg" width="500" height="449" alt="mutex_spin_wait_v3"></a></p>
<div class="myt2">3.4 小结mutex_struct</div>
<p>再回头看看mutex_struct的一般使用方法，先是mutex_create，然后是mutex_enter，最后是mutex_exit，该函数会调用os_event_set进行一次广播（广播函数本身是会释放pthread_mutex_t的）。</p>
<div class="myt1">4. 休息一会儿</div>
<p>至此，我们看到，InnoDB一般通过数据结构mutex_struct来实现对内存块（变量）的完全排他访问。如果只是排他访问，可能会导致效率较低，因为很多时候，不需要排他，只需要共享方式访问就可以了。InnoDB在mutex_struct基础上，再做了一次封装，对应的数据结构为rw_lock_struct。</p>
<p>好了，这篇文章已经够长了....欲知后事如何，且听下回分解。</p>
<p>广告时间：<a href="http://www.orczhou.com/index.php/projects/we-are-hunting-mysql-hacker/">工作机会--MySQL Hacker</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/06/mysql-innodb-source-code-sync-1/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>MySQL Optimize Table</title>
		<link>http://www.orczhou.com/index.php/2010/05/mysql-optimize-table/</link>
		<comments>http://www.orczhou.com/index.php/2010/05/mysql-optimize-table/#comments</comments>
		<pubDate>Thu, 06 May 2010 13:07:34 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[InnoDB]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=1912</guid>
		<description><![CDATA[<p><a href="http://www.xaprb.com/blog/2010/02/07/how-often-should-you-use-optimize-table/">很难说</a>Optimize Table到底能不能提高系统运行效率，但是有一点是肯定的：它能够帮我们回收更多的空间、<a href="http://dev.mysql.com/doc/refman/5.1/en/optimize-table.html">减少“碎片”（defragment）</a>。</p>
<p><font color="red">1. 回收空间 Defragment</font></p>
<p>在InnoDB的维护过程中，我们总会遇到磁盘耗尽、或者<a href="http://dev.mysql.com/doc/refman/5.0/en/multiple-tablespaces.html">InnoDB Tablespaces</a>用完的情况。这时候，在考虑扩容等方案之前，最好先使用Optimize Table试试。如果你的表大字段（Text Blob Varchar），并且更新、删除较频繁的话，Optimize之后可能会腾出大量的空间。[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/05/mysql-optimize-table/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.xaprb.com/blog/2010/02/07/how-often-should-you-use-optimize-table/">很难说</a>Optimize Table到底能不能提高系统运行效率，但是有一点是肯定的：它能够帮我们回收更多的空间、<a href="http://dev.mysql.com/doc/refman/5.1/en/optimize-table.html">减少“碎片”（defragment）</a>。</p>
<p><font color="red">1. 回收空间 Defragment</font></p>
<p>在InnoDB的维护过程中，我们总会遇到磁盘耗尽、或者<a href="http://dev.mysql.com/doc/refman/5.0/en/multiple-tablespaces.html">InnoDB Tablespaces</a>用完的情况。这时候，在考虑扩容等方案之前，最好先使用Optimize Table试试。如果你的表大字段（Text Blob Varchar），并且更新、删除较频繁的话，Optimize之后可能会腾出大量的空间。<span id="more-1912"></span></p>
<pre>
<div class="mycode">#ls -lah users_0.ibd
-rwxr-xr-x  1 mysql dba 736M May  6 09:50 users_0.ibd

root@test 10:10:53>optimize table users_0;
+---------------------+----------+----------+----------+
| Table               | Op       | Msg_type | Msg_text |
+---------------------+----------+----------+----------+
|     test.users_0    | optimize | status   | OK       |
+---------------------+----------+----------+----------+

#ls -lah users_0.ibd
-rw-rw----  1 mysql dba 644M May  6 10:09 users_0.ibd</div>
</pre>
<p>可以看到这里的ibd文件大小释放了约100M。</p>
<p><font color="red">2. InnoDB 和 MyISAM</font></p>
<p>目前支持optimize命令的引擎有 <a href="http://dev.mysql.com/doc/refman/5.1/en/optimize-table.html">MyISAM, InnoDB, and ARCHIVE</a>，对于InnoDB，会将optimize命令映射为ALTER TABLE命令，该命令会重建数据表，更新索引统计信息、回收主键索引中空间。</p>
<p><font color="red">3. InnoDB 和 MyISAM</font></p>
<p>如果你的MySQL是有备库的，如果你只希望在主库上执行的话，那么可以加上关键字NO_WRITE_TO_BINLOG（或者LOCAL，意思完全相同）。</p>
<div class="mycode">OPTIMIZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE</div>
<p>这对于<strong>MM结构的MySQL数据库尤为重要</strong>，因为很多时候，你只是想在备库上执行，而不希望影响主库。</p>
<p>参考文献</p>
<p>[1]. <a href="http://dev.mysql.com/doc/refman/5.1/en/optimize-table.html">MySQL Manual OPTIMIZE TABLE Syntax</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/05/mysql-optimize-table/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>InnoDB Plugin特性介绍-1</title>
		<link>http://www.orczhou.com/index.php/2010/04/innodb-plugin-some-feature-1/</link>
		<comments>http://www.orczhou.com/index.php/2010/04/innodb-plugin-some-feature-1/#comments</comments>
		<pubDate>Sun, 04 Apr 2010 01:21:56 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[InnoDB Plugin]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=1680</guid>
		<description><![CDATA[<p>迫于开源社区、团体（Google Facebook Percona  Xaprb等）的巨大压力，InnoDB Plugin相比Built-in版本有了很大的提升，这些提升多数来自开源社区贡献的代码，虽然本身也有一些改进。</p>
<p><font color="red">1. 关于Fast locking</font></p>
<p>在多个线程并发访问相同资源的时候，需要使用一些InnoDB实现的互斥量（mutex）和读写锁（read/write lock）。在Unix-like平台上，InnoDB是通过Pthread的互斥量（这是POSIX标准的一部分）来实现的。在现代OS和硬件平台中，一般都有了更有效的替代方案，多数情况下这些替代方案使用更少的CPU指令和时钟周期来完成这些互斥处理。<br />
[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/04/innodb-plugin-some-feature-1/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>迫于开源社区、团体（Google Facebook Percona  Xaprb等）的巨大压力，InnoDB Plugin相比Built-in版本有了很大的提升，这些提升多数来自开源社区贡献的代码，虽然本身也有一些改进。</p>
<p><font color="red">1. 关于Fast locking</font></p>
<p>在多个线程并发访问相同资源的时候，需要使用一些InnoDB实现的互斥量（mutex）和读写锁（read/write lock）。在Unix-like平台上，InnoDB是通过Pthread的互斥量（这是POSIX标准的一部分）来实现的。在现代OS和硬件平台中，一般都有了更有效的替代方案，多数情况下这些替代方案使用更少的CPU指令和时钟周期来完成这些互斥处理。<br />
<span id="more-1680"></span></p>
<p>从InnoDB1.0.3开始，如果你使用的GCC版本高于4.12，那么互斥量（mutex）和读写锁（read/write lock）可以通过GCC内置的<a href="http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html">atomic_memory_access</a>功能来实现；如果你是在Solaris 10上，还可以通过系统自带的<a href="http://docs.sun.com/app/docs/doc/816-5168/atomic-ops-3c">atomic_operations</a>功能来实现互斥量（mutex）和读写锁（read/write lock）。</p>
<p>在编译时，InnoDB如果检测到了GCC的<a href="http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html">atomic_memory_access</a>则会使用该功能，否则如果检测到有Solaris 10的<a href="http://docs.sun.com/app/docs/doc/816-5168/atomic-ops-3c">atomic_operations</a>，则会使用该功能。如果，上面条件都不满足，则会使用InnoDB使用POSIX Pthread实现的InnoDB互斥量（mutex）和读写锁（read/write lock）。</p>
<p>所以，这个特性不需要我们做任何配置。在InnoDB启动的时候，会在错误日志打印相关信息。如果InnoDB使用POSIX Pthread实现互斥量和读写锁将打印如下信息：</p>
<blockquote><p>InnoDB: Mutexes and rw_locks use InnoDB's own implementation</p></blockquote>
<p>如果使用GCC的<a href="http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html">atomic memory access</a>特性：</p>
<blockquote><p>InnoDB: Mutexes and rw_locks use GCC atomic builtins</p></blockquote>
<p><font color="red">2. 关于内存分配</font></p>
<p>在InnoDB刚刚被开发的时候，当时多核CPU还并不是这么流行，当时也并没有针对多核CPU优化的内存分配器（memory allocator libraries ），所以InnoDB实现了一个自己的内存分配子系统（mem subsystem），这个子系统是通过单个互斥量（mutex）实现管理的，而这可能是一个瓶颈。除此，InnoDB还对OS自带的内存分配管理（malloc和free）作了一次简单封装，这些管理函数实现也类似地通过互斥量的机制实现的，所以这并没有解决问题。现在多核CPU到处都是（个人PC都是），现在也有了新的内存分配器，大大提升了对多核CPU的支持。例如： Hoard, libumem, mtmalloc, ptmalloc, tbbmalloc, and TCMalloc等等，如果你的应用有大量的内存分配和释放操作，使用这些内存分配器将对性能有很大的提升。</p>
<p>可以通过参数<a href="http://dev.mysql.com/doc/refman/5.1/en/innodb-parameters.html#sysvar_innodb_use_sys_malloc">innodb_use_sys_malloc</a>来控制InnoDB_Plugin是否使用系统的内存分配器。<a href="http://dev.mysql.com/doc/refman/5.1/en/innodb-parameters.html#sysvar_innodb_use_sys_malloc">innodb_use_sys_malloc</a>设置成0，InnoDB将使用自己实现的内存管理功能；设置为ON时（默认设置），则使用系统的内存分配器。这时如果系统中安装了新的内存分配器（如TCMalloc），只需在启动MySQL时，设定环境变量LD_PRELOAD和LD_LIBRARY_PATH指向新分配的动态连接库就可以了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/04/innodb-plugin-some-feature-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

