<?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; InnoDB</title>
	<atom:link href="http://www.orczhou.com/index.php/tag/innodb/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.orczhou.com</link>
	<description>一个故事@MySQL DBA</description>
	<lastBuildDate>Tue, 24 Aug 2010 01:44:36 +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</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>在innobase_xa_prepare函数中需要需要获取排他锁<a href="http://www.orczhou.com/index.php/2010/06/mysql-innodb-source-code-sync-1/">prepare_commit_mutex</a>，直到上面的第一阶段结束才释放该排他锁。这意味着，上面的“第一阶段”就无法再并发一次性提交fsync了，即Group Commit也就无法完成了。（注：第一阶段包括了binlog的fsync操作，直到整个第一阶段结束prepare_commit_mutex才释放，所以Binlog就无法Group commit了，如果prepare_commit_mutex提前释放了，则可能导致InnoDB内部的事务顺序和Binlog内部事务的顺序不一致，Replication就没有意义了）</p>
<div class="myt2">2.2 Facebook的解决方案</div>
<p>Facebook作为一个SNS（Social network service）网站，在互联网界已经创造了奇迹。在技术领域Facebook也为开源社区（包括MySQL PHP Linux等）做了很多的杰出的贡献，希望我们公司能以此为目标。</p>
<p>开源社区为此也展开了很多讨论，Mark Callaghan（Facebook）提出的&#8221;<a href="http://www.facebook.com/note.php?note_id=386328905932">Ticket System</a>&#8220;目前看到的最简洁而有效的实现。</p>
<p>Facebook尝试两种方式实现，一，通过一个InnoDB的UT_LIST链表来确保InnoDB和Binlog内部的顺序一致；二，实现一个简单的“Ticket System”，当多个线程都进入innobase_xa_prepare()阶段时，就分配一个&#8221;ticket&#8221;值，并且分配的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已经解决了这个问题。下面是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>很遗憾，我还不知道具体的实现细节，也还没有去检查源代码看看有没有相关实现说明。</p>
<div class="myt2">参考文献</div>
<p>1. <a href="http://mysqlmusings.blogspot.com/2010/04/binary-log-group-commit-implementation.html">Binary Log Group Commit &#8211; 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>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 />
&#8211;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 />
&#8211;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>本文中&#8221;共享锁&#8221;即&#8221;读锁&#8221;，&#8221;排他锁&#8221;即&#8221;写锁&#8221;。关于读写锁的基本知识这里就不再介绍了。先说说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（0&#215;00100000），当每一个线程获得一次读锁时，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>
]]></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>好了，这篇文章已经够长了&#8230;.欲知后事如何，且听下回分解。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/06/mysql-innodb-source-code-sync-1/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>InnoDB Corruption</title>
		<link>http://www.orczhou.com/index.php/2010/05/innodb-corruption/</link>
		<comments>http://www.orczhou.com/index.php/2010/05/innodb-corruption/#comments</comments>
		<pubDate>Sat, 08 May 2010 06:27:15 +0000</pubDate>
		<dc:creator>orczhou</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[InnoDB]]></category>

		<guid isPermaLink="false">http://www.orczhou.com/?p=1903</guid>
		<description><![CDATA[<p>以前没有遇到InnoDB文件损坏的情况，特此记录。从日志来看，损坏的应该是一个索引页，导致对应的数据表无法访问，也无法获取对应数据表中的数据，尝试check optimize也无法修复。最后设置innodb_force_recovery=1后启动数据库后，能够导出全部数据。</p>
<p><font color="red">1. 故障</font></p>
<p>最近，在从一个Xtrabackup的备份中恢复数据后，发现MySQL数据库能正常启动，但是发现use dbname一下MySQL就会崩溃，看日志：[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/05/innodb-corruption/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>以前没有遇到InnoDB文件损坏的情况，特此记录。从日志来看，损坏的应该是一个索引页，导致对应的数据表无法访问，也无法获取对应数据表中的数据，尝试check optimize也无法修复。最后设置innodb_force_recovery=1后启动数据库后，能够导出全部数据。</p>
<p><font color="red">1. 故障</font></p>
<p>最近，在从一个Xtrabackup的备份中恢复数据后，发现MySQL数据库能正常启动，但是发现use dbname一下MySQL就会崩溃，看日志：<span id="more-1903"></span></p>
<div class="mycode">InnoDB: Database page corruption on disk or a failed<br />
InnoDB: file read of page 4.<br />
InnoDB: You may have to recover from a backup.<br />
100505 10:19:07  InnoDB: Page dump in ascii and hex (16384 bytes):<br />
 len 16384; hex 89ecae1d00000004ffffffffffffffff0000014bf7119c4545<br />
&#8230;&#8230;<br />
&#8230;&#8230;<br />
100505 10:19:07  InnoDB: Page checksum 2313989661, prior-to-4.0.14-form checksum 3285785245<br />
InnoDB: stored checksum 2313989661, prior-to-4.0.14-form stored checksum 1519970954<br />
InnoDB: Page lsn 331 4145126469, low 4 bytes of lsn at page end 1983060<br />
InnoDB: Page number (if stored to page already) 4,<br />
InnoDB: space id (if created with >= MySQL-4.1.1 and stored already) 884<br />
InnoDB: Page may be an index page where index id is 0 1929<br />
InnoDB: (index &#8220;IDX_fa_login_userid&#8221; of table &#8220;fa&#8221;.&#8221;login_history_0015&#8243;)<br />
InnoDB: Database page corruption on disk or a failed<br />
InnoDB: file read of page 4.
</div>
<p><!--more--></p>
<p>从上面的信息我们看到，可能是表&#8221;fa&#8221;.&#8221;login_history_0015&#8243;的索引页有损坏。虽然这个表故障了，不过MySQL还是能够正常启动，而且对其他表也是能够正常操作的。需要注意的是，用mysql client连接进去时需要使用参数-A，否则use dbname的时候可能会有问题。</p>
<div class="mycode">$mysql -uroot -A -p</div>
<p><font color="red">2. check修复</font></p>
<p>根据Eerror Log中的提示使用CHECK TABLE修复：</p>
<div class="mycode">root@fa 10:28:14>check table login_history_0015;<br />
ERROR 2013 (HY000): Lost connection to MySQL server during query<br />
root@fa 10:28:49>100505 10:28:49 mysqld_safe Number of processes running now: 0<br />
100505 10:28:49 mysqld_safe mysqld restarted
</div>
<p>未果！</p>
<p><font color="red">2. OPTIMIZE修复</font></p>
<p>尝试OPTIMIZE TABLE</p>
<div class="mycode">root@fa 10:35:30> OPTIMIZE TABLE login_history_0015;<br />
ERROR 2013 (HY000): Lost connection to MySQL server during query</div>
<p>未果</p>
<p><font color="red">3. 尝试innodb_force_recovery<del datetime="2010-05-08T06:22:05+00:00">了</del></font></p>
<p>在my.cnf中设置innodb_force_recovery=1，该参数使得InnoDB即使检测到corrupt page仍然启动MySQL，并将InnoDB置于只读状态：</p>
<div class="mycode">InnoDB: innodb_force_recovery is on: we do not allow<br />
InnoDB: database modifications by the user. Shut down</div>
<p>OK，发现这样可以访问数据表，虽然无法修改数据表。但是这就足够了，这样就可以用msyqldump导出全部的数据了。</p>
<p><font color="red">4. 故障原因</font></p>
<p>因为是数据是从备库中恢复过来的，先怀疑是不是这台主机有问题（恢复的时候磁盘出现某个莫名的故障），换了一台主机恢复，故障依旧。这说明是这个备份文件是有问题的。</p>
<p>于是从同一台主机上前一天的Xtrabackup中恢复，没有遇到故障；后来第二天，我又在主机上做了一个Xtrabackup备份，再恢复，问题没有重现。这说明只有那一天的Xtrabackup有故障。</p>
<p>无法定位原因。</p>
<p>因为是从备份中恢复的数据，检查了一下主库上这个表是正常，所以认为是备份的时候出现的故障，猜测：备份是用Xtrabackup做的，有可能是Xtrabackup的Bug（用前一天Xtrabackup再做了一次恢复，并没有出现类似的问题），也可能是备份的时候想NAS写的时候出现“意外”&#8230;&#8230;</p>
<p>谁知道呢</p>
<p>参考文献：<br />
[1]. <a href="http://www.mysqlperformanceblog.com/2008/07/04/recovering-innodb-table-corruption/">Recovering Innodb table Corruption</a><br />
[2]. <a href="http://dev.mysql.com/doc/refman/5.1/en/forcing-recovery.html">Forcing InnoDB Recovery</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/05/innodb-corruption/feed/</wfw:commentRss>
		<slash:comments>0</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特性介绍-2：快速DDL</title>
		<link>http://www.orczhou.com/index.php/2010/04/innodb-plugin-some-feature-2/</link>
		<comments>http://www.orczhou.com/index.php/2010/04/innodb-plugin-some-feature-2/#comments</comments>
		<pubDate>Mon, 26 Apr 2010 11:11:42 +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=1810</guid>
		<description><![CDATA[<p>InnoDB Plugin在快速DDL上做了一些改进，在做的实验中看到，创建secondary indexes时，大约快了20%。</p>
<p><font color=red>1. 原理</font></p>
<p>在MySQL5.0里面，如果数据表的记录数很多，增加和删除索引是非常慢的。CREATE INDEX and DROP INDEX命令是通过先创建一个空的临时表，这个表就是你新增或删除索引后的结构，然后把原表中的全部记录都拷贝（插入）到新的临时表中，最后把原表删除，临时表重命名成原表。</p>
<p>MySQL5.1的一些架构上的改变，可以简化上面的过程（不再需要逐行拷贝数据）。InnoDB Plugin利用这个改变，缩短了大多数情况下的索引变更时间。对InnoDB来说有两类索引：the clustered index and secondary indexes。因为InnoDB的主键是the clustered index，数据存放再此，所以，删除或者添加主键（the clustered index）逐行拷贝也是必须[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/04/innodb-plugin-some-feature-2/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>InnoDB Plugin在快速DDL上做了一些改进，在做的实验中看到，创建secondary indexes时，大约快了20%。</p>
<p><font color=red>1. 原理</font></p>
<p>在MySQL5.0里面，如果数据表的记录数很多，增加和删除索引是非常慢的。CREATE INDEX and DROP INDEX命令是通过先创建一个空的临时表，这个表就是你新增或删除索引后的结构，然后把原表中的全部记录都拷贝（插入）到新的临时表中，最后把原表删除，临时表重命名成原表。</p>
<p>MySQL5.1的一些架构上的改变，可以简化上面的过程（不再需要逐行拷贝数据）。InnoDB Plugin利用这个改变，缩短了大多数情况下的索引变更时间。对InnoDB来说有两类索引：the clustered index and secondary indexes。因为InnoDB的主键是the clustered index，数据存放再此，所以，删除或者添加主键（the clustered index）逐行拷贝也是必须<span id="more-1810"></span>的。<!--more--></p>
<p>InnoDB Pluing在删除一个secondary indexes时，先更改一下InnoDB内部数据字典和MySQL的数据字典，然后把释放的空间归还给InnoDB以供重复使用。如果是<strong>增加</strong>一个secondary indexes，还是有点复杂的，Plugin先将数据表中的数据取出到memory buffers或者临时表中，并按照新建索引列排好序，然后建立索引的B-Tree。实验表明，这样还是稍微快一些。</p>
<p><font color=red>2. 实验</font></p>
<p>以下是一组关于secondary indexes对比数据：</p>
<p>ADD INDEX:</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/4520260416/" title="PluginFaster by orczhou, on Flickr"><img src="http://farm5.static.flickr.com/4059/4520260416_800342635c_o.png" width="472" height="198" alt="PluginFaster" /></a></p>
<p>Drop INDEX:</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/4520278022/" title="PluginFaster-2 by orczhou, on Flickr"><img src="http://farm5.static.flickr.com/4059/4520278022_c2e9f7113e_o.png" width="473" height="198" alt="PluginFaster-2" /></a></p>
<p>以上图表纵坐标为秒，测试数据表有500万记录，ibd文件大小556M。（尝试了将tmp_table_size和max_heap_table_size设置成1G，效果并没有什么不同）</p>
<p><font color=red>3. 小结</font></p>
<p>可以看到创建索引Plugin比Built-in大约快<strong>20%</strong>，而删除索引的速度，区别并不大。</p>
<p>实验的结果并没有理论上那么理想。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/04/innodb-plugin-some-feature-2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Faster Recovery of InnoDB Plugin 1.0.7GA</title>
		<link>http://www.orczhou.com/index.php/2010/04/innodb-plugin-faster-recovery/</link>
		<comments>http://www.orczhou.com/index.php/2010/04/innodb-plugin-faster-recovery/#comments</comments>
		<pubDate>Fri, 16 Apr 2010 09:43:44 +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=1832</guid>
		<description><![CDATA[<p>这两天是<a href="http://en.oreilly.com/mysql2010/">O&#8217;Reilly MySQL Conference &#038; Expo</a>，期间有很多关于MySQL的相关话题的介绍，包括MySQL、PBXT、Drizzle、MariaDB、XtraDB等等，不过最抢风头的应该是InnoDB1.0.7GA（with MySQL-5.5）的新特性，回头想想，Oracle/InnoDB也酝酿了很久的吧。</p>
<p>无论如何，这些新特性中最吸引我的是InnoDB的Faster Recovery。</p>
<p>一般地，MySQL/InnoDB都是运行在普通的PC Server + Linux（Unix），虽然不期待小型机+AIX的高可用，但想尽一切办法缩短MySQL的不可用时间，仍然是DBA的目标。根据经验，主机OS崩溃、硬件故障，仍然是影响MySQL可用性的最主要因素，当这些故障（OS、硬件）恢复后，另一个<strong>非常耗时</strong>的恢复就是InnoDB自己的恢复时间。一般主机发生一次重启，正常大约&#60;5分钟，而这时InnoDB恢复可能需要40分钟或者更久（这依赖于Buffer Pool、脏页面比例、TPS等因素）。[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/04/innodb-plugin-faster-recovery/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>这两天是<a href="http://en.oreilly.com/mysql2010/">O&#8217;Reilly MySQL Conference &#038; Expo</a>，期间有很多关于MySQL的相关话题的介绍，包括MySQL、PBXT、Drizzle、MariaDB、XtraDB等等，不过最抢风头的应该是InnoDB1.0.7GA（with MySQL-5.5）的新特性，回头想想，Oracle/InnoDB也酝酿了很久的吧。</p>
<p>无论如何，这些新特性中最吸引我的是InnoDB的Faster Recovery。</p>
<p>一般地，MySQL/InnoDB都是运行在普通的PC Server + Linux（Unix），虽然不期待小型机+AIX的高可用，但想尽一切办法缩短MySQL的不可用时间，仍然是DBA的目标。根据经验，主机OS崩溃、硬件故障，仍然是影响MySQL可用性的最主要因素，当这些故障（OS、硬件）恢复后，另一个<strong>非常耗时</strong>的恢复就是InnoDB自己的恢复时间。一般主机发生一次重启，正常大约<5分钟，而这时InnoDB恢复可能需要40分钟或者更久（这依赖于Buffer Pool、脏页面比例、TPS等因素）。<span id="more-1832"></span></p>
<p>试想，如果每次能够把故障时间控制在10分钟之内，那么通过应用容错、Cache支持等办法，用户体验和可用时间都将有进一步的提升。</p>
<p>这次Plugin1.0.7GA通过算法和内存管理上的改进，将恢复时间<a href="http://blogs.innodb.com/wp/2010/04/innodb-performance-recovery/">大大缩短</a>。</p>
<p><font color="red">实验：</font></p>
<p>在InnoDB Blog中，给出了一个<a href="http://blogs.innodb.com/wp/2010/04/innodb-performance-recovery/">测试</a>：Plugin1.0.6花费7小时38分钟恢复的过程，使用Plugin1.0.7则仅仅花了13分56秒，总共快了32倍，其中扫描Redo阶段快了16倍，应用日志阶段快了35倍。（实验中Buffer Pool是18G）</p>
<p>现在，MySQL-5.5.4-m3自带的是InnoDB-1.1，也可以自己测试一下。</p>
<pre>
<div class="mycode">root@(none) 05:33:03>select version(),@@innodb_version;
+--------------+------------------+
| version()    | @@innodb_version |
+--------------+------------------+
| 5.5.4-m3-log | 1.1.0            |
+--------------+------------------+
</div>
</pre>
<p><font color="red">原理：</font></p>
<p>崩溃恢复（Crash recovery）可以看成两个阶段，第一阶段称为扫描重做日志（Redo scan），这时InnoDB读取磁盘上的Redo Log，并将其存放到一个Hash表中；第二阶段应用这些Redo Log，将这些日志应用到Data Page上。</p>
<p>在将Redo Log读取到Buffer Pool的Hash表的过程中，InnoDB在需要的时候分配16K的Block用来存储这个这些Redo。为了确保Buffer Pool中有足够剩余空间来存储数据页（Data Page），这样如果Redo很大的话，这个Block heap也会很大。这里InnoDB做了一件很2的事情，每次读取一个Redo的时候，都会遍历一次前面的Heap来确保，没有占用太多的空间。所以，如果崩溃前InnoDB的Buffer Pool很大，Dirty Page很多，这个Heap可能很大，每次遍历就会大大降低恢复时的效率。</p>
<p>InnoDB通过给这个Heap增加一个header来存储这些信息，解决了上面的问题。</p>
<p>恢复过程中，另一个耗时的操作是发生在应用Redo的阶段。每一个应用了Redo Log的Data Page都会被放到一个叫Flush_list的链表中等待Flush，而这个链表中的Data Page是严格安装其<a href="http://www.mysqlperformanceblog.com/2007/12/19/mvcc-transaction-ids-log-sequence-numbers-and-snapshots/">LSN</a>顺序排列的，在InnoDB正常工作的时候，这总是没有问题的，因为Data Page的LSN值总是单调增加的。但是在恢复阶段，InnoDB则需要不断的扫描这整个链表来确定一个Data Page的位置。</p>
<p>这个问题由来已久（<a href="http://bugs.mysql.com/bug.php?id=29847">Bug</a>，<a href="http://www.mysqlperformanceblog.com/2007/07/17/innodb-recovery-is-large-buffer-pool-always-better/">Improving InnoDB recovery time</a>），最终，在社区给出Patch之后，InnoDB终于修复了这个问题。</p>
<p>InnoDB在恢复阶段，通过一棵辅助的<a href="http://en.wikipedia.org/wiki/Red-black_tree">红黑树</a>（Red-Black Tree）来存储这些Page，借此来避免单纯的扫描。在恢复阶段结束后，这棵红黑树将被删除，Flush_list仍然保持原来的结构。</p>
<p><font color="red">最后：</font></p>
<p>如果这个特性稳定，那么，以后Buffer Pool就可以更放心的调大些了。</p>
<p>参考文献：</p>
<ol>
<li><a href="http://dev.mysql.com/doc/innodb-plugin/1.1/en/innodb-changes-107.html"> Changes in InnoDB Plugin 1.0.7</a></li>
<li><a href="http://dev.mysql.com/tech-resources/articles/introduction-to-mysql-55.html">Introduction to MySQL 5.5</a></li>
<li><a href="http://blogs.innodb.com/wp/2010/04/innodb-performance-recovery/">InnoDB recovery is now faster…much faster</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/04/innodb-plugin-faster-recovery/feed/</wfw:commentRss>
		<slash:comments>1</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&#8217;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>
		<item>
		<title>InnoDB Plugin压缩特性</title>
		<link>http://www.orczhou.com/index.php/2010/03/innodb-plugin-compression/</link>
		<comments>http://www.orczhou.com/index.php/2010/03/innodb-plugin-compression/#comments</comments>
		<pubDate>Thu, 25 Mar 2010 13:28:55 +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=1644</guid>
		<description><![CDATA[<p>Plugin的一个重要的特性就是增加了压缩存储。对于数据中有很多Long column(包括TEXT BLOB或者大VARCHAR)的场景能够大幅节约空间，并提升效率。</p>
<p><font color="red">1. 谁需要压缩？</font></p>
<p>在InnoDB中，是<a href="http://www.orczhou.com/index.php/2009/08/image-innodb-tablespace/">以16K的页（Page）为基本的存储单位</a>的。我们知道，InnoDB是的数据是在Clustered index中存储的，在Secondary index中仅存储对应数据的PK。Clustered index和Secondary index都是B-Tree结构的，所以<strong>对InnoDB数据页和索引页的压缩很大程度上就是对B-Tree节点页的压缩</strong>。</p>
<p>在InnoDB中，除了B-Tree节点页，还有一类数据页（Page），称为“overflow page”。当需要存储Long column时，如果当前页能够完全存储全部字段时，则存储在当前页中；如果当前页不足以存储全部，则InnoDB选择最长的字段，将其存储到一个单独的页中，我们称这样的页为“overflow page”，而原数据页仅仅需存储一个20Bytes的指针。参考下图：</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/4457179774/" title="InnoDB_overflow_page by orczhou, on Flickr"><img src="http://farm3.static.flickr.com/2715/4457179774_f6d1ceb851_o.png" width="331" height="144" alt="InnoDB_overflow_page" /></a></p>
<p>所以InnoDB除了有上面的B-Tree页外，InnoDB还存储overflow页。事实上，需要压缩的数据页也就是这两类，InnoDB为了获得更好的效率，针对这两类数据页的压缩是使用不同的策略的。</p>
<p>[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/03/innodb-plugin-compression/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>Plugin的一个重要的特性就是增加了压缩存储。对于数据中有很多Long column(包括TEXT BLOB或者大VARCHAR)的场景能够大幅节约空间，并提升效率。</p>
<p><font color="red">1. 谁需要压缩？</font></p>
<p>在InnoDB中，是<a href="http://www.orczhou.com/index.php/2009/08/image-innodb-tablespace/">以16K的页（Page）为基本的存储单位</a>的。我们知道，InnoDB是的数据是在Clustered index中存储的，在Secondary index中仅存储对应数据的PK。Clustered index和Secondary index都是B-Tree结构的，所以<strong>对InnoDB数据页和索引页的压缩很大程度上就是对B-Tree节点页的压缩</strong>。</p>
<p>在InnoDB中，除了B-Tree节点页，还有一类数据页（Page），称为“overflow page”。当需要存储Long column时，如果当前页能够完全存储全部字段时，则存储在当前页中；如果当前页不足以存储全部，则InnoDB选择最长的字段，将其存储到一个单独的页中，我们称这样的页为“overflow page”，而原数据页仅仅需存储一个20Bytes的指针。参考下图：</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/4457179774/" title="InnoDB_overflow_page by orczhou, on Flickr"><img src="http://farm3.static.flickr.com/2715/4457179774_f6d1ceb851_o.png" width="331" height="144" alt="InnoDB_overflow_page" /></a></p>
<p>所以InnoDB除了有上面的B-Tree页外，InnoDB还存储overflow页。事实上，需要压缩的数据页也就是这两类，InnoDB为了获得更好的效率，针对这两类数据页的压缩是使用不同的策略的。</p>
<p><span id="more-1644"></span></p>
<p><font color="red">2. 如何压缩B-Tree页</font></p>
<p>未来尽可能多的避免压缩、解压带来的额外消耗，InnoDB在压缩后的B-Tree页中新增了一个modification log区，通过记录当前的页的修改日志，来避免频繁压缩解压（参考左图）。</p>
<div style="float:left;margin-right:15px"><a href="http://www.flickr.com/photos/26825745@N06/4456426179/" title="InnoDB_Page_compression by orczhou, on Flickr"><img src="http://farm3.static.flickr.com/2782/4456426179_b7e962d095_o.png" width="154" height="290" alt="InnoDB_Page_compression" /></a></div>
<p>当modification log空间不足时，才进行解压并应用这些Log，然后再压缩回去。如果必要，这里可能会出现B-Tree页的分裂。</p>
<p><font color="red">3. 如何压缩over-flow页</font></p>
<p>overflow的压缩页面则相对简单，仅包含一个页面头（header），页面尾（Trailer）中间就是压缩数据。试想，如果存储的Long Column是能够高度压缩的内容（例如文本，Xml等），这样将节省很多的存储空间，进而能够节省I/O。（参考下图）<a href="http://www.flickr.com/photos/26825745@N06/4457242772/" title="InnoDB_over_page_2 by orczhou, on Flickr"><img src="http://farm5.static.flickr.com/4024/4457242772_583deaf273_o.png" width="435" height="46" alt="InnoDB_over_page_2" /></a></p>
<p><font color="red">4. 压缩的算法是什么</font></p>
<p>压缩使用的是<a href="http://www.zlib.net/">zlib library</a>中的<a href="http://zh.wikipedia.org/wiki/LZ77%E4%B8%8ELZ78">LZ77</a>算法。</p>
<p><font color="red">5. 压缩和Buffer Pool</font></p>
<p>当使用压缩存储的页面，当Buffer Pool载入后，会将其解压。这时，该页面在Buffer Pool中同时存在“压缩版”和“解压版”。当Buffer Pool需要驱逐这些页的时候，有两种情况会发生：如果InnoDB认为当前应用是IO-Bound，相比CPU还有额外能力来做解压操作，则InnoDB选择仅驱逐页面的“解压版”；否则InnoDB会将页面的两个版本同时驱逐出去。也就是说Buffer Pool会是下图的状态：</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/4457270684/" title="Compress_Buffer_Pool by orczhou, on Flickr"><img src="http://farm5.static.flickr.com/4038/4457270684_4df8f95a78_o.png" width="415" height="255" alt="Compress_Buffer_Pool" /></a></p>
<p><font color="red">6. 如何创建一个压缩数据表</font></p>
<p>原理是复杂的，操作其实很简单：</p>
<div class="mycode">CREATE TABLE name (column1 INT PRIMARY KEY) ENGINE=InnoDB<br />
ROW_FORMAT=COMPRESSED<br />
KEY_BLOCK_SIZE=4;
</div>
<p>这里创建了一个，压缩后页面大小为4K（默认为8K）的压缩数据表。</p>
<p>参考文献：<a href="http://www.innodb.com/doc/innodb_plugin-1.0/index.html">InnoDB Plugin User Guide</a></p>
<p>（全文完）</p>
<div style="clear:both"></div>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/03/innodb-plugin-compression/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>InnoDB Plugin文件格式(概述)</title>
		<link>http://www.orczhou.com/index.php/2010/03/innodb-plugin-file-format/</link>
		<comments>http://www.orczhou.com/index.php/2010/03/innodb-plugin-file-format/#comments</comments>
		<pubDate>Wed, 24 Mar 2010 01:23:57 +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=1604</guid>
		<description><![CDATA[<p>本文将介绍InnoDB Plugin数据表格式的基本概念。</p>
<p><font color="red">1. 配置参数innodb_file_format</font></p>
<p>这是一个很容易混淆的概念。目前，在InnoDB Plugin（1.0.6）配置文件中innodb_file_format支持两种：Antelope/ˈæntɪləʊp/、Barracuda/ˌbærəˈkjuːdə/。他们分别是两种文件格式的代号，在未来版本中，InnoDB将继续延续这种代号机制，它们会是Antelope, Barracuda, Cheetah, Dragon, Elk, Fox等等。</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/4456338484/" title="Antelope_Barracuda by orczhou, on Flickr"><img src="http://farm5.static.flickr.com/4004/4456338484_1518d4d347_o.png" width="457" height="192" alt="Antelope_Barracuda" /></a></p>
<p>[......]</p><p class='read-more'><a href='http://www.orczhou.com/index.php/2010/03/innodb-plugin-file-format/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p>本文将介绍InnoDB Plugin数据表格式的基本概念。</p>
<p><font color="red">1. 配置参数innodb_file_format</font></p>
<p>这是一个很容易混淆的概念。目前，在InnoDB Plugin（1.0.6）配置文件中innodb_file_format支持两种：Antelope/ˈæntɪləʊp/、Barracuda/ˌbærəˈkjuːdə/。他们分别是两种文件格式的代号，在未来版本中，InnoDB将继续延续这种代号机制，它们会是Antelope, Barracuda, Cheetah, Dragon, Elk, Fox等等。</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/4456338484/" title="Antelope_Barracuda by orczhou, on Flickr"><img src="http://farm5.static.flickr.com/4004/4456338484_1518d4d347_o.png" width="457" height="192" alt="Antelope_Barracuda" /></a></p>
<p><span id="more-1604"></span></p>
<p>Antelope是Built-in-InnoDB（MySQL内置的InnoDB）支持文件格式的代号，有两种“数据表格式”（row_format）：Redundant、Compact；Barracuda是InnoDB Plugin支持的文件格式，在<strong>原来的基础上</strong>新增了两种数据表格式的支持：Dynamic和Compressed。</p>
<p>对应关系表：</p>
<p><a href="http://www.flickr.com/photos/26825745@N06/4455602019/" title="innodb_plugin_com by orczhou, on Flickr"><img src="http://farm3.static.flickr.com/2457/4455602019_f1bf0e14d3_o.png" width="576" height="63" alt="innodb_plugin_com" /></a></p>
<p>一般，innodb_file_format在<strong>配置文件中</strong>指定；row_format则在<strong>创建数据表时</strong>指定。</p>
<p><font color="red">2. 新建数据表的格式、已有数据表格式</font></p>
<p>要支持新的数据表格式，我们需要在配置文件中新增配置选项：</p>
<div class="mycode">innodb_file_format=barracuda<br />
innodb_file_per_table = 1<br />
innodb_strict_mode=1    #建议加上
</div>
<p>然后，在创建数据表时，指定参数ROW_FORMAT就可以指定特定的格式了。例如：</p>
<div class="mycode">CREATE TABLE name (column1 INT PRIMARY KEY) ENGINE=InnoDB<br />
ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;</div>
<p>这里创建了一个COMPRESSED的数据表，指定压缩后页大小为4K。</p>
<p>一般地，我们通过SHOW CREATE TABLE就可以看到数据表的具体格式了。特别地，我们也可以通过观察ibd的文件头来确定文件的格式：</p>
<div class="mycode">$od -t x1 -j 54 -N 4 t3.ibd     #compact<br />
0000066 00 00 00 27<br />
0000072<br />
$od -t x1 -j 54 -N 4 t2.ibd      #compressed<br />
0000066 00 00 00 00<br />
0000072</div>
<p><font color="red">3. 文件格式兼容性检查</font></p>
<p>InnoDB Plugin引入的新的文件格式，也引入较为完整的文件兼容性检查，以防止误操作非兼容的文件格式。兼容性检查一共有三类：启动数据库时、创建数据表时、访问数据表时。</p>
<p><font color="blue">3.1</font> 在数据库启动时候，参数innodb_file_format_check（>=5.1.38）会要求InnoDB在启动时检查当前数据表的格式。设置为ON时，如果检测到不支持的格式，那么InnoDB会启动失败；设置为OFF时，检测到不支持的仅会给出警告，并不会导致启动失败。</p>
<p>把innodb_file_format_check设置为OFF是很危险的。在InnoDB启动后，一般需要做一些恢复工作，例如Double write buffer/Insert buffer中的数据处理（这依赖于innodb_fast_shutdown参数），试想如果成功启动，但是某些表是不支持的格式，但是InnoDB仍然安装旧版本做恢复，这可能会<strong>毁掉</strong>相关数据。</p>
<p>所以，一般建议innodb_file_format_check设置为ON。如果是OFF，关闭InnoDB的innodb_fast_shutdown参数务必设置成0。</p>
<p><font color="blue">3.2</font> 除了启动时会检查兼容性外，当创建数据表时，InnoDB会依据参数InnoDB_file_format进行检查，如果创建的数据表格式高于InnoDB_file_format，则创建会失败。</p>
<p><font color="blue">3.3</font> 当访问某个数据表（table-access）时，InnoDB也会进行兼容性检查。只要当前运行的InnoDB版本能够支持的格式，都能够被访问，无论参数InnoDB_file_format的配置。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.orczhou.com/index.php/2010/03/innodb-plugin-file-format/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
