<?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>Alexander Rubin&#039;s Blog on MySQL</title>
	<atom:link href="http://www.arubin.org/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.arubin.org/blog</link>
	<description>MySQL, FullText Search, Performance, High Availability</description>
	<lastBuildDate>Sat, 05 Jan 2013 00:43:58 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>MySQL Visual Explain</title>
		<link>http://www.arubin.org/blog/2012/09/26/mysql-visual-explain/</link>
		<comments>http://www.arubin.org/blog/2012/09/26/mysql-visual-explain/#comments</comments>
		<pubDate>Wed, 26 Sep 2012 17:33:03 +0000</pubDate>
		<dc:creator>arubin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.arubin.org/blog/?p=134</guid>
		<description><![CDATA[<p>If you are tied of reading the old &#8220;text-only&#8221; output of MySQL Explain, then you will enjoy the new MySQL Visual Explain feature of MySQL Workbench (works with MySQL 5.6+).</p>
Before:
<p>mysql> explain select max(DepDelayMinutes),  carrier, dayofweek   from ontime.ontime_2010   where dayofweek = 7   group by Carrier,  dayofweek\G
*************************** 1. row [...]]]></description>
			<content:encoded><![CDATA[<p>If you are tied of reading the old &#8220;text-only&#8221; output of MySQL Explain, then you will enjoy the new MySQL Visual Explain feature of MySQL Workbench (works with MySQL 5.6+).</p>
<h3>Before:</h3>
<p><code>mysql> explain select max(DepDelayMinutes),  carrier, dayofweek   from ontime.ontime_2010   where dayofweek = 7   group by Carrier,  dayofweek\G<br />
*************************** 1. row ***************************<br />
           id: 1<br />
  select_type: SIMPLE<br />
        table: ontime_2010<br />
         type: ref<br />
possible_keys: DayOfWeek,dw_carr,covered<br />
          key: covered<br />
      key_len: 2<br />
          ref: const<br />
         rows: 1337314<br />
        Extra: Using where; Using index<br />
1 row in set (0.00 sec)<br />
</code></p>
<h3>After:</h3>
<p><img src="http://www.arubin.org/blog/wp-content/uploads/2012/09/visual_explain.png" alt="MySQL Visual Explain" /></p>
<h3>How to test:</h3>
<ol>
<li><a href="http://dev.mysql.com/downloads/mysql/#downloads">Download and Install MySQL 5.6</a>
<li><a href="http://dev.mysql.com/downloads/workbench/5.2.html">Download and Install MySQL Workbench</a>
<li>Enjoy
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.arubin.org/blog/2012/09/26/mysql-visual-explain/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Speaking at MySQL Connect This Weekend</title>
		<link>http://www.arubin.org/blog/2012/09/24/speaking-at-mysql-connect-this-weekend/</link>
		<comments>http://www.arubin.org/blog/2012/09/24/speaking-at-mysql-connect-this-weekend/#comments</comments>
		<pubDate>Tue, 25 Sep 2012 02:21:58 +0000</pubDate>
		<dc:creator>arubin</dc:creator>
				<category><![CDATA[full text search]]></category>
		<category><![CDATA[innodb]]></category>
		<category><![CDATA[mysql]]></category>

		<guid isPermaLink="false">http://www.arubin.org/blog/?p=131</guid>
		<description><![CDATA[<p>I will give 2 talks at MySQL Connect
1. New MySQL Full-Text Search Features and Solutions, where I will focus on the new (and very promising!) InnoDB full text search. I&#8217;ve done some benchmarks recently and will publish it here.
2. In-Depth Query Optimization for MySQL, where we will work on the real word examples of MySQL [...]]]></description>
			<content:encoded><![CDATA[<p>I will give 2 talks at<a href="http://www.oracle.com/mysqlconnect/"> MySQL Connect</a><br />
1. New MySQL Full-Text Search Features and Solutions, where I will focus on the new (and very promising!) InnoDB full text search. I&#8217;ve done some benchmarks recently and will publish it here.<br />
2. In-Depth Query Optimization for MySQL, where we will work on the real word examples of MySQL query tuning.</p>
<p>*SESSION SCHEDULE INFORMATION*</p>
<p>Session ID: CON9283<br />
Session Title: New MySQL Full-Text Search Features and Solutions<br />
Venue / Room: Hilton San Francisco &#8211; Golden Gate 8<br />
Date and Time: 9/29/12, 14:30 &#8211; 15:30</p>
<p>Session ID: CON8811<br />
Session Title: In-Depth Query Optimization for MySQL<br />
Venue / Room: Hilton San Francisco &#8211; Golden Gate 8<br />
Date and Time: 9/30/12, 14:45 &#8211; 15:45</p>
<p><a href="http://www.oracle.com/mysqlconnect/learn/agenda/index.html">Full Agenda</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.arubin.org/blog/2012/09/24/speaking-at-mysql-connect-this-weekend/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>MySQL PAM/LDAP authentication module configuration</title>
		<link>http://www.arubin.org/blog/2012/07/16/mysql-pam-ldap-authentication-module-configuration/</link>
		<comments>http://www.arubin.org/blog/2012/07/16/mysql-pam-ldap-authentication-module-configuration/#comments</comments>
		<pubDate>Mon, 16 Jul 2012 18:44:35 +0000</pubDate>
		<dc:creator>arubin</dc:creator>
				<category><![CDATA[mysql]]></category>
		<category><![CDATA[mysql enterprise]]></category>
		<category><![CDATA[openldap]]></category>
		<category><![CDATA[authentication]]></category>

		<guid isPermaLink="false">http://www.arubin.org/blog/?p=118</guid>
		<description><![CDATA[<p>MySQL Enterprise 5.5 (trial version available here) includes MySQL PAM authentication plugin. In this post I will show how to configure it with the OpenLDAP and Active Directory.</p>
<p>MySQL PAM authentication uses Linux pam_ldap library to send the calls. To configure MySQL LDAP authentication we will need to configure pam_ldap on linux.</p>
OpenLDAP Linux configuration

Make sure that [...]]]></description>
			<content:encoded><![CDATA[<p>MySQL Enterprise 5.5 (trial version available <a href="http://edelivery.oracle.com">here</a>) includes MySQL PAM authentication plugin. In this post I will show how to configure it with the OpenLDAP and Active Directory.</p>
<p>MySQL PAM authentication uses Linux pam_ldap library to send the calls. To configure MySQL LDAP authentication we will need to configure pam_ldap on linux.</p>
<h2>OpenLDAP Linux configuration</h2>
<ul>
<li>Make sure that libpam-ldap/openldap is installed. If not, on RedHat/CentOS use commands:</li>
</ul>
<p><code># yum install openldap openldap-clients</code></p>
<ul>
<li>Configure /etc/ldap.conf. Sample configuration:</li>
</ul>
<p><code>debug 10 # set debug level only during the initial configuration<br />
base dc=corp,dc=company_name,dc=com<br />
binddn cn=service_account,OU=Service Accounts,OU=US Security,DC=corp,DC=company_name,DC=com<br />
bindpw &lt;password&gt;<br />
timelimit 120<br />
idle_timelimit 3600<br />
uri ldaps://&lt;LDAP URL&gt;:&lt;LDAP PORT&gt;<br />
</code><br />
<b># The following lines are needed only for Active Directory</b><br />
<code><br />
pam_login_attribute samaccountname<br />
pam_member_attribute member<br />
nss_map_objectclass posixAccount user<br />
nss_map_objectclass shadowAccount user<br />
nss_map_attribute uid sAMAccountName<br />
nss_map_attribute homeDirectory unixHomeDirectory<br />
nss_map_attribute shadowLastChange pwdLastSet<br />
nss_map_objectclass posixGroup group<br />
nss_map_attribute uniqueMember member<br />
pam_login_attribute sAMAccountName<br />
pam_filter objectclass=User<br />
pam_password ad<br />
</code></p>
<h3>Make sure you can connect to ldap</h3>
<p><code># telnet &lt;ldap server&gt; &lt;ldap password&gt; (should show “connected”)</code></p>
<h3>Make sure you can search ldap with ldapsearch</h3>
<p><code># ldapsearch –w &lt;password for bind user&gt; -x –D 'cn=USER,OU=People …' “(&amp;(ObjectClass=user)(cn=USERNAME))”</code></p>
<h2>MySQL configuration</h2>
<p>To setup mysql follow the instructions from <a href="http://dev.mysql.com/doc/refman/5.5/en/pam-authentication-plugin.html">http://dev.mysql.com/doc/refman/5.5/en/pam-authentication-plugin.html</a></p>
<ul>
<li>Make sure you use MySQL      5.5.16+ enterprise version</li>
<li>Add line:</li>
</ul>
<p><code>plugin-load=authentication_pam.so</code></p>
<p>into <strong>my.cnf</strong> and restart mysql server</p>
<ul>
<li> Put the following in /etc/pam.d/mysql:</li>
</ul>
<p><code>#%PAM-1.0<br />
auth        required    pam_ldap.so<br />
account     required    pam_ldap.so<br />
</code></p>
<h2>Individual User Authentication</h2>
<ul>
<li>Create user for group      substitution</li>
</ul>
<p><code>CREATE USER 'user_name'@'localhost'<br />
IDENTIFIED WITH authentication_pam<br />
AS 'mysql';</code></p>
<p>Where mysql = name of the /etc/pam.d/mysql file above, user_name should match the LDAP’s uid (<strong>samaccountname for AD</strong>). </p>
<ul>
<li>Login to mysql with your <strong><em>LDAP      username and password</em></strong></li>
</ul>
<p><code>mysql --user=user_name --password=verysecret mydb</code></p>
<h2>Group Authentication</h2>
<ul>
<li>Create user for group  substitution</li>
</ul>
<p><code>CREATE USER ''@''<br />
IDENTIFIED WITH authentication_pam<br />
AS 'mysql, ldap_group1=mysql_user1, ldap_group2=mysql_user2';<br />
</code></p>
<p>Where mysql = name of the /etc/pam.d/mysql file above, ldap_group1 is the ldap group, mysql_user1 is the user to substitute (we need to create this user)</p>
<ul>
<li>Create user for each LDAP group</li>
</ul>
<p><code>CREATE USER 'mysql_user1'@'localhost' IDENTIFIED BY '<strong><em>very secret password</em></strong>';<br />
GRANT ALL PRIVILEGES ON mydevdb.* TO 'mysql_user1'@'localhost';<br />
GRANT PROXY ON 'mysql_user1'@'localhost' TO ''@'';</code></p>
<ul>
<li>Login to mysql with your <strong><em> LDAP username and password</em></strong>and make sure the user was substituted correctly:</li>
</ul>
<p><code>mysql --user=user_name --password=verysecret mydb</p>
<p>mysql&gt;  <strong>SELECT USER(), CURRENT_USER(), @@proxy_user;</strong><br />
+-------------------+-----------------------+--------------+<br />
| USER()            | CURRENT_USER()        | @@proxy_user |<br />
+-------------------+-----------------------+--------------+<br />
| user_name@localhost   | mysql_user1@localhost | ''@'%'       |<br />
+-------------------+-----------------------+--------------+<br />
</code></p>
<h2>Debugging</h2>
<ul>
<li>Add “debug 16” (or other      level, 0 to 256) into /etc/ldap.conf</li>
<li>Add “export AUTHENTICATION_PAM_LOG=1”      to /etc/init.d/mysql and restart mysql</li>
<li>Then for troubleshoot look      into mysql error log (i.e. /var/log/mysqld.log).</li>
</ul>
<h2>Current issues</h2>
<p>Due to some PAM library&#8217;s limitations the group authentication may not work with Active Directory (individual authentication works fine), probably because the &#8220;posix_group&#8221; is not fully supported by Active Directory.</p>
<h3>Links and more info</h3>
<ul>
<a href="http://dev.mysql.com/doc/refman/5.5/en/pam-authentication-plugin.html">MySQL PAM Module documentation</a><br />
<a href="http://edelivery.oracle.com">Download MySQL Enterprise Trial from Oracle E-delivery</a><br />
We plan a session on MySQL LDAP Auth at <a href="http://www.oracle.com/mysqlconnect/index.html">MySQL Connect </a> (Sept 29-30, San Francisco)
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.arubin.org/blog/2012/07/16/mysql-pam-ldap-authentication-module-configuration/feed/</wfw:commentRss>
		<slash:comments>53</slash:comments>
		</item>
		<item>
		<title>Using MySQL 5.6 to find queries creating disk temporary tables</title>
		<link>http://www.arubin.org/blog/2011/05/01/using-mysql-5-6-to-find-queries-creating-disk-temporary-tables/</link>
		<comments>http://www.arubin.org/blog/2011/05/01/using-mysql-5-6-to-find-queries-creating-disk-temporary-tables/#comments</comments>
		<pubDate>Sun, 01 May 2011 23:58:10 +0000</pubDate>
		<dc:creator>arubin</dc:creator>
				<category><![CDATA[performance tuning]]></category>
		<category><![CDATA[temporary tables]]></category>
		<category><![CDATA[disk temporary tables]]></category>
		<category><![CDATA[mysql 5.6]]></category>
		<category><![CDATA[performance_schema]]></category>
		<category><![CDATA[slow query]]></category>

		<guid isPermaLink="false">http://www.arubin.org/blog/?p=98</guid>
		<description><![CDATA[<p>In my previous post, I&#8217;ve showed how to use Dtrace to find queries creating disk temporary tables (only available for OS with dtrace: solaris, freebsd, etc). </p>
<p>In MySQL 5.6 (which is not released yet, use &#8220;labs&#8221; version for now) we can use new performance_schema table events_statements_history or events_statements_history_long to find all performance metrics for all [...]]]></description>
			<content:encoded><![CDATA[<p>In my previous post, I&#8217;ve showed how to use Dtrace to <a href="http://www.arubin.org/blog/2009/10/02/using-dtrace-to-find-queries-creating-disk-temporary-tables/">find queries creating disk temporary tables</a> (only available for OS with dtrace: solaris, freebsd, etc). </p>
<p>In MySQL 5.6 (which is not released yet, use &#8220;labs&#8221; version for now) we can use new performance_schema table events_statements_history or events_statements_history_long to find all performance metrics for all queries including created disk/memory tables, use of index, etc. WOW! This is what I have been waiting for a long time!</p>
<p>To illustrate, I have grabbed mysql-5.6.3-labs-performance-schema-linux2.6-x86_64.tar.gz from <a href="http://labs.mysql.com/">labs.mysql.com</a> (this feature is only in labs version) and run sysbench readonly test (you need to disable prepared statements in sysbench, seems to be not working with prepared statements, I will check it later).</p>
<p>Here are the results:</p>
<blockquote>
<pre>mysql> select * from events_statements_history_long where  CREATED_TMP_DISK_TABLES > 0 limit 10\G
*************************** 10. row ***************************
              THREAD_ID: 74
               EVENT_ID: 3295633
             EVENT_NAME: statement/sql/select
                 SOURCE: sql_parse.cc:935
            TIMER_START: 633828149000000
              TIMER_END: 633843868000000
             TIMER_WAIT: 15719000000
              LOCK_TIME: 53000000
               SQL_TEXT: SELECT DISTINCT c from sbtest where id between 847399 and 847499 order by c
         CURRENT_SCHEMA: sbtest
            OBJECT_TYPE: NULL
          OBJECT_SCHEMA: NULL
            OBJECT_NAME: NULL
  OBJECT_INSTANCE_BEGIN: NULL
            MYSQL_ERRNO: 0
      RETURNED_SQLSTATE: NULL
           MESSAGE_TEXT: NULL
                 ERRORS: 0
               WARNINGS: 0
          ROWS_AFFECTED: 0
              ROWS_SENT: 1
          ROWS_EXAMINED: 103
CREATED_TMP_DISK_TABLES: 1
     CREATED_TMP_TABLES: 1
       SELECT_FULL_JOIN: 0
 SELECT_FULL_RANGE_JOIN: 0
           SELECT_RANGE: 1
     SELECT_RANGE_CHECK: 0
            SELECT_SCAN: 0
      SORT_MERGE_PASSES: 0
             SORT_RANGE: 0
              SORT_ROWS: 1
              SORT_SCAN: 1
          NO_INDEX_USED: 0
     NO_GOOD_INDEX_USED: 0
       NESTING_EVENT_ID: NULL
     NESTING_EVENT_TYPE: NULL
10 rows in set (0.00 sec)
</pre>
</blockquote>
<p>Or if you need only list of queries:</p>
<blockquote><pre>
mysql> select sql_text, count(*) as cnt  from events_statements_history_long
where  CREATED_TMP_DISK_TABLES > 0
group by sql_text order by cnt desc  limit 10;
+-----------------------------------------------------------------------------+-----+
| sql_text                                                                    | cnt |
+-----------------------------------------------------------------------------+-----+
| SELECT DISTINCT c from sbtest where id between 242012 and 242112 order by c |   2 |
| SELECT DISTINCT c from sbtest where id between 797388 and 797488 order by c |   2 |
| SELECT DISTINCT c from sbtest where id between 973150 and 973250 order by c |   1 |
| SELECT DISTINCT c from sbtest where id between 478783 and 478883 order by c |   1 |
| SELECT DISTINCT c from sbtest where id between 967035 and 967135 order by c |   1 |
| SELECT DISTINCT c from sbtest where id between 602102 and 602202 order by c |   1 |
| SELECT DISTINCT c from sbtest where id between 123827 and 123927 order by c |   1 |
| SELECT DISTINCT c from sbtest where id between 980527 and 980627 order by c |   1 |
| SELECT DISTINCT c from sbtest where id between 450354 and 450454 order by c |   1 |
| SELECT DISTINCT c from sbtest where id between 674804 and 674904 order by c |   1 |
+-----------------------------------------------------------------------------+-----+
10 rows in set (0.04 sec)
</pre>
</blockquote>
<p>We can filter and order by rows_examined,  SORT_MERGE_PASSES,  NO_INDEX_USED,  NO_GOOD_INDEX_USED, etc.</p>
<p>Links:</p>
<ul>
<li><a href="http://dev.mysql.com/tech-resources/articles/whats-new-in-mysql-5.6.html">What is new in MySQL 5.6: all long waited great features of MySQL 5.6</a> (btw: Multi-Threaded Slaves are coming up, now in labs only)</li>
<li><a href="http://www.markleith.co.uk/?p=471">A Big Bag of Epic Awesomeness</a>, by Mark Leith</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.arubin.org/blog/2011/05/01/using-mysql-5-6-to-find-queries-creating-disk-temporary-tables/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fixing data warehousing queries with group-by</title>
		<link>http://www.arubin.org/blog/2010/11/29/fixing-data-warehousing-queries-with-group-by/</link>
		<comments>http://www.arubin.org/blog/2010/11/29/fixing-data-warehousing-queries-with-group-by/#comments</comments>
		<pubDate>Mon, 29 Nov 2010 20:50:59 +0000</pubDate>
		<dc:creator>arubin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.arubin.org/blog/?p=78</guid>
		<description><![CDATA[<p>With the standard data warehousing queries we have a fact table and dimension tables and we join them.
For example, the fact table (Table size: 5M rows, ~2G in size) from my previous Loose index scan vs. covered indexes in MySQL post:</p>

    CREATE TABLE `ontime_2010` (
      `YearD` int(11) [...]]]></description>
			<content:encoded><![CDATA[<p>With the standard data warehousing queries we have a fact table and dimension tables and we join them.<br />
For example, the fact table (Table size: 5M rows, ~2G in size) from my previous <a href="http://www.arubin.org/blog/2010/11/18/loose-index-scan-vs-covered-indexes-in-mysql/">Loose index scan vs. covered indexes in MySQL</a> post:</p>
<blockquote><pre>
    CREATE TABLE `ontime_2010` (
      `YearD` int(11) DEFAULT NULL,
      `MonthD` tinyint(4) DEFAULT NULL,
      `DayofMonth` tinyint(4) DEFAULT NULL,
      `DayOfWeek` tinyint(4) DEFAULT NULL,
      `Carrier` char(2) DEFAULT NULL,
      `Origin` char(5) DEFAULT NULL,
      `DepDelayMinutes` int(11) DEFAULT NULL,
      `AirlineID` int(11) DEFAULT NULL,
      `Cancelled` tinyint(4) DEFAULT NULL,
    ... more fields here ...
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1
</pre>
</blockquote>
<p>(this is not the best possible fact table as the data is not aggregated by I&#8217;ll use it for now).</p>
<p>And we have those dimensions tables:</p>
<blockquote>
<pre>
 CREATE TABLE `airlines` (
  `AirlineID` int(11) NOT NULL DEFAULT '0',
  `AirlineName` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`AirlineID`),
  KEY `AirlineName` (`AirlineName`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

CREATE TABLE `date_dayofweek` (
  `code` int(11) NOT NULL DEFAULT '0',
  `description` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`code`),
  KEY `description` (`description`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> select * from date_dayofweek order by code;
+------+-------------+
| code | description |
+------+-------------+
|    1 | Monday      |
|    2 | Tuesday     |
|    3 | Wednesday   |
|    4 | Thursday    |
|    5 | Friday      |
|    6 | Saturday    |
|    7 | Sunday      |
|    9 | Unknown     |
+------+-------------+
8 rows in set (0.00 sec)
</pre>
</blockquote>
<p>So here is the example query (find sum of cancelled flights on Sundays for the given airline group by day):</p>
<blockquote>
<pre>select sum(Cancelled), FlightDate, AirlineName
from ontime_2010 o, date_dayofweek dow, airlines a
where o.dayofweek=dow.code and dow.description = 'Sunday'
and a.AirlineID = o.AirlineID and a.AirlineName = 'Delta Air Lines Inc.: DL'
group by FlightDate order by FlightDate desc limit 10\G
</pre>
</blockquote>
<p>To fix the query we can add a covered index for ontime_2010, so that all fields for ontime_2010 table will be covered:</p>
<blockquote><p>alter table ontime_2010 add key cov2(AirlineID, dayofweek, FlightDate, Cancelled); </p></blockquote>
<p>However we will still have &#8220;temporary table and filesort&#8221;:</p>
<blockquote>
<pre>

mysql> explain select sum(Cancelled), FlightDate
from ontime_2010 o, date_dayofweek dow, airlines a
where o.dayofweek=dow.code and dow.description = 'Sunday'
and a.AirlineID = o.AirlineID and a.AirlineName = 'Delta Air Lines Inc.: DL'
group by FlightDate order by FlightDate desc limit 10\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: dow
         type: ref
possible_keys: PRIMARY,description
          key: description
      key_len: 258
          ref: const
         rows: 1
        Extra: Using where; Using index; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: ref
possible_keys: PRIMARY,AirlineName
          key: AirlineName
      key_len: 258
          ref: const
         rows: 1
        Extra: Using where; Using index
*************************** 3. row ***************************
           id: 1
  select_type: SIMPLE
        table: o
         type: ref
possible_keys: DayOfWeek,covered,AirlineID,cov2
          key: cov2
      key_len: 7
          ref: ontime.a.AirlineID,ontime.dow.code
         rows: 24417
        Extra: Using where; Using index
3 rows in set (0.00 sec)
<pre></blockquote>

To avoid filesort we can re-write this query with "subqueries":
<blockquote>
<pre>
mysql> explain select sum(Cancelled), FlightDate  from ontime_2010 o
where o.dayofweek= (select code from date_dayofweek where description = 'Sunday')
and AirlineID = (select AirlineID from airlines where AirlineName = 'Delta Air Lines Inc.: DL')
group by FlightDate limit 10\G
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: o
         type: ref
possible_keys: DayOfWeek,covered,AirlineID,cov2
          key: cov2
      key_len: 7
          ref: const,const
         rows: 152510
        Extra: Using where; Using index
*************************** 2. row ***************************
           id: 3
  select_type: SUBQUERY
        table: airlines
         type: ref
possible_keys: AirlineName
          key: AirlineName
      key_len: 258
          ref:
         rows: 1
        Extra: Using where; Using index
*************************** 3. row ***************************
           id: 2
  select_type: SUBQUERY
        table: date_dayofweek
         type: ref
possible_keys: description
          key: description
      key_len: 258
          ref:
         rows: 1
        Extra: Using where; Using index
3 rows in set (0.00 sec)
</pre>
</blockquote>
<p>As MySQL will use indexes when we have "field = (select .. )" and now all fields in the index belong to the single table, MySQL will use index and avoid filesort. Please note: this will not work with "field in (select ...)" and also make sure that the subselect part will return only 1 row.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.arubin.org/blog/2010/11/29/fixing-data-warehousing-queries-with-group-by/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Converting queries with OR to Union to ulitize indexes</title>
		<link>http://www.arubin.org/blog/2010/11/22/converting-queries-with-or-to-union-to-ulitize-indexes/</link>
		<comments>http://www.arubin.org/blog/2010/11/22/converting-queries-with-or-to-union-to-ulitize-indexes/#comments</comments>
		<pubDate>Mon, 22 Nov 2010 21:25:04 +0000</pubDate>
		<dc:creator>arubin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.arubin.org/blog/?p=69</guid>
		<description><![CDATA[<p>Lets say we have a table storing mail messages and we need to show user&#8217;s mailbox: messages sent &#8220;from&#8221; and &#8220;to&#8221; the specified user.</p>
<p>Here is our table:</p>


CREATE TABLE `internalmail` (
  `mail_id` int(10) NOT NULL AUTO_INCREMENT,
  `senderaddress_id` int(10) NOT NULL,
  `recipientaddress_id` int(10) NOT NULL,
  `mail_timestamp` timestamp NULL DEFAULT NULL,
... message body, etc [...]]]></description>
			<content:encoded><![CDATA[<p>Lets say we have a table storing mail messages and we need to show user&#8217;s mailbox: messages sent &#8220;from&#8221; and &#8220;to&#8221; the specified user.</p>
<p>Here is our table:</p>
<blockquote>
<pre>
CREATE TABLE `internalmail` (
  `mail_id` int(10) NOT NULL AUTO_INCREMENT,
  `senderaddress_id` int(10) NOT NULL,
  `recipientaddress_id` int(10) NOT NULL,
  `mail_timestamp` timestamp NULL DEFAULT NULL,
... message body, etc ...
  PRIMARY KEY (`mail_id`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1
</pre>
</blockquote>
<p>And our query:</p>
<blockquote><pre>select * from internalmail
 where (senderaddress_id = 247 or recipientaddress_id = 247 or recipientaddress_id = 0)
and mail_timestamp > '2010-08-01 12:30:47'
order by mail_timestamp desc </pre>
</blockquote>
<p>In this query we show all messages from and to user_id = 247 plus all messages to system user (user_id=0). We need to show only messages for the last 3 months and show the most recent messages first.</p>
<p>To speed up the query we can try creating indexes:</p>
<blockquote><p>
  KEY `recipientaddress_id` (`recipientaddress_id`),<br />
  KEY `senderaddress_id` (`senderaddress_id`),<br />
  KEY `mail_timestamp` (`mail_timestamp`),
</p></blockquote>
<p>However, as the query uses &#8220;OR&#8221;, MySQL will use a filesort. </p>
<blockquote>
<pre>
mysql> explain select * from internalmail
where (senderaddress_id = 247 or recipientaddress_id = 247 or recipientaddress_id = 0)
and mail_timestamp > '2010-08-01 12:30:47'
order by mail_timestamp desc\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: internalmail
         type: ALL
possible_keys: recipientaddress_id,senderaddress_id,mail_timestamp
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 4843257
        Extra: Using where; Using filesort
1 row in set (0.00 sec)
</pre>
</blockquote>
<p><strong>UPDATE: </strong> even if we will create combined indexes on (recipientaddress_id,mail_timestamp) and/or (senderaddress_id,mail_timestamp) those indexes will not be used, as the query contains &#8220;OR&#8221; in the where clause.</p>
<p>And original query runs for 3 seconds. To fix this query we can do 2 things:</p>
<ol>
<li>Rewrite query with UNION instead of OR</li>
<li>Create combined indexes</li>
</ol>
<p>First, we rewrite query with UNION:</p>
<blockquote><p>
(select * from internalmail where senderaddress_id = 247  and mail_timestamp > &#8216;2010-08-19 12:30:47&#8242;)<br />
union<br />
(select * from internalmail where  recipientaddress_id = 247  and mail_timestamp > &#8216;2010-08-19 12:30:47&#8242;)<br />
union<br />
(select * from internalmail where  recipientaddress_id = 0  and mail_timestamp > &#8216;2010-08-19 12:30:47&#8242;)<br />
order by mail_timestamp desc;
</p></blockquote>
<p>Second, we create 2 indexes:</p>
<blockquote><p>
mysql> alter table internalmail add key send_dt(senderaddress_id, mail_timestamp);<br />
mysql> alter table internalmail add key recieve_dt(recipientaddress_id, mail_timestamp);
</p></blockquote>
<p>After that, MySQL will be able to fully utilize index for each of the 3 queries in union:</p>
<blockquote>
<pre>
mysql> explain
(select * from internalmail where senderaddress_id = 247  and mail_timestamp > '2010-08-19 12:30:47')
union
(select * from internalmail where  recipientaddress_id = 247  and mail_timestamp > '2010-08-19 12:30:47')
union
(select * from internalmail where  recipientaddress_id = 0  and mail_timestamp > '2010-08-19 12:30:47')
order by mail_timestamp desc\G
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: internalmail
         type: range
possible_keys: senderaddress_id,mail_timestamp,send_dt
          key: send_dt
      key_len: 9
          ref: NULL
         rows: 5
        Extra: Using where
*************************** 2. row ***************************
           id: 2
  select_type: UNION
        table: internalmail
         type: range
possible_keys: recipientaddress_id,mail_timestamp,recieve_dt
          key: recieve_dt
      key_len: 9
          ref: NULL
         rows: 11
        Extra: Using where
*************************** 3. row ***************************
           id: 3
  select_type: UNION
        table: internalmail
         type: range
possible_keys: recipientaddress_id,mail_timestamp,recieve_dt
          key: recieve_dt
      key_len: 9
          ref: NULL
         rows: 1
        Extra: Using where
*************************** 4. row ***************************
           id: NULL
  select_type: UNION RESULT
        table: <union1,2,3>
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: Using filesort
4 rows in set (0.00 sec)
</pre>
</blockquote>
<p>Although this query has to perform a final filesort it is much faster: now it runs in 0 sec compared to 3 seconds originally.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.arubin.org/blog/2010/11/22/converting-queries-with-or-to-union-to-ulitize-indexes/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Loose index scan vs. covered indexes in MySQL</title>
		<link>http://www.arubin.org/blog/2010/11/18/loose-index-scan-vs-covered-indexes-in-mysql/</link>
		<comments>http://www.arubin.org/blog/2010/11/18/loose-index-scan-vs-covered-indexes-in-mysql/#comments</comments>
		<pubDate>Thu, 18 Nov 2010 17:51:24 +0000</pubDate>
		<dc:creator>arubin</dc:creator>
				<category><![CDATA[performance tuning]]></category>
		<category><![CDATA[covered index]]></category>
		<category><![CDATA[index]]></category>
		<category><![CDATA[loose index scan]]></category>
		<category><![CDATA[mysql performance]]></category>

		<guid isPermaLink="false">http://www.arubin.org/blog/?p=58</guid>
		<description><![CDATA[<p>Loose index scan in MySQL can really help optimizing &#8220;group by&#8221; queries in some cases (for example, if you have only min() and/or max() as your aggregate functions). For example, if you have this query (to find maximum delay for all US flights with departure on Sundays in 2010):</p>

select max(DepDelayMinutes), 	carrier, dayofweek
from ontime_2010
where dayofweek = [...]]]></description>
			<content:encoded><![CDATA[<p>Loose index scan in MySQL can really help optimizing &#8220;group by&#8221; queries in some cases (for example, if you have only min() and/or max() as your aggregate functions). For example, if you have this query (to find maximum delay for all US flights with departure on Sundays in 2010):</p>
<blockquote>
<pre>select max(DepDelayMinutes), 	carrier, dayofweek
from ontime_2010
where dayofweek = 7
group by Carrier,  dayofweek
</pre>
</blockquote>
<p>the usual case will be adding a covered index on (dayofweek, Carrier, DepDelayMinutes). And MySQL will use this index fine (using index mean it will use the covered index):</p>
<blockquote>
<pre>
mysql> explain select max(DepDelayMinutes), Carrier, dayofweek from ontime_2010
where dayofweek =7 group by Carrier, dayofweek\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: ontime_2010
         type: ref
possible_keys: covered
          key: covered
      key_len: 2
          ref: const
         rows: 905138
        Extra: Using where; Using index
1 row in set (0.00 sec)
</pre>
</blockquote>
<p>However, as the dayofweek part has low number of unique values, mysql will have to scan a lots of index entries (estimated rows: 905138).<br />
<span id="more-58"></span></p>
<p>MySQL can use <a href="http://dev.mysql.com/doc/refman/5.1/en/group-by-optimization.html#loose-index-scan" target="_new">loose index scan</a>. Unfortunately, a o lots of limitations apply:</p>
<ul>
<li>The query is over a single table.
<li>The GROUP BY names only columns that form a leftmost prefix of the index and no other columns.
<li>The only aggregate functions used in the select list (if any) are MIN() and MAX(), same column
<li> etc&#8230; (see <a href="http://dev.mysql.com/doc/refman/5.1/en/group-by-optimization.html#loose-index-scan" target="_new">docs</a> for details)
</ul>
<p>As our example query is suitable for loose index scan, we can create another index:</p>
<blockquote>
<pre>
mysql> alter table ontime_2010 add key lis1(Carrier, dayofweek, DepDelayMinutes);

mysql> explain select max(DepDelayMinutes), Carrier, dayofweek from ontime_2010
where dayofweek =7 group by Carrier, dayofweek \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: ontime_2010
         type: range
possible_keys: DayOfWeek,covered
          key: lis1
      key_len: 5
          ref: NULL
         rows: 19
        Extra: Using where; Using index for group-by
1 row in set (0.01 sec)
</pre>
</blockquote>
<p>Here, Using index for group-by, means that MySQL uses loose index scan.<br />
Also, and it is really great, it works with range on dayofweek too:</p>
<blockquote>
<pre>
mysql> explain select max(DepDelayMinutes), Carrier, dayofweek from ontime_2010
where dayofweek > 3 group by Carrier, dayofweek \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: ontime_2010
         type: range
possible_keys: DayOfWeek,covered
          key: lis1
      key_len: 5
          ref: NULL
         rows: 19
        Extra: Using where; Using index for group-by
1 row in set (0.00 sec)
</pre>
</blockquote>
<p>And original covered index does not work with ranges in where clause:</p>
<blockquote>
<pre>
mysql> explain select max(DepDelayMinutes), Carrier, dayofweek from ontime_2010 use index (covered)
where dayofweek > 3 group by Carrier, dayofweek \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: ontime_2010
         type: range
possible_keys: covered
          key: covered
      key_len: 2
          ref: NULL
         rows: 2416543
        Extra: Using where; Using index; Using temporary; Using filesort
</pre>
</blockquote>
<p>In the above example, MySQL uses index but still have to create temporary table and filesort.</p>
<p>Now speed comparison:<br />
I&#8217;m using &#8220;ontime&#8221; flight performance statistics data from <a href="http://www.transtats.bts.gov/DL_SelectFields.asp?Table_ID=236&#038;DB_Short_Name=On-Time" target="_new">transtats.bts.gov</a><br />
The table only consist of data for 2010.<br />
Table size: 5M rows, ~2G in size. Table structure:</p>
<blockquote><pre>
CREATE TABLE `ontime_2010` (
  `YearD` int(11) DEFAULT NULL,
  `MonthD` tinyint(4) DEFAULT NULL,
  `DayofMonth` tinyint(4) DEFAULT NULL,
  `DayOfWeek` tinyint(4) DEFAULT NULL,
  `Carrier` char(2) DEFAULT NULL,
  `Origin` char(5) DEFAULT NULL,
  `DepDelayMinutes` int(11) DEFAULT NULL,
... more fields here ...
) ENGINE=InnoDB DEFAULT CHARSET=latin1
</pre>
</blockquote>
<p>Results (cached index and data):<br />
&#8220;where dayofweek = 7&#8243; (ref)</p>
<ul>
<li>Loose index scan: 0 sec
<li>Covered index: 0.6 sec
</ul>
<p>&#8220;where dayofweek > 3&#8243; (range)</p>
<ul>
<li>Loose index scan: 0 sec
<li>index (+ filesort): 5.53 sec
</ul>
<p>I will also present the findings from this article (among other things) during the upcoming webinar, <a href="http://mysql.com/news-and-events/web-seminars/display-582.html" target="_new">Getting the Best MySQL Performance in Your Products: Part 3, Query Tuning</a>, which will take place on Tuesday, Nov 23 (webinar is free, <a href="http://mysql.com/news-and-events/web-seminars/display-582.html">webinar registration</a>)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.arubin.org/blog/2010/11/18/loose-index-scan-vs-covered-indexes-in-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Why mysqldump is converting my tables from InnoDB to MyISAM?</title>
		<link>http://www.arubin.org/blog/2010/11/12/why-mysqldump-is-converting-my-tables-from-innodb-to-myisam/</link>
		<comments>http://www.arubin.org/blog/2010/11/12/why-mysqldump-is-converting-my-tables-from-innodb-to-myisam/#comments</comments>
		<pubDate>Fri, 12 Nov 2010 21:23:31 +0000</pubDate>
		<dc:creator>arubin</dc:creator>
				<category><![CDATA[innodb]]></category>
		<category><![CDATA[migration]]></category>
		<category><![CDATA[myisam]]></category>
		<category><![CDATA[mysqldump]]></category>

		<guid isPermaLink="false">http://www.arubin.org/blog/?p=50</guid>
		<description><![CDATA[<p>First of all: mysqldump is not converting tables. It is something else. Here is the story:</p>
<p>One of my clients had a case when they were migrating to a new mysql server: they used mysqldump to export data from the old server (all InnoDB) and imported it to the new server. When finished, all the tables [...]]]></description>
			<content:encoded><![CDATA[<p>First of all: mysqldump is not converting tables. It is something else. Here is the story:</p>
<p>One of my clients had a case when they were migrating to a new mysql server: they used mysqldump to export data from the old server (all InnoDB) and imported it to the new server. When finished, all the tables became MyISAM on the new server. So they asked me this question:<br />
&#8220;Why mysqldump is converting my tables from InnoDB to MyISAM?&#8221;<br />
<span id="more-50"></span></p>
<p>First of all we made sure that the tables are InnoDB on the old server. It was true.<br />
Second we run &#8220;show engines&#8221; on the new server:<br />
<code></p>
<blockquote><p>
+------------+---------+----------------------------------------------------------------+--------------+------+------------+<br />
| Engine     | Support | Comment                                                        | Transactions | XA   | Savepoints |<br />
+------------+---------+----------------------------------------------------------------+--------------+------+------------+<br />
| MyISAM     | DEFAULT | Default engine as of MySQL 3.23 with great performance         | NO           | NO   | NO         |<br />
| MRG_MYISAM | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |<br />
| BLACKHOLE  | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |<br />
| CSV        | YES     | CSV storage engine                                             | NO           | NO   | NO         |<br />
| MEMORY     | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |<br />
| FEDERATED  | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |<br />
| ARCHIVE    | YES     | Archive storage engine                                         | NO           | NO   | NO         |<br />
+------------+---------+----------------------------------------------------------------+--------------+------+------------+</p></blockquote>
<p></code><br />
As we see, there is no InnoDB in the list. So, InnoDB was not started.<br />
Next we look into the error log to find out, why InnoDB was not started. And we saw this:</p>
<blockquote><p><code>InnoDB: Error: log file ./ib_logfile0 is of different size 0 5242880 bytes<br />
InnoDB: than specified in the .cnf file 0 134217728 bytes!<br />
[ERROR] Plugin 'InnoDB' init function returned error.<br />
[ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.<br />
[Note] Event Scheduler: Loaded 0 events<br />
 [Note] /usr/sbin/mysqld: ready for connections.<br />
Version: '5.1.51-log'  socket: '/var/lib/mysql/mysql.sock'  port: 3306  MySQL Community Server (GPL)</code></p></blockquote>
<p>So, InnoDB was not stared as the size of log files was changed in the my.cnf and the old log files were not moved. Although, mysql server was started, but without InnoDB. In this case mysql restored the tables, but the storage engine was substituted from InnoDB to MyISAM. For example if we create a table with non-existing storage engine, MySQL will use MyISAM instead:</p>
<blockquote><p><code><br />
mysql> create table aaa(i int) engine=non_existing_engine;<br />
Query OK, 0 rows affected, 2 warnings (0.16 sec)</p>
<p>mysql> show warnings;<br />
+---------+------+---------------------------------------------+<br />
| Level   | Code | Message                                     |<br />
+---------+------+---------------------------------------------+<br />
| Warning | 1286 | Unknown table engine 'non_existing_engine'  |<br />
| Warning | 1266 | Using storage engine MyISAM for table 'aaa' |<br />
+---------+------+---------------------------------------------+<br />
2 rows in set (0.00 sec)</code>
</p></blockquote>
<p>That was happened: mysql used MyISAM instead of InnoDB, produced warnings, but they are usually ignored.</p>
<p>The fix was easy: restart mysql using this instructions (http://dev.mysql.com/doc/refman/5.0/en/adding-and-removing.html) and upload the dump again (or convert myisam to innodb manually).</p>
<p><strong>UPDATE: </strong> To prevent this in the future you can do 2 things (thanks Shantanu for pointing this out):</p>
<ol>
<li> If using innodb plugin and mysql 5.1 add this to my.cnf: innodb=FORCE. In this case MySQL will not start if InnoDB failed to start:<br />
<blockquote><p>I<code>nnoDB: Error: log file ./ib_logfile0 is of different size 0 536870912 bytes<br />
InnoDB: than specified in the .cnf file 0 53477376 bytes!<br />
[ERROR] Plugin 'InnoDB' init function returned error.<br />
[ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.<br />
[ERROR] Failed to initialize plugins.<br />
[ERROR] Aborting</code>
</p></blockquote>
<li> use sql_mode=NO_ENGINE_SUBSTITUTION:
<p><code><br />
<blockquote>mysql> set sql_mode=NO_ENGINE_SUBSTITUTION;<br />
Query OK, 0 rows affected (0.00 sec)</p>
<p>mysql> create table aaa(i int) engine=non_existing_engine;<br />
ERROR 1286 (42000): Unknown table engine 'non_existing_engine'
</p></blockquote>
<p></code>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.arubin.org/blog/2010/11/12/why-mysqldump-is-converting-my-tables-from-innodb-to-myisam/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Speaking at MySQL Users Conference and Expo 2010</title>
		<link>http://www.arubin.org/blog/2010/04/10/speaking-at-mysql-users-conference-and-expo-2010/</link>
		<comments>http://www.arubin.org/blog/2010/04/10/speaking-at-mysql-users-conference-and-expo-2010/#comments</comments>
		<pubDate>Sat, 10 Apr 2010 20:58:36 +0000</pubDate>
		<dc:creator>arubin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.arubin.org/blog/?p=46</guid>
		<description><![CDATA[<p>I&#8217;ll be speaking on MySQL Users Conference 2010. Talk: MySQL Architecture Design Patterns for Performance, Scalability, and Availability, 11:55am  Thursday, 04/15/2010.  Details.</p>
]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ll be speaking on MySQL Users Conference 2010. Talk: MySQL Architecture Design Patterns for Performance, Scalability, and Availability, 11:55am  Thursday, 04/15/2010.  <a href=": http://en.oreilly.com/mysql2010/public/schedule/detail/13384">Details.</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.arubin.org/blog/2010/04/10/speaking-at-mysql-users-conference-and-expo-2010/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Speaking at Linux Conference in Wellington, New Zealand</title>
		<link>http://www.arubin.org/blog/2010/01/17/speaking-at-linux-conference-in-wellington-new-zealand/</link>
		<comments>http://www.arubin.org/blog/2010/01/17/speaking-at-linux-conference-in-wellington-new-zealand/#comments</comments>
		<pubDate>Sun, 17 Jan 2010 21:36:07 +0000</pubDate>
		<dc:creator>arubin</dc:creator>
				<category><![CDATA[mysql]]></category>
		<category><![CDATA[sphinxsearch]]></category>

		<guid isPermaLink="false">http://www.arubin.org/blog/?p=41</guid>
		<description><![CDATA[<p>I&#8217;ll be speaking at the data retrieval miniconf at Linux Conference in Wellington, New Zealand (Full Text Search with MySQL, Program)
I&#8217;ll cover some new sphinx search features (online updates)</p>
]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ll be speaking at the data retrieval miniconf at Linux Conference in Wellington, New Zealand (Full Text Search with MySQL, <a href="http://miniconf.osda.asn.au/program">Program</a>)<br />
I&#8217;ll cover some new sphinx search features (online updates)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.arubin.org/blog/2010/01/17/speaking-at-linux-conference-in-wellington-new-zealand/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
