<?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>Eric Bergen</title>
	<atom:link href="http://ebergen.net/wordpress/feed/" rel="self" type="application/rss+xml" />
	<link>http://ebergen.net/wordpress</link>
	<description>You will probably want some waders, a pickaxe, and one of those hats with a light on it before you go in here.</description>
	<lastBuildDate>Wed, 09 May 2012 03:47:25 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Percona Live MySQL Conference</title>
		<link>http://ebergen.net/wordpress/2012/05/08/percona-live-mysql-conference/</link>
		<comments>http://ebergen.net/wordpress/2012/05/08/percona-live-mysql-conference/#comments</comments>
		<pubDate>Wed, 09 May 2012 03:47:25 +0000</pubDate>
		<dc:creator>Eric Bergen</dc:creator>
				<category><![CDATA[Geek]]></category>
		<category><![CDATA[MariaDB]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://ebergen.net/wordpress/?p=568</guid>
		<description><![CDATA[These are things I like that I consider differences from last years conference. There are plenty of other things I like that don&#8217;t need to be listed here. The overall tone and feel of the conference was much less marking and much more technical Refreshingly honest keynotes. There was a lot of coming clean about [...]]]></description>
			<content:encoded><![CDATA[<p>These are things I like that I consider differences from last years conference. There are plenty of other things I like that don&#8217;t need to be listed here.</p>
<ul>
<li>The overall tone and feel of the conference was much less marking and much more technical</li>
<li>Refreshingly honest keynotes. There was a lot of coming clean about the history of MySQL and the conference.</li>
<li>Percona is very technical but it is also a business. They are very good about bringing out the technical and not being pushy about the business.</li>
<li>No ice cream social. A thousand people shaking sticky hands with each other is never a good idea.</li>
<li>percona.tv</li>
<li>The conference was busy but never crowded</li>
</ul>
<p>Now for the dislike:</p>
<ul>
<li>Only one song before every session.</li>
<li>The chairs. Damn the chairs.</li>
<li>Wifi failed more often than it worked. Most of the time I was tethered to my phone.</li>
<li>smartcity doesn&#8217;t work with ssh port forwarded dns/socks poor man vpn.</li>
</ul>
<p>Just some things to note and tips for next year</p>
<ul>
<li>Classic rock bump in music for the keynotes didn&#8217;t really fit.</li>
<li>Percona folks are usually really good about having information in the slides. So if you have to choose between skipping a Percona talk for some other talk skip the Percona one because you can go back and read the slides.</li>
<li>There is a secret clean bathroom that usually stays clean until the last day. I&#8217;m not saying where this is.</li>
<li>Monty&#8217;s BoF always has black vodka.</li>
<li>The black vodka is dangerous so be careful.</li>
</ul>
<p>I was tempted to skip the conference this year because last year was so depressing. I decided to give Percona a chance at hosting it and I&#8217;m glad I did. I look forward to attending and maybe presenting next year.</p>
]]></content:encoded>
			<wfw:commentRss>http://ebergen.net/wordpress/2012/05/08/percona-live-mysql-conference/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>MySQL 5.0 can deadlock when flush logs, show processlist, and a slave connection happen at the same time</title>
		<link>http://ebergen.net/wordpress/2012/05/08/mysql-5-0-can-deadlock-when-flush-logs-show-processlist-and-a-slave-connection-happen-at-the-same-time/</link>
		<comments>http://ebergen.net/wordpress/2012/05/08/mysql-5-0-can-deadlock-when-flush-logs-show-processlist-and-a-slave-connection-happen-at-the-same-time/#comments</comments>
		<pubDate>Wed, 09 May 2012 03:32:46 +0000</pubDate>
		<dc:creator>Eric Bergen</dc:creator>
				<category><![CDATA[Geek]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://ebergen.net/wordpress/?p=545</guid>
		<description><![CDATA[[ Note: I haven't fully investigated this problem but the post has been hanging around in my queue for months. Rather than have it rot there I am publishing what I know in hopes that it helps someone else. ] There are a lot of different kinds of locks in MySQL. Some of these locks [...]]]></description>
			<content:encoded><![CDATA[<p>[ Note: I haven't fully investigated this problem but the post has been hanging around in my queue for months. Rather than have it rot there I am publishing what I know in hopes that it helps someone else. ]</p>
<p>There are a lot of different kinds of locks in MySQL. Some of these locks are exposed to users such as intention locks, table locks, and row locks. There are other locks that aren&#8217;t exposed as well. These are mutexes that MySQL uses internally to protect resources from being modified by more than one thread at a time. These locks are numerous and complicated. When these locks deadlock mysql can stop dead in it&#8217;s tracks. The last deadlock I found happens when flush logs, show processlist, and a slave reconnect happen at the same time. I don&#8217;t have a core from the mysqld process, only stack traces. The breakdowns of stack traces are locks that I&#8217;m pretty sure the threads own and ones that they may be stuck trying on. I am working on gathering more data and making a repeatable test case. I will update this post or link to a new one when I do.</p>
<p>Flush logs is responsible for rotating and deleting old logs. It rotates every log under the sun including the relay and binary logs. I created a patch a few years ago to allow users to specify which log to rotate that is now included in MySQL 5.5. Flush logs has a safe guard built in when flushing binary logs to a make sure that it isn&#8217;t going to delete  a log that a binlog dump thread hasn&#8217;t sent to a slave yet. For this safe guard to work it has to loop through every client thread in MySQL and check if it is a dump thread. If it is then it has to check which log it is on to make sure the log isn&#8217;t in use.</p>
<p>Each log file has a few mutexes associated with it. The flush logs thread owns LOCK_log and LOCK_index from the binary log. bl in this case refers to the binary log object. linfo is the log info object which is part of the thread for some reason.</p>
<p>Owns locks<br />
bl-&gt;LOCK_log<br />
bl-&gt;LOCK_index<br />
Tries to lock<br />
LOCK_thread_count<br />
linfo-&gt;lock</p>
<blockquote><p>Thread 11 (Thread 0x458e6940 (LWP 27068)):<br />
#0 0x000000377560d4c4 in __lll_lock_wait () from /lib64/libpthread.so.0<br />
#1 0x0000003775608e50 in _L_lock_1233 () from /lib64/libpthread.so.0<br />
#2 0x0000003775608dd3 in pthread_mutex_lock () from /lib64/libpthread.so.0<br />
#3 0x000000000066f8f6 in log_in_use(char const*) () &lt;&#8211; LOCK_thread_count<br />
#4 0x00000000005f88d0 in MYSQL_LOG::purge_logs_before_date(long) () &lt;&#8211; gets bl-&gt;LOCK_index<br />
#5 0x00000000005fa09c in MYSQL_LOG::rotate_and_purge(unsigned int) () &lt;&#8211; gets bl-&gt;LOCK_log<br />
#6 0x000000000058715d in reload_acl_and_cache(THD*, unsigned long, TABLE_LIST*, bool*) ()<br />
#7 0x000000000058c3d3 in mysql_execute_command(THD*) ()<br />
#8 0x00000000005915f1 in mysql_parse(THD*, char const*, unsigned int, char const**) ()<br />
#9 0x000000000059279e in dispatch_command(enum_server_command, THD*, char*, unsigned int) ()<br />
#10 0x000000000059374b in handle_one_connection ()<br />
#11 0x000000377560673d in start_thread () from /lib64/libpthread.so.0<br />
#12 0x0000003774ad3d1d in clone () from /lib64/libc.so.6</p></blockquote>
<p>&nbsp;</p>
<p>What I mean by slave connection is what happens on the master when a slave connects. The master will spawn a thread that is responsible for killing any other threads with the same server id and then sending binlogs to the slave. This is why when two slaves are configured with the same server id they will disconnect each other. Slaves will try to reconnect a minute later by default 60 seconds. So every minute each slave will get to download binlogs for a minute. The important part for this deadlock is that when a new slave connects the thread spawned by the master will walk through every thread connected to MySQL looking for an old thread with the same slave server id and try to kill it.</p>
<p>This is the stack from the new slave thread trying to kill the old one</p>
<p>Owns<br />
tmp-&gt;LOCK_delete from thread 16.<br />
16-&gt;mysys_var-&gt;mutex<br />
Tries<br />
bl-&gt;LOCK_log from thread 16-&gt;mysys_var-&gt;current_mutex</p>
<blockquote><p>Thread 17 (Thread 0&#215;45968940 (LWP 27194)):<br />
#0 0x000000377560d4c4 in __lll_lock_wait () from /lib64/libpthread.so.0<br />
#1 0x0000003775608e1a in _L_lock_1034 () from /lib64/libpthread.so.0<br />
#2 0x0000003775608cdc in pthread_mutex_lock () from /lib64/libpthread.so.0<br />
#3 0x000000000056863f in THD::awake(THD::killed_state) ()<br />
#4 0x000000000066f8a3 in kill_zombie_dump_threads(unsigned int) ()<br />
#5 0x0000000000592a6e in dispatch_command(enum_server_command, THD*, char*, unsigned int) ()<br />
#6 0x000000000059374b in handle_one_connection ()<br />
#7 0x000000377560673d in start_thread () from /lib64/libpthread.so.0<br />
#8 0x0000003774ad3d1d in clone () from /lib64/libc.so.6</p></blockquote>
<p>The current_mutex deserves a bit of explanation here. In MySQL when a thread wants to wait on a condition it copies the mutex and condition into mysys_var-&gt;current_mutex and mysys_var-&gt;current_cond. The mysys_var-&gt;mutex is the mutex that protects the mysys_var struct. This way if any other thread needs to kill it it knows the condition and mutex it is waiting on so it can be woken up. This is an easy mechanism to kill threads but it makes it more difficult to figure out what a thread is locking on. In this case I think current_mutex is bl-&gt;LOCK_log from thread 16 which is coming up.</p>
<blockquote><p>Thread 16 (Thread 0x45aee940 (LWP 3819)):<br />
#0 0x000000377560d4c4 in __lll_lock_wait () from /lib64/libpthread.so.0<br />
#1 0x00000037756101b1 in _L_cond_lock_989 () from /lib64/libpthread.so.0<br />
#2 0x000000377561007f in __pthread_mutex_cond_lock () from /lib64/libpthread.so.0<br />
#3 0x000000377560af84 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0<br />
#4 0x00000000005f6637 in MYSQL_LOG::wait_for_update(THD*, bool) ()<br />
#5 0x000000000067133a in mysql_binlog_send(THD*, char*, unsigned long long, unsigned short) ()<br />
#6 0&#215;0000000000592639 in dispatch_command(enum_server_command, THD*, char*, unsigned int) ()<br />
#7 0x000000000059374b in handle_one_connection ()<br />
#8 0x000000377560673d in start_thread () from /lib64/libpthread.so.0<br />
#9 0x0000003774ad3d1d in clone () from /lib64/libc.so.6</p></blockquote>
<p>Thread 16 is the old master thread waiting to dump a binglog. It should be killed by the new thread but isn&#8217;t.</p>
<p>Show processlist also loop through the list of client threads to get their id, hostname, user, state and query that they may be running. The common theme between all of these threads is that they all need to loop through the list of client threads in order to do their jobs. When looping through this list they must grab the LOCK_thread_count mutex. Owning this mutex means that no other thread will be able to change the list of client threads making it safe to loop through.</p>
<p>Owns<br />
LOCK_thread_count<br />
Tries<br />
mysys_var-&gt;mutex probably from thread 16/17</p>
<blockquote><p>Thread 3 (Thread 0x45a6c940 (LWP 13482)):<br />
#0 0x000000377560d4c4 in __lll_lock_wait () from /lib64/libpthread.so.0<br />
#1 0x0000003775608e50 in _L_lock_1233 () from /lib64/libpthread.so.0<br />
#2 0x0000003775608dd3 in pthread_mutex_lock () from /lib64/libpthread.so.0<br />
#3 0x000000000065879a in mysqld_list_processes(THD*, char const*, bool) ()<br />
#4 0x000000000058ecf6 in mysql_execute_command(THD*) ()<br />
#5 0x00000000005915f1 in mysql_parse(THD*, char const*, unsigned int, char const**) ()<br />
#6 0x000000000059279e in dispatch_command(enum_server_command, THD*, char*, unsigned int) ()<br />
#7 0x000000000059374b in handle_one_connection ()<br />
#8 0x000000377560673d in start_thread () from /lib64/libpthread.so.0<br />
#9 0x0000003774ad3d1d in clone () from /lib64/libc.so.6</p></blockquote>
<p>I haven&#8217;t quite solved this one yet but I hope the breakdown may help someone else with the same issue. I worked around this one by disabling a script that runs show processlist and records this output. This makes it far less likely that all three conditions will be met to produce the deadlock. Other options are changing the log rotation script to only flush the logs it needs or figure out why the slave reconnect happens. I have a few theories on long running log rotations causing this but I haven&#8217;t been able to reproduce them.</p>
]]></content:encoded>
			<wfw:commentRss>http://ebergen.net/wordpress/2012/05/08/mysql-5-0-can-deadlock-when-flush-logs-show-processlist-and-a-slave-connection-happen-at-the-same-time/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Changing caps lock to control with .Xmodmap</title>
		<link>http://ebergen.net/wordpress/2011/12/28/changing-caps-lock-to-control-with-xmodmap/</link>
		<comments>http://ebergen.net/wordpress/2011/12/28/changing-caps-lock-to-control-with-xmodmap/#comments</comments>
		<pubDate>Thu, 29 Dec 2011 03:08:56 +0000</pubDate>
		<dc:creator>Eric Bergen</dc:creator>
				<category><![CDATA[Geek]]></category>
		<category><![CDATA[QFTPWE]]></category>

		<guid isPermaLink="false">http://ebergen.net/wordpress/?p=538</guid>
		<description><![CDATA[There are a lot of instructions around the internet for various ways to swap the caps lock and control keys. I usually want to just get rid of caps lock all together. There are a few ways but one of the easiest is to use a ~/.Xmodmap file. To use this create .Xmodmap in your [...]]]></description>
			<content:encoded><![CDATA[<p>There are a lot of instructions around the internet for various ways to swap the caps lock and control keys. I usually want to just get rid of caps lock all together. There are a few ways but one of the easiest is to use a ~/.Xmodmap file. To use this create .Xmodmap in your home directory and reboot or restart X windows. This example .Xmodmap gets rid of caps lock and makes both the caps lock key and control keys use the control functionality. As the comment says uncomment the rest of the lines to swap caps lock and control instead of just getting rid of control.</p>
<blockquote><p>!<br />
! Make caps lock control. Uncomment the commented out lines below to swap<br />
! caps lock and control.<br />
!<br />
remove Lock = Caps_Lock<br />
!remove Control = Control_L<br />
!keysym Control_L = Caps_Lock<br />
keysym Caps_Lock = Control_L<br />
!add Lock = Caps_Lock<br />
add Control = Control_L</p></blockquote>
<p>[Update: 2011-12-29 I still use my <a title="Keyboard of Champions" href="http://ebergen.net/wordpress/2005/01/15/keyboard-of-champions/">HHK</a> at work but switched back to a regular style keyboard at home.]</p>
]]></content:encoded>
			<wfw:commentRss>http://ebergen.net/wordpress/2011/12/28/changing-caps-lock-to-control-with-xmodmap/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Is group_concat_max_len in bytes or characters?</title>
		<link>http://ebergen.net/wordpress/2011/09/05/is-group_concat_max_len-in-bytes-or-characters/</link>
		<comments>http://ebergen.net/wordpress/2011/09/05/is-group_concat_max_len-in-bytes-or-characters/#comments</comments>
		<pubDate>Mon, 05 Sep 2011 18:04:19 +0000</pubDate>
		<dc:creator>Eric Bergen</dc:creator>
				<category><![CDATA[Geek]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://ebergen.net/wordpress/?p=517</guid>
		<description><![CDATA[The manual says bytes but sometimes it is measured in characters. It seems like group_concat_max_len is in bytes when being passed through a temporary table and in characters otherwise. This works fine when using latin1 but when converting to utf8 mysql must reserve 3 bytes per character when setting types in a temporary table. This [...]]]></description>
			<content:encoded><![CDATA[<p>The manual says bytes but sometimes it is measured in characters. It seems like group_concat_max_len is in bytes when being passed through a temporary table and in characters otherwise. This works fine when using latin1 but when converting to utf8 mysql must reserve 3 bytes per character when setting types in a temporary table. This is yet another reason to dislike group_concat..</p>
<blockquote><p>mysql&gt; create table group_concat_bug (str1 varchar(255), str2 varchar(255), str3 varchar(255)) charset=utf8 engine=innodb;<br />
Query OK, 0 rows affected (0.02 sec)</p>
<p>mysql&gt; show create table group_concat_bug;<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
| Table            | Create Table                                                                                                                                                                      |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
| group_concat_bug | CREATE TABLE `group_concat_bug` (<br />
`str1` varchar(255) default NULL,<br />
`str2` varchar(255) default NULL,<br />
`str3` varchar(255) default NULL<br />
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
1 row in set (0.00 sec)</p>
<p>mysql&gt; insert into group_concat_bug set str1=&#8217;a';<br />
Query OK, 1 row affected (0.01 sec)</p>
<p>mysql&gt; insert into group_concat_bug set str1=&#8217;a';<br />
Query OK, 1 row affected (0.00 sec)</p>
<p>mysql&gt; insert into group_concat_bug set str1=&#8217;a';<br />
Query OK, 1 row affected (0.00 sec)</p>
<p>mysql&gt; insert into group_concat_bug set str1=&#8217;a';<br />
Query OK, 1 row affected (0.00 sec)</p>
<p>mysql&gt; insert into group_concat_bug set str1=&#8217;a';<br />
Query OK, 1 row affected (0.00 sec)</p>
<p>mysql&gt; insert into group_concat_bug set str1=&#8217;a';<br />
Query OK, 1 row affected (0.00 sec)</p>
<p>mysql&gt; select group_concat(str1) from group_concat_bug group by str2;<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
| group_concat(str1) |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
| a,a,a,a,a,a        |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
1 row in set (0.00 sec)</p>
<p>mysql&gt; set group_concat_max_len=5;<br />
Query OK, 0 rows affected (0.00 sec)</p>
<p>mysql&gt; explain select group_concat(str1) from group_concat_bug group by str2;<br />
+&#8212;-+&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| id | select_type | table            | type | possible_keys | key  | key_len | ref  | rows | Extra          |<br />
+&#8212;-+&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
|  1 | SIMPLE      | group_concat_bug | ALL  | NULL          | NULL | NULL    | NULL |    6 | Using filesort |<br />
+&#8212;-+&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
1 row in set (0.00 sec)</p>
<p>mysql&gt; select group_concat(str1) from group_concat_bug group by str2;<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
| group_concat(str1) |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
| a,a,a              |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
1 row in set, 1 warning (0.00 sec)</p>
<p>mysql&gt; explain select group_concat(str1) from group_concat_bug group by str2 order by str3;<br />
+&#8212;-+&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
| id | select_type | table            | type | possible_keys | key  | key_len | ref  | rows | Extra                           |<br />
+&#8212;-+&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
|  1 | SIMPLE      | group_concat_bug | ALL  | NULL          | NULL | NULL    | NULL |    6 | Using temporary; Using filesort |<br />
+&#8212;-+&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
1 row in set (0.00 sec)</p>
<p>mysql&gt; select group_concat(str1) from group_concat_bug group by str2 order by str3;<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
| group_concat(str1) |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
| a                  |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
1 row in set, 1 warning (0.00 sec)</p>
<p>mysql&gt; show variables like &#8216;group_concat_max_len&#8217;;<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;-+<br />
| Variable_name        | Value |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;-+<br />
| group_concat_max_len | 5     |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;-+<br />
1 row in set (0.00 sec)</p></blockquote>
<p>I tried to patch item_sum.cc dump_leaf_key() to include the max character length in bytes in the string max length calculation with:</p>
<blockquote><p>-  if (result-&gt;length() &gt; item-&gt;max_length)<br />
+  if ((result-&gt;length() * item-&gt;collation.collation-&gt;mbmaxlen) &gt; item-&gt;max_length)</p></blockquote>
<p>This didn&#8217;t work out as I planned. Repeat runs of queries return different results.</p>
<blockquote><p>mysql&gt; set group_concat_max_len=5; select group_concat(str1 separator &#8221;) from group_concat_bug group by str2;<br />
Query OK, 0 rows affected (0.00 sec)</p>
<p>+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
| group_concat(str1 separator &#8221;) |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
| aa                              |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
1 row in set, 1 warning (0.00 sec)</p>
<p>mysql&gt; set group_concat_max_len=5; select group_concat(str1 separator &#8221;) from group_concat_bug group by str2;<br />
Query OK, 0 rows affected (0.00 sec)</p>
<p>+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
| group_concat(str1 separator &#8221;) |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
| aau                             |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
1 row in set, 1 warning (0.00 sec)</p></blockquote>
<p>At this point I stopped investigating the problem because the application that was using group_concat was just parsing the result back into an array anyway. The developers agreed it was best to just get rid of the group_concat and issue two queries. This is what I recommend for fixing group_concat. The risk of having truncated values or running out of memory is too much to justify saving the cost of issuing a second query.</p>
]]></content:encoded>
			<wfw:commentRss>http://ebergen.net/wordpress/2011/09/05/is-group_concat_max_len-in-bytes-or-characters/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>The #mysql drinking game</title>
		<link>http://ebergen.net/wordpress/2011/08/12/the-mysql-drinking-game/</link>
		<comments>http://ebergen.net/wordpress/2011/08/12/the-mysql-drinking-game/#comments</comments>
		<pubDate>Fri, 12 Aug 2011 21:05:53 +0000</pubDate>
		<dc:creator>Eric Bergen</dc:creator>
				<category><![CDATA[Geek]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://ebergen.net/wordpress/?p=512</guid>
		<description><![CDATA[These are the rules of the Freenode #mysql drinking game. Any non op posts a pastebin link either either the query and no error or the error and no query drink Any non op posts a query with, &#8220;why doesn&#8217;t this work?&#8221; without any explanation about the results they want or what their getting drink [...]]]></description>
			<content:encoded><![CDATA[<p>These are the rules of the Freenode #mysql drinking game.</p>
<ol>
<li>Any non op posts a pastebin link either either the query and no error or the error and no query drink</li>
<li>Any non op posts a query with, &#8220;why doesn&#8217;t this work?&#8221; without any explanation about the results they want or what their getting drink</li>
<li>Domas trolls the channel with a legit issue drink</li>
<li>When a postgres guy answers a question about sqlite in #mysql 3 drinks</li>
<li>Someone answers a question with, &#8220;kill yourself&#8221; drink</li>
<li>Someone asks a phpmyadmin question</li>
<li>Someone asks a workbench question</li>
<li>Someone can&#8217;t figure out how to reset the root password</li>
<li>Someone says they are getting an access denied error but they insist the username and password are correct.</li>
<li>Someone asks a mssql question but tries to disguise it as a mysql question because there is no mssql channel.</li>
<li>Whenever someone either complains about auto_increment leaving holes in the sequence or asks how to reset it.</li>
</ol>
<p>Special set of rules for Alexander Keremidarski (salle)</p>
<ol>
<li>Just drink.</li>
</ol>
<p>Please suggest new rules in the comments.</p>
]]></content:encoded>
			<wfw:commentRss>http://ebergen.net/wordpress/2011/08/12/the-mysql-drinking-game/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Second update of modifying table statistics in MariaDB</title>
		<link>http://ebergen.net/wordpress/2011/07/28/second-update-of-modifying-table-statistics-in-mariadb/</link>
		<comments>http://ebergen.net/wordpress/2011/07/28/second-update-of-modifying-table-statistics-in-mariadb/#comments</comments>
		<pubDate>Thu, 28 Jul 2011 19:43:11 +0000</pubDate>
		<dc:creator>Eric Bergen</dc:creator>
				<category><![CDATA[Geek]]></category>
		<category><![CDATA[MariaDB]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://ebergen.net/wordpress/?p=492</guid>
		<description><![CDATA[Since my last post I&#8217;ve changed how the table statistics work quite a bit in MariaDB. I ran into a few problems with my original changes. In the TiVo 5.0 patch the show table_statistics command chose from one of three hash tables to read from depending on the flags. There is a global hash table for [...]]]></description>
			<content:encoded><![CDATA[<p>Since my last <a title="Update on porting table_statistics to MariaDB" href="http://ebergen.net/wordpress/2011/04/24/update-on-porting-table_statistics-to-mariadb-2/">post</a> I&#8217;ve changed how the table statistics work quite a bit in MariaDB. I ran into a few problems with my original changes. In the <a title="Cool stuff from TiVo’s MySQL patch" href="http://ebergen.net/wordpress/2010/09/28/cool-stuff-from-tivos-mysql-patch/">TiVo 5.0 patch</a> the show table_statistics command chose from one of three hash tables to read from depending on the flags. There is a global hash table for global stats and two in the thd object for session and query stats. Each time a non show query is executed the query statistics are reset. In 5.1 the implementation of show command changed from reading arbitrary data structures to constructing queries to run against information_schema tables. The information_schema tables are constructed on the fly, placed into a temporary table and have the select resulting from the show command executed on them. This works ok for the show commands but broke my new information schema tables.</p>
<p>As part of porting my changes into mariadb 5.2 I added a few new information schema tables called QUERY_TABLE_STATISTICS, QUERY_INDEX_STATISTICS, SESSION_TABLE_STATISTICS, and SESSION_INDEX_STATISTICS. For the part these tables query the hash tables from the 5.0 implementation. This worked fine for the show commands in my previous update. It broke when querying the QUERY_TABLE_STATISTICS directly. In 5.0 I reset the per query table statistics on every query that isn&#8217;t a SHOW query. This works fine except that SELECT * FROM QUERY_TABLE_STATISTICS reset the query statistics that the select query was supposed to retrieve.</p>
<p>After a conversation with serg in #mariadb on freenode we came up with a plan to fix the QUERY_TABLE_STATISTICS and QUERY_INDEX_STATISTICS tables. Instead of tracking the most recent query the plan is to track the most recent N queries tracked with a QUERY_ID. I realized this is very similar to how SHOW PROFILES worked. I decided to try to integrate the query statistics in with the profile. This seemed like a good idea until I realized that I want to enable the slow query log statistics without the overhead of leaving profiling on in production.</p>
<p>All of the profiling methods are controlled by the profiling variable. All of the table, index, and user statistics are controlled by the userstat variable. The profiling and my changes to the table statistics both use a QUERY_ID to show a list of queries. The problem is that depending on when the profiling and userstat variables are enabled the query_ids can be inconsistent. For example if a user enables userstat, executes a query, then enables profiling and executes another query the table statistics query_id 1 and the profiling query_id 1 will be different.</p>
<p>Every show command that uses a information schema table puts rows into a temporary table. I&#8217;ve also added temporary table tracking into the table statistics. This has an interesting side effect when combined with per query statistics tracking. Queries are only put into the queue of queries for query statistics when they read/write rows. The query_statistics_history_size controls how many queries to keep stats for. Each show command now uses a temporary table so the show commands are now tracked using row statistics. Here is an example using show variables.</p>
<blockquote><p>mysql&gt; set query_statistics_history_size=5;<br />
Query OK, 0 rows affected (0.00 sec)</p>
<p>mysql&gt; show variables;<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| Variable_name                             | Value                                                                                                          |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| aria_block_size                           | 8192<br />
&#8230;&#8230;&#8230;<br />
| warning_count                             | 0                                                                                                              |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
395 rows in set (0.00 sec)</p>
<p>mysql&gt; show query table_statistics;<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| Query_id | Table_schema | Table_name | Rows_read | Rows_changed | Rows_changed_x_#indexes |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
|        8 | #temp#       | #temp#     |       395 |          395 |                     395 |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
1 row in set (0.00 sec)</p>
<p>mysql&gt; show query table_statistics;<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| Query_id | Table_schema | Table_name | Rows_read | Rows_changed | Rows_changed_x_#indexes |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
|        8 | #temp#       | #temp#     |       395 |          395 |                     395 |<br />
|        9 | #temp#       | #temp#     |         1 |            1 |                       1 |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
2 rows in set (0.00 sec)</p>
<p>mysql&gt; show query table_statistics;<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| Query_id | Table_schema | Table_name | Rows_read | Rows_changed | Rows_changed_x_#indexes |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
|        8 | #temp#       | #temp#     |       395 |          395 |                     395 |<br />
|        9 | #temp#       | #temp#     |         1 |            1 |                       1 |<br />
|       10 | #temp#       | #temp#     |         2 |            2 |                       2 |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
3 rows in set (0.00 sec)</p></blockquote>
<p>See how each new show command adds in another query into the queue of tracked queries? While technically correct I don&#8217;t think this is ideal. I&#8217;m open to suggestions on how to keep the temp table tracking useful while not polluting the list of queries with unnecessary results.</p>
<p>I went back to #mariadb again and we came up with a single unified query_id per thread that both show profiles and the table statistics can use. This unifies the query ids with the down side that a lot of ids can be wasted. Doing set profiling=1, executing a query, and running show profiles isn&#8217;t guaranteed to use query_id 1 like it did before. This is where I&#8217;m looking to the community to decide how to handle the two different commands and query ids.</p>
<p>The main reason for tracking the per query statistics is to dump them to the slow query log. If userstat is enabled and a query is written to the slow query log it will include two new comments that look like:</p>
<blockquote><p># Time: 110712 13:12:16<br />
# User@Host: [ebergen] @ localhost []<br />
# Thread_id: 1  Schema: test  QC_hit: No<br />
# Query_time: 64.666276  Lock_time: 0.000113  Rows_sent: 10  Rows_examined: 719488<br />
# Row_Stats: test:rows_read=677984,rows_changed=0,rows_changed_x_indexes=0;#temp#:rows_read=41504,rows_changed=41494,rows_changed_x_indexes=41494;<br />
# Index_Stats:<br />
SET timestamp=1310501536;<br />
select * from t2 group by u order by u desc limit 10;</p></blockquote>
<p>Things that are missing or wrong so far. The first thing is that I don&#8217;t have a query similar to show profiles for queries with their ids. I don&#8217;t want to duplicate show profiles for statistics. I&#8217;m open to suggestion on how to unify the profiling and table statistics. Some of the structures for profiling and query stats are similar. I think they can be unified but this is more work than I want to put into the tivo branch. If mariadb is willing to accept this kind of feature I can work on unifying them.</p>
<p>Here is an example of where I&#8217;m stuck with show profiles vs show query statistics.</p>
<blockquote><p>mysql&gt; use test;<br />
Database changed<br />
mysql&gt; set query_statistics_history_size=5;<br />
Query OK, 0 rows affected (0.00 sec)</p>
<p>mysql&gt; set profiling=1;<br />
Query OK, 0 rows affected (0.00 sec)</p>
<p>mysql&gt; select * from t limit 5;<br />
+&#8212;&#8212;-+&#8212;&#8212;&#8211;+<br />
| t     | u      |<br />
+&#8212;&#8212;-+&#8212;&#8212;&#8211;+<br />
| 14515 | 282874 |<br />
| 14521 | 258653 |<br />
| 14573 | 113276 |<br />
| 14577 | 826475 |<br />
| 14585 | 444645 |<br />
+&#8212;&#8212;-+&#8212;&#8212;&#8211;+<br />
5 rows in set (0.00 sec)</p>
<p>mysql&gt; show profiles;<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| Query_ID | Duration   | Query                   |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
|        7 | 0.00052300 | select * from t limit 5 |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
1 row in set (0.00 sec)</p>
<p>mysql&gt; set profiling=0;<br />
Query OK, 0 rows affected (0.00 sec)</p>
<p>mysql&gt; select * from t limit 10;<br />
+&#8212;&#8212;-+&#8212;&#8212;&#8211;+<br />
| t     | u      |<br />
+&#8212;&#8212;-+&#8212;&#8212;&#8211;+<br />
| 14515 | 282874 |<br />
| 14521 | 258653 |<br />
| 14573 | 113276 |<br />
| 14577 | 826475 |<br />
| 14585 | 444645 |<br />
| 14612 | 792545 |<br />
| 14626 | 483300 |<br />
| 14842 | 447267 |<br />
| 15325 | 38865  |<br />
| 15340 | 744424 |<br />
+&#8212;&#8212;-+&#8212;&#8212;&#8211;+<br />
10 rows in set (0.00 sec)</p>
<p>mysql&gt; show query table_statistics;<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| Query_id | Table_schema | Table_name | Rows_read | Rows_changed | Rows_changed_x_#indexes |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
|        2 | #temp#       | #temp#     |        15 |           15 |                      15 |<br />
|        7 | test         | t          |         5 |            0 |                       0 |<br />
|       10 | test         | t          |        10 |            0 |                       0 |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
3 rows in set (0.00 sec)</p>
<p>mysql&gt; show profiles;<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| Query_ID | Duration   | Query                   |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
|        7 | 0.00052300 | select * from t limit 5 |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
1 row in set (0.00 sec)</p></blockquote>
<p>Notice how show query table_statistics has three queries. Query id 2 is actually the first show command. With profiling enabled or disabled show profiles doesn&#8217;t have as many queries as show table_statistics does.</p>
]]></content:encoded>
			<wfw:commentRss>http://ebergen.net/wordpress/2011/07/28/second-update-of-modifying-table-statistics-in-mariadb/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A bit on SANs and system dependencies</title>
		<link>http://ebergen.net/wordpress/2011/06/13/a-bit-on-sans-and-system-dependencies/</link>
		<comments>http://ebergen.net/wordpress/2011/06/13/a-bit-on-sans-and-system-dependencies/#comments</comments>
		<pubDate>Tue, 14 Jun 2011 05:02:45 +0000</pubDate>
		<dc:creator>Eric Bergen</dc:creator>
				<category><![CDATA[Geek]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://ebergen.net/wordpress/?p=486</guid>
		<description><![CDATA[It&#8217;s fairly well known that I&#8217;m not a fan of SANs for mysql. I&#8217;m really not a fan of them in general but most of this is from not being a fan of them for mysql. Over the past few decades the server world has migrated from few large expensive servers to lots of small [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s fairly well known that I&#8217;m not a fan of SANs for mysql. I&#8217;m really not a fan of them in general but most of this is from not being a fan of them for mysql. Over the past few decades the server world has migrated from few large expensive servers to lots of small cheap servers. For the most part this is accepted as a good thing. Large mainframe computers were slowly replaced by relatively smaller yet still expensive servers which are now replaced by smaller cheaper servers. I apply this same logic and trend to storage. I think storage should be replaced by smaller local storage or storage services rather than large centralized storage.</p>
<p>The idea of a SAN seems great on paper. You get a large pool of storage which can be sliced up into smaller pools as needed by different clients. If one client demands more storage it&#8217;s fairly straight forward to allocate them more storage and expand the filesystem over it. if you need more storage you simply add it to the pool and  dole it out as needed. When it comes to mysql there are some problems with this approach. Some of the problems are technical others are with people and process.</p>
<p>People tend to think of storage as a sack that can hold N apples. When the sack is full of apples either get another sack or a larger sack and add more apples. This is only one dimension of storage. The other one is more difficult to plan for and often times overlooked until it&#8217;s too late. This is the rate at which apples can be added or removed from the sack or sorting through the sack to get at specific apples. The time it takes to access data and the number of concurrent requests that can be supported is more important for the speed of a database than the amount of data that can be stored.</p>
<p>Not all SAN storage is created equal. It&#8217;s possible that the large shared pool of storage is created by different underlying disks. Some of these disks can only support a few hundred iops (input/output operations per second) while others can support several thousand. By mixing the type of disks the once large shared storage pool is now smaller pools classified by the rate at which they can read and write data. Many SANs also support caching reads or writes in front of those disks.  So not only do you now have to decide which clients will use which speed of disks but making sure the cache isn&#8217;t overloaded by any of the clients.</p>
<p>On top of all of this there is the problem of changing workloads. During almost every SAN performance related conversation I have ever had someone always suggests that I benchmark the SAN vs DAS and let the benchmark drive the architecture. This is fine if the SAN performance doesn&#8217;t change but as I&#8217;ve already established allocating SAN space is an ongoing task. This is because the workload of clients can change and the number of clients on the SAN can also change. Adding more clients or changing the workload of clients can change the performance for other clients.</p>
<p>I understand that it is possible to allocate clients in a way that will prevent some clients from interfering with others but in practice I&#8217;ve never seen this work out. Inevitably some clients interfere with others. This becomes a huge problem as people tend to think of i/o latency as a constant within some small range of variation. I&#8217;ve seen i/o latency go from a few milliseconds to several seconds for the same storage. Tracking down the source of these problems can be difficult especially if the clients are separate teams or business units. Adding new clients to a SAN or changing the workload invalidates any previous benchmarks or capacity plans done against it.</p>
<p>Back to mysql. Most mysql installations commit transactions serially. As part of this process they run an fsync on the transaction log to make sure the transaction is sycned to disk. This means that doubling the i/o latency will halve the number of transactions that can be sent through. This makes mysql more sensitive to i/o latency than other types of applications. Mysql slaves will perform every i/o of a transaction sequentially, not just the commit phase which makes them even more sensitive. While it&#8217;s technically possible to have a SAN function correctly it isn&#8217;t as simple as carving up the global pool of storage for clients.</p>
<p>I have to be fair and say that there is a potential for performance problems with DAS as well. Performance can degrade for various reasons such as when a disk dies or a battery dies. In these situations performance can degrade a little bit or it can degrade severely such as the controller refusing to cache writes without a battery. The difference between these scenarios and the allocation scenarios on a SAN is that DAS failures are states that can be monitored. If a disk dies your monitor sends an alert and you replace it. If the battery dies simply replace it. This means your monitoring has to be in place and you need a HA pair of machines but I don&#8217;t feel like that is a tall order.</p>
<p>The upside of using DAS is that each machine gets it&#8217;s own set of disks. If it overloads those disks and causes problems those problems won&#8217;t be reflected on the other machines io latency. What it does mean is that it&#8217;s more difficult to add space to each machine meaning that you need to plan accordingly. It can also mean that there is a larger pool over unused space because each machine needs a certain percentage of overhead. This depends on the workload of course but it must be considered. This also means that it is more difficult to scale vertically although SSDs and fusionio have raised the vertical scalability ceiling significantly in the past few years.</p>
<p>&nbsp;</p>
<p>[ Update 2011-06-19 Daniël van Eeden has posted a <a title="RE: A bit on SANs and system dependencies" href="http://daniel-database.blogspot.com/2011/06/re-bit-on-sans-and-system-dependencies.html">good followup</a> on his blog ]</p>
]]></content:encoded>
			<wfw:commentRss>http://ebergen.net/wordpress/2011/06/13/a-bit-on-sans-and-system-dependencies/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Update on porting table_statistics to MariaDB</title>
		<link>http://ebergen.net/wordpress/2011/04/24/update-on-porting-table_statistics-to-mariadb-2/</link>
		<comments>http://ebergen.net/wordpress/2011/04/24/update-on-porting-table_statistics-to-mariadb-2/#comments</comments>
		<pubDate>Mon, 25 Apr 2011 05:59:00 +0000</pubDate>
		<dc:creator>Eric Bergen</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[MariaDB]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://ebergen.net/wordpress/?p=475</guid>
		<description><![CDATA[Over the past few weeks I&#8217;ve been working on porting the per table and per session row stats from the tivo patch into mariadb. This is the show table_statistics functionality. I&#8217;ve been pushing the code to a branch on lanchpad as I get features working. Up to revision 2953 the row stats are working but [...]]]></description>
			<content:encoded><![CDATA[<p>Over the past few weeks I&#8217;ve been working on porting the per table and per session row stats from the <a href="http://ebergen.net/wordpress/2010/09/28/cool-stuff-from-tivos-mysql-patch/">tivo patch</a> into mariadb. This is the show table_statistics functionality.  I&#8217;ve been pushing the code to <a href="https://code.launchpad.net/~provenscaling-eric/maria/tivo">a branch</a> on lanchpad as I get features working. Up to revision 2953 the row stats are working but the index stats aren&#8217;t. I think I have a reasonable implementation of the row stats code given my level of C++ skill. I&#8217;m changing the syntax a bit from how it worked in the tivo patch to make it be more compatible with how the original row stats code worked that was ported into mariadb. </p>
<p>In MariaDB 5.2 there are show table_statistics show index_statistics, and show user_statistics functions that return global stats collected by the server when the userstats variable is enabled. There are also associated information schema tables that feed these commands. So far I&#8217;ve changed the syntax of the show command to be </p>
<blockquote><p>SHOW [ QUERY | SESSION | GLOBAL ] TABLE_STATISTICS</p></blockquote>
<p>Where the default show table_statistics returns the global stats. This is different from the tivo patch against 5.0.72 which changed the default to be the last query. The new syntax keeps things backwards compatible with the original show command. The query keyword will return the previous query_statistics_history_size queries of statistics. This is a bit different from the 5.0 version which only tracked one query. </p>
<p>For example if I set query_statistics_history_size=5 then mariadb will track the row stats from the previous 5 queries:</p>
<blockquote><p>mysql> set query_statistics_history_size=5;<br />
Query OK, 0 rows affected (0.00 sec)</p>
<p>mysql> show tables;<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| Tables_in_test |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| t              |<br />
| t2             |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
2 rows in set (0.00 sec)</p>
<p>mysql> select * from t;<br />
+&#8212;+<br />
| t |<br />
+&#8212;+<br />
| 1 |<br />
| 2 |<br />
| 3 |<br />
| 4 |<br />
| 5 |<br />
+&#8212;+<br />
5 rows in set (0.00 sec)</p>
<p>mysql> select * from t2;<br />
+&#8212;-+<br />
| t2 |<br />
+&#8212;-+<br />
|  1 |<br />
|  2 |<br />
|  3 |<br />
+&#8212;-+<br />
3 rows in set (0.00 sec)</p>
<p>mysql> show query table_statistics;<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| Query_id | Table_schema | Table_name | Rows_read | Rows_changed | Rows_changed_x_#indexes |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
|       81 | test         | t          |         5 |            0 |                       0 |<br />
|       82 | test         | t2         |         3 |            0 |                       0 |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
2 rows in set (0.00 sec)</p>
<p>mysql> select count(*) from t,t2;<br />
+&#8212;&#8212;&#8212;-+<br />
| count(*) |<br />
+&#8212;&#8212;&#8212;-+<br />
|       15 |<br />
+&#8212;&#8212;&#8212;-+<br />
1 row in set (0.00 sec)</p>
<p>mysql> show query table_statistics;<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| Query_id | Table_schema | Table_name | Rows_read | Rows_changed | Rows_changed_x_#indexes |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
|       81 | test         | t          |         5 |            0 |                       0 |<br />
|       82 | test         | t2         |         3 |            0 |                       0 |<br />
|       84 | test         | t          |         5 |            0 |                       0 |<br />
|       84 | test         | t2         |         3 |            0 |                       0 |<br />
+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
4 rows in set (0.00 sec)</p></blockquote>
<p>Note that show query table_statistics shows the stats only for the current connection. There isn&#8217;t a way to get lists from other threads. My next task is to add a separate table with the query_id and query text similar to show profiles that can be used to correlate a query back to the different statistics kept for it. After that I plan on adding index statistics.</p>
<p>The older version of this in the tivo patch has been extremely useful for optimizing queries. I hope I can make this even better and get it into mariadb for other people to use. If you have any suggestions for how I should change this please post in the comments.</p>
]]></content:encoded>
			<wfw:commentRss>http://ebergen.net/wordpress/2011/04/24/update-on-porting-table_statistics-to-mariadb-2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Rare MyISAM failing to delete MYD file for temporary tables.</title>
		<link>http://ebergen.net/wordpress/2010/12/24/rare-myisam-failing-to-delete-myd-file-for-temporary-tables/</link>
		<comments>http://ebergen.net/wordpress/2010/12/24/rare-myisam-failing-to-delete-myd-file-for-temporary-tables/#comments</comments>
		<pubDate>Fri, 24 Dec 2010 21:12:54 +0000</pubDate>
		<dc:creator>Eric Bergen</dc:creator>
				<category><![CDATA[Geek]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://ebergen.net/wordpress/?p=444</guid>
		<description><![CDATA[I manage a few systems that every hundred million disk temporary table deletions or so one of them will fail. MySQL will delete the MYI file but leave the MYD behind. It&#8217;s very strange. There isn&#8217;t an error in the error log but subsequent queries that try to use the same temporary table name will [...]]]></description>
			<content:encoded><![CDATA[<p>I manage a few systems that every hundred million disk temporary table deletions or so one of them will fail. MySQL will delete the MYI file but leave the MYD behind. It&#8217;s very strange. There isn&#8217;t an error in the error log but subsequent queries that try to use the same temporary table name will error because the MYD file still exists. The queries fail with an error like ERROR 1 (HY000): Can&#8217;t create/write to file &#8216;/tmp/#sql_25d1_0.MYD&#8217;. Fortunately the client error gives the temporary table name so it&#8217;s easy to clean up by deleted the MYD file. While I still don&#8217;t know what the root cause is but I was able to patch MySQL to fix the issue.</p>
<p>The old mi_delete_table function which is responsible for deleting only the MYI and MYD file would delete the MYI file and leave the MYD file behind. This function would try to delete the MYI file, get an error back from the filesystem and not attempt to delete the corresponding MYD file. The strange part is that the file is actually deleted. The error is captured in mi_delete_table but is lost by the functions calling it. I&#8217;ve tried to patch some parts of the temporary table code to try to track down the actual filesystem error but haven&#8217;t seen any errors returned. I don&#8217;t think I&#8217;ve correctly patched all the temporary table code to capture the error. This hasn&#8217;t been a priority because the patch clears up the symptom of the issue which is the MYD file being left behind.</p>
<p>In my new mi_delete_table function I try to delete both the MYD and MYI file before returning an error. This has fixed the issue of MYD files being left behind but I still don&#8217;t know the root cause of why they weren&#8217;t being deleted in the first place. I&#8217;m not sure about filing a bug for this because even though I think my mi_delete_table is better than the stock one I can&#8217;t really reproduce the issue.</p>
<p>Anyway here is <a href="http://ebergen.net/patches/temp_table_delete.patch">the patch</a> if anyone is interested.</p>
]]></content:encoded>
			<wfw:commentRss>http://ebergen.net/wordpress/2010/12/24/rare-myisam-failing-to-delete-myd-file-for-temporary-tables/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Second draft of the per session row and index stats patch</title>
		<link>http://ebergen.net/wordpress/2010/12/24/second-draft-of-the-per-session-row-and-index-stats-patch/</link>
		<comments>http://ebergen.net/wordpress/2010/12/24/second-draft-of-the-per-session-row-and-index-stats-patch/#comments</comments>
		<pubDate>Fri, 24 Dec 2010 20:03:00 +0000</pubDate>
		<dc:creator>Eric Bergen</dc:creator>
				<category><![CDATA[Geek]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://ebergen.net/wordpress/?p=456</guid>
		<description><![CDATA[I&#8217;ve taken the part of the tivo patch that includes table and index statistics and broken it out into it&#8217;s own patch. This patch includes the ability to do show [session &#124; global ] table_statistics and the same for index_statistics. In this version the row stats are also logged in the slow query log. To [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve taken the part of the tivo patch that includes table and index statistics and broken it out into it&#8217;s own <a href="http://ebergen.net/patches/table_stats.patch">patch</a>. This patch includes the ability to do show [session | global ] table_statistics and the same for index_statistics. In this version the row stats are also logged in the slow query log. To log per query stats I had to track them separately from the per session stats. Because the tracking was already done for the slow query log I&#8217;ve modified the command to allow uses to access row stats for the previous query separate from the sum for the current session. The flush commands also act similarly. </p>
<p>Along with changing the slow query log format I&#8217;ve also change it to log the timestamp with every query. This made it easier to do automated parsing of the slow query log.</p>
<p>The queries now support three different modes. I&#8217;ve detailed how the show commands work in a <a href="/wordpress/2010/09/28/cool-stuff-from-tivos-mysql-patch/">previous post</a>. This patch has helped developers diagnose numerous query problems that weren&#8217;t obvious by the row estimates in the explain output. I highly recommend it for users who run more complicated queries in mysql and have been having a hard time diagnosing why they are slow. This has proved particularly adept at finding misbehaving sub queries and bad join optimization which doesn&#8217;t show up in explain output. As I find example queries I will try to post them here.</p>
<p>The <a href="http://ebergen.net/patches/table_stats.patch">patch</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://ebergen.net/wordpress/2010/12/24/second-draft-of-the-per-session-row-and-index-stats-patch/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

