<?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>code.openark.org &#187; InnoDB</title>
	<atom:link href="http://code.openark.org/blog/tag/innodb/feed" rel="self" type="application/rss+xml" />
	<link>http://code.openark.org/blog</link>
	<description>Blog by Shlomi Noach</description>
	<lastBuildDate>Wed, 01 Feb 2012 08:19:12 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Announcing common_schema: common views &amp; routines for MySQL</title>
		<link>http://code.openark.org/blog/mysql/announcing-common_schema-common-views-routines-for-mysql</link>
		<comments>http://code.openark.org/blog/mysql/announcing-common_schema-common-views-routines-for-mysql#comments</comments>
		<pubDate>Wed, 13 Jul 2011 04:25:24 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Analysis]]></category>
		<category><![CDATA[common_schema]]></category>
		<category><![CDATA[Data Types]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Indexing]]></category>
		<category><![CDATA[INFORMATION_SCHEMA]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Schema]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Stored routines]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=3794</guid>
		<description><![CDATA[Today I have released common_schema, a utility schema for MySQL which includes many views and functions, and is aimed to be installed on any MySQL server. What does it do? There are views answering for all sorts of useful information: stuff related to schema analysis, data dimensions, monitoring, processes &#38; transactions, security, internals... There are [...]]]></description>
			<content:encoded><![CDATA[<p>Today I have released <a title="common_schema" href="http://code.openark.org/forge/common_schema">common_schema</a>, a utility schema for MySQL which includes many views and functions, and is aimed to be installed on any MySQL server.</p>
<h4>What does it do?</h4>
<p>There are views answering for all sorts of useful information: stuff related to schema analysis, data dimensions, monitoring, processes &amp; transactions, security, internals... There are basic functions answering for common needs.</p>
<p>Some of the views/routines simply formalize those queries we tend to write over and over again. Others take the place of external tools, answering complex questions via SQL and metadata. Still others help out with SQL generation.</p>
<p>Here are a few highlights:</p>
<ul>
<li>Did you know you can work out <a href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/global_status_diff_nonzero.html">simple monitoring</a> of your server with a <em>query</em>?  There's a view to do that for you.</li>
<li>How about showing just <a href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/processlist_top.html">the good parts of the processlist</a>?</li>
<li>Does your schema have <a href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/redundant_keys.html">redundant keys</a>?</li>
<li>Or InnoDB tables with <a href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/no_pk_innodb_tables.html">no PRIMARY KEY</a>?</li>
<li>Is AUTO_INCREMENT <a href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/auto_increment_columns.html">running out of space</a>?</li>
<li>Can I get the SQL statements to <a href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/sql_foreign_keys.html">generate my FOREIGN KEYs</a>? To drop them?</li>
<li>And can we finally get <a href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/sql_show_grants.html">SHOW GRANTS for all accounts</a>, and as an <a href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/sql_grants.html">SQL query</a>?</li>
<li>Ever needed a <a href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/general_functions.html#crc64">64 bit CRC function</a>?</li>
<li>And aren't you tired of writing the cumbersome SUBSTRING_INDEX(SUBSTRING_INDEX(str, ',', 3), ',', -1)? <a href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/string_functions.html#split_token">There's an alternative</a>.</li>
</ul>
<p>There's more. Take a look at the <a href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/introduction.html">common_schema documentation</a> for full listing. And it's evolving: I've got quite a few ideas already for future components.</p>
<p>Some of these views rely on heavyweight INFORMATION_SCHEMA tables. You should be aware of the impact and <a href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/risks.html">risks</a>.</p>
<h4>What do I need to install?</h4>
<p>There's no script or executable file. It's just a schema. The distribution in an SQL file which generates <em>common_schema</em>. Much like a dump file.</p>
<h4><span id="more-3794"></span>What are the system requirements?</h4>
<p>It's just between you and your MySQL. There are currently three distribution files, dedicated for different versions of MySQL (and allowing for increased functionality):</p>
<ul>
<li><strong>common_schema_mysql_51</strong>: fits all MySQL &gt;= 5.1 distributions</li>
<li><strong>common_schema_innodb_plugin</strong>: fits MySQL &gt;= 5.1, with InnoDB plugin + INFORMATION_SCHEMA tables enabled</li>
<li><strong>common_schema_percona_server</strong>: fits Percona Server &gt;= 5.1</li>
</ul>
<p>Refer to the <a rel="nofollow" href="http://common-schema.googlecode.com/svn/trunk/common_schema/doc/html/download.html">documentation</a> for more details.</p>
<h4>What are the terms of use?</h4>
<p><em>common_schema</em> is released under the <a href="http://www.opensource.org/licenses/bsd-license.php">BSD license</a>.</p>
<h4>Where can I download it?</h4>
<p>On the <a href="http://code.google.com/p/common-schema/">common_schema project page</a>. Enjoy it!</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/announcing-common_schema-common-views-routines-for-mysql/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Useful sed / awk liners for MySQL</title>
		<link>http://code.openark.org/blog/mysql/useful-sed-awk-liners-for-mysql</link>
		<comments>http://code.openark.org/blog/mysql/useful-sed-awk-liners-for-mysql#comments</comments>
		<pubDate>Wed, 06 Jul 2011 06:41:00 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Configuration]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[mysqldump]]></category>
		<category><![CDATA[scripts]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=3685</guid>
		<description><![CDATA[Listing some useful sed / awk liners to use with MySQL. I use these on occasion. sed, awk &#38; grep have many overlapping features. Some simple tasks can be performed by either. For example, stripping empty lines can be performed by either: grep '.' awk '/./' sed '/./!d' grep -v '^$' awk '!/^$/' sed '/^$/d' [...]]]></description>
			<content:encoded><![CDATA[<p>Listing some useful <strong>sed</strong> / <strong>awk</strong> liners to use with MySQL. I use these on occasion.</p>
<p><strong>sed</strong>, <strong>awk</strong> &amp; <strong>grep</strong> have many overlapping features. Some simple tasks can be performed by either. For example, stripping empty lines can be performed by either:</p>
<blockquote>
<pre><strong>grep</strong> '.'
<strong>awk</strong> '/./'
<strong>sed</strong> '/./!d'
<strong>grep</strong> -v '^$'
<strong>awk</strong> '!/^$/'
<strong>sed</strong> '/^$/d'</pre>
</blockquote>
<p>It's a matter of taste &amp; convention which tool and variation to use. So for any script I suggest, there may be many variations, possibly cleaner, shorter; feel free to comment.</p>
<h4>mysqldump</h4>
<p>The output of <em>mysqldump</em> is in particular useful when one wishes to make transformation on data or metadata.<span id="more-3685"></span></p>
<ul>
<li>Convert MyISAM tables to InnoDB:</li>
</ul>
<blockquote>
<pre>mysqldump | sed -e 's/^) ENGINE=MyISAM/) ENGINE=InnoDB/'</pre>
</blockquote>
<p>I've had several occasion when people said this type of conversion assumes no <strong>'ENGINE=MyISAM'</strong> snippet exists within row data. This is not so. The <strong>'^) ENGINE=MyISAM/'</strong> pattern strictly requires that this text is outside row data. No row data begins with a <strong>')'</strong>. This is a safe conversion.</p>
<ul>
<li>Convert InnoDB to InnoDB plugin, compressed tables:</li>
</ul>
<blockquote>
<pre>mysqldump | sed -e 's/^) ENGINE=InnoDB/) ENGINE=InnoDB ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8/'</pre>
</blockquote>
<ul>
<li>Slice out a specific database (assumes existence of the <strong>USE</strong> statement):</li>
</ul>
<blockquote>
<pre>sed -n "/^USE \`employees\`/,/^USE \`/p"</pre>
</blockquote>
<ul>
<li>Slice out a specific table:</li>
</ul>
<blockquote>
<pre>sed -n "/^-- Table structure for table \`departments\`/,/^-- Table structure for table/p"</pre>
</blockquote>
<ul>
<li>Combine the above two statements to slice a specific table from a specific database:</li>
</ul>
<blockquote>
<pre>sed -n "/^USE \`employees\`/,/^USE \`/p" | sed -n "/^-- Table structure for table \`departments\`/,/^-- Table structure for table/p"</pre>
</blockquote>
<p>See also <a rel="bookmark" href="http://code.openark.org/blog/mysql/on-restoring-a-single-table-from-mysqldump">On restoring a single table from mysqldump</a>.</p>
<h4>my.cnf</h4>
<p>Some <em>my.cnf</em> files are just a mess to read. Here's some normalizing scripts:</p>
<ul>
<li>Strip a <em>my.cnf</em> file from comments, remove blank lines, normalize spaces:</li>
</ul>
<blockquote>
<pre>cat my.sandbox.cnf | sed '/^#/d' | sed '/^$/d' | sed -e 's/[ \t]\+//g'</pre>
</blockquote>
<ul>
<li>Same, but only present <strong>[mysqld]</strong> section parameters:</li>
</ul>
<blockquote>
<pre>cat my.sandbox.cnf | sed -n '/^\[mysqld\]/,/^\[/p' | sed '/^\[/d' | sed '/^#/d' | sed '/^$/d' | sed -e 's/[ \t]\+//g'</pre>
</blockquote>
<ul>
<li>Only present <strong>[mysqld]</strong> section parameters, tab delimited (this is useful in exporting and comparing instance parameters):</li>
</ul>
<blockquote>
<pre>cat my.sandbox.cnf | sed -n '/^\[mysqld\]/,/^\[/p' | sed '/^\[/d' | sed '/^#/d' | sed '/^$/d' | sed -e 's/[ \t]\+//g' | sed -e 's/=/\t/'</pre>
</blockquote>
<ul>
<li>Multi-word parameters in <em>my.cnf</em> can be written with either hyphens or underscores. <strong>innodb_file_per_</strong>table is the same as <strong>innodb-file-per-table</strong>, as well as <strong>innodb_file-per_table</strong>. The following normalizes the parameter names to using underscores only, keeping from changing values (e.g. <strong>'mysql-bin' </strong>parameter value should not change). It isn't pretty!</li>
</ul>
<blockquote>
<pre>cat my.sandbox.cnf | awk -F "=" 'NF &lt; 2 {print} sub("=", "=~placeholder~=") {print}' | awk -F "=~placeholder~=" 'NF &lt; 2 {gsub("-", "_", $0); print} NF==2 {gsub("-", "_", $1); print $1 "=" $2}'</pre>
</blockquote>
<div id="_mcePaste" class="mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">grep "."<br />
awk '/./'<br />
sed '/./!d'<br />
grep -v '^$'<br />
awk '!/^$/'<br />
sed '/^$/d'</div>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/useful-sed-awk-liners-for-mysql/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Reasons to use AUTO_INCREMENT columns on InnoDB</title>
		<link>http://code.openark.org/blog/mysql/reasons-to-use-auto_increment-columns-on-innodb</link>
		<comments>http://code.openark.org/blog/mysql/reasons-to-use-auto_increment-columns-on-innodb#comments</comments>
		<pubDate>Tue, 22 Mar 2011 06:31:18 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Indexing]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[Schema]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=3196</guid>
		<description><![CDATA[An InnoDB table must have a primary key (one is created if you don't do it yourself). You may have a natural key at hand. Stop! Allow me to suggest an AUTO_INCREMENT may be better. Why should one add an AUTO_INCREMENT PRIMARY KEY on a table on which there's a natural key? Isn't an AUTO_INCREMENT [...]]]></description>
			<content:encoded><![CDATA[<p>An InnoDB table must have a primary key (one is created if you don't do it yourself). You may have a <a href="http://en.wikipedia.org/wiki/Natural_key">natural key</a> at hand. Stop! Allow me to suggest an AUTO_INCREMENT may be better.</p>
<p>Why should one add an AUTO_INCREMENT PRIMARY KEY on a table on which there's a natural key? Isn't an AUTO_INCREMENT a pseudo key, meaning, it doesn't have any explicit relation to the row data, other than it is a number and unique?</p>
<p>Yes, indeed so. Nevertheless, consider:</p>
<ul>
<li>Natural keys are many times multi-columned.</li>
<li>Multi column PRIMARY KEYs make for larger keys, and make for bloated secondary keys as well. You may be wasting space for storing the additional AUTO_INCREMENT column, but you may gain space back on secondary keys.</li>
<li>Multi column PRIMARY KEYs make for more locks. See also <a href="http://code.openark.org/blog/mysql/reducing-locks-by-narrowing-primary-key">this post</a>.</li>
<li>InnoDB INSERTs work considerably faster when worked in ascending PRIMARY KEY order. Can you ensure your natural key is in such order?</li>
<li>Even though an AUTO_INCREMENT makes for an INSERT bottleneck (values must be given serially), it is in particular helpful to InnoDB by ensuring PRIMARY KEY values are in ascending order.</li>
<li>AUTO_INCEMENT makes for chronological resolution. You <em>know</em> what came first, and what came next.</li>
<li>In many datasets, more recent entries are often being accessed more, and are therefore "hotter". By using AUTO_INCREMENT, you're ensuring that recent entries are grouped together within the B+ Tree. This means less random I/O when looking for recent data.</li>
<li>A numerical key is in particular helpful in splitting your table (and tasks on your table) into smaller chunks. I write <a href="http://code.google.com/p/openarkkit/">tools</a> which can work out with any PRIMARY KEY combination, but it's easier to work with numbers.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/reasons-to-use-auto_increment-columns-on-innodb/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Limiting table disk quota in MySQL</title>
		<link>http://code.openark.org/blog/mysql/limiting-table-disk-quota-in-mysql</link>
		<comments>http://code.openark.org/blog/mysql/limiting-table-disk-quota-in-mysql#comments</comments>
		<pubDate>Mon, 07 Mar 2011 07:08:21 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[File System]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[MyISAM]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Triggers]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=3359</guid>
		<description><![CDATA[Question asked by a student: is there a way to limit a table's quote on disk? Say, limit a table to 2GB, after which it will refuse to grow? Note that the requirement is that rows are never DELETEd. The table must simply refuse to be updated once it reaches a certain size. There is [...]]]></description>
			<content:encoded><![CDATA[<p>Question asked by a student: is there a way to limit a table's quote on disk? Say, limit a table to 2GB, after which it will refuse to grow? Note that the requirement is that rows are never DELETEd. The table must simply refuse to be updated once it reaches a certain size.</p>
<p>There is no built-in way to limit a table's quota on disk. First thing to observe is that MySQL has nothing to do with this. It is entirely up to the storage engine to provide with such functionality. The storage engine is the one to handle data storage: how table and keys are stored on disk. Just consider the difference between MyISAM's <strong>.MYD</strong> &amp; <strong>.MYI</strong> to InnoDB's shared tablespace <strong>ibdata1</strong> to InnoDB's file-per table <strong>.ibd</strong> files.</p>
<p>The only engine I know of that has a quota is the MEMORY engine: it accepts the <strong>max_heap_table_size</strong>, which limits the size of a single table in memory. Hrmmm... In memory...</p>
<h4>Why limit?</h4>
<p>I'm not as yet aware of the specific requirements of said company, but this is not the first time I heard this question.</p>
<p>The fact is: when MySQL runs out of disk space, it goes with a BOOM. It crashed ungracefully, with binary logs being out of sync, replication being out of sync. To date, and I've seen some cases, InnoDB merely crashes and manages to recover once disk space is salvaged, but I am not certain this is guaranteed to be the case. Anyone?</p>
<p>And, with MyISAM..., who knows?</p>
<p>Rule #1 of MySQL disk usage: <em>don't run out of disk space.</em></p>
<h4>Workarounds</h4>
<p>I can think of two workarounds, none of which is pretty. The first involves triggers (actually, a few variations for this one), the second involves privileges.<span id="more-3359"></span></p>
<h4>Triggers</h4>
<p>The following code (first presented in <a title="Permanent Link to Triggers Use Case Compilation, Part II" rel="bookmark" href="http://code.openark.org/blog/mysql/triggers-use-case-compilation-part-ii">Triggers Use Case Compilation, Part II</a>) assumed the DATA_LENGTH and INDEX_LENGTH values in INFORMATION_SCHEMA to be good indicators:</p>
<blockquote>
<pre>DROP TABLE IF EXISTS `world`.`logs`;
CREATE TABLE  `world`.`logs` (
  `logs_id` int(11) NOT NULL auto_increment,
  `ts` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `message` varchar(255) character set utf8 NOT NULL,
  PRIMARY KEY  (`logs_id`)
) ENGINE=MyISAM;

DELIMITER $$

DROP TRIGGER IF EXISTS logs_bi $$
CREATE TRIGGER logs_bi BEFORE INSERT ON logs
FOR EACH ROW
BEGIN
  SELECT DATA_LENGTH+INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='world' AND TABLE_NAME='LOGS' INTO @estimated_table_size;
  IF (@estimated_table_size &gt; 25*1024) THEN
    SELECT 0 FROM `logs table is full` INTO @error;
  END IF;
END $$

DELIMITER ;
</pre>
</blockquote>
<p>Or, you could write your own UDF, e.g. <strong>get_table_file_size(fully_qualified_table_name)</strong> and be more accurate:</p>
<blockquote>
<pre>DELIMITER $$

DROP TRIGGER IF EXISTS logs_bi $$
CREATE TRIGGER logs_bi BEFORE INSERT ON logs
FOR EACH ROW
BEGIN
  SELECT get_table_file_size('world.logs') INTO @table_size;
  IF (@table_size &gt; 25*1024) THEN
    SELECT 0 FROM `logs table is full` INTO @error;
  END IF;
END $$

DELIMITER ;
</pre>
</blockquote>
<p>(Same should be done for <strong>UPDATE</strong> operations)</p>
<p>In both workarounds above, triggers are pre-defined. But triggers are performance-killers.</p>
<p>How about preventing writing to the table only when it's truly on the edge? A simple shell script, spawned by a cronjob, could do this well: get the file size of a specific table, and test if it's larger than <em>n</em> bytes. If not, the script exits. If the file is indeed too large, the scripts invokes the following on <em>mysql</em>:</p>
<blockquote>
<pre>DELIMITER $$

DROP TRIGGER IF EXISTS logs_bi $$
CREATE TRIGGER logs_bi BEFORE INSERT ON logs
FOR EACH ROW
BEGIN
  SELECT 0 FROM `logs table is full` INTO @error;
END $$

DELIMITER ;
</pre>
</blockquote>
<p>So, during most of the time, there is no trigger. Only when the external script detects that table is too large, does it create a trigger. The trigger has no logic: it simply raises an error (PS, use <strong>raise</strong> in MySQL <strong>5.5</strong>).</p>
<h4>Privileges</h4>
<p>Another way to work around the problem is to use security features. Instead of creating a trigger on the table, <strong>REVOKE</strong> the <strong>INSERT</strong> &amp; <strong>UPDATE</strong> privileges from the appropriate user on that table.</p>
<p>This may turn out to be a difficult task, since MySQL has no notion of <em>fine grain changes</em>. That is, suppose we have:</p>
<blockquote>
<pre>GRANT INSERT, UPDATE, DELETE, SELECT ON mydb.* TO 'webuser'@'%.webdomain'</pre>
</blockquote>
<p>If we just do:</p>
<blockquote>
<pre>REVOKE SELECT ON mydb.logs FROM 'webuser'@'%.webdomain'</pre>
</blockquote>
<p>We get:</p>
<blockquote>
<pre>There is no such grant defined for user 'webuser' on host '%.webdomain' on table 'logs'.</pre>
</blockquote>
<p>So this requires setting up privileges on the table level in the first place. Plus note that as long as the grants on the database level do allow for INSERTs, you cannot override it on the table level.</p>
<h4>Other ideas?</h4>
<p>I never actually implemented table disk quota. I'm not sure this is a viable solution; but I haven't heard all the arguments in favor as yet, so I don't want to rule this out.</p>
<p>Please share below if you are using other means of table size control, other than the trivial cleanup of old records.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/limiting-table-disk-quota-in-mysql/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Upgrading to Barracuda &amp; getting rid of huge ibdata1 file</title>
		<link>http://code.openark.org/blog/mysql/upgrading-to-barracuda-getting-rid-of-huge-ibdata1-file</link>
		<comments>http://code.openark.org/blog/mysql/upgrading-to-barracuda-getting-rid-of-huge-ibdata1-file#comments</comments>
		<pubDate>Tue, 15 Feb 2011 08:01:15 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Backup]]></category>
		<category><![CDATA[Configuration]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[mysqldump]]></category>
		<category><![CDATA[Replication]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=3304</guid>
		<description><![CDATA[Some of this is old stuff, but more people are now converting to InnoDB plugin, so as to enjoy table compression, performance boosts. Same holds for people converting to Percona's XtraDB. InnoDB plugin requires innodb_file_per_table. No more shared tablespace file. So your ibdata1 file is some 150GB, and it won't reduce. Really, it won't reduce. [...]]]></description>
			<content:encoded><![CDATA[<p>Some of this is old stuff, but more people are now converting to InnoDB plugin, so as to enjoy table compression, performance boosts. Same holds for people converting to Percona's XtraDB. InnoDB plugin requires <strong>innodb_file_per_table</strong>. No more shared tablespace file.</p>
<p>So your <strong>ibdata1</strong> file is some <strong>150GB</strong>, and it won't reduce. Really, it won't reduce. You set <strong>innodb_file_per_table=1</strong>, do <strong>ALTER TABLE t ENGINE=InnoDB</strong> (optionally <strong>ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8</strong>), and you get all your tables in file-per-table <strong>.ibd</strong> files.</p>
<p>But the original <strong>ibdata1</strong> file is still there. It has to be there, don't delete it! It contains more than your old data.</p>
<p>InnoDB tablespace files never reduce in size, it's an old-time annoyance. The only way to go round it, if you need the space, is to completely drop them and start afresh. That's one of the things so nice about file-per-table: an <strong>ALTER TABLE</strong> actually creates a new tablespace file and drops the original one.</p>
<h4>The procedure</h4>
<p>The procedure is somewhat painful:</p>
<ul>
<li>Dump everything logically (either use <em>mysqldump</em>, <a href="http://www.maatkit.org/doc/mk-parallel-dump.html">mk-parallel-dump</a>, or do it your own way)</li>
<li>Erase your data (literally, delete everything under your <strong>datadir</strong>)</li>
<li>Generate a new empty database</li>
<li>Load your dumped data.<span id="more-3304"></span></li>
</ul>
<h4>Using replication</h4>
<p>Replication makes this less painful. Set up a slave, have it follow up on the master.</p>
<ul>
<li>Stop your slave.</li>
<li>Make sure to backup the replication position (e.g. write <strong>SHOW SLAVE STATUS</strong> on a safe location, or copy <strong>master.info</strong> file).</li>
<li>Work out the dump-erase-generate-load steps on the slave.</li>
<li>Reattach the slave to the master using saved data.</li>
</ul>
<p>For this to succeed you must keep enough binary logs on the master for the entire dump-load period, which could be lengthy.</p>
<h4>Upgrading to barracuda</h4>
<p>If you wish to upgrade your InnoDB tables to <em>Barracuda</em> format, my advice is this:</p>
<ol>
<li>Follow the steps above to generate a file-per-table working slave</li>
<li>Stop the slave</li>
<li>Configure <strong>skip_slave_start</strong></li>
<li>Restart MySQL</li>
<li>One by one do the <strong>ALTER TABLE</strong> into <em>Barracuda</em> format (<strong>ROW_FORMAT=COMPACT</strong> or <strong>ROW_FORMAT=COMPRESSED</strong>)</li>
</ol>
<p>Note that if you're about to do table compression, the <strong>ALTER</strong> statements become <em>considerably</em> slower the better the compression is.</p>
<p>If your dataset is very large, and you can't keep so many binary logs, you may wish to break step <strong>5</strong> above into:</p>
<ul>
<li>ALTER a large table</li>
<li>Restart MySQL</li>
<li>Start slave, wait for it to catch up</li>
<li>Restart MySQL again</li>
</ul>
<p>and do the same for all large tables.</p>
<h4>Why all these restarts?</h4>
<p>I've been upgrading to Barracuda for a long time now. I have clearly noticed that <strong>ALTER</strong> into a <strong>COMPRESSED</strong> format works considerably slower after the slave has done some "real work". This in particular relates to the last "renaming table" stage. There was a bug with earlier InnoDB plugin versions which made this stage hang. It was solved. But it still takes some time for this last, weird stage, where the new replacement table is complete, and it's actually been renamed in place of the old table, and the old table renamed into something like "#sql-12345.ibd", and all that needs to be done is have it dropped, and... Well, it takes time.</p>
<p>My observation is it works faster on a freshly started server. Which is why I take the bother to restart MySQL before each large table conversion.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/upgrading-to-barracuda-getting-rid-of-huge-ibdata1-file/feed</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Simple guideline for choosing appropriate InnoDB PRIMARY KEYs</title>
		<link>http://code.openark.org/blog/mysql/simple-guideline-for-choosing-appropriate-innodb-primary-keys</link>
		<comments>http://code.openark.org/blog/mysql/simple-guideline-for-choosing-appropriate-innodb-primary-keys#comments</comments>
		<pubDate>Thu, 21 Oct 2010 05:52:45 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Indexing]]></category>
		<category><![CDATA[InnoDB]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=2104</guid>
		<description><![CDATA[Risking some flames, I'd like to suggest only two options for choosing PRIMARY KEYs for InnoDB tables. I suggest they should cover 99% (throwing numbers around) of cases. PRIMARY KEY cases An integer (SMALLINT / INT / BIGINT), possibly AUTO_INCREMENT column. The combination of two columns on a many-to-many connecting table (e.g. film_actor, which connects [...]]]></description>
			<content:encoded><![CDATA[<p>Risking some flames, I'd like to suggest only two options for choosing <strong>PRIMARY KEY</strong>s for InnoDB tables. I suggest they should cover 99% (throwing numbers around) of cases.</p>
<h4>PRIMARY KEY cases</h4>
<ol>
<li>An integer (SMALLINT / INT / BIGINT), possibly <strong>AUTO_INCREMENT</strong> column.</li>
<li>The combination of two columns on a many-to-many connecting table (e.g. <strong>film_actor</strong>, which connects <strong>film</strong>s to <strong>actor</strong>s), the two columns being the <strong>PRIMARY KEY</strong>s of respective data tables. This rule may be extended to 3-way relation tables.</li>
</ol>
<p>A short recap: an InnoDB must have a <strong>PRIMARY KEY</strong>. It will pick one if you don't offer it. It can pick a really bad <strong>UNIQUE KEY</strong> (e.g. <strong>website_url(255)</strong>) or make one up using InnoDB internal row ids. If you don't have a good candidate, an <strong>AUTO_INCREMENT PRIMARY KEY</strong> is probably the easiest way out.</p>
<p>A 2-column combination for a many-to-many connection table is common and viable. The <strong>PRIMARY KEY</strong> will not only provide with good join access method, but will also provide with the required <strong>UNIQUE</strong> constraint.</p>
<p>An integer-based <strong>PRIMARY KEY</strong> will make for more compact &amp; shallow index tree structures, which leads to less I/O and page reads.</p>
<p>An <strong>AUTO_INCREMENT</strong> will allow for ascending <strong>PRIMARY KEY</strong> order of <strong>INSERT</strong>, which is InnoDB-friendly: index pages will be more utilized, less fragmented.<span id="more-2104"></span></p>
<h4>Exceptions</h4>
<ul>
<li><strong>You have a partitioned table, e.g. on date range.</strong> With partitioned tables, every UNIQUE KEY, including the PRIMARY KEY, must include partitioning columns. In such case you will have to extend the PRIMARY KEY.</li>
<li><strong>The only key on your table is a unique constraint on some column, e.g. UNIQUE KRY (url).</strong> On one hand, it seems wasteful to create <em>another</em> column (e.g. AUTO_INCREMENT) to use as PRIMARY KEY. On the other hand, I've seen many cases where this kind of PK didn't hold up. At some point there was need for another index. Or some method had to be devised for chunking up table data (<a href="http://code.openark.org/forge/openark-kit/oak-chunk-update">oak-chunk-update</a> can do that even with non-integer PKs). I'm reluctant to use such keys as PRIMARY.</li>
<li>I'm sure there are others.</li>
</ul>
<h4>Umm...</h4>
<p>I wrote the draft for this post a while ago. And then came <a href="http://mituzas.lt/2010/07/30/on-primary-keys/">Domas</a> and ruined it. <a href="http://bugs.mysql.com/bug.php?id=55656">Wait for</a> <strong>5.1.52</strong>?</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/simple-guideline-for-choosing-appropriate-innodb-primary-keys/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Thoughts and ideas for Online Schema Change</title>
		<link>http://code.openark.org/blog/mysql/thoughts-and-ideas-for-online-schema-change</link>
		<comments>http://code.openark.org/blog/mysql/thoughts-and-ideas-for-online-schema-change#comments</comments>
		<pubDate>Thu, 07 Oct 2010 08:29:10 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Indexing]]></category>
		<category><![CDATA[INFORMATION_SCHEMA]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[openark kit]]></category>
		<category><![CDATA[Opinions]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[Schema]]></category>
		<category><![CDATA[scripts]]></category>
		<category><![CDATA[Triggers]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=3005</guid>
		<description><![CDATA[Here's a few thoughts on current status and further possibilities for Facebook's Online Schema Change (OSC) tool. I've had these thoughts for months now, pondering over improving oak-online-alter-table but haven't got around to implement them nor even write them down. Better late than never. The tool has some limitations. Some cannot be lifted, some could. [...]]]></description>
			<content:encoded><![CDATA[<p>Here's a few thoughts on current status and further possibilities for Facebook's <a href="http://www.facebook.com/note.php?note_id=430801045932">Online Schema Change</a> (OSC) tool. I've had these thoughts for months now, pondering over improving <a href="../../forge/openark-kit/oak-online-alter-table">oak-online-alter-table</a> but haven't got around to implement them nor even write them down. Better late than never.</p>
<p>The tool has some limitations. Some cannot be lifted, some could. Quoting from the <a href="http://www.facebook.com/notes/mysql-at-facebook/online-schema-change-for-mysql/430801045932">announcement</a> and looking at the code, I add a few comments. I conclude with a general opinion on the tool's abilities.</p>
<h4>"The original table must have PK. Otherwise an error is returned."</h4>
<p>This restriction could be lifted: it's enough that the table has a UNIQUE KEY. My original <em>oak-online-alter-table</em> handled that particular case. As far as I see from their code, the Facebook code would work just as well with any unique key.</p>
<p>However, this restriction is of no real interest. As we're mostly interested in InnoDB tables, and since any InnoDB table <em>should have</em> a PRIMARY KEY, we shouldn't care too much.</p>
<h4>"No foreign keys should exist. Otherwise an error is returned."</h4>
<p>Tricky stuff. With <em>oak-online-alter-table</em>, changes to the original table were immediately reflected in the <em>ghost</em> table. With InnoDB tables, that meant same transaction. And although I never got to update the text and code, there shouldn't be a reason for not using child-side foreign keys (the child-side is the table on which the FK constraint is defined).</p>
<p>The Facebook patch works differently: it captures changes and writes them to a <strong>delta</strong> table,  to be later (asynchronously) analyzed and make for a <em>replay</em> of actions on the <em>ghost</em> table.<span id="more-3005"></span></p>
<p>So in the Facebook code, some cases will lead to undesired behavior. Consider two tables, <strong>country</strong> and <strong>city</strong>, with city holding a RESTRICT/NO ACTION foreign key on <strong>country</strong>'s id. Now consider the scenario:</p>
<ol>
<li>Rows from <strong>city</strong> are DELETEd, where the country Id is Spain's.
<ul>
<li><strong>city</strong>'s ghost table is still unaffected, Spain's cities are still there.</li>
<li>A change is written to the delta table to mark these rows for deletion.</li>
</ul>
</li>
<li>A DELETE is issued on <strong>country</strong>'s Spain record.
<ul>
<li>The DELETE should work, from the user's perspective</li>
<li>But it will fail: city's ghost table has not received the changes yet. There's still matching rows. The NO ACTION constraint will fail the DELETE statement.</li>
</ul>
</li>
</ol>
<p>Now, this does not lead to corruption, just to seemingly unreasonable behavior on the database part. This behavior is probably undesired. NO ACTION constraint won't do.</p>
<p>However, with CASCADE or SET NULL options, there is less of an issue: operations on the parent table (e.g. <strong>country</strong>) cannot fail. We must make sure operations on the ghost table make it consistent with the original table (e.g. <strong>city</strong>).</p>
<p>Consider the following scenario:</p>
<ol>
<li>A new country is created, called "Sleepyland". An INSERT is made to <strong>country</strong>.
<ul>
<li>Both <strong>city</strong> and <strong>city</strong>'s ghost are immediately aware of it.</li>
</ul>
</li>
<li>A new town is created and INSERTed to <strong>city</strong>. The town is called "Naphaven".
<ul>
<li>The change takes time to propagate to <strong>city</strong>'s ghost table.</li>
</ul>
</li>
<li>Meanwhile, we realized we made a mistake. We've been had. There's no such city nor country.
<ol>
<li>We DELETE "Naphaven" from <strong>city</strong>.</li>
<li>We DELETE "Sleepyland" from <strong>country</strong>.</li>
</ol>
<ul>
<li>Note that <strong>city</strong>'s ghost table still hasn't caught up with the changes.</li>
</ul>
</li>
<li>Eventually, the INSERT statement for "Naphaven" reaches <strong>city</strong>'s ghost table.
<ul>
<li>What should happen now? The INSERT cannot succeed.</li>
<li>Will this fail the entire process?</li>
</ul>
</li>
</ol>
<p>Looking at the PHP code, I see that changes written on the <strong>delta</strong> table are blindly replayed on the ghost table.</p>
<p>Since the process is asynchronous, this should not be the case. We can solve the above if we use INSERT IGNORE instead of INSERT. The statement will fail without failing anything else. The row cannot exist, and that's because the original row does not exist anymore.</p>
<p>Unlike a replication corruption, this does not lead to accumulation mistakes. The <strong>replay</strong> is static, somewhat like in <em>binary log format</em>. Changes are <em>just written</em>, regardless of existing data.</p>
<p>I have given this considerable thought, and I can't say I've covered all the possible scenario. However I believe that with proper use of INSERT IGNORE and REPLACE INTO (two statements I heavily relied on with <em>oak-online-alter-table</em>), correctness can be achieved.</p>
<p>There's the small pain of re-generating the foreign key definition on the "ghost" table (<strong>CREATE TABLE LIKE ...</strong> does not copy FK definitions). And since foreign key names are unique, a new name must be picked up. Not pretty, but perfectly doable.</p>
<h4>"No AFTER_{INSERT/UPDATE/DELETE} triggers must exist."</h4>
<p>It would be nicer if MySQL had an ALTER TRIGGER statement. There isn't such statement. If there were such an atomic statement, then we would be able to rewrite the trigger, so as to add our own code to the <em>end of the trigger's code</em>. Yuck. Would be even nicer if we were <a href="http://code.openark.org/blog/mysql/triggers-use-case-compilation-part-ii">allowed to have multiple triggers</a> of same event.</p>
<p>So, we are left with DROP and CREATE triggers. Alas, this makes for a short period where the trigger does not exist. Bad. The easy solution would be to LOCK WRITE the table, but apparently you can't DROP the trigger (*) when the table is locked. Sigh.</p>
<p>(*) Happened to me, apparently to Facebook too; With latest 5.1 (5.1.51) version this actually works. With 5.0 it didn't use to; this needs more checking.</p>
<h4>Use of INFORMATION_SCHEMA</h4>
<p>As with oak-online-alter-table, the OSC checks for triggers, indexes, column by searching on the INFORMATION_SCHEMA tables. This makes for nice SQL for getting the exact listing and types of PRIMARY KEY columns, whether or not AFTER triggers exist, and so on.</p>
<p>I've always considered this to be the weak part of <a href="http://code.openark.org/forge/openark-kit">openark-kit</a>, that it relies on INFORMATION_SCHEMA so much. It's easier, it's cleaner, it's even <em>more correct</em> to work that way -- but it just puts too much locks. I think Baron Schwartz (and now Daniel Nichter) did amazing work on analyzing table schemata by parsing the SHOW CREATE TABLE and other SHOW commands regex-wise with <a href="http://www.maatkit.org/">Maatkit</a>. It's a crazy work! Had I written <em>openark-kit</em> in Perl, I would have just import their code. But I'm too <span style="text-decoration: line-through;">lazy</span> busy to do the conversion from Perl to Python, and rewrite that code, what with all the debugging.</p>
<p>OSC is written in PHP. Again, much conversion work. I think performance-wise this is an important step to make.</p>
<h4>A word for the critics</h4>
<p>Finally, a word for the critics. I've read some Facebook/MySQL bashing comments and wish to relate.</p>
<p>In his <a href="http://www.theregister.co.uk/2010/09/21/facebook_online_schema_change_for_mysql/">interview to The Register</a>, Mark Callaghan gave the example that "Open Schema Change lets the company update indexes without user downtime, according to Callaghan".</p>
<p>PostgreSQL was mentioned for being able to add index with only read locks taken, or being able to do the work with no locks using CREATE INDEX CONCURRENTLY. I wish MySQL had that feature! Yes, MySQL has a lot to improve upon, and the latest PostgreSQL 9.0 brings valuable new features. (Did I make it clear I have no intention of bashing PostgreSQL? If not, please re-read this paragraph until convinced).</p>
<p>Bashing related to the notion of MySQL being so poor that Facebook used an even poorer mechanism to work out the ALTER TABLE.</p>
<p>Well, allow me to add a few words: the CREATE INDEX is by far not the only thing you can achieve with OSC (although it may be Facebook's major concern). You should be able to:</p>
<ul>
<li>Add columns</li>
<li>Drop columns</li>
<li>Convert character sets</li>
<li>Modify column types</li>
<li>Add partitioning</li>
<li>Reorganize partitioning</li>
<li>Compress the table</li>
<li>Otherwise changing table format</li>
<li>Heck, you could even modify the storage engine! (To other transactional engine)</li>
</ul>
<p>These are giant steps. How easy would it be to write these down into the database? It only takes a few weeks time to work out a working solution with reasonable limitations, just using the resources the MySQL server provides you with. The <a href="http://www.facebook.com/MySQLatFacebook">MySQL@Facebook team</a> should be given credit for that.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/thoughts-and-ideas-for-online-schema-change/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>How often should you use OPTIMIZE TABLE? - followup</title>
		<link>http://code.openark.org/blog/mysql/how-often-should-you-use-optimize-table-followup</link>
		<comments>http://code.openark.org/blog/mysql/how-often-should-you-use-optimize-table-followup#comments</comments>
		<pubDate>Mon, 04 Oct 2010 08:07:45 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Indexing]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=2882</guid>
		<description><![CDATA[This post follows up on Baron's How often should you use OPTIMIZE TABLE?. I had the opportunity of doing some massive purging of data from large tables, and was interested to see the impact of the OPTIMIZE operation on table's indexes. I worked on some production data I was authorized to provide as example. The [...]]]></description>
			<content:encoded><![CDATA[<p>This post follows up on Baron's <a href="http://www.xaprb.com/blog/2010/02/07/how-often-should-you-use-optimize-table/">How often should you use OPTIMIZE TABLE?</a>. I had the opportunity of doing some massive purging of data from large tables, and was interested to see the impact of the <strong>OPTIMIZE</strong> operation on table's indexes. I worked on some production data I was authorized to provide as example.</p>
<h4>The use case</h4>
<p>I'll present a single use case here. The table at hand is a compressed InnoDB table used for logs. I've rewritten some column names for privacy:</p>
<blockquote>
<pre>mysql&gt; show create table logs \G

Create Table: CREATE TABLE `logs` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(20) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
 `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 `origin` varchar(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
 `message` text NOT NULL,
 `level` tinyint(11) NOT NULL DEFAULT '0',
 `s` char(16) CHARACTER SET ascii COLLATE ascii_bin NOT NULL DEFAULT '',
 PRIMARY KEY (`id`),
 KEY `s` (`s`),
 KEY `name` (`name`,`ts`),
 KEY `origin` (`origin`,`ts`)
) ENGINE=InnoDB AUTO_INCREMENT=186878729 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8</pre>
</blockquote>
<p>The table had log records starting <strong>2010-08-23</strong> and up till <strong>2010-09-02</strong> noon. Table status:<span id="more-2882"></span></p>
<blockquote>
<pre>mysql&gt; show table status like 'logs'\G
*************************** 1. row ***************************
           Name: logs
         Engine: InnoDB
        Version: 10
     Row_format: Compressed
           Rows: 22433048
 Avg_row_length: 206
    Data_length: 4625285120
Max_data_length: 0
   Index_length: 1437073408
      Data_free: 4194304
 Auto_increment: 186878920
    Create_time: 2010-08-24 18:10:49
    Update_time: NULL
     Check_time: NULL
      Collation: utf8_general_ci
       Checksum: NULL
 Create_options: row_format=COMPRESSED KEY_BLOCK_SIZE=8
        Comment:</pre>
</blockquote>
<p>(A bit puzzled on the <strong>Create_time</strong>; the table was taken from an LVM snapshot of another server, so it existed for a very long time before. Not sure why the <strong>Create_time</strong> field is as it is here; I assume the MySQL upgrade marked it so, did not have the time nor need to look into it).</p>
<p>I was using <a href="http://www.percona.com/downloads/Percona-Server-5.1/">Percona-Server-5.1.47-11.2</a>, and so was able to look at the index statistics for that table:</p>
<blockquote>
<pre>mysql&gt; SELECT * FROM information_schema.INNODB_INDEX_STATS WHERE table_name='logs';
+--------------+------------+--------------+--------+----------------+------------+------------+
| table_schema | table_name | index_name   | fields | row_per_keys   | index_size | leaf_pages |
+--------------+------------+--------------+--------+----------------+------------+------------+
| newsminer    | logs       | PRIMARY      |      1 | 1              |     282305 |     246856 |
| newsminer    | logs       | s            |      2 | 17, 1          |      38944 |      33923 |
| newsminer    | logs       | name         |      3 | 2492739, 10, 2 |      22432 |      19551 |
| newsminer    | logs       | origin       |      3 | 1303, 4, 1     |      26336 |      22931 |
+--------------+------------+--------------+--------+----------------+------------+------------+</pre>
</blockquote>
<h4>Status after massive purge</h4>
<p>My first requirement was to purge out all record up to <strong>2010-09-01 00:00:00</strong>. I did so in small chunks, using <a href="http://code.openark.org/forge/openark-kit">openark kit</a>'s oak-chunk-update (same can be achieved with <a href="http://www.maatkit.org/">maatkit</a>'s mk-archiver). The process purged <strong>1000</strong> rows at a time, with some sleep in between, and ran for about a couple of hours. It may be interesting to note that since ts is in <a href="http://code.openark.org/blog/mysql/monotonic-functions-sql-and-mysql">monotonically ascending</a> values, purging of old rows also means purging of lower PKs, which means we're trimming the PK tree from left.</p>
<p>Even while purging took place, I could see the index_size/leaf_pages values dropping, until, finally:</p>
<blockquote>
<pre>mysql&gt; SELECT * FROM information_schema.INNODB_INDEX_STATS WHERE table_name='logs';
+--------------+------------+--------------+--------+--------------+------------+------------+
| table_schema | table_name | index_name   | fields | row_per_keys | index_size | leaf_pages |
+--------------+------------+--------------+--------+--------------+------------+------------+
| newsminer    | logs       | PRIMARY      |      1 | 1            |      40961 |      35262 |
| newsminer    | logs       | s            |      2 | 26, 1        |      34440 |       3798 |
| newsminer    | logs       | name         |      3 | 341011, 4, 1 |       4738 |       2774 |
| newsminer    | logs       | origin       |      3 | 341011, 4, 2 |      10178 |       3281 |
+--------------+------------+--------------+--------+--------------+------------+------------+</pre>
</blockquote>
<p>The number of deleted rows was roughly <strong>85%</strong> of total rows, so down to <strong>15%</strong> number of rows.</p>
<h4>Status after OPTIMIZE TABLE</h4>
<p>Time to see whether <strong>OPTIMIZE</strong> really optimizes! Will it reduce number of leaf pages in PK? In secondary keys?</p>
<blockquote>
<pre>mysql&gt; OPTIMIZE TABLE logs;
...
mysql&gt; SELECT * FROM information_schema.INNODB_INDEX_STATS WHERE table_name='logs';
+--------------+------------+--------------+--------+--------------+------------+------------+
| table_schema | table_name | index_name   | fields | row_per_keys | index_size | leaf_pages |
+--------------+------------+--------------+--------+--------------+------------+------------+
| newsminer    | logs       | PRIMARY      |      1 | 1            |      40436 |      35323 |
| newsminer    | logs       | s            |      2 | 16, 1        |       5489 |       4784 |
| newsminer    | logs       | name         |      3 | 335813, 7, 1 |       3178 |       2749 |
| newsminer    | logs       | origin       |      3 | 335813, 5, 2 |       3951 |       3446 |
+--------------+------------+--------------+--------+--------------+------------+------------+
4 rows in set (0.00 sec)</pre>
</blockquote>
<p>The above shows no significant change in either of the indexes: not for <strong>index_size</strong>, not for <strong>leaf_pages</strong>, not for statistics (<strong>row_per_keys</strong>). The <strong>OPTIMIZE</strong> did not reduce index size. It did not reduce the number of index pages (<strong>leaf_pages</strong> are the major factor here). Some <strong>leaff_pages</strong> values have even increased, but in small enough margin to consider as equal.</p>
<p>Index-wise, the above example does not show an advantage to using <strong>OPTIMIZE</strong>. I confess, I was surprised. And for the better. This indicates InnoDB makes good merging of index pages after massive purging.</p>
<h4>So, no use for OPTIMIZE?</h4>
<p>Think again: file system-wise, things look different.</p>
<p>Before purging of data:</p>
<blockquote>
<pre>bash:~# ls -l logs.* -h
-rw-r----- 1 mysql mysql 8.6K 2010-08-15 17:40 logs.frm
-rw-r----- 1 mysql mysql 2.9G 2010-09-02 14:01 logs.ibd</pre>
</blockquote>
<p>After purging of data:</p>
<blockquote>
<pre>bash:~# ls -l logs.* -h
-rw-r----- 1 mysql mysql 8.6K 2010-08-15 17:40 logs.frm
-rw-r----- 1 mysql mysql 2.9G 2010-09-02 14:21 logs.ibd</pre>
</blockquote>
<p>Recall that InnoDB never releases table space back to file system!</p>
<p>After <strong>OPTIMIZE</strong> on table:</p>
<blockquote>
<pre>bash:~# ls -l logs.* -h
-rw-rw---- 1 mysql mysql 8.6K 2010-09-02 14:26 logs.frm
-rw-rw---- 1 mysql mysql 428M 2010-09-02 14:43 logs.ibd</pre>
</blockquote>
<p>On <strong>innodb_file_per_table</strong> an <strong>OPTIMIZE</strong> creates a new table space, and the old one gets destroyed. Space goes back to file system. Don't know about you; I like to have my file system with as much free space as possible.</p>
<h4>Need to verify</h4>
<p>I've tested Percona Server, since this is where I can find <strong>INNODB_INDEX_STATS</strong>. But this begs the following questions:</p>
<ul>
<li>Perhaps the results only apply for Percona Server? (I'm guessing not).</li>
<li>Or only for InnoDB plugin? Does the same hold for "builtin" InnoDB? (dunno)</li>
<li>Only on &gt;= 5.1? (Maybe; 5.0 is becoming rare now anyway)</li>
<li>Only on InnoDB (Well, of course this test is storage engine dependent!)</li>
</ul>
<h4>Conclusion</h4>
<p>The use case above is a particular example. Other use cases may include tables where deletions often occur in middle of table (remember we were trimming the tree from left side only). Other yet may need to handle <strong>UPDATE</strong>s to indexed columns. I have some more operations to do here, with larger tables (e.g. <strong>40GB</strong> compressed). If anything changes, I'll drop a note.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/how-often-should-you-use-optimize-table-followup/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Personal observation: more migrations from MyISAM to InnoDB</title>
		<link>http://code.openark.org/blog/mysql/personal-observation-more-migrations-from-myisam-to-innodb</link>
		<comments>http://code.openark.org/blog/mysql/personal-observation-more-migrations-from-myisam-to-innodb#comments</comments>
		<pubDate>Wed, 16 Jun 2010 16:43:42 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[MyISAM]]></category>
		<category><![CDATA[Opinions]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=2517</guid>
		<description><![CDATA[I'm evidencing an increase in the planning, confidence &#38; execution for MyISAM to InnoDB migration. How much can a single consultant observe? I agree Oracle should not go to PR based on my experience. But I find that: More companies are now familiar with InnoDB than there used to. More companies are interested in migration [...]]]></description>
			<content:encoded><![CDATA[<p>I'm evidencing an increase in the planning, confidence &amp; execution for MyISAM to InnoDB migration.</p>
<p>How much can a single consultant observe? I agree Oracle should not go to PR based on my experience. But I find that:</p>
<ul>
<li>More companies are now familiar with InnoDB than there used to.</li>
<li>More companies are interested in migration to InnoDB than there used to.</li>
<li>More companies feel such migration to be safe.</li>
<li>More companies start up with an InnoDB based solution than with a MyISAM based solution.</li>
</ul>
<p>This is the way I see it. No doubt, the Oracle/Sun deal made its impact. The fact that InnoDB is no longer a 3rd party; the fact Oracle invests in InnoDB and no other engine (Falcon is down, no real development on MyISAM); the fact InnoDB is to be the default engine: all these put companies at ease with migration.</p>
<p><span id="more-2517"></span>I am happy with this change. I believe for most installations InnoDB provides with a clear advantage over MyISAM (though MyISAM has its uses), and this makes for more robust, correct and manageable MySQL instances; the kind that make a DBA's life easier and quieter. And it is easier to make customers see the advantages.</p>
<p>I am not inclined to say <em>"You should migrate your entire database to InnoDB"</em>. I don't do that a lot. But recently, more customers approach and say <em>"We were thinking about migrating our entire database to InnoDB, what do you think?"</em>. What a change of approach.</p>
<p>And, yes: there are still <em>a lot</em> of companies using MyISAM based databases, who still live happily.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/personal-observation-more-migrations-from-myisam-to-innodb/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>A MyISAM backup is blocking as read-only, including mysqldump backup</title>
		<link>http://code.openark.org/blog/mysql/a-myisam-backup-is-blocking-as-read-only-including-mysqldump-backup</link>
		<comments>http://code.openark.org/blog/mysql/a-myisam-backup-is-blocking-as-read-only-including-mysqldump-backup#comments</comments>
		<pubDate>Tue, 18 May 2010 17:29:05 +0000</pubDate>
		<dc:creator>shlomi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Backup]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[MyISAM]]></category>

		<guid isPermaLink="false">http://code.openark.org/blog/?p=2441</guid>
		<description><![CDATA[Actually this is (almost) all I wanted to say. This is intentionally posted with all related keywords in title, in the hope that a related search on Google will result with this post on first page. I'm just still encountering companies who use MyISAM as their storage engine and are unaware that their nightly backup [...]]]></description>
			<content:encoded><![CDATA[<p>Actually this is (almost) all I wanted to say. This is intentionally posted with all related keywords in title, in the hope that a related search on Google will result with this post on first page.</p>
<p>I'm just still encountering companies who use MyISAM as their storage engine and are <em>unaware</em> that their nightly backup actually blocks their application, basically rendering their product unavailable for long minutes to hours on a nightly basis.</p>
<p>So this is posted as a warning for those who were not aware of this fact.</p>
<p>There is no hot (non blocking) backup for MyISAM. Closest would be file system snapshot, but even this requires flushing of tables, which may take a while to complete. If you must have a hot backup, then either use replication - and take the risk of the slave not being in complete sync with the master - or use another storage engine, i.e. InnoDB.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.openark.org/blog/mysql/a-myisam-backup-is-blocking-as-read-only-including-mysqldump-backup/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

