Archive for the ‘MySQL’ Category.

Innodb handler_delete and handler_update status

I woke up this morning to a nice surprise. Peter Zaitsev from MySQL Performance Blog mentioned my fix for a crash in MySQL. Thanks Peter! That reminded me that I need to write this post about another fix.

A storage engine is called a “handler” in MySQL internals. Handler events are per row at the storage engine layer. For example an insert query that inserts 3 rows will cause one Com_insert increment and 3 handler_write increments. The way these statistics are implemented the storage engine is responsible for incrementing statistics itself. Innodb is missing function calls to increment the handler_delete and handler_update status variables. I’ve filed bug #35537 with MySQL that contains a patch adding in the missing statistic_increment calls.

It seems strange that storage engines are responsible for incrementing global statistics. Brian Aker tells me that this is because the handler interface is a direct pass through to the storage engine. I’m guessing that it was easier to do the statistics increment in the storage engine rather than chase down all the handler calls in the server and change them to functions that call the handler and increment statistics.

ref or null join type crash and patch

While working with a client we found a query that can crash MySQL. It has to do with ref_or_null outer joins where the table contains a null value. MySQL fails to tell the storage engine to initialize the index before reading from it. It crashes when used with MyISAM tables but it could result in an incorrect key file for table error with other storage engines. You can find the test case and patch in bug #34945. I’ve waited to blog about it until MySQL approved my patch. As of this writing my patch has been approved and committed for release in 5.0.60.

MySQL Enterprise Registration Emails

[Updated: 2008-03-05 Jeremy writes to tell that I left out the provenscaling.com contact form submission on the 21st and the blank email with a pdf on the 27th]
[Updated: 2008-03-26 Rob Young from MySQL AB sent me an email on the 17th saying, "We are currently working to implement PayPal to cut down on the email interactions we have in place around our manual fulfillment process. Testing is going well and we should have this implemented in the next few weeks."]

Recently Proven Scaling decided to purchase a MySQL Enterprise subscription. We will be evaluating Enterprise to see if it’s knowledge base is valuable to our customers as a supplement to Proven Scaling’s consulting services.

The subscription process seems remarkably manual on MySQL’s part. It involves them sending us five emails (not counting the mysql.com registration), all of which are from different senders. Some are from people others are automated. Here is a breakdown in chronological order:

Feb 20 12:25PM
shop@mysql.com – Online Order Confirmation

Feb 20 8:20PM
support-feedback@mysql.com – Welcome to MySQL Enterprise

Feb 20 8:49PM
enterprise-feedback@mysql.com – MySQL Enterprise: User account password changed

Feb 21 8:51AM
provenscaling.com contact form submission with creepy upsell – “Please provide my contact information to the person(s) responsible so I can assist with future orders. There are packaging options that are not available with on line purchases….”

Feb 22 10:39AM
ar@mysql.com – Your – MySQL – Invoice

Feb 22 8:41PM
rwolfejones@mysql.com – Credit Card receipt from MySql

Feb 27th 4:25pm
julie@mysql.com – MySQL receipt for Invoice

This seems a bit excessive. It also raises several questions about the registration process. Why does it take 8 hours for the order to be filled? Does someone have to manually approve the order? The second email is “Welcome to MySQL Enterprise” from support-feedback while the third email is from enterprise-feedback with notification of an account change. This email is because Enterprise generates and emails out a password on the first login.

What’s the difference between support-feedback and enterprise-feedback? A few days later after the account is setup accounts receivable sends us an invoice. Then ten hours later a credit card receipt. Why isn’t MySQL using automated credit card processing? The credit card receipt also looks like it was copied and pasted into the email.

I hope that we’re some kind of special case and that not all of the MySQL Enterprise customers have to go through the same process. I look forward to hearing comments from MySQL on this process.

innodb_log_file_size

In Issue 3 of MySQL Magazine Peter Zaitsev writes, ” I would set [innodb_log_file_size] to 256 megabytes for small sized boxes up to 5% of the total memory on the big boxes.” I tend to set my log file size to around 128M. Depending on your system setting the log file size around a few hundred megs should be the right balance between preventing checkpointing and still having fast recovery times.

The manual states, “Sensible values range from 1MB to 1/N-th of the size of the buffer pool, where N is the number of log files in the group.” The sample my.cnf files that come with MySQL state to set the innodb_log_file_size to 25% of the innodb buffer pool size. I think this mistakenly advises people to set their log files much larger than they need to be causing unnecessarily long recovery times.

