<?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>Robin Clarke - Perl and Life &#187; MySQL</title>
	<atom:link href="http://www.robinclarke.net/archives/category/mysql/feed" rel="self" type="application/rss+xml" />
	<link>http://www.robinclarke.net</link>
	<description>Just another WordPress weblog</description>
	<lastBuildDate>Wed, 16 Jun 2010 15:33:21 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Amazon RDS</title>
		<link>http://www.robinclarke.net/archives/amazon-rds</link>
		<comments>http://www.robinclarke.net/archives/amazon-rds#comments</comments>
		<pubDate>Fri, 30 Oct 2009 09:27:58 +0000</pubDate>
		<dc:creator>robin</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Reviews]]></category>
		<category><![CDATA[amazon]]></category>
		<category><![CDATA[rds]]></category>

		<guid isPermaLink="false">http://www.robinclarke.net/?p=284</guid>
		<description><![CDATA[A couple of days ago, Amazon released their RDS service.  Being a MySQL fan, I had to have a try! The cool stuff: It is well documented, and pretty easy to set up an instance. You can dynamically change the class (CPU power and Memory) of a database instance &#8211; this means you can give [...]]]></description>
			<content:encoded><![CDATA[<p>A couple of days ago, Amazon released their <a title="Amazon RDS" href="http://aws.amazon.com/rds/" target="_blank">RDS service</a>.  Being a MySQL fan, I had to have a try!</p>
<p>The cool stuff:</p>
<ul>
<li>It is well documented, and pretty easy to set up an instance.</li>
<li>You can dynamically change the class (CPU power and Memory) of a database instance &#8211; this means you can give it more power without shutting it down, and easily scale it without having to do complex MySQL proxy configuration and synchronisation.</li>
</ul>
<p>What&#8217;s still missing:</p>
<ul>
<li>Not available in EU yet (it should be soon) &#8211; I set up a few instances in the US, but can&#8217;t really test what the performance is in the EU between EC2 instances and RDS instances, and that&#8217;s what I&#8217;m really interested in&#8230;</li>
<li>I takes a long time for the instance to become available!  I was waiting between 5 and 30 minutes.  I guess that&#8217;s not such an issue if you are just creating it once and leaving it running as the data hub for an application, but it&#8217;s annoyingly slow for testing/playing.  At least they don&#8217;t seem to start billing for time until it is online.</li>
</ul>
<p>Other interesting notes</p>
<ul>
<li>If you start the instance without any parameters, it will have a typical configuration for the machine used (memory allocation spread between InnoDB and MyISAM).  It is however possible to <a title="Amazon RDS Developer guide" href="http://docs.amazonwebservices.com/AmazonRDS/latest/DeveloperGuide/" target="_blank">specify parameters</a> when the instance is started, or even during run time &#8211; this gives a lot of room for optimisation: it&#8217;s not just a dumb service, you can tune it to your requirements.</li>
<li>Just for kicks and giggles, I tried scaling a small instance to a large instance, without any custom parameters: the only change was allocating all the additional memory to innodb_buffer_pool_size.  I guess that&#8217;s what most people want&#8230;</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.robinclarke.net/archives/amazon-rds/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>User groups with MySQL</title>
		<link>http://www.robinclarke.net/archives/user-groups-with-mysql</link>
		<comments>http://www.robinclarke.net/archives/user-groups-with-mysql#comments</comments>
		<pubDate>Mon, 12 Jan 2009 04:05:43 +0000</pubDate>
		<dc:creator>robin</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[groups]]></category>
		<category><![CDATA[meta]]></category>
		<category><![CDATA[procedure]]></category>
		<category><![CDATA[user groups]]></category>

		<guid isPermaLink="false">http://www.robinclarke.net/?p=59</guid>
		<description><![CDATA[MySQL User Groups Most operating systems have a user privilege granting system where privileges are given to groups, and users are asigned to groups, thereby inheriting all the privileges of this group. Unfortunately this is not currently (using MySQL server version 5.1) natively possible: the privileges must be defined for every user. Managing a MySQL [...]]]></description>
			<content:encoded><![CDATA[<h1>MySQL User Groups</h1>
<p>Most operating systems have a user privilege granting system where privileges are given to groups, and users are asigned to groups, thereby inheriting all the privileges of this group. Unfortunately this is not currently (using MySQL server version 5.1) natively possible: the privileges must be defined for every user. Managing a MySQL installation with many users can become a very laborious, and non transparent process, and many admins opt to have fewer MySQL users, with many real users using one MySQL user with the desired privileges. This increases difficulty in managing the server, because you never have just the right set of privileges for a given person, nor can you quickly determine which processes are being carried out by which person.</p>
<p>Following is a work around to implement a kind of group privilege management in MySQL.</p>
<h2>Setup</h2>
<p>See attached <a href="http://www.robinclarke.net/wp-content/uploads/2009/01/procedures_createssqltar.gz">procedure and creates</a> to set it up the tables on your system.</p>
<p>Two additional tables must be defined to hold the groups.  These are created in a new `mysql_meta` database (must be created first). The first table contains a list of the users which should be managed, and the groups which they are in. The second table contains a list of the privileges given for each group.</p>
<p>After entering the users, groups, the procedure updateMysqlUserPrivileges when called will first revoke all privileges from all users listed, and then proceed to grant the privileges anew. This is not very elegant&#8230; but if it was, I wouldn&#8217;t call it a workaround!</p>
<p>It should be noted at this point that <strong>every user listed</strong> will be processed. That means that if a user is defined, but he has no group, or no privileges defined, all his privileges will be revoked, and he will be left with nothing but USAGE on the server.</p>
<h2>Adding users</h2>
<p>Users must be defined manually before adding them to the mysql_meta tables:</p>
<pre>CREATE USER newuser@localhost IDENTIFIED BY 'password';</pre>
<p>You can then add your user to the mysql_meta.user_groups table.  If the user should belong to multiple groups, make multiple entries in this table, one for each group.</p>
<p>In the mysql_meta.user_group_privileges you can now define the privileges which should be given to each group. Please note that I have simplified by using one column `schema` to contain the database/table definition. As a result, it is not currently possible to grant permissions to specific columns. For example:</p>
<pre>INSERT INTO `mysql_meta`.`user_groups` (`user`,`host`,`user_group`)
   VALUES( 'newuser','localhost','newgroup');
INSERT INTO `mysql_meta`.`user_group_privileges`
  (`user_group`,`schema`,`privilege_type`,`isa`)
  VALUES ( 'newgroup','mydb.*' ,'SELECT','TABLE'),
  ('newgroup','mydb.*','UPDATE'),
  ('newgroup','mydb.*','INSERT','TABLE'),
  ('newgroup','mydb.fancyProcedure','EXECUTE','PROCEDURE');</pre>
<p>Would be equivalent to:</p>
<pre>GRANT UPDATE, INSERT, SELECT ON mydb.* TO newuser@localhost;
GRANT EXECUTE PROCEDURE ON mydb.fancyProcedure TO newuser@localhost</pre>
<h2>Committing new privileges</h2>
<p>Once the new privilege tables are completed, simply call the procedure to update:</p>
<pre>CALL mysql_meta.updateMysqlUserPrivileges;</pre>
<p>And here we stumble on another shortcoming of MySQL: there is no real way to do error handling in procedures or functions&#8230; I have opted to return a row with two columns: &#8220;Errors&#8221; and &#8220;Warnings&#8221;. If any users are defined in the user_groups table, but are not yet created as MySQL users, these will be listed as Errors, and if there are users which are defined, but which have no privileges, they will be listed in the Warnings column.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.robinclarke.net/archives/user-groups-with-mysql/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>View and Union very slow</title>
		<link>http://www.robinclarke.net/archives/view-and-union-very-slow</link>
		<comments>http://www.robinclarke.net/archives/view-and-union-very-slow#comments</comments>
		<pubDate>Thu, 11 Dec 2008 00:07:22 +0000</pubDate>
		<dc:creator>robin</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[union]]></category>
		<category><![CDATA[view]]></category>

		<guid isPermaLink="false">http://www.robinclarke.net/?p=42</guid>
		<description><![CDATA[I&#8217;ve found quite a few articles like this one from MySQL selling views as the perfect way to join archive data with live data to provide seamless access. What I wanted: keep archive data in a compact, non modifiable table (I chose packed MyISAM tables), and live data in a transactional table (I chose InnoDB), [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve found quite a few articles like <a title="Federated tables" href="http://dev.mysql.com/tech-resources/articles/mysql-federated-storage.html" target="_blank">this one </a>from MySQL selling views as the perfect way to join archive data with live data to provide seamless access.</p>
<p>What I wanted: keep archive data in a compact, non modifiable table (I chose packed MyISAM tables), and live data in a transactional table (I chose InnoDB), possibly even on two different machines.  I was thrilled when I found the article linked above, with the sweet idea how to create a view which would combine both, and imagined that while it would make a performance hit, it shouldn&#8217;t be that bad, because the data in the packed, optimised MyISAM tables should have much faster access than the InnoDB, and given that the volume of the data is in there, and all select conditions are on indexed columns, it should be pretty fast.</p>
<p>WRONG!</p>
<p>Views using a UNION are very poorly optimised, and create complete temporary tables before matching any where conditions.  Even on a COUNT(*) which should not even require /any/ data to be retreived, it took significantly longer.</p>
<p>Here by example:</p>
<p>Get the sample employees database <a title="Sample database" href="https://launchpad.net/test-db/employees-db-1/1.0.5" target="_blank">here</a>, and load it.</p>
<p>Create a test database, and a salaries table in it:</p>
<pre>(root@127.0.0.1) [employees]&gt;CREATE DATABASE `test`;
(root@127.0.0.1) [employees]&gt;CREATE TABLE `test`.`salaries` (
  `emp_no` int(11) NOT NULL,
  `salary` int(11) NOT NULL,
  `from_date` date NOT NULL,
  `to_date` date NOT NULL,
  PRIMARY KEY (`emp_no`,`from_date`),
  KEY `emp_no` (`emp_no`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;</pre>
<p>Then split the table salaries:</p>
<pre>(root@127.0.0.1) [employees]&gt;INSERT INTO `test`.`salaries` SELECT * FROM `employees`.`salaries` WHERE emp_no&lt;242293;</pre>
<pre>(root@127.0.0.1) [employees]&gt;DELETE FROM `employees`.`salaries` WHERE emp_no&lt;242293;</pre>
<p>How long does it take to count each one?</p>
<pre>(root@127.0.0.1) [employees]&gt;SELECT SQL_NO_CACHE COUNT(*) FROM `employees`.`salaries`;</pre>
<pre>+----------+</pre>
<pre>| COUNT(*) |</pre>
<pre>+----------+</pre>
<pre>|  1492975 |</pre>
<pre>+----------+</pre>
<pre>1 row in set (0.30 sec)</pre>
<pre>(root@127.0.0.1) [employees]&gt;SELECT SQL_NO_CACHE COUNT(*) FROM `test`.`salaries`;</pre>
<pre>+----------+</pre>
<pre>| COUNT(*) |</pre>
<pre>+----------+</pre>
<pre>|  1351072 |</pre>
<pre>+----------+</pre>
<pre>1 row in set (0.29 sec)</pre>
<p>That&#8217;s ok, and we see from the explain that it is indeed using a SIMPLE select, using the index on epm_no:</p>
<pre>(root@127.0.0.1) [employees]&gt;EXPLAIN SELECT SQL_NO_CACHE COUNT(*) FROM `test`.`salaries`;</pre>
<pre>+----+-------------+----------+-------+---------------+--------+---------+------+---------+-------------+</pre>
<pre>| id | select_type | table    | type  | possible_keys | key    | key_len | ref  | rows    | Extra       |</pre>
<pre>+----+-------------+----------+-------+---------------+--------+---------+------+---------+-------------+</pre>
<pre>|  1 | SIMPLE      | salaries | index | NULL          | emp_no | 4       | NULL | 1351439 | Using index |</pre>
<pre>+----+-------------+----------+-------+---------------+--------+---------+------+---------+-------------+</pre>
<pre>1 row in set (0.01 sec)</pre>
<p>So let&#8217;s make a view and join the tables, and try that again on the view:</p>
<pre>(root@127.0.0.1) [employees]&gt;CREATE VIEW salaries_all AS SELECT * FROM `employees`.`salaries` UNION ALL SELECT * FROM `test`.`salaries`;</pre>
<pre>(root@127.0.0.1) [employees]&gt;EXPLAIN SELECT SQL_NO_CACHE COUNT(*) FROM `employees`.`salaries_all`;
+----+--------------+------------+------+---------------+------+---------+------+---------+------------------------------+
| id | select_type  | table      | type | possible_keys | key  | key_len | ref  | rows    | Extra                        |
+----+--------------+------------+------+---------------+------+---------+------+---------+------------------------------+
|  1 | PRIMARY      | NULL       | NULL | NULL          | NULL | NULL    | NULL |    NULL | Select tables optimized away |
|  2 | DERIVED      | salaries   | ALL  | NULL          | NULL | NULL    | NULL | 2713588 |                              |
|  3 | UNION        | salaries   | ALL  | NULL          | NULL | NULL    | NULL | 1351439 |                              |
| NULL | UNION RESULT | &lt;union2,3&gt; | ALL  | NULL          | NULL | NULL    | NULL |    NULL |                              |
+----+--------------+------------+------+---------------+------+---------+------+---------+------------------------------+
4 rows in set (9.85 sec)</pre>
<p>Oh my god!</p>
<p>Even a query with sub-queries is faster by a factor of 20!</p>
<pre>(root@127.0.0.1) [employees]&gt;SELECT SQL_NO_CACHE ( SELECT COUNT(*) FROM `test`.`salaries` ) + ( SELECT COUNT(*) FROM `employees`.`salaries` );</pre>
<pre>+----------------------------------------------------------------------------------------------+</pre>
<pre>| ( SELECT COUNT(*) FROM `test`.`salaries` ) + ( SELECT COUNT(*) FROM `employees`.`salaries` ) |</pre>
<pre>+----------------------------------------------------------------------------------------------+</pre>
<pre>|                                                                                      2844047 |</pre>
<pre>+----------------------------------------------------------------------------------------------+</pre>
<pre>1 row in set (0.56 sec)</pre>
<pre>(root@127.0.0.1) [employees]&gt;EXPLAIN SELECT SQL_NO_CACHE ( SELECT COUNT(*) FROM `test`.`salaries` ) + ( SELECT COUNT(*) FROM `employees`.`salaries` );</pre>
<pre>+----+-------------+----------+-------+---------------+--------+---------+------+---------+----------------+</pre>
<pre>| id | select_type | table    | type  | possible_keys | key    | key_len | ref  | rows    | Extra          |</pre>
<pre>+----+-------------+----------+-------+---------------+--------+---------+------+---------+----------------+</pre>
<pre>|  1 | PRIMARY     | NULL     | NULL  | NULL          | NULL   | NULL    | NULL |    NULL | No tables used |</pre>
<pre>|  3 | SUBQUERY    | salaries | index | NULL          | emp_no | 4       | NULL | 2713588 | Using index    |</pre>
<pre>|  2 | SUBQUERY    | salaries | index | NULL          | emp_no | 4       | NULL | 1351439 | Using index    |</pre>
<pre>+----+-------------+----------+-------+---------------+--------+---------+------+---------+----------------+</pre>
<pre>3 rows in set (0.00 sec)</pre>
<p>I guess for now I&#8217;ll have to wait till views are optimised before I can use them like this&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.robinclarke.net/archives/view-and-union-very-slow/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
