<?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>PlanetMysql.ru - информация о СУБД MySQL &#187; bugs</title>
	<atom:link href="http://planetmysql.ru/category/bugs/feed/" rel="self" type="application/rss+xml" />
	<link>http://planetmysql.ru</link>
	<description>Блог о самой популярной СУБД MySQL</description>
	<lastBuildDate>Thu, 24 May 2012 14:20:27 +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>MySQL must improve error messages</title>
		<link>http://en.latindevelopers.com/ivancp/2012/mysql-must-improve-error-messages/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=mysql-must-improve-error-messages</link>
		<comments>http://en.latindevelopers.com/ivancp/2012/mysql-must-improve-error-messages/#comments</comments>
		<pubDate>Mon, 12 Mar 2012 06:24:25 +0000</pubDate>
		<dc:creator>Ivan Cachicatari</dc:creator>
				<category><![CDATA[bugs]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[mysql]]></category>

		<guid isPermaLink="false">http://en.latindevelopers.com/ivancp/?p=72</guid>
		<description><![CDATA[I just finished a database modification, a new foreign key creation  shouldn&#8217;t be take more than 5 mins, but I spent 2 hours because MySQL still have some useless error messages.
There is a way to create a new foreign key:


-- Create two tables foo and bar
CREATE TABLE foo &#040;
	id INTEGER NOT NULL PRIMARY KEY,
	bar_id INT NOT NULL    -- foreign key
&#041;;
CREATE TABLE bar &#040;
	id INTEGER NOT NULL PRIMARY KEY
&#041;;
-- Try to create a foreign key on `foo`
ALTER TABLE foo
	ADD FOREIGN KEY&#040;bar_id&#041; REFERENCES bar&#040;SOME_FIELD&#041; ;


The last sentence returns a generic error message:
Error Code: 1005. Can't create table 'temp.#sql-4bd7_11' (errno: 150)
Everything would have been easier if I had noticed that wrong field name bar(SOME_FIELD), sometimes happens,  but if MySQL would have shown a different message like "field bar.SOME_FIELD don't exists" I would not be awake until 2 am.
I&#8217;m using MySQL 5.5.21 community edition.
if I had noticed that wrong field name]]></description>
			<content:encoded><![CDATA[<p>I just finished a database modification, a new foreign key creation  shouldn&#8217;t be take more than 5 mins, but I spent 2 hours because MySQL still have some useless error messages.</p>
<p>There is a way to create a new foreign key:</p>


<div><div><div><div><div><div><div><pre><span>-- Create two tables foo and bar</span>
<span>CREATE</span> <span>TABLE</span> foo <span>&#40;</span>
	id <span>INTEGER</span> <span>NOT</span> <span>NULL</span> <span>PRIMARY</span> <span>KEY</span><span>,</span>
	bar_id <span>INT</span> <span>NOT</span> <span>NULL</span>    <span>-- foreign key</span>
<span>&#41;</span>;
<span>CREATE</span> <span>TABLE</span> bar <span>&#40;</span>
	id <span>INTEGER</span> <span>NOT</span> <span>NULL</span> <span>PRIMARY</span> <span>KEY</span>
<span>&#41;</span>;
<span>-- Try to create a foreign key on `foo`</span>
<span>ALTER</span> <span>TABLE</span> foo
	<span>ADD</span> <span>FOREIGN</span> <span>KEY</span><span>&#40;</span>bar_id<span>&#41;</span> <span>REFERENCES</span> bar<span>&#40;</span>SOME_FIELD<span>&#41;</span> ;</pre></div></div></div></div></div></div></div>


<p>The last sentence returns a generic error message:</p>
<p><code>Error Code: 1005. Can't create table 'temp.#sql-4bd7_11' (errno: 150)</code></p>
<p>Everything would have been easier if I had noticed that wrong field name <code>bar(SOME_FIELD)</code>, sometimes happens,  but if MySQL would have shown a different message like <code>"field bar.SOME_FIELD don't exists"</code> I would not be awake until 2 am.</p>
<p>I&#8217;m using MySQL 5.5.21 community edition.</p>
<p>if I had noticed that wrong field name</p><br/>PlanetMySQL Voting:
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=32296&vote=1&apivote=1">Vote UP</a> /
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=32296&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
			<wfw:commentRss>http://planetmysql.ru/2012/03/12/mysql-must-improve-error-messages/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to submit a good database bug report</title>
		<link>http://datacharmer.blogspot.com/2011/12/how-to-submit-good-database-bug-report.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-submit-a-good-database-bug-report</link>
		<comments>http://datacharmer.blogspot.com/2011/12/how-to-submit-good-database-bug-report.html#comments</comments>
		<pubDate>Sat, 10 Dec 2011 21:17:00 +0000</pubDate>
		<dc:creator>Giuseppe Maxia</dc:creator>
				<category><![CDATA[bugs]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[Replication]]></category>
		<category><![CDATA[tungsten]]></category>

		<guid isPermaLink="false">http://planetmysql.ru/?guid=23e79feddeada75e4744b7f4a6ed17bb</guid>
		<description><![CDATA[When an open source project becomes popular, bug reports start flocking in. This is both good and bad news for the project developers.  The good news is that someone is using the product, and they are finding ways of breaking it that we didn't think of. The bad news is that most of the times the reporters assume that the developers have super human powers, and that they will find what's wrong by the simple mentioning that a given feature is not working as expected. Unfortunately, it doesn't work that way. An effective bug report should have enough information that the ones in charge will be able to reproduce it and examine in lab conditions to find the problem. When dealing with databases and database tools, there are several cases, from simple to complex. Let's cover them in order.  Installation issuesThis is often a straightforward case of lack of functionality. When a tool does not install what it is supposed to, it is a show stopper, and a solution must be found. In this case, it's often enough to state the environment where the installation is happening (operating system, version of the tool, version of core components such as the MySQL database used) and the command used to start the installation. The error message could be an expected failure, when the installation procedure checks for requirements and fails if they are not met. For example: "Missing Ruby interpreter". The message tells (or suggests) you what to do. Filing a bug report on an expected failure is a waste of time. You should install the missing part and try again. Even if the message is about an unexpected failure (e.g. you get a stack trace from Ruby or Java), usually the first message tells you enough to be able to find a workaround. For example, if you get an exception from Ruby complaining about a missing 'curl' command, you can file a bug report to ask for the installer to check for 'curl' in the requirements, but if you install 'curl' yourself, the installation should continue.  Simple database issuesReporting a database bug means complaining that the DBMS is not behaving as advertising by the documentation, or as common usage dictates. If it is a missing or misbehaving functionality, the best way of showing the problem is by starting with an empty DBMS, followed by the creation of the objects needed to reproduce the issue (CREATE SCHEMA, TABLE, INDEX) and by a minimal amount of data that triggers the problem.  Some information about what operating system and database version was used is probably necessary to reproduce the problem consistently.  Simple database replication issuesBy simple replication we mean a vanilla master/slave topology. In this scheme, data inserted in the master will eventually end up in the slave. Bugs in this category may fail to replicate the data totally or partially, or they may cause a break in the replication flow. Reproducing them is almost as easy as with simple database bugs. If you start with an empty system and manage to reproduce the error with a short sequence of commands, it should probably reproducible by a third party. Sometimes, settings in the master and the slave are essential to reproduce the problem. In MySQL, the format of binary logs, the default database engine and SQL modes can affect replication and produce different results with the same stream of SQL commands.  Complex database replication issuesThe most difficult bugs to report are the ones where the error shows up only in a given topology. While MySQL native replication offers only few options to pipe data around (single, circular, hierarchical), Tungsten replicator allows a rich set of combined pipelines that can change the outcome of a data change event, depending on the originating node and the direction it took. In these case, information o how the cluster was installed becomes essential.   Concurrency issuesThis is one of the most difficult bugs to report. When an error happens only because of the contemporary action of two or more threads, there is no easy way of reporting it in a way that it can be easily reproduced. Three methods are possible: Describe the action of the first thread, then mark the change of thread and describe the actions of the second thread, continuing in this way until you reach the error point.If you are a developer and feel comfortable with multi thread applications, write a script that reproduces the error by running several threads (Perl, Python, and Ruby offer the best environment for this kind of tests).If the database offers a tool to write such multi-threading tests, consider using it.Heavy load issuesThis is a more complex case of the above one. Not only you need concurrency, but a lot of it happening at the same time. Reproducing this kind or error is challenging. If you have a support agreement with the provider of the database or the tool, you may let the support engineer have a look at your running environment, to find some clues. But even in this case, the support engineers or yourself need to ultimately reproduce the case in such a way that a developer can fix the problem and test the fix. There are two methods to report this problem: Simplification: if you can reduce the concurrency to the elements that are misbehaving, the methods for concurrency issues will apply also in this case.Enabling a query log could lead to identifying the sequence of events that have generated the error. The log should be integrated by the DDL of the objects involved in the action (schemas, tables, triggers, views, etc).Large data issuesIf your error only shows itself with large data, there is often a logistical problem, as you can't easily provide gigabytes of data, even if there privacy and security issues weren't in the way (which usually are). There are three strategies that you can use to report such bugs: If only the size matters, then describing the kind of data used could be enough. E.g. When a table with such fields and such indexes grows beyond X GB, then the optimizer warp drive explodes. (Don't try this at home)Create a script to generate the data that will ultimately trigger the error. This method requires both skills and an understanding of what the error is.You may use a publicly available database to reproduce the error (for example, the Sample database with test suite.) Just mention in the bug report where to find the database, eventually how to load it if it is not simple, and then describe the steps needed to reproduce the bug after loading it.DOs and DONTsDO  Search the bug repository and the mailing lists (or forums) before submitting yours. Someone may have had the same problem before you did. (Thanks, Robert Hodges, for this important reminder.)Put yourself in the receiver's position, and try to reproduce the problem from a clean initial state.If there is a workaround, mention it: it might give a good clue to the developers.More information is better. Anything that can improve the identification of the bug root cause will be welcome. (But don't overdo: see below)If you feel that a missing feature should be useful, report it as a feature request. (Even better: suggest a patch)DON'T  Don't report a missing feature as a bug, unless the docs say that the feature should be there.Don't just send the error message without the events that generated it.Don't include SQL commands embedded in code.Don't say "my application doesn't work anymore," assuming it's the database's fault. Remember The First Rule of Programming: It's Always Your FaultLess is better. If there is a long way and a short way of reproducing the bug, the shorter one is better. Don't send more info just for the sake of it.Don't tell the developers that they are retarded. This will not increase the priority given to your bug, or your credibility.]]></description>
			<content:encoded><![CDATA[When an open source project becomes popular, bug reports start flocking in. This is both good and bad news for the project developers.  The good news is that someone is using the product, and they are finding ways of breaking it that we didn't think of. The bad news is that most of the times the reporters assume that the developers have super human powers, and that they will find what's wrong by the simple mentioning that a given feature is not working as expected. Unfortunately, it doesn't work that way. An effective bug report should have enough information that the ones in charge will be able to reproduce it and examine in lab conditions to find the problem. When dealing with databases and database tools, there are several cases, from simple to complex. Let's cover them in order.  <h3>Installation issues</h3>This is often a straightforward case of lack of functionality. When a tool does not install what it is supposed to, it is a show stopper, and a solution must be found. In this case, it's often enough to state the environment where the installation is happening (operating system, version of the tool, version of core components such as the MySQL database used) and the command used to start the installation. The error message could be an expected failure, when the installation procedure checks for requirements and fails if they are not met. For example: "Missing Ruby interpreter". The message tells (or suggests) you what to do. Filing a bug report on an expected failure is a waste of time. You should install the missing part and try again. Even if the message is about an unexpected failure (e.g. you get a stack trace from Ruby or Java), usually the first message tells you enough to be able to find a workaround. For example, if you get an exception from Ruby complaining about a missing 'curl' command, you can file a bug report to ask for the installer to check for 'curl' in the requirements, but if you install 'curl' yourself, the installation should continue.  <h3>Simple database issues</h3>Reporting a database bug means complaining that the DBMS is not behaving as advertising by the documentation, or as common usage dictates. If it is a missing or misbehaving functionality, the best way of showing the problem is by starting with an empty DBMS, followed by the creation of the objects needed to reproduce the issue (CREATE SCHEMA, TABLE, INDEX) and by a minimal amount of data that triggers the problem.  Some information about what operating system and database version was used is probably necessary to reproduce the problem consistently.  <h3>Simple database replication issues</h3>By <i>simple replication</i> we mean a vanilla master/slave topology. In this scheme, data inserted in the master will eventually end up in the slave. Bugs in this category may fail to replicate the data totally or partially, or they may cause a break in the replication flow. Reproducing them is almost as easy as with simple database bugs. If you start with an empty system and manage to reproduce the error with a short sequence of commands, it should probably reproducible by a third party. Sometimes, settings in the master and the slave are essential to reproduce the problem. In MySQL, the format of binary logs, the default database engine and SQL modes can affect replication and produce different results with the same stream of SQL commands.  <h3>Complex database replication issues</h3>The most difficult bugs to report are the ones where the error shows up only in a given topology. While MySQL native replication offers only few options to pipe data around (single, circular, hierarchical), Tungsten replicator allows a rich set of combined pipelines that can change the outcome of a data change event, depending on the originating node and the direction it took. In these case, information o how the cluster was installed becomes essential.   <h3>Concurrency issues</h3>This is one of the most difficult bugs to report. When an error happens only because of the contemporary action of two or more threads, there is no easy way of reporting it in a way that it can be easily reproduced. Three methods are possible: <ul><li>Describe the action of the first thread, then mark the change of thread and describe the actions of the second thread, continuing in this way until you reach the error point.</li><li>If you are a developer and feel comfortable with multi thread applications, write a script that reproduces the error by running several threads (Perl, Python, and Ruby offer the best environment for this kind of tests).</li><li>If the database offers a tool to write such multi-threading tests, consider using it.</li></ul><h3>Heavy load issues</h3>This is a more complex case of the above one. Not only you need concurrency, but a lot of it happening at the same time. Reproducing this kind or error is challenging. If you have a support agreement with the provider of the database or the tool, you may let the support engineer have a look at your running environment, to find some clues. But even in this case, the support engineers or yourself need to ultimately reproduce the case in such a way that a developer can fix the problem and test the fix. There are two methods to report this problem: <ul><li>Simplification: if you can reduce the concurrency to the elements that are misbehaving, the methods for concurrency issues will apply also in this case.</li><li>Enabling a query log could lead to identifying the sequence of events that have generated the error. The log should be integrated by the DDL of the objects involved in the action (schemas, tables, triggers, views, etc).</li></ul><h3>Large data issues</h3>If your error only shows itself with large data, there is often a logistical problem, as you can't easily provide gigabytes of data, even if there privacy and security issues weren't in the way (which usually are). There are three strategies that you can use to report such bugs: <ul><li>If only the size matters, then describing the kind of data used could be enough. E.g. <i>When a table with such fields and such indexes grows beyond X GB, then the optimizer warp drive explodes.</i> (Don't try this at home)</li><li>Create a script to generate the data that will ultimately trigger the error. This method requires both skills and an understanding of what the error is.</li><li>You may use a publicly available database to reproduce the error (for example, the <a href="https://launchpad.net/test-db">Sample database with test suite</a>.) Just mention in the bug report where to find the database, eventually how to load it if it is not simple, and then describe the steps needed to reproduce the bug after loading it.</li></ul><h3>DOs and DONTs</h3><ul><li>DO  <ul><li>Search the bug repository and the mailing lists (or forums) before submitting yours. Someone may have had the same problem before you did. (Thanks, Robert Hodges, for this important reminder.)</li><li>Put yourself in the receiver's position, and try to reproduce the problem from a clean initial state.</li><li>If there is a workaround, mention it: it might give a good clue to the developers.</li><li>More information is better. Anything that can improve the identification of the bug root cause will be welcome. (But don't overdo: see below)</li><li>If you feel that a missing feature should be useful, report it as a feature request. (Even better: suggest a patch)</li></ul></li></ul><ul><li>DON'T  <ul><li>Don't report a missing feature as a bug, unless the docs say that the feature should be there.</li><li>Don't just send the error message without the events that generated it.</li><li>Don't include SQL commands embedded in code.</li><li>Don't say "my application doesn't work anymore," assuming it's the database's fault. Remember <a href="http://www.codinghorror.com/blog/2008/03/the-first-rule-of-programming-its-always-your-fault.html">The First Rule of Programming: It's Always Your Fault</a></li><li>Less is better. If there is a long way and a short way of reproducing the bug, the shorter one is better. Don't send more info just for the sake of it.</li><li>Don't tell the developers that they are retarded. This will not increase the priority given to your bug, or your credibility.</li></ul></li></ul><div><img width="1" height="1" src="https://blogger.googleusercontent.com/tracker/16959946-3106136869470004468?l=datacharmer.blogspot.com" alt="" /></div><br/>PlanetMySQL Voting:
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=31181&vote=1&apivote=1">Vote UP</a> /
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=31181&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
			<wfw:commentRss>http://planetmysql.ru/2011/12/11/how-to-submit-a-good-database-bug-report/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Data Corruption, DRBD and story of bug</title>
		<link>http://www.mysqlperformanceblog.com/2010/11/29/data-corruption-drbd-and-story-of-bug/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=data-corruption-drbd-and-story-of-bug</link>
		<comments>http://www.mysqlperformanceblog.com/2010/11/29/data-corruption-drbd-and-story-of-bug/#comments</comments>
		<pubDate>Tue, 30 Nov 2010 05:47:47 +0000</pubDate>
		<dc:creator>MySQL Performance Blog</dc:creator>
				<category><![CDATA[bugs]]></category>
		<category><![CDATA[High Availability]]></category>
		<category><![CDATA[mysql]]></category>

		<guid isPermaLink="false">http://www.mysqlperformanceblog.com/?p=4037</guid>
		<description><![CDATA[Working with customer, I faced pretty nasty bug, which is actually not rare situation , but in this particular there are some lessons I would like to share. 
The case is pretty much described in bug 55981, or
in pastebin.
Everything below is related to InnoDB-plugin/XtraDB, but not to regular InnoDB ( i.e in MySQL 5.0)
In short, if you use big BLOBS ( TEXT, MEDIUMBLOB, etc) (that allocated in external segment in InnoDB), you can get your database in trash state just executing update on row with blob and rolling back transaction twice ( on the same row)
The keywords that diagnose you hit this bug is

InnoDB: Serious error! InnoDB is trying to free page N
InnoDB: though it is already marked as free in the tablespace!
InnoDB: The tablespace free space info is corrupt.
InnoDB: You may need to dump your InnoDB tables and recreate the whole
InnoDB: database!

Trash state means that InnoDB won&#8217;t start, and you need to use innodb_force_recovery=3 and mysqldump your data. What makes problem even worse is that InnoDB does not report tablename, so you are pretty much blind and need to dump whole dataset, which can be long process.
The moment where DRBD come in play, is if you use DRBD for HA purposes ( as is in the case I worked with), you screwed,
as DRBD mirroring physical data, and MySQL keeping crashing on both instances &#8211; active and passive.
So DRBD can&#8217;t be considered fully reliable HA solution if there is risk that application corrupts data by itself 
Now to bug 55981. It has MySQL version 5.6, but the problem exists in MySQL 5.1.50 or below and in MySQL 5.5, and
corresponding bug is 55543, which you actually can&#8217;t see, as
&#8220;You do not have access to bug #55543.&#8221;, because it is marked as &#8220;Security problem&#8221;.
And I actually tend to agree that bug can be considered as &#8220;security&#8221;.
If you running public hosting or your public users can execute direct SQL statements, I strongly recommend to upgrade to
MySQL version  5.1.51+ .
Now another interesting point &#8211; how can you be sure that 5.1.51 works.
The bug 55543 is not mentioned in ChangeLog for 5.1.51 nor 5.1.52. However if you look into source code and revision history, you can see that bug 55543 is fixed in MySQL 5.1.51. I assume it is technical problem with ChangeLog process and it will be fixed soon, but I reported it so it is not lost 
At the end let me reiterate my points:
- if you have BLOB/TEXT fields in your InnoDB-plugin schema, it is recommended to upgrade to 5.1.51+
- if you provide public access to MySQL instance ( hosting provider, etc) with InnoDB-plugin installed &#8211; it is STRONGLY recommended to upgrade to 5.1.51+
- Review your HA schema. DRBD by itself (without additional solutions) can&#8217;t guaranty decent level of High Availability. And just to be clear, it is not DRBD  problem, DRBD basically can&#8217;t help if there is possibility that application corrupts data by itself. For this case regular Master-Slave setup (in addition to DRBD) would protect from such problem.
    
    Entry posted by Vadim &#124;
      No comment
    Add to:  &#124;  &#124;  &#124;  &#124; ]]></description>
			<content:encoded><![CDATA[<p>Working with customer, I faced pretty nasty bug, which is actually not rare situation , but in this particular there are some lessons I would like to share. </p>
<p>The case is pretty much described in <a href="http://bugs.mysql.com/bug.php?id=55981">bug 55981</a>, or<br />
in <a href="http://pastebin.com/LxiyLg6m">pastebin</a>.</p>
<p>Everything below is related to InnoDB-plugin/XtraDB, but not to regular InnoDB ( i.e in MySQL 5.0)</p>
<p>In short, if you use big BLOBS ( TEXT, MEDIUMBLOB, etc) (that allocated in external segment in InnoDB), you can get your database in trash state just executing update on row with blob and rolling back transaction twice ( on the same row)</p>
<p>The keywords that diagnose you hit this bug is<br />
<code><br />
InnoDB: Serious error! InnoDB is trying to free page N<br />
InnoDB: though it is already marked as free in the tablespace!<br />
InnoDB: The tablespace free space info is corrupt.<br />
InnoDB: You may need to dump your InnoDB tables and recreate the whole<br />
InnoDB: database!<br />
</code></p>
<p>Trash state means that InnoDB won&#8217;t start, and you need to use innodb_force_recovery=3 and <strong>mysqldump</strong> your data. What makes problem even worse is that InnoDB does not report tablename, so you are pretty much blind and need to dump whole dataset, which can be long process.</p>
<p>The moment where DRBD come in play, is if you use DRBD for HA purposes ( as is in the case I worked with), you screwed,<br />
as DRBD mirroring physical data, and MySQL keeping crashing on both instances &#8211; active and passive.</p>
<p>So <strong>DRBD can&#8217;t be considered fully reliable HA solution if there is risk that application corrupts data by itself</strong> </p>
<p>Now to bug 55981. It has MySQL version 5.6, but the problem exists in MySQL 5.1.50 or below and in MySQL 5.5, and<br />
corresponding <a href="http://bugs.mysql.com/bug.php?id=55543">bug is 55543</a>, which you actually can&#8217;t see, as<br />
&#8220;You do not have access to bug #55543.&#8221;, because it is marked as &#8220;Security problem&#8221;.</p>
<p>And I actually tend to agree that bug can be considered as &#8220;security&#8221;.<br />
If you running public hosting or your public users can execute direct SQL statements, I strongly recommend to upgrade to<br />
MySQL version  5.1.51+ .</p>
<p>Now another interesting point &#8211; how can you be sure that 5.1.51 works.</p>
<p>The bug 55543 is not mentioned in ChangeLog for 5.1.51 nor 5.1.52. However if you look into source code and revision history, you can see that bug 55543 is fixed in MySQL 5.1.51. I assume it is technical problem with ChangeLog process and it will be fixed soon, but I reported it so <a href="http://bugs.mysql.com/58306">it is not lost</a> </p>
<p>At the end let me reiterate my points:<br />
- if you have BLOB/TEXT fields in your InnoDB-plugin schema, it is recommended to upgrade to 5.1.51+<br />
- if you provide public access to MySQL instance ( hosting provider, etc) with InnoDB-plugin installed &#8211; it is STRONGLY recommended to upgrade to 5.1.51+<br />
- Review your HA schema. DRBD by itself (without additional solutions) can&#8217;t guaranty decent level of High Availability. And just to be clear, it is not DRBD  problem, DRBD basically can&#8217;t help if there is possibility that application corrupts data by itself. For this case regular Master-Slave setup (in addition to DRBD) would protect from such problem.</p>
    <hr noshade style="margin:0;height:1px" />
    <p>Entry posted by Vadim |
      <a href="http://www.mysqlperformanceblog.com/2010/11/29/data-corruption-drbd-and-story-of-bug/#comments">No comment</a></p>
    <p>Add to: <a href="http://del.icio.us/post?url=http://www.mysqlperformanceblog.com/2010/11/29/data-corruption-drbd-and-story-of-bug/&amp;title=Data%20Corruption,%20DRBD%20and%20story%20of%20bug" title="Bookmark this post on del.icio.us"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/delicious.png" alt="delicious" /></a> | <a href="http://digg.com/submit?phase=2&amp;url=http://www.mysqlperformanceblog.com/2010/11/29/data-corruption-drbd-and-story-of-bug/&amp;title=Data%20Corruption,%20DRBD%20and%20story%20of%20bug" title="Digg this post on Digg.com"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/digg.png" alt="digg" /></a> | <a href="http://reddit.com/submit?url=http://www.mysqlperformanceblog.com/2010/11/29/data-corruption-drbd-and-story-of-bug/&amp;title=Data%20Corruption,%20DRBD%20and%20story%20of%20bug" title="Submit this post on reddit.com"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/reddit.png" alt="reddit" /></a> | <a href="http://www.netscape.com/submit/?U=http://www.mysqlperformanceblog.com/2010/11/29/data-corruption-drbd-and-story-of-bug/&amp;T=Data%20Corruption,%20DRBD%20and%20story%20of%20bug" title="Vote for this article on Netscape"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/netscape.gif" alt="netscape" /></a> | <a href="http://www.google.com/bookmarks/mark?op=add&amp;bkmk=http://www.mysqlperformanceblog.com/2010/11/29/data-corruption-drbd-and-story-of-bug/&amp;title=Data%20Corruption,%20DRBD%20and%20story%20of%20bug" title="Add to Google Bookmarks"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/google.png" alt="Google Bookmarks" /></a></p><br/>PlanetMySQL Voting:
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=26614&vote=1&apivote=1">Vote UP</a> /
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=26614&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
			<wfw:commentRss>http://planetmysql.ru/2010/11/30/data-corruption-drbd-and-story-of-bug/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How MySQL Workbench breaks itself</title>
		<link>http://datacharmer.blogspot.com/2010/11/how-mysql-workbench-breaks-itself.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-mysql-workbench-breaks-itself</link>
		<comments>http://datacharmer.blogspot.com/2010/11/how-mysql-workbench-breaks-itself.html#comments</comments>
		<pubDate>Sun, 21 Nov 2010 16:32:00 +0000</pubDate>
		<dc:creator>Giuseppe Maxia</dc:creator>
				<category><![CDATA[bugs]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[workbench]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Once upon a time, there was a policy in MySQL not to add new features after the beta stage.To my surprise, MySQL Workbench 5.2.30 introduces a new feature, the query formatter. I gave it a try. The results are not extremely encouraging. Granted, it's a plugin and not a feature in the core application, but nonetheless one would expect something more stable in a GA release, especially since the plugin features are displayed in the main menu, and unless you have read the announcement, you couldn't easily tell the core from the plugins.This is what I have got in just a few minutes: Bug #58356: beautify function fails on CREATE TABLE  Bug #58357: beutify function erases statement on CREATE INDEX   Bug #58358: query formatter fails on selected query   Bug #58359: query formatter indentation fails on partially selected query  Bug #58360 query formatter converts non-keywords to uppercase Bug #58361 Query formatter mangles query when CASE operator is usedMySQL Workbench is a great product. I would like it to be more solid. New features, even as a plugin, should be more carefully released that this one.]]></description>
			<content:encoded><![CDATA[Once upon a time, there was a policy in MySQL not to add new features after the beta stage.<br />To my surprise, <a href="http://wb.mysql.com/?p=833">MySQL Workbench 5.2.30</a> introduces a new feature, the query formatter. I gave it a try. The results are not extremely encouraging. Granted, it's a plugin and not a feature in the core application, but nonetheless one would expect something more stable in a GA release, especially since the plugin features are displayed in the main menu, and unless you have read the <a href="http://wb.mysql.com/?p=833">announcement</a>, you couldn't easily tell the core from the plugins.<br />This is what I have got in just a few minutes:<br /><br /><a href="http://bugs.mysql.com/58356"> Bug #58356: beautify function fails on CREATE TABLE </a><br /><a href="http://bugs.mysql.com/58357"> Bug #58357: beutify function erases statement on CREATE INDEX </a> <br /><a href="http://bugs.mysql.com/58358"> Bug #58358: query formatter fails on selected query  </a><br /><a href="http://bugs.mysql.com/58359"> Bug #58359: query formatter indentation fails on partially selected query </a><br /><a href="http://bugs.mysql.com/58360"> Bug #58360 query formatter converts non-keywords to uppercase </a><br /><a href="http://bugs.mysql.com/58360">Bug #58361 Query formatter mangles query when CASE operator is used</a><br /><br />MySQL Workbench is a great product. I would like it to be more solid. New features, even as a plugin, should be more carefully released that this one.<div><img width="1" height="1" src="https://blogger.googleusercontent.com/tracker/16959946-977563335068143061?l=datacharmer.blogspot.com" alt="" /></div><br/>PlanetMySQL Voting:
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=26520&vote=1&apivote=1">Vote UP</a> /
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=26520&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
			<wfw:commentRss>http://planetmysql.ru/2010/11/21/how-mysql-workbench-breaks-itself/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Warnings are now actual problems</title>
		<link>http://www.flamingspork.com/blog/2010/09/23/warnings-are-now-actual-problems/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=warnings-are-now-actual-problems</link>
		<comments>http://www.flamingspork.com/blog/2010/09/23/warnings-are-now-actual-problems/#comments</comments>
		<pubDate>Thu, 23 Sep 2010 03:47:50 +0000</pubDate>
		<dc:creator>Stewart Smith</dc:creator>
				<category><![CDATA[bugs]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[compiler]]></category>
		<category><![CDATA[Drizzle]]></category>
		<category><![CDATA[haildb]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[warnings]]></category>

		<guid isPermaLink="false">http://www.flamingspork.com/blog/?p=2136</guid>
		<description><![CDATA[Yesterday, I reached a happy milestone in HailDB development. All compiler warnings left in the api/ directory (the public interface to the database engine) are now either probable/possible bugs (that we need to look at closely) or are warnings due to unfinished code (that we should finish).
There&#8217;s still a bunch of compiler warnings that we&#8217;ve inherited (HailDB compiles with lots of warnings enabled) that we have to get through, but a lot will wait until after we update the core to be based on InnoDB 1.1.




		
			Share this on Facebook
		
		
			Tweet This!
		
		
			Share this on del.icio.us
		
		
			Digg this!
		
		
			Post on Google Buzz
		


]]></description>
			<content:encoded><![CDATA[<p>Yesterday, I reached a happy milestone in <a href="http://www.haildb.com">HailDB</a> development. All compiler warnings left in the api/ directory (the public interface to the database engine) are now either probable/possible bugs (that we need to look at closely) or are warnings due to unfinished code (that we should finish).</p>
<p>There&#8217;s still a bunch of compiler warnings that we&#8217;ve inherited (HailDB compiles with lots of warnings enabled) that we have to get through, but a lot will wait until after we update the core to be based on InnoDB 1.1.</p>


<div>
<ul>
		<li>
			<a href="http://www.facebook.com/share.php?v=4&amp;src=bm&amp;u=http://www.flamingspork.com/blog/2010/09/23/warnings-are-now-actual-problems/&amp;t=Warnings+are+now+actual+problems" rel="nofollow" title="Share this on Facebook">Share this on Facebook</a>
		</li>
		<li>
			<a href="http://twitter.com/home?status=Warnings+are+now+actual+problems+-+http://b2l.me/atzahp&amp;source=shareaholic" rel="nofollow" title="Tweet This!">Tweet This!</a>
		</li>
		<li>
			<a href="http://delicious.com/post?url=http://www.flamingspork.com/blog/2010/09/23/warnings-are-now-actual-problems/&amp;title=Warnings+are+now+actual+problems" rel="nofollow" title="Share this on del.icio.us">Share this on del.icio.us</a>
		</li>
		<li>
			<a href="http://digg.com/submit?phase=2&amp;url=http://www.flamingspork.com/blog/2010/09/23/warnings-are-now-actual-problems/&amp;title=Warnings+are+now+actual+problems" rel="nofollow" title="Digg this!">Digg this!</a>
		</li>
		<li>
			<a href="http://www.google.com/buzz/post?url=http://www.flamingspork.com/blog/2010/09/23/warnings-are-now-actual-problems/&amp;imageurl=" rel="nofollow" title="Post on Google Buzz">Post on Google Buzz</a>
		</li>
</ul>
<div></div>
</div><br/>PlanetMySQL Voting:
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=25966&vote=1&apivote=1">Vote UP</a> /
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=25966&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
			<wfw:commentRss>http://planetmysql.ru/2010/09/23/warnings-are-now-actual-problems/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Two subtle bugs in OUTER JOIN queries</title>
		<link>http://www.xaprb.com/blog/2010/08/02/two-subtle-bugs-in-outer-join-queries/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=two-subtle-bugs-in-outer-join-queries</link>
		<comments>http://www.xaprb.com/blog/2010/08/02/two-subtle-bugs-in-outer-join-queries/#comments</comments>
		<pubDate>Tue, 03 Aug 2010 02:38:18 +0000</pubDate>
		<dc:creator>Baron Schwartz (xaprb)</dc:creator>
				<category><![CDATA[bugs]]></category>
		<category><![CDATA[Maatkit]]></category>
		<category><![CDATA[null]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://www.xaprb.com/blog/?p=1971</guid>
		<description><![CDATA[OUTER JOIN queries in SQL are susceptible to two very subtle bugs that I&#8217;ve observed a number of times in the real world.  Daniel and I have been hammering out ways to automatically detect queries that suffer from these bugs, in a relatively new Maatkit tool called mk-query-advisor.  It&#8217;s part of our series of advisor tools for MySQL.  I wrote a blog post about it a while ago.  Automated analysis of bad query patterns is a good thing to write tools to do, because catching buggy queries is hard work if you do it manually.

Let&#8217;s dive right in and analyze these subtle bugs.  Warning: if you don&#8217;t understand how SQL handles NULL, you&#8217;re not going to understand the following.  Many people have a hard time with NULL, which is why these bugs are so hard to understand and avoid.  This is one reason why SQL is a hard language to use properly.

Bug 1: a column could be NULL for two reasons, and you can&#8217;t distinguish them

If the outer table in your query contains NULL-able columns, and you place a WHERE clause to filter out all but those rows, you&#8217;re going to get bugs because a non-matching row in the outer table will be all-NULL.  Here&#8217;s an example.  Let&#8217;s start with a plain outer join query:


select * from L left join R on l_id = r_id;
+------+------+---------+
&#124; l_id &#124; r_id &#124; r_other &#124;
+------+------+---------+
&#124;    1 &#124;    1 &#124;       5 &#124; 
&#124;    2 &#124;    2 &#124;    NULL &#124; 
&#124;    3 &#124; NULL &#124;    NULL &#124; 
+------+------+---------+


Here we see that one row in the outer table is missing, and one row (the middle row) has a NULL r_other column.  Now, let&#8217;s add a WHERE clause:


select * from L left join R on l_id = r_id where r_other is null;
+------+------+---------+
&#124; l_id &#124; r_id &#124; r_other &#124;
+------+------+---------+
&#124;    2 &#124;    2 &#124;    NULL &#124; 
&#124;    3 &#124; NULL &#124;    NULL &#124; 
+------+------+---------+


This query is buggy, because the two rows are returned for completely different reasons, and you can&#8217;t be sure which is which.  IS NULL clauses can safely be placed on the columns used in the JOIN clause, but not on other columns in the outer table that might be NULL.

Bug 2: an OUTER JOIN is converted to INNER

If you place a non-null-safe comparison operator on any column in the outer table that isn&#8217;t part of the JOIN clause, you implicitly disable the outer-ness of the query and convert it to an INNER JOIN.  Here&#8217;s an example:


select * from L left join R on l_id = r_id where r_other &#62; 1;
+------+------+---------+
&#124; l_id &#124; r_id &#124; r_other &#124;
+------+------+---------+
&#124;    1 &#124;    1 &#124;       5 &#124; 
+------+------+---------+


The left-outer-ness of the above query is what causes the third row to be output in the first query I showed you above.  The greater-than operator in this example automatically makes the left-ness impossible, because anytime there&#8217;s a row in the inner table that has no match in the outer table, it&#8217;ll be filled in with NULLs, and those NULLs will be eliminated by the operator.  So the effect is that only matching rows will ever be output.

If you want to ponder variations and subtleties of the above, you can read more discussion on the issue report where we&#8217;re hammering out the details of automatically detecting and warning about these sneaky errors.

Related posts:How to simulate FULL OUTER JOIN in MySQLHow to write a SQL exclusion joinHow to write SQL JOIN clauses more compactlyThe dangerous subtleties of LEFT JOIN and COUNT() in SQLHow to write INSERT IF NOT EXISTS queries in standard  SQL]]></description>
			<content:encoded><![CDATA[<p>OUTER JOIN queries in SQL are susceptible to two very subtle bugs that I&#8217;ve observed a number of times in the real world.  Daniel and I have been hammering out ways to automatically detect queries that suffer from these bugs, in <a href="http://www.maatkit.org/doc/mk-query-advisor.html">a relatively new Maatkit tool called mk-query-advisor</a>.  It&#8217;s part of our series of advisor tools for MySQL.  I wrote <a href="http://www.xaprb.com/blog/2010/03/16/try-mk-query-advisor-a-new-maatkit-tool/">a blog post about it</a> a while ago.  Automated analysis of bad query patterns is a good thing to write tools to do, because catching buggy queries is hard work if you do it manually.</p>

<p>Let&#8217;s dive right in and analyze these subtle bugs.  Warning: if you don&#8217;t understand how SQL handles NULL, you&#8217;re not going to understand the following.  Many people have a hard time with NULL, which is why these bugs are so hard to understand and avoid.  This is one reason why SQL is a hard language to use properly.</p>

<h3>Bug 1: a column could be NULL for two reasons, and you can&#8217;t distinguish them</h3>

<p>If the outer table in your query contains NULL-able columns, and you place a WHERE clause to filter out all but those rows, you&#8217;re going to get bugs because a non-matching row in the outer table will be all-NULL.  Here&#8217;s an example.  Let&#8217;s start with a plain outer join query:</p>

<code><pre>
select * from L left join R on l_id = r_id;
+------+------+---------+
| l_id | r_id | r_other |
+------+------+---------+
|    1 |    1 |       5 | 
|    2 |    2 |    NULL | 
|    3 | NULL |    NULL | 
+------+------+---------+
</pre></code>

<p>Here we see that one row in the outer table is missing, and one row (the middle row) has a NULL r_other column.  Now, let&#8217;s add a WHERE clause:</p>

<code><pre>
select * from L left join R on l_id = r_id where r_other is null;
+------+------+---------+
| l_id | r_id | r_other |
+------+------+---------+
|    2 |    2 |    NULL | 
|    3 | NULL |    NULL | 
+------+------+---------+
</pre></code>

<p>This query is buggy, because the two rows are returned for completely different reasons, and you can&#8217;t be sure which is which.  IS NULL clauses can safely be placed on the columns used in the JOIN clause, but not on other columns in the outer table that might be NULL.</p>

<h3>Bug 2: an OUTER JOIN is converted to INNER</h3>

<p>If you place a non-null-safe comparison operator on any column in the outer table that isn&#8217;t part of the JOIN clause, you implicitly disable the outer-ness of the query and convert it to an INNER JOIN.  Here&#8217;s an example:</p>

<code><pre>
select * from L left join R on l_id = r_id where r_other > 1;
+------+------+---------+
| l_id | r_id | r_other |
+------+------+---------+
|    1 |    1 |       5 | 
+------+------+---------+
</pre></code>

<p>The left-outer-ness of the above query is what causes the third row to be output in the first query I showed you above.  The greater-than operator in this example automatically makes the left-ness impossible, because anytime there&#8217;s a row in the inner table that has no match in the outer table, it&#8217;ll be filled in with NULLs, and those NULLs will be eliminated by the operator.  So the effect is that only matching rows will ever be output.</p>

<p>If you want to ponder variations and subtleties of the above, you can read more discussion on <a href="http://code.google.com/p/maatkit/issues/detail?id=950">the issue report where we&#8217;re hammering out the details</a> of automatically detecting and warning about these sneaky errors.</p>

<p>Related posts:<ol><li><a href="http://www.xaprb.com/blog/2006/05/26/how-to-write-full-outer-join-in-mysql/" rel="bookmark" title="Permanent Link: How to simulate FULL OUTER JOIN in MySQL">How to simulate FULL OUTER JOIN in MySQL</a></li><li><a href="http://www.xaprb.com/blog/2005/09/23/how-to-write-a-sql-exclusion-join/" rel="bookmark" title="Permanent Link: How to write a SQL exclusion join">How to write a SQL exclusion join</a></li><li><a href="http://www.xaprb.com/blog/2006/11/28/how-to-write-sql-join-clauses-more-compactly/" rel="bookmark" title="Permanent Link: How to write SQL JOIN clauses more compactly">How to write SQL JOIN clauses more compactly</a></li><li><a href="http://www.xaprb.com/blog/2009/04/08/the-dangerous-subtleties-of-left-join-and-count-in-sql/" rel="bookmark" title="Permanent Link: The dangerous subtleties of LEFT JOIN and COUNT() in SQL">The dangerous subtleties of LEFT JOIN and COUNT() in SQL</a></li><li><a href="http://www.xaprb.com/blog/2005/09/25/insert-if-not-exists-queries-in-mysql/" rel="bookmark" title="Permanent Link: How to write INSERT IF NOT EXISTS queries in standard  SQL">How to write INSERT IF NOT EXISTS queries in standard  SQL</a></li></ol></p><br/>PlanetMySQL Voting:
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=25469&vote=1&apivote=1">Vote UP</a> /
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=25469&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
			<wfw:commentRss>http://planetmysql.ru/2010/08/03/two-subtle-bugs-in-outer-join-queries/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Joining on range? Wrong!</title>
		<link>http://www.mysqlperformanceblog.com/2010/05/17/joining-on-range-wrong/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=joining-on-range-wrong</link>
		<comments>http://www.mysqlperformanceblog.com/2010/05/17/joining-on-range-wrong/#comments</comments>
		<pubDate>Mon, 17 May 2010 23:39:47 +0000</pubDate>
		<dc:creator>MySQL Performance Blog</dc:creator>
				<category><![CDATA[bugs]]></category>
		<category><![CDATA[JOIN Performance]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[optimizer]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[problems]]></category>

		<guid isPermaLink="false">http://www.mysqlperformanceblog.com/?p=2656</guid>
		<description><![CDATA[The problem I am going to describe is likely to be around since the very beginning of MySQL, however unless you carefully analyse and profile your queries, it might easily go unnoticed. I used it as one of the examples in our talk given at phpDay.it conference last week to demonstrate some pitfalls one may hit when designing schemas and queries, but then I thought it could be a good idea to publish this on the blog as well.
To demonstrate the issue let’s use a typical example – a sales query. Our data is a tiny store directory consisting of three very simple tables:
PLAIN TEXT
SQL:




CREATE TABLE `products` &#40;


&#160; `prd_id` int&#40;10&#41; UNSIGNED NOT NULL AUTO_INCREMENT,


&#160; `prd_name` varchar&#40;32&#41; NOT NULL,


&#160; PRIMARY KEY &#40;`prd_id`&#41;,


&#160; KEY `name` &#40;`prd_name`&#41;


&#41;


&#160;


CREATE TABLE `tags` &#40;


&#160; `tag_prd_id` int&#40;10&#41; UNSIGNED NOT NULL,


&#160; `tag_name` varchar&#40;32&#41; NOT NULL,


&#160; PRIMARY KEY &#40;`tag_name`, `tag_prd_id`&#41;


&#41;


&#160;


CREATE TABLE `items_ordered` &#40;


&#160; `itm_id` int&#40;10&#41; UNSIGNED NOT NULL AUTO_INCREMENT,


&#160; `itm_prd_id` int&#40;10&#41; UNSIGNED NOT NULL,


&#160; `itm_order_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,


&#160; PRIMARY KEY &#40;`itm_id`&#41;,


&#160; KEY `itm_prd_id__and__itm_order_timestamp` &#40;`itm_prd_id`,`itm_order_timestamp`&#41;


&#41; 






"Please excuse the crudity of this model, I didn't have time to build it to scale or to paint it." -- Dr. Emmett Brown
I populated these tables with enough data to serve our purpose.
Our hypothetical sales query could be to figure out how many LCD TVs were sold yesterday.
PLAIN TEXT
SQL:




SELECT&#160; &#160; &#160; &#160; COUNT&#40;1&#41;


&#160; &#160; &#160; &#160;FROM&#160; &#160;tags t


&#160; &#160; &#160; &#160; &#160; &#160; &#160; JOIN products p


&#160; &#160; &#160; &#160; &#160; &#160; &#160; ON&#160; &#160; &#160;p.prd_id = t.tag_prd_id


&#160; &#160; &#160; &#160; &#160; &#160; &#160; JOIN items_ordered i


&#160; &#160; &#160; &#160; &#160; &#160; &#160; ON&#160; &#160; &#160;i.itm_prd_id&#160; &#160; = p.prd_id


&#160; &#160; &#160; &#160;WHERE&#160; t.tag_name&#160; &#160; &#160; &#160; &#160; &#160; &#160;= 'lcd'


&#160; &#160; &#160; &#160;AND&#160; &#160; i.itm_order_timestamp&#62;= '2010-05-16 00:00:00'


&#160; &#160; &#160; &#160;AND&#160; &#160; i.itm_order_timestamp&#160; &#60;'2010-05-17 00:00:00'


+----------+


&#124; COUNT&#40;1&#41; &#124;


+----------+


&#124;&#160; &#160; &#160;4103 &#124; 


+----------+ 






Seems like a very successful day!  
When we look at the data structures it looks quite good – there is index on `tag_name` in `tags`, there is index on (`itm_prd_id`, `itm_order_timestamp`) in `items_ordered` and indexes on other columns used in joins. Let’s verify how the query performed in greater detail:
PLAIN TEXT
CODE:




SHOW STATUS LIKE 'Handler_read%';&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;


&#160;


+-----------------------+--------+


&#124; Variable_name&#160; &#160; &#160; &#160; &#160;&#124; Value&#160; &#124;


+-----------------------+--------+


&#124; Handler_read_first&#160; &#160; &#124; 0&#160; &#160; &#160; &#124; 


&#124; Handler_read_key&#160; &#160; &#160; &#124; 3&#160; &#160; &#160; &#124; 


&#124; Handler_read_next&#160; &#160; &#160;&#124; 118181 &#124; 


&#124; Handler_read_prev&#160; &#160; &#160;&#124; 0&#160; &#160; &#160; &#124; 


&#124; Handler_read_rnd&#160; &#160; &#160; &#124; 0&#160; &#160; &#160; &#124; 


&#124; Handler_read_rnd_next &#124; 0&#160; &#160; &#160; &#124; 


+-----------------------+--------+ 






Somehow this does not look as good as the sales numbers. Query matched 4103 rows, but almost 120000 were scanned. And we have proper indexes on all necessary columns! What does EXPLAIN have to say about this?
PLAIN TEXT
CODE:




*************************** 1. row ***************************


&#160; &#160; &#160; &#160; &#160; &#160;id: 1


&#160; select_type: SIMPLE


&#160; &#160; &#160; &#160; table: t


&#160; &#160; &#160; &#160; &#160;type: ref


possible_keys: PRIMARY


&#160; &#160; &#160; &#160; &#160; key: PRIMARY


&#160; &#160; &#160; key_len: 98


&#160; &#160; &#160; &#160; &#160; ref: const


&#160; &#160; &#160; &#160; &#160;rows: 1


&#160; &#160; &#160; &#160; Extra: Using where; Using index


*************************** 2. row ***************************


&#160; &#160; &#160; &#160; &#160; &#160;id: 1


&#160; select_type: SIMPLE


&#160; &#160; &#160; &#160; table: p


&#160; &#160; &#160; &#160; &#160;type: eq_ref


possible_keys: PRIMARY


&#160; &#160; &#160; &#160; &#160; key: PRIMARY


&#160; &#160; &#160; key_len: 4


&#160; &#160; &#160; &#160; &#160; ref: example_db.t.tag_prd_id


&#160; &#160; &#160; &#160; &#160;rows: 1


&#160; &#160; &#160; &#160; Extra: Using index


*************************** 3. row ***************************


&#160; &#160; &#160; &#160; &#160; &#160;id: 1


&#160; select_type: SIMPLE


&#160; &#160; &#160; &#160; table: i


&#160; &#160; &#160; &#160; &#160;type: ref


possible_keys: itm_prd_id__and__itm_order_timestamp


&#160; &#160; &#160; &#160; &#160; key: itm_prd_id__and__itm_order_timestamp


&#160; &#160; &#160; key_len: 4


&#160; &#160; &#160; &#160; &#160; ref: example_db.p.prd_id


&#160; &#160; &#160; &#160; &#160;rows: 10325


&#160; &#160; &#160; &#160; Extra: Using where; Using index 






To remind - our structure design is:
PLAIN TEXT
SQL:




`itm_prd_id` int&#40;10&#41; UNSIGNED NOT NULL


&#160; `itm_order_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP


&#160; KEY `itm_prd_id__and__itm_order_timestamp` &#40;`itm_prd_id`,`itm_order_timestamp`&#41; 






In 3rd row key_len is only 4 bytes, while the full key length is 4 bytes for `itm_prd_id` plus 4 bytes for `itm_order_timestamp`, so 8 bytes in total! Also ref shows only one column being used by the last join. 
How should we understand this then? Database reads all ordered items where tag is 'lcd', which totals to about 120000 rows as shown by the counters in SHOW STATUS output above, and then filters out those not matching the date range. A very inefficient approach! MySQL was unable to optimize those simple conditions to match both product id and date range by index and read only the relevant rows.
This affects joins only. When you use a range condition on the first (or the only) table, it works as expected:
PLAIN TEXT
SQL:




EXPLAIN


SELECT&#160; &#160; &#160; &#160; COUNT&#40;1&#41;


&#160; &#160; &#160; &#160;FROM&#160; &#160;items_ordered i


&#160; &#160; &#160; &#160;WHERE&#160; i.itm_prd_id&#160; &#160; &#160; &#160; &#160; &#160;= 5


&#160; &#160; &#160; &#160;AND&#160; &#160; i.itm_order_timestamp&#62;= '2010-05-16 00:00:00'


&#160; &#160; &#160; &#160;AND&#160; &#160; i.itm_order_timestamp&#160; &#60;'2010-05-17 00:00:00'


&#160;


*************************** 1. row ***************************


&#160; &#160; &#160; &#160; &#160; &#160;id: 1


&#160; select_type: SIMPLE


&#160; &#160; &#160; &#160; TABLE: i


&#160; &#160; &#160; &#160; &#160;type: range


possible_keys: itm_prd_id__and__itm_order_timestamp


&#160; &#160; &#160; &#160; &#160; KEY: itm_prd_id__and__itm_order_timestamp


&#160; &#160; &#160; key_len: 8


&#160; &#160; &#160; &#160; &#160; ref: NULL


&#160; &#160; &#160; &#160; &#160;rows: 1306


&#160; &#160; &#160; &#160; Extra: USING WHERE; USING INDEX 






In this case MySQL does not print ref at all, because there is no join, however you can notice key_len is 8 bytes, so the full index length. It means both index columns will be used to execute the query.
There may be many workarounds to this problem, all depends on the specific case you may need to solve. Essentially it always comes down to removing range condition from join one way or another. For our example query this could mean introducing additional DATE column and using it for filtering instead:
PLAIN TEXT
SQL:




ALTER TABLE items_ordered ADD itm_order_date DATE NOT NULL, ADD INDEX itm_prd_id__and__itm_order_date &#40;itm_prd_id, itm_order_date&#41;;


UPDATE items_ordered SET itm_order_date = DATE&#40;itm_order_timestamp&#41;; 






Now the rewritten query:
PLAIN TEXT
SQL:




EXPLAIN


SELECT&#160; &#160; &#160; &#160; COUNT&#40;1&#41;


&#160; &#160; &#160; &#160;FROM&#160; &#160;tags t


&#160; &#160; &#160; &#160; &#160; &#160; &#160; JOIN products p


&#160; &#160; &#160; &#160; &#160; &#160; &#160; ON&#160; &#160; &#160;p.prd_id = t.tag_prd_id


&#160; &#160; &#160; &#160; &#160; &#160; &#160; JOIN items_ordered i


&#160; &#160; &#160; &#160; &#160; &#160; &#160; ON&#160; &#160; &#160;i.itm_prd_id = p.prd_id


&#160; &#160; &#160; &#160;WHERE&#160; t.tag_name&#160; &#160; &#160; &#160; &#160; = 'lcd'


&#160; &#160; &#160; &#160;AND&#160; &#160; i.itm_order_date&#160; &#160; = '2010-05-16'


&#160;


*************************** 1. row ***************************


&#160; &#160; &#160; &#160; &#160; &#160;id: 1


&#160; select_type: SIMPLE


&#160; &#160; &#160; &#160; TABLE: t


&#160; &#160; &#160; &#160; &#160;type: ref


possible_keys: PRIMARY


&#160; &#160; &#160; &#160; &#160; KEY: PRIMARY


&#160; &#160; &#160; key_len: 98


&#160; &#160; &#160; &#160; &#160; ref: const


&#160; &#160; &#160; &#160; &#160;rows: 1


&#160; &#160; &#160; &#160; Extra: USING WHERE; USING INDEX


*************************** 2. row ***************************


&#160; &#160; &#160; &#160; &#160; &#160;id: 1


&#160; select_type: SIMPLE


&#160; &#160; &#160; &#160; TABLE: p


&#160; &#160; &#160; &#160; &#160;type: eq_ref


possible_keys: PRIMARY


&#160; &#160; &#160; &#160; &#160; KEY: PRIMARY


&#160; &#160; &#160; key_len: 4


&#160; &#160; &#160; &#160; &#160; ref: example_db.t.tag_prd_id


&#160; &#160; &#160; &#160; &#160;rows: 1


&#160; &#160; &#160; &#160; Extra: USING INDEX


*************************** 3. row ***************************


&#160; &#160; &#160; &#160; &#160; &#160;id: 1


&#160; select_type: SIMPLE


&#160; &#160; &#160; &#160; TABLE: i


&#160; &#160; &#160; &#160; &#160;type: ref


possible_keys: itm_prd_id__and__itm_order_timestamp,itm_prd_id__and__itm_order_date


&#160; &#160; &#160; &#160; &#160; KEY: itm_prd_id__and__itm_order_date


&#160; &#160; &#160; key_len: 7


&#160; &#160; &#160; &#160; &#160; ref: example_db.p.prd_id,const


&#160; &#160; &#160; &#160; &#160;rows: 206494


&#160; &#160; &#160; &#160; Extra: USING WHERE; USING INDEX 






This query uses 7 bytes of `itm_prd_id__and__itm_order_date` index – 4 bytes is `itm_prd_id` and 3  bytes is `itm_order_date` (DATE type uses 3 bytes). Also ref shows two columns used in join.
PLAIN TEXT
CODE:




SHOW STATUS LIKE 'Handler_read%';&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;


+-----------------------+-------+


&#124; Variable_name&#160; &#160; &#160; &#160; &#160;&#124; Value &#124;


+-----------------------+-------+


&#124; Handler_read_first&#160; &#160; &#124; 0&#160; &#160; &#160;&#124; 


&#124; Handler_read_key&#160; &#160; &#160; &#124; 3&#160; &#160; &#160;&#124; 


&#124; Handler_read_next&#160; &#160; &#160;&#124; 4104&#160; &#124; 


&#124; Handler_read_prev&#160; &#160; &#160;&#124; 0&#160; &#160; &#160;&#124; 


&#124; Handler_read_rnd&#160; &#160; &#160; &#124; 0&#160; &#160; &#160;&#124; 


&#124; Handler_read_rnd_next &#124; 0&#160; &#160; &#160;&#124; 


+-----------------------+-------+ 






Statistics also look much better.  
But remember - different query will likely need a different solution.
You can find several bug reports regarding this problem (e.g. #8569, #19548). Some replies from MySQL indicate this may be eventually fixed in 6.0 or some future version. Others say "it’s a documented behaviour – deal with it".  But in the real world this is a serious bug, not a feature, and it needs fixing.
    
    Entry posted by Maciej Dobrzanski &#124;
      No comment
    Add to:  &#124;  &#124;  &#124;  &#124; ]]></description>
			<content:encoded><![CDATA[<p>The problem I am going to describe is likely to be around since the very beginning of MySQL, however unless you carefully analyse and profile your queries, it might easily go unnoticed. I used it as one of the examples in our talk given at <a href="http://www.phpday.it/" >phpDay.it</a> conference last week to demonstrate some pitfalls one may hit when designing schemas and queries, but then I thought it could be a good idea to publish this on the blog as well.</p>
<p>To demonstrate the issue let’s use a typical example – a sales query. Our data is a tiny store directory consisting of three very simple tables:</p>
<div><span><a href="http://www.mysqlperformanceblog.com">PLAIN TEXT</a></span></div>
<div><span>SQL:</span>
<div>
<div>
<ol>
<li>
<div><span>CREATE</span> <span>TABLE</span> <span>`products`</span> <span>&#40;</span></div>
</li>
<li>
<div>&nbsp; <span>`prd_id`</span> int<span>&#40;</span><span>10</span><span>&#41;</span> <span>UNSIGNED</span> <span>NOT</span> <span>NULL</span> <span>AUTO_INCREMENT</span>,</div>
</li>
<li>
<div>&nbsp; <span>`prd_name`</span> varchar<span>&#40;</span><span>32</span><span>&#41;</span> <span>NOT</span> <span>NULL</span>,</div>
</li>
<li>
<div>&nbsp; <span>PRIMARY</span> <span>KEY</span> <span>&#40;</span><span>`prd_id`</span><span>&#41;</span>,</div>
</li>
<li>
<div>&nbsp; <span>KEY</span> <span>`name`</span> <span>&#40;</span><span>`prd_name`</span><span>&#41;</span></div>
</li>
<li>
<div><span>&#41;</span></div>
</li>
<li>
<div>&nbsp;</div>
</li>
<li>
<div><span>CREATE</span> <span>TABLE</span> <span>`tags`</span> <span>&#40;</span></div>
</li>
<li>
<div>&nbsp; <span>`tag_prd_id`</span> int<span>&#40;</span><span>10</span><span>&#41;</span> <span>UNSIGNED</span> <span>NOT</span> <span>NULL</span>,</div>
</li>
<li>
<div>&nbsp; <span>`tag_name`</span> varchar<span>&#40;</span><span>32</span><span>&#41;</span> <span>NOT</span> <span>NULL</span>,</div>
</li>
<li>
<div>&nbsp; <span>PRIMARY</span> <span>KEY</span> <span>&#40;</span><span>`tag_name`</span>, <span>`tag_prd_id`</span><span>&#41;</span></div>
</li>
<li>
<div><span>&#41;</span></div>
</li>
<li>
<div>&nbsp;</div>
</li>
<li>
<div><span>CREATE</span> <span>TABLE</span> <span>`items_ordered`</span> <span>&#40;</span></div>
</li>
<li>
<div>&nbsp; <span>`itm_id`</span> int<span>&#40;</span><span>10</span><span>&#41;</span> <span>UNSIGNED</span> <span>NOT</span> <span>NULL</span> <span>AUTO_INCREMENT</span>,</div>
</li>
<li>
<div>&nbsp; <span>`itm_prd_id`</span> int<span>&#40;</span><span>10</span><span>&#41;</span> <span>UNSIGNED</span> <span>NOT</span> <span>NULL</span>,</div>
</li>
<li>
<div>&nbsp; <span>`itm_order_timestamp`</span> timestamp <span>NOT</span> <span>NULL</span> <span>DEFAULT</span> CURRENT_TIMESTAMP,</div>
</li>
<li>
<div>&nbsp; <span>PRIMARY</span> <span>KEY</span> <span>&#40;</span><span>`itm_id`</span><span>&#41;</span>,</div>
</li>
<li>
<div>&nbsp; <span>KEY</span> <span>`itm_prd_id__and__itm_order_timestamp`</span> <span>&#40;</span><span>`itm_prd_id`</span>,<span>`itm_order_timestamp`</span><span>&#41;</span></div>
</li>
<li>
<div><span>&#41;</span> </div>
</li>
</ol>
</div>
</div>
</div>
<p></p>
<p><em>"Please excuse the crudity of this model, I didn't have time to build it to scale or to paint it." -- Dr. Emmett Brown</em></p>
<p>I populated these tables with enough data to serve our purpose.</p>
<p>Our hypothetical sales query could be to figure out how many LCD TVs were sold yesterday.</p>
<div><span><a href="http://www.mysqlperformanceblog.com">PLAIN TEXT</a></span></div>
<div><span>SQL:</span>
<div>
<div>
<ol>
<li>
<div><span>SELECT</span>&nbsp; &nbsp; &nbsp; &nbsp; COUNT<span>&#40;</span><span>1</span><span>&#41;</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp;<span>FROM</span>&nbsp; &nbsp;tags t</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span>JOIN</span> products p</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span>ON</span>&nbsp; &nbsp; &nbsp;p.prd_id = t.tag_prd_id</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span>JOIN</span> items_ordered i</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span>ON</span>&nbsp; &nbsp; &nbsp;i.itm_prd_id&nbsp; &nbsp; = p.prd_id</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp;<span>WHERE</span>&nbsp; t.tag_name&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;= <span>'lcd'</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp;<span>AND</span>&nbsp; &nbsp; i.itm_order_timestamp&gt;= <span>'2010-05-16 00:00:00'</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp;<span>AND</span>&nbsp; &nbsp; i.itm_order_timestamp&nbsp; &lt;<span>'2010-05-17 00:00:00'</span></div>
</li>
<li>
<div>+<span>----------+</span></div>
</li>
<li>
<div>| COUNT<span>&#40;</span><span>1</span><span>&#41;</span> |</div>
</li>
<li>
<div>+<span>----------+</span></div>
</li>
<li>
<div>|&nbsp; &nbsp; &nbsp;<span>4103</span> | </div>
</li>
<li>
<div>+<span>----------+ </span></div>
</li>
</ol>
</div>
</div>
</div>
<p></p>
<p>Seems like a very successful day! <img src="http://www.mysqlperformanceblog.com/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /> </p>
<p>When we look at the data structures it looks quite good – there is index on <code>`tag_name`</code> in <code>`tags`</code>, there is index on <code>(`itm_prd_id`, `itm_order_timestamp`)</code> in <code>`items_ordered`</code> and indexes on other columns used in joins. Let’s verify how the query performed in greater detail:</p>
<div><span><a href="http://www.mysqlperformanceblog.com">PLAIN TEXT</a></span></div>
<div><span>CODE:</span>
<div>
<div>
<ol>
<li>
<div>SHOW STATUS LIKE <span>'Handler_read%'</span>;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
</li>
<li>
<div>&nbsp;</div>
</li>
<li>
<div>+-----------------------+--------+</div>
</li>
<li>
<div>| Variable_name&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| Value&nbsp; |</div>
</li>
<li>
<div>+-----------------------+--------+</div>
</li>
<li>
<div>| Handler_read_first&nbsp; &nbsp; | <span>0</span>&nbsp; &nbsp; &nbsp; | </div>
</li>
<li>
<div>| Handler_read_key&nbsp; &nbsp; &nbsp; | <span>3</span>&nbsp; &nbsp; &nbsp; | </div>
</li>
<li>
<div>| Handler_read_next&nbsp; &nbsp; &nbsp;| <span>118181</span> | </div>
</li>
<li>
<div>| Handler_read_prev&nbsp; &nbsp; &nbsp;| <span>0</span>&nbsp; &nbsp; &nbsp; | </div>
</li>
<li>
<div>| Handler_read_rnd&nbsp; &nbsp; &nbsp; | <span>0</span>&nbsp; &nbsp; &nbsp; | </div>
</li>
<li>
<div>| Handler_read_rnd_next | <span>0</span>&nbsp; &nbsp; &nbsp; | </div>
</li>
<li>
<div>+-----------------------+--------+ </div>
</li>
</ol>
</div>
</div>
</div>
<p></p>
<p>Somehow this does not look as good as the sales numbers. Query matched 4103 rows, but almost <strong>120000 were scanned</strong>. And we have proper indexes on all necessary columns! What does EXPLAIN have to say about this?</p>
<div><span><a href="http://www.mysqlperformanceblog.com">PLAIN TEXT</a></span></div>
<div><span>CODE:</span>
<div>
<div>
<ol>
<li>
<div>*************************** <span>1</span>. <span>row</span> ***************************</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;id: <span>1</span></div>
</li>
<li>
<div>&nbsp; select_type: SIMPLE</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; table: t</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;type: ref</div>
</li>
<li>
<div>possible_keys: PRIMARY</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; key: PRIMARY</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; key_len: <span>98</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ref: const</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;rows: <span>1</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Extra: Using where; Using index</div>
</li>
<li>
<div>*************************** <span>2</span>. <span>row</span> ***************************</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;id: <span>1</span></div>
</li>
<li>
<div>&nbsp; select_type: SIMPLE</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; table: p</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;type: eq_ref</div>
</li>
<li>
<div>possible_keys: PRIMARY</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; key: PRIMARY</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; key_len: <span>4</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ref: example_db.<span>t</span>.<span>tag_prd_id</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;rows: <span>1</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Extra: Using index</div>
</li>
<li>
<div>*************************** <span>3</span>. <span>row</span> ***************************</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;id: <span>1</span></div>
</li>
<li>
<div>&nbsp; select_type: SIMPLE</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; table: i</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;type: ref</div>
</li>
<li>
<div>possible_keys: itm_prd_id__and__itm_order_timestamp</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; key: itm_prd_id__and__itm_order_timestamp</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; key_len: <span>4</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ref: example_db.<span>p</span>.<span>prd_id</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;rows: <span>10325</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Extra: Using where; Using index </div>
</li>
</ol>
</div>
</div>
</div>
<p></p>
<p>To remind - our structure design is:</p>
<div><span><a href="http://www.mysqlperformanceblog.com">PLAIN TEXT</a></span></div>
<div><span>SQL:</span>
<div>
<div>
<ol>
<li>
<div><span>`itm_prd_id`</span> int<span>&#40;</span><span>10</span><span>&#41;</span> <span>UNSIGNED</span> <span>NOT</span> <span>NULL</span></div>
</li>
<li>
<div>&nbsp; <span>`itm_order_timestamp`</span> timestamp <span>NOT</span> <span>NULL</span> <span>DEFAULT</span> CURRENT_TIMESTAMP</div>
</li>
<li>
<div>&nbsp; <span>KEY</span> <span>`itm_prd_id__and__itm_order_timestamp`</span> <span>&#40;</span><span>`itm_prd_id`</span>,<span>`itm_order_timestamp`</span><span>&#41;</span> </div>
</li>
</ol>
</div>
</div>
</div>
<p></p>
<p>In 3rd row <em>key_len</em> is only <strong>4 bytes</strong>, while the full key length is 4 bytes for `itm_prd_id` plus 4 bytes for `itm_order_timestamp`, so <strong>8 bytes</strong> in total! Also <em>ref</em> shows only one column being used by the last join. </p>
<p>How should we understand this then? Database <strong>reads all</strong> ordered items where tag is 'lcd', which totals to about 120000 rows as shown by the counters in SHOW STATUS output above, and then <strong>filters out those not matching</strong> the date range. A very inefficient approach! MySQL was unable to optimize those simple conditions to match both product id and date range by index and read only the relevant rows.</p>
<p>This affects joins only. When you use a range condition on the first (or the only) table, it works as expected:</p>
<div><span><a href="http://www.mysqlperformanceblog.com">PLAIN TEXT</a></span></div>
<div><span>SQL:</span>
<div>
<div>
<ol>
<li>
<div><span>EXPLAIN</span></div>
</li>
<li>
<div><span>SELECT</span>&nbsp; &nbsp; &nbsp; &nbsp; COUNT<span>&#40;</span><span>1</span><span>&#41;</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp;<span>FROM</span>&nbsp; &nbsp;items_ordered i</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp;<span>WHERE</span>&nbsp; i.itm_prd_id&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;= <span>5</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp;<span>AND</span>&nbsp; &nbsp; i.itm_order_timestamp&gt;= <span>'2010-05-16 00:00:00'</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp;<span>AND</span>&nbsp; &nbsp; i.itm_order_timestamp&nbsp; &lt;<span>'2010-05-17 00:00:00'</span></div>
</li>
<li>
<div>&nbsp;</div>
</li>
<li>
<div>*************************** <span>1</span>. row ***************************</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;id: <span>1</span></div>
</li>
<li>
<div>&nbsp; select_type: SIMPLE</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; <span>TABLE</span>: i</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;type: range</div>
</li>
<li>
<div>possible_keys: itm_prd_id__and__itm_order_timestamp</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span>KEY</span>: itm_prd_id__and__itm_order_timestamp</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; key_len: <span>8</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ref: <span>NULL</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;rows: <span>1306</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Extra: <span>USING</span> <span>WHERE</span>; <span>USING</span> <span>INDEX</span> </div>
</li>
</ol>
</div>
</div>
</div>
<p></p>
<p>In this case MySQL does not print <em>ref</em> at all, because there is no join, however you can notice <em>key_len</em> is 8 bytes, so the full index length. It means both index columns will be used to execute the query.</p>
<p>There may be many workarounds to this problem, all depends on the specific case you may need to solve. Essentially it always comes down to removing range condition from join one way or another. For our example query this could mean introducing additional DATE column and using it for filtering instead:</p>
<div><span><a href="http://www.mysqlperformanceblog.com">PLAIN TEXT</a></span></div>
<div><span>SQL:</span>
<div>
<div>
<ol>
<li>
<div><span>ALTER</span> <span>TABLE</span> items_ordered <span>ADD</span> itm_order_date DATE <span>NOT</span> <span>NULL</span>, <span>ADD</span> <span>INDEX</span> itm_prd_id__and__itm_order_date <span>&#40;</span>itm_prd_id, itm_order_date<span>&#41;</span>;</div>
</li>
<li>
<div><span>UPDATE</span> items_ordered <span>SET</span> itm_order_date = DATE<span>&#40;</span>itm_order_timestamp<span>&#41;</span>; </div>
</li>
</ol>
</div>
</div>
</div>
<p></p>
<p>Now the rewritten query:</p>
<div><span><a href="http://www.mysqlperformanceblog.com">PLAIN TEXT</a></span></div>
<div><span>SQL:</span>
<div>
<div>
<ol>
<li>
<div><span>EXPLAIN</span></div>
</li>
<li>
<div><span>SELECT</span>&nbsp; &nbsp; &nbsp; &nbsp; COUNT<span>&#40;</span><span>1</span><span>&#41;</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp;<span>FROM</span>&nbsp; &nbsp;tags t</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span>JOIN</span> products p</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span>ON</span>&nbsp; &nbsp; &nbsp;p.prd_id = t.tag_prd_id</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span>JOIN</span> items_ordered i</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span>ON</span>&nbsp; &nbsp; &nbsp;i.itm_prd_id = p.prd_id</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp;<span>WHERE</span>&nbsp; t.tag_name&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = <span>'lcd'</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp;<span>AND</span>&nbsp; &nbsp; i.itm_order_date&nbsp; &nbsp; = <span>'2010-05-16'</span></div>
</li>
<li>
<div>&nbsp;</div>
</li>
<li>
<div>*************************** <span>1</span>. row ***************************</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;id: <span>1</span></div>
</li>
<li>
<div>&nbsp; select_type: SIMPLE</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; <span>TABLE</span>: t</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;type: ref</div>
</li>
<li>
<div>possible_keys: <span>PRIMARY</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span>KEY</span>: <span>PRIMARY</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; key_len: <span>98</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ref: const</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;rows: <span>1</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Extra: <span>USING</span> <span>WHERE</span>; <span>USING</span> <span>INDEX</span></div>
</li>
<li>
<div>*************************** <span>2</span>. row ***************************</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;id: <span>1</span></div>
</li>
<li>
<div>&nbsp; select_type: SIMPLE</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; <span>TABLE</span>: p</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;type: eq_ref</div>
</li>
<li>
<div>possible_keys: <span>PRIMARY</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span>KEY</span>: <span>PRIMARY</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; key_len: <span>4</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ref: example_db.t.tag_prd_id</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;rows: <span>1</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Extra: <span>USING</span> <span>INDEX</span></div>
</li>
<li>
<div>*************************** <span>3</span>. row ***************************</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;id: <span>1</span></div>
</li>
<li>
<div>&nbsp; select_type: SIMPLE</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; <span>TABLE</span>: i</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;type: ref</div>
</li>
<li>
<div>possible_keys: itm_prd_id__and__itm_order_timestamp,itm_prd_id__and__itm_order_date</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span>KEY</span>: itm_prd_id__and__itm_order_date</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; key_len: <span>7</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ref: example_db.p.prd_id,const</div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;rows: <span>206494</span></div>
</li>
<li>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Extra: <span>USING</span> <span>WHERE</span>; <span>USING</span> <span>INDEX</span> </div>
</li>
</ol>
</div>
</div>
</div>
<p></p>
<p>This query uses <strong>7 bytes</strong> of <code>`itm_prd_id__and__itm_order_date`</code> index – 4 bytes is <code>`itm_prd_id`</code> and 3  bytes is <code>`itm_order_date`</code> (DATE type uses 3 bytes). Also <em>ref</em> shows two columns used in join.</p>
<div><span><a href="http://www.mysqlperformanceblog.com">PLAIN TEXT</a></span></div>
<div><span>CODE:</span>
<div>
<div>
<ol>
<li>
<div>SHOW STATUS LIKE <span>'Handler_read%'</span>;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
</li>
<li>
<div>+-----------------------+-------+</div>
</li>
<li>
<div>| Variable_name&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| Value |</div>
</li>
<li>
<div>+-----------------------+-------+</div>
</li>
<li>
<div>| Handler_read_first&nbsp; &nbsp; | <span>0</span>&nbsp; &nbsp; &nbsp;| </div>
</li>
<li>
<div>| Handler_read_key&nbsp; &nbsp; &nbsp; | <span>3</span>&nbsp; &nbsp; &nbsp;| </div>
</li>
<li>
<div>| Handler_read_next&nbsp; &nbsp; &nbsp;| <span>4104</span>&nbsp; | </div>
</li>
<li>
<div>| Handler_read_prev&nbsp; &nbsp; &nbsp;| <span>0</span>&nbsp; &nbsp; &nbsp;| </div>
</li>
<li>
<div>| Handler_read_rnd&nbsp; &nbsp; &nbsp; | <span>0</span>&nbsp; &nbsp; &nbsp;| </div>
</li>
<li>
<div>| Handler_read_rnd_next | <span>0</span>&nbsp; &nbsp; &nbsp;| </div>
</li>
<li>
<div>+-----------------------+-------+ </div>
</li>
</ol>
</div>
</div>
</div>
<p></p>
<p>Statistics also look much better.  </p>
<p>But remember - different query will likely need a different solution.</p>
<p>You can find several bug reports regarding this problem (e.g. <a href="http://bugs.mysql.com/bug.php?id=8569" >#8569</a>, <a href="http://bugs.mysql.com/bug.php?id=19548" >#19548</a>). Some replies from MySQL indicate this may be eventually fixed in 6.0 or some future version. Others say "it’s a documented behaviour – deal with it".  But in the real world this is a serious bug, not a feature, and it needs fixing.</p>
    <hr noshade style="margin:0;height:1px" />
    <p>Entry posted by Maciej Dobrzanski |
      <a href="http://www.mysqlperformanceblog.com/2010/05/17/joining-on-range-wrong/#comments">No comment</a></p>
    <p>Add to: <a href="http://del.icio.us/post?url=http://www.mysqlperformanceblog.com/2010/05/17/joining-on-range-wrong/&amp;title=Joining%20on%20range?%20Wrong!" title="Bookmark this post on del.icio.us"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/delicious.png" alt="delicious" /></a> | <a href="http://digg.com/submit?phase=2&amp;url=http://www.mysqlperformanceblog.com/2010/05/17/joining-on-range-wrong/&amp;title=Joining%20on%20range?%20Wrong!" title="Digg this post on Digg.com"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/digg.png" alt="digg" /></a> | <a href="http://reddit.com/submit?url=http://www.mysqlperformanceblog.com/2010/05/17/joining-on-range-wrong/&amp;title=Joining%20on%20range?%20Wrong!" title="Submit this post on reddit.com"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/reddit.png" alt="reddit" /></a> | <a href="http://www.netscape.com/submit/?U=http://www.mysqlperformanceblog.com/2010/05/17/joining-on-range-wrong/&amp;T=Joining%20on%20range?%20Wrong!" title="Vote for this article on Netscape"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/netscape.gif" alt="netscape" /></a> | <a href="http://www.google.com/bookmarks/mark?op=add&amp;bkmk=http://www.mysqlperformanceblog.com/2010/05/17/joining-on-range-wrong/&amp;title=Joining%20on%20range?%20Wrong!" title="Add to Google Bookmarks"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/google.png" alt="Google Bookmarks" /></a></p><br/>PlanetMySQL Voting:
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=24790&vote=1&apivote=1">Vote UP</a> /
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=24790&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
			<wfw:commentRss>http://planetmysql.ru/2010/05/18/joining-on-range-wrong/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>mysql_upgrade and Innodb Tables</title>
		<link>http://www.mysqlperformanceblog.com/2010/05/14/mysql_upgrade-and-innodb-tables/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=mysql_upgrade-and-innodb-tables</link>
		<comments>http://www.mysqlperformanceblog.com/2010/05/14/mysql_upgrade-and-innodb-tables/#comments</comments>
		<pubDate>Sat, 15 May 2010 01:48:20 +0000</pubDate>
		<dc:creator>MySQL Performance Blog</dc:creator>
				<category><![CDATA[bugs]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[mysql]]></category>

		<guid isPermaLink="false">http://www.mysqlperformanceblog.com/?p=2653</guid>
		<description><![CDATA[Upgrading from MySQL 5.0 to   MySQL 5.1 or Percona Server 5.1 you may run into issues with mysql_upgrade  &#8211; it will identify some tables to be upgraded and will attempt to run REPAIR TABLE for them.  This will fail with &#8220;The storage engine for the table doesn&#8217;t support repair&#8221; error message.    This seems to confuse a lot of people and I&#8217;ve seen people doing failsafe upgrade path of dumping and reloading complete database confused by this error message, which of course works, but can take quite a lot of time.
Another solution is to simply run ALTER TABLE tbl ENGINE=INNODB which will rebuild table with new MySQL version and normally will fix issues identified by mysql_upgrade.
You can use  mysqlcheck -A &#8211;check-upgrade  to identify tables which need to be fixed such a way. 
With Oracle intentions to make Innodb default storage engine in next MySQL release I&#8217;m hopeful minor annoyances like this will be fixed.  It should not be that complicated to at least map REPAIR TABLE to null ALTER TABLE which will help with most issues.  
There is nice bug  filed  about it though it only covers documentation aspects.
    
    Entry posted by peter &#124;
      No comment
    Add to:  &#124;  &#124;  &#124;  &#124; ]]></description>
			<content:encoded><![CDATA[<p>Upgrading from MySQL 5.0 to   MySQL 5.1 or Percona Server 5.1 you may run into issues with <strong>mysql_upgrade</strong>  &#8211; it will identify some tables to be upgraded and will attempt to run REPAIR TABLE for them.  This will fail with &#8220;The storage engine for the table doesn&#8217;t support repair&#8221; error message.    This seems to confuse a lot of people and I&#8217;ve seen people doing failsafe upgrade path of dumping and reloading complete database confused by this error message, which of course works, but can take quite a lot of time.</p>
<p>Another solution is to simply run <strong>ALTER TABLE tbl ENGINE=INNODB</strong> which will rebuild table with new MySQL version and normally will fix issues identified by mysql_upgrade.<br />
You can use <strong> mysqlcheck -A &#8211;check-upgrade</strong>  to identify tables which need to be fixed such a way. </p>
<p>With Oracle intentions to make Innodb default storage engine in next MySQL release I&#8217;m hopeful minor annoyances like this will be fixed.  It should not be that complicated to at least map REPAIR TABLE to null ALTER TABLE which will help with most issues.  </p>
<p>There is nice <a href="http://bugs.mysql.com/bug.php?id=44640">bug</a>  filed  about it though it only covers documentation aspects.</p>
    <hr noshade style="margin:0;height:1px" />
    <p>Entry posted by peter |
      <a href="http://www.mysqlperformanceblog.com/2010/05/14/mysql_upgrade-and-innodb-tables/#comments">No comment</a></p>
    <p>Add to: <a href="http://del.icio.us/post?url=http://www.mysqlperformanceblog.com/2010/05/14/mysql_upgrade-and-innodb-tables/&amp;title=mysql_upgrade%20and%20Innodb%20Tables" title="Bookmark this post on del.icio.us"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/delicious.png" alt="delicious" /></a> | <a href="http://digg.com/submit?phase=2&amp;url=http://www.mysqlperformanceblog.com/2010/05/14/mysql_upgrade-and-innodb-tables/&amp;title=mysql_upgrade%20and%20Innodb%20Tables" title="Digg this post on Digg.com"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/digg.png" alt="digg" /></a> | <a href="http://reddit.com/submit?url=http://www.mysqlperformanceblog.com/2010/05/14/mysql_upgrade-and-innodb-tables/&amp;title=mysql_upgrade%20and%20Innodb%20Tables" title="Submit this post on reddit.com"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/reddit.png" alt="reddit" /></a> | <a href="http://www.netscape.com/submit/?U=http://www.mysqlperformanceblog.com/2010/05/14/mysql_upgrade-and-innodb-tables/&amp;T=mysql_upgrade%20and%20Innodb%20Tables" title="Vote for this article on Netscape"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/netscape.gif" alt="netscape" /></a> | <a href="http://www.google.com/bookmarks/mark?op=add&amp;bkmk=http://www.mysqlperformanceblog.com/2010/05/14/mysql_upgrade-and-innodb-tables/&amp;title=mysql_upgrade%20and%20Innodb%20Tables" title="Add to Google Bookmarks"><img src="http://www.mysqlperformanceblog.com/wp-content/themes/boxy-but-gold/images/google.png" alt="Google Bookmarks" /></a></p><br/>PlanetMySQL Voting:
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=24768&vote=1&apivote=1">Vote UP</a> /
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=24768&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
			<wfw:commentRss>http://planetmysql.ru/2010/05/15/mysql_upgrade-and-innodb-tables/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Two quick performance tips with MySQL 5.1 partitions</title>
		<link>http://datacharmer.blogspot.com/2010/05/two-quick-performance-tips-with-mysql.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=two-quick-performance-tips-with-mysql-5-1-partitions</link>
		<comments>http://datacharmer.blogspot.com/2010/05/two-quick-performance-tips-with-mysql.html#comments</comments>
		<pubDate>Thu, 06 May 2010 09:20:00 +0000</pubDate>
		<dc:creator>Giuseppe Maxia</dc:creator>
				<category><![CDATA[5.1]]></category>
		<category><![CDATA[bugs]]></category>
		<category><![CDATA[Feature]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[partitioning]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[tip]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[While I was researching for my partitions tutorial, I came across two hidden problems, which may happen often, but are somehow difficult to detect and even more difficult to fix, unless you know what's going on, and why. I presented both cases during my tutorial, but there were no pictures to convey the mechanics of the problem. Here is the full story.TO_DAYS() prunes two partitions instead of oneIf you are partitioning by date, chances are that you are using TO_DAYS(). And depending on how you have partitioned your table, your queries are as fast as you expect them to be. However, there are cases where your query takes twice as long as it should, and of course this will not make you happy.For example, in a table partitioned by month, when your query searches for values within one specific month, EXPLAIN PARTITIONS tells you that the search involves two partitions (see figure above). This means that, instead of searching through 1,000,000 rows in one partitions, the partitions engine is searching through 2,000,000 rows in two partitions.But why? The reasoning, as reported from the developers, is that This is not a bug, since TO_DAYS() returns NULL for invalid dates, it needs to scan the first partition as well (since that holds all NULL values) for ranges.Bug#49754: Partitioning by RANGE with TO_DAYS always includes first partition when pruningThis makes sense, from a developer's standpoint. From a user's experience, though, it's a bug.Anyway, it doesn't do us any good to rant about it. Our query is still twice as slow as we want it. We need to take action. The workaround is to create an empty partition in first position. If we are creating a new table, it's simple. Just say PARTITION p000 VALUES LESS THAN (0) and all will be well. The partition pruning mechanism will still find two partitions, but since the first one is empty, it won't impact the performance. If you have an existing table already partitioned, like in our example, then you need to perform a different operationNow we have a different first partition, with no records. When we issue the same query, the partition pruning will look at partition p0, but it will skip it because there are no records.Inserting single rows in partitions is slowAlso this bug is sometimes difficult to detect. If you want to test partitions in MySQL 5.1, probably you will take an existing table and convert it to a partitioned one, or you create a new table and load the contents from a dump. Either way, you are unlikely to insert millions of records with single INSERT statements. These single inserts are slower than bulk inserts in the first place, but with partitions there is an additional penalty. Whenever you insert a record, the partitioning engine locks the entire table. When you insert thousands of records, the partitioning engine will lock all partitions before the insert, and unlock them after the insert. If you have 500 partitions, that's 500 locks and 500 unlocks for every statement. Ouch! It's a design problem, and it is not likely to be fixed without turning around the whole architecture of partitions. Also in this case, there is a bug report, Partitioning performance drops drastically with hundreds of partitions, although nobody says that this is a feature.What can you do, then? You have several choices:You can use a bulk insert. Instead of single statements, use INSERT with multiple records, or LOAD DATA INFILE.Explicitly LOCK the table before inserting and UNLOCK it after you finish with all the inserts. This will avoid the overhead, although it won't make your table concurrently accessible until you finish.If you use partitioning only to facilitate heavy queries, consider using a non-partitioned table on the master, and partitioned  ARCHIVE tables on the slaves (see figure below).  As I have said many times in my presentations, always benchmark before using partitions in production. Whether you think that it will boost your performance or that it will slow things down, don't trust your instincts, and test. You may be up for a surprise.]]></description>
			<content:encoded><![CDATA[<table border="0"><tr><td><a href="http://datacharmer.blogspot.com/"><img src="http://lh5.ggpht.com/_gVfZHGgf5LA/S95nd5lfemI/AAAAAAAAA3s/P-n0eh81rG8/partitions.png" alt="partitions" title="MySQL partitions" width="200" border="0" /></a></td><td>While I was researching for my partitions tutorial, I came across two hidden problems, which may happen often, but are somehow difficult to detect and even more difficult to fix, unless you know what's going on, and why. I presented both cases during my tutorial, but there were no pictures to convey the mechanics of the problem. Here is the full story.<br /></td></tr></table><h3>TO_DAYS() prunes two partitions instead of one</h3><br />If you are partitioning by date, chances are that you are using <code>TO_DAYS()</code>. And depending on how you have partitioned your table, your queries are as fast as you expect them to be. However, there are cases where your query takes twice as long as it should, and of course this will not make you happy.<br /><img src="http://lh3.ggpht.com/_gVfZHGgf5LA/S-KAg5VRbaI/AAAAAAAAA3w/YfNPUraukQI/partitions_to_days1.png" width="500" /><br />For example, in a table partitioned by month, when your query searches for values within one specific month, <code>EXPLAIN PARTITIONS</code> tells you that the search involves two partitions (see figure above). This means that, instead of searching through 1,000,000 rows in one partitions, the partitions engine is searching through 2,000,000 rows in two partitions.<br />But why? The reasoning, as reported from the developers, is that <blockquote><i>This is not a bug, since TO_DAYS() returns NULL for invalid dates, it needs to scan the first partition as well (since that holds all NULL values) for ranges.</i></blockquote><br /><a href="http://bugs.mysql.com/bug.php?id=49754">Bug#49754: Partitioning by RANGE with TO_DAYS always includes first partition when pruning</a><br />This makes sense, from a developer's standpoint. From a user's experience, though, <a href="http://datacharmer.blogspot.com/2007/01/what-is-bug.html">it's a bug</a>.<br />Anyway, it doesn't do us any good to rant about it. Our query is still twice as slow as we want it. We need to take action. The workaround is to create an empty partition in first position. If we are creating a new table, it's simple. Just say <pre><code>PARTITION p000 VALUES LESS THAN (0)</code></pre> and all will be well. The partition pruning mechanism will still find two partitions, but since the first one is empty, it won't impact the performance. <br />If you have an existing table already partitioned, like in our example, then you need to perform a different operation<br /><img src="http://lh4.ggpht.com/_gVfZHGgf5LA/S-KAhLC9_qI/AAAAAAAAA30/kZl4R7EnUPo/partitions_to_days2.png" width="500" /><br />Now we have a different first partition, with no records. When we issue the same query, the partition pruning will look at partition p0, but it will skip it because there are no records.<br /><img src="http://lh4.ggpht.com/_gVfZHGgf5LA/S-KAhL-5KFI/AAAAAAAAA34/91eJPrH2CxE/partitions_to_days3.png" width="500" /><br /><h3>Inserting single rows in partitions is slow</h3><br />Also this bug is sometimes difficult to detect. If you want to test partitions in MySQL 5.1, probably you will take an existing table and convert it to a partitioned one, or you create a new table and load the contents from a dump. Either way, you are unlikely to insert millions of records with single INSERT statements. These single inserts are slower than bulk inserts in the first place, but with partitions there is an additional penalty. Whenever you insert a record, the partitioning engine <b>locks the entire table</b>. When you insert thousands of records, the partitioning engine will lock all partitions before the insert, and unlock them after the insert. If you have 500 partitions, that's 500 locks and 500 unlocks for every statement. Ouch! <br />It's a design problem, and it is not likely to be fixed without turning around the whole architecture of partitions. Also in this case, there is a bug report, <a href="http://bugs.mysql.com/bug.php?id=37252">Partitioning performance drops drastically with hundreds of partitions</a>, although nobody says that this is a feature.<br />What can you do, then? You have several choices:<br /><ul><li>You can use a bulk insert. Instead of single statements, use INSERT with multiple records, or LOAD DATA INFILE.</li><li>Explicitly LOCK the table before inserting and UNLOCK it after you finish with all the inserts. This will avoid the overhead, although it won't make your table concurrently accessible until you finish.</li><li>If you use partitioning only to facilitate heavy queries, consider using a non-partitioned table on the master, and partitioned  ARCHIVE tables on the slaves (see figure below).  </li></ul><br /><img src="http://lh6.ggpht.com/_gVfZHGgf5LA/S-KG9KVvF-I/AAAAAAAAA38/8OTrzRkx2Jo/s640/partitions_replication_scheme.png" width="500" /><br />As I have said many times in my presentations, always benchmark before using partitions in production. Whether you think that it will boost your performance or that it will slow things down, don't trust your instincts, and test. You may be up for a surprise.<div><img width="1" height="1" src="https://blogger.googleusercontent.com/tracker/16959946-3808783844661595298?l=datacharmer.blogspot.com" alt="" /></div><br/>PlanetMySQL Voting:
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=24666&vote=1&apivote=1">Vote UP</a> /
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=24666&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
			<wfw:commentRss>http://planetmysql.ru/2010/05/06/two-quick-performance-tips-with-mysql-5-1-partitions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The MySQL documentation is not always right</title>
		<link>http://ronaldbradford.com/blog/the-mysql-documentation-is-not-always-right-2010-04-30/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=the-mysql-documentation-is-not-always-right</link>
		<comments>http://ronaldbradford.com/blog/the-mysql-documentation-is-not-always-right-2010-04-30/#comments</comments>
		<pubDate>Fri, 30 Apr 2010 15:24:45 +0000</pubDate>
		<dc:creator>Ronald Bradford</dc:creator>
				<category><![CDATA[bugs]]></category>
		<category><![CDATA[Databases]]></category>
		<category><![CDATA[documentation]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[Professional]]></category>

		<guid isPermaLink="false">http://ronaldbradford.com/blog/?p=2727</guid>
		<description><![CDATA[Let me premise this post with the statement I think the MySQL documentation is an excellent and highly accurate resource. I think the MySQL docs team do a great job, however like software and people, documentation is not perfect.
As members of the MySQL community you can always contribute to improve the process by reading the documentation and logging any issues as Documentation Bugs.
Some time ago in a discussion with a friend and colleague, we were talking about changes in historical defaults that had been improved finally in MySQL 5.4  The specific discussion was on the new default innodb_buffer_pool_size and we both agreed it increased significantly. One said 1GB, the other said 128MB.  Who was right? Well we both were, and we were both inaccurate depending on versions.
Referencing the 5.4 Manual in InnoDB Startup Options and System Variables the current value for Linux is 128M, but for Windows it&#8217;s 1GB.  
However I was confident I was told in a presentation, perhaps even the keynote the value was 1GB.  Firing up my server and seeing the original version I used of 5.4.0 (which was not available on Windows) we find that the default for Linux was 1GB at some time, i.e. the first release.

mysql&#62; show global variables like 'innodb_buffer_pool_size';
+-------------------------+------------+
&#124; Variable_name           &#124; Value      &#124;
+-------------------------+------------+
&#124; innodb_buffer_pool_size &#124; 1073741824 &#124;
+-------------------------+------------+
1 row in set (0.00 sec)

mysql&#62; select 1073741824/1024/1024 as MB;
+---------------+
&#124; MB            &#124;
+---------------+
&#124; 1024.00000000 &#124;
+---------------+
1 row in set (0.00 sec)

mysql&#62; show global variables like 'version%';
+-------------------------+------------------------------+
&#124; Variable_name           &#124; Value                        &#124;
+-------------------------+------------------------------+
&#124; version                 &#124; 5.4.0-beta                   &#124;
&#124; version_comment         &#124; MySQL Community Server (GPL) &#124;
&#124; version_compile_machine &#124; x86_64                       &#124;
&#124; version_compile_os      &#124; unknown-linux-gnu            &#124;
+-------------------------+------------------------------+
4 rows in set (0.00 sec)

I&#8217;m not trying to nit pick here, I&#8217;m highlighting that MySQL is a evolving product with many different versions and architectures. It&#8217;s our job of the MySQL community to help make the documentation the best for all readers.  In this above case I&#8217;ve not logged the issue, because 5.4 is a defunct product, however if you want an example of how I identified a problem, provided a test case, and saw that my contribution was reviewed, verified and implemented check out Bug #51739 &#8211;core-file is not default TRUE (incorrect docs).
In conclusion, always read the documentation but pay special attention to the current version that matches the documentation, and the version you are actually running.  Defaults change between versions, e.g. innodb_thread_concurrency is a complex example, and I&#8217;ve been caught with a large enterprise client with assuming the default of a Connector/J options as true, when it was in 5.0.6, but in 5.0.5 the version the client was running it was false.
An old saying, &#8220;trust by verify&#8221; is a good motto to consider.]]></description>
			<content:encoded><![CDATA[<p>Let me premise this post with the statement I think the MySQL documentation is an excellent and highly accurate resource. I think the MySQL docs team do a great job, however like software and people, documentation is not perfect.</p>
<p>As members of the MySQL community you can always contribute to improve the process by reading the documentation and logging any issues as <a href="http://bugs.mysql.com/">Documentation Bugs</a>.</p>
<p>Some time ago in a discussion with a friend and colleague, we were talking about changes in historical defaults that had been improved finally in MySQL 5.4  The specific discussion was on the new default innodb_buffer_pool_size and we both agreed it increased significantly. One said 1GB, the other said 128MB.  Who was right? Well we both were, and we were both inaccurate depending on versions.</p>
<p>Referencing the 5.4 Manual in <a href="http://dev.mysql.com/doc/refman/5.4/en/innodb-parameters.html#sysvar_innodb_buffer_pool_size">InnoDB Startup Options and System Variables</a> the current value for Linux is 128M, but for Windows it&#8217;s 1GB.  </p>
<p>However I was confident I was told in a presentation, perhaps even the keynote the value was 1GB.  Firing up my server and seeing the original version I used of 5.4.0 (which was not available on Windows) we find that the default for Linux was 1GB at some time, i.e. the first release.</p>
<pre>
mysql> show global variables like 'innodb_buffer_pool_size';
+-------------------------+------------+
| Variable_name           | Value      |
+-------------------------+------------+
| innodb_buffer_pool_size | 1073741824 |
+-------------------------+------------+
1 row in set (0.00 sec)

mysql> select 1073741824/1024/1024 as MB;
+---------------+
| MB            |
+---------------+
| 1024.00000000 |
+---------------+
1 row in set (0.00 sec)

mysql> show global variables like 'version%';
+-------------------------+------------------------------+
| Variable_name           | Value                        |
+-------------------------+------------------------------+
| version                 | 5.4.0-beta                   |
| version_comment         | MySQL Community Server (GPL) |
| version_compile_machine | x86_64                       |
| version_compile_os      | unknown-linux-gnu            |
+-------------------------+------------------------------+
4 rows in set (0.00 sec)
</pre>
<p>I&#8217;m not trying to nit pick here, I&#8217;m highlighting that MySQL is a evolving product with many different versions and architectures. It&#8217;s our job of the MySQL community to help make the documentation the best for all readers.  In this above case I&#8217;ve not logged the issue, because 5.4 is a defunct product, however if you want an example of how I identified a problem, provided a test case, and saw that my contribution was reviewed, verified and implemented check out <a href="http://bugs.mysql.com/bug.php?id=51739">Bug #51739</a> &#8211;core-file is not default TRUE (incorrect docs).</p>
<p>In conclusion, always read the documentation but pay special attention to the current version that matches the documentation, and the version you are actually running.  Defaults change between versions, e.g. innodb_thread_concurrency is a complex example, and I&#8217;ve been caught with a large enterprise client with assuming the default of a Connector/J options as true, when it was in 5.0.6, but in 5.0.5 the version the client was running it was false.</p>
<p>An old saying, &#8220;trust by verify&#8221; is a good motto to consider.</p><br/>PlanetMySQL Voting:
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=24587&vote=1&apivote=1">Vote UP</a> /
	 <a href="http://planet.mysql.com/entry/vote/?entry_id=24587&vote=-1&apivote=1">Vote DOWN</a>]]></content:encoded>
			<wfw:commentRss>http://planetmysql.ru/2010/04/30/the-mysql-documentation-is-not-always-right/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