Crash recovery time is an important feature of InnoDB. In the event of a server crash large log files could mean waiting hours for InnoDB to recover data while your site is down. A log file size of a few hundred megs will enable InnoDB to perform recovery in a few minutes on reasonable hardware.

MySQL 5.0.48 proof that the MySQL release cycle is completely broken.

When I received and email almost two years ago about a announcement that MySQL was going to release and enterprise product I was very excited. I was looking forward to a redhat style model of vetting releases in the community then offering a proven stable version to paying customers. I saw it as a great way to for MySQL to generate revenue as well as eliminate the need for people to stay a few releases back from the head and guess when to upgrade.

The release shocked me. What was originally emailed to me and the final plan were two very different things. It was a plan to hand paying customers bleeding edge code that had been tested only by MySQL’s QA team. It seems MySQL has forgotten the years of testing by millions of community members that has given MySQL the stability we have grown to trust. I predicted the instability of MySQL enterprise back in October ’06 by saying that releasing patches in enterprise that hadn’t been tested by the community would result in instable releases being delivered to paying customers. These should always be tested by the community first.

MySQL 5.0.48 enterprise is rock solid proof that the release cycle MySQL chose to implement is completely broken. It was released then pulled due to some very basic sorting functionality being broken. I hope they’re working to change the release cycle to more of a redhat model both to put trust back in the community for testing and to give enterprise customers the stable version they’re paying for.

MySQL bugbot and status changes

MySQL Bug Tracker

I realize that MySQL is in a transitional phase making it’s development more transparent to the community so this entry isn’t really a rant but something that will hopefully ease the transition. The public MySQL bug tracker doesn’t seem to have any way to view the history of status changes to a bug. Things like changing a bug from open to inactive. I assume there will always be things that should be hidden from bugs like links to sites internal to MySQL AB but things like simple status changes should be visible to the public.

For example on this Bug #20358 Heikki posted a comment that he was moving the bug from open to unable to repeat. Did he actually make the change? I have no idea because I can’t access the status log. Was it ever changed from unable to repeat back to open or verified? Again I have no idea…

Another “feature” of the bug system that I find questionable is the bug bot. The script trolls the bug database looking for bugs that need an automatic state change. In my opinion it should never find anything. I’ve noticed that it changes state on bugs that haven’t been commented on for a month to an inactive status. I have one example of a Bug #26489 that I think is fairly critical being marked in an inactive state not because the bug wasn’t still occurring, but because we had no new useful information to post at the time. Just because no new information has been found doesn’t necessarily mean that a bug should be taken off of people’s radar. If humans want to mark them as inactive it’s fine but a script making that decision worries me.

I do appreciate the things MySQL has done recently to make the development process more accessible for those of us without MySQL AB on our name tag. #mysql-dev on freenode is well populated with very intelligent minds. It’s nice to have them readily accessible to bounce ideas off of.

Keep the momentum on this transition going and please don’t mark my blog post as inactive :)

tests and mysql-test sub directories

Recently I’ve been making more modifications to the MySQL source. Part of making modifications is testing them. For years I’ve been curious why there are both tests and mysql-test sub directories in the source tarballs. Arjen Lentz tells me that tests is old and that mysql-test is the current testing framework. This makes sense since the manual testing pages are all about mysql-test.

Are there plans to clean up the remnants of the old testing framework? What else is there in the tarball that’s outdated and needs to be removed?

set sql_log_slow=0 to control the slow query log.

Currently there isn’t a good method to control sending individual queries to the slow query log. Typically this isn’t an issue. It becomes an issue when using very short query times and importing sql files. The slow query log doesn’t have a limit on the length of queries it will write to a log. If you’re importing a sql file with inserts that get sent to the log mysql will dump the entire insert query. This can cause the slow query log to grow to gigs in size in a very short time.

I’ve patched mysql 5.0.41 to add a session variable called sql_log_slow (think sql_log_bin) that when set to 0 will prevent queries from that session from being sent to the slow query log. The patch also updates mysqlbinlog to set that variable in it’s output. One issue with this patch is that the executable comment version in mysqldump is set to 5.0.41 but it will error when imported on any 5.0.41 server that doesn’t have this patch. If it’s merged the version in the comment will need to be bumped.

sql_log_slow is also handy for controlling noise in the slow query log. It can be used to prune down queries that are known to be slow so only true problem queries are logged.

Here is the patch. I’ll post this on internals soon.

How alter table locks tables and handles transactions

I’ve talked to several people that have questions about how alter table works under the hood. They want to know how it handles locking tables why they can sometimes use a table during alter table and other times they can’t. Also why it’s so slow :)

First let’s look at the basic process alter table typically goes through.

  1. If a transaction is open on this thread, commit it.
  2. Acquire a read lock for the table.
  3. Make a temporary table with new structure
  4. Copy the old table to the temporary table row by row changing the structure of the rows on the fly.
  5. Rename the original table out of the way
  6. Rename the temporary table to the original table name.
  7. Drop the original table.
  8. Release the read lock.

The slowest part of the process is copying rows from the original table to the temporary table. For large tables this can take minutes to hours. There are a few optimizations built into this process. If the alter table query only renames the table then mysql doesn’t bother copying all the rows to a temporary table and just renames it. For most other things such as renaming columns, adding/dropping indexes, making columns nullable, changing the column default all require copying the entire table.

During the first 4 steps MySQL allows other clients to read from the table being altered. When alter table is done copying rows to the temporary table and is ready to rename it it changes the table lock. MySQL instructs all other clients currently reading from the table to close the table when they are done. While alter table is waiting for existing clients to finish reading from the table it prevents other clients from starting to read from the table. During this time selects will be blocked on “Waiting for tables”. When the last client is done reading from the table alter table continues renaming the table.

This has some interesting implications for transactions and repeatable reads. Internally innodb keeps track of when rows are created. When a transaction is started it can only see rows that were created before the transaction was started (using repeatable read). Any rows created after are not returned. Since alter table copied rows from the old table to the new table rows get a new version number as they are inserted into the temporary table. An alter table can cause a dirty read in transactions that span an alter table. Transactions started before alter table will get no rows back from the table after alter table is finished. If your application is sensitive to dirty reads or getting no rows back from a table (really dirty read :) ) then don’t run alter table on a server when clients are running.

Here is an example.

mysql a> alter ignore table t add unique index (t);

mysql b> begin;
Query OK, 0 rows affected (0.00 sec)

#This select is from the original table while alter table is copying rows
mysql> select * from t limit 10;
+——+
| t |
+——+
| 10 |
| 10 |
| 10 |
| 10 |
| 10 |
| 10 |
| 10 |
| 10 |
| 10 |
| 10 |
+——+
10 rows in set (0.00 sec)
#alter table finishes
#Rows created in the temporary table before we issued begin
mysql> select * from t limit 10;
+———-+
| t |
+———-+
| 10 |
| 6920631 |
| 27998430 |
| 41865298 |
| 49403894 |
+———-+
5 rows in set (0.00 sec)

#Get a new view of the table
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

#This select returns all the rows.
mysql> select * from t limit 10;
+———–+
| t |
+———–+
| 10 |
| 6920631 |
| 27998430 |
| 41865298 |
| 49403894 |
| 50522347 |
| 84441015 |
| 109401269 |
| 110202688 |
| 123590778 |
+———–+
10 rows in set (0.00 sec)

If we start the transaction before alter table the select after alter table has finished will return no rows even though there are rows in the table. I’m not sure why innodb allows rename of a table when transactions still have a few of that table open. It seems like a bug to me.

[Updated 2007-05-14 Bug #28432

select sql_cache_ttl

I’ve been thinking about the query cache since last years user conference. One of the features of the query cache is that it’s completely transparent to client. It achieves this by clearing cache entries for tables as soon as the tables are updated. This makes the cache inefficient for tables that are constantly updating.

While thinking about this also thought about slaves and non critical reads. In a replication setup reads that are sent to slaves expect to get data back that might not be the most current. In this situation it doesn’t make sense to expire the cache for every single update because queries running against the slave already know that they are going to get back slightly old data. I thought, “Why not add a time to live to cache entries instead of clearing them for every update?” So, I did.

I added an option to select called sql_cache_ttl. This option instructs the query cache hold the entry in the cache for query_cache_ttl seconds. query_cache_ttl can be set globally or per thread. Initial testing shows that it does help eliminate the cache thrashing that occurs during updates on tables that have many large cache entries.

I have a few more optimizations that I want to add to the patch before sending it into MySQL. The patch below is a preview that works with 5.0.37.

The patch.