<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4622775563416752930</id><updated>2012-01-18T07:01:45.607Z</updated><category term='myisam'/><category term='mysql big-data'/><category term='cassandra'/><category term='transactions'/><category term='change control'/><category term='cluster linux coding'/><category term='data distributed'/><category term='mysql'/><category term='scm'/><category term='mysql high-availabilty'/><category term='performance'/><category term='canvas'/><category term='robustness'/><category term='nonsense'/><category term='game-programming'/><category term='rant'/><category term='database'/><title type='text'>Mark's stream of verbiage</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>25</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-7710649045577135747</id><published>2011-05-23T20:19:00.004+01:00</published><updated>2011-05-23T20:38:14.800+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql high-availabilty'/><title type='text'>Expecting the unexpected</title><content type='html'>Few developers consider, when trying to build robust platforms, all the possible modes of failure. Indeed, it is difficult to consider them all, let alone plan for them, or design tests which exercise particular symptoms.&lt;br /&gt;&lt;br /&gt;In this post, I discuss some of the types of failure we can see in real systems.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Complete server failure&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Most developers DO consider this. In a "Complete server failure", what generally happens is:&lt;br /&gt;&lt;br /&gt;* The server stops processing new requests, completely.&lt;br /&gt;* The server's OS no longer responds to any network request at all (e.g. "ping")&lt;br /&gt;* Processing does not continue within the server&lt;br /&gt;* The contents of memory are immediately and irretrievably lost.&lt;br /&gt;&lt;br /&gt;Typically, the server recovers, and when it does so, it is rebooted and restored to full health. All writes which were acknowledged before its failure have been persisted.&lt;br /&gt;&lt;br /&gt;This is very easy to simulate (just hit the "power off" button in your VM hypervisor) and fairly easy to plan for; most robust systems consider this kind of scenario.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Network failure&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;There are many different kinds of network failure, but consider the simplest, most severe network failure:&lt;br /&gt;&lt;br /&gt;* One or more machines in the infrastructure lose network connectivity&lt;br /&gt;* None of them can talk to anything at all, including each other&lt;br /&gt;* Local processing on these servers continues as normal&lt;br /&gt;* No machines need to be rebooted to fix the fault, when it is repaired everything is back to normal.&lt;br /&gt;&lt;br /&gt;This is a symptom of, perhaps a switch failure, where a "complete" failure occurs.&lt;br /&gt;&lt;br /&gt;I won't discuss network failures at all, but there are many different kinds. My experience suggests that the most common is partial or complete loss of internet connectivity from one location (datacentre).&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;IO subsystem failures&lt;/h2&gt;&lt;br /&gt;* One or more discs / volumes suddenly become unavailable&lt;br /&gt;* The OS does not reboot; processes do not stop&lt;br /&gt;&lt;br /&gt;These are the kinds of failures which developers typically don't consider and are a lot more difficult to simulate. What might happen is, the power fails for a disc enclosure unit, but not its host server, in this case the OS and its boot discs remain available, but data discs are not. In these cases, failover might not be triggered or might behave incorrectly.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Heavy load or unexpected poor performance&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;* A single server unexpectedly starts performing very badly&lt;br /&gt;* In the extreme, this means without sufficient capacity to do useful work&lt;br /&gt;* But it's not failed; no subsystem is individually totally unavailable&lt;br /&gt;* Sometimes the effect is severe enough to prevent operations engineers logging in to diagnose / fix the fault&lt;br /&gt;&lt;br /&gt;These kinds of faults usually cause a larger problem, because failover systems aren't triggered, or cannot take over in a timely fashion. Common causes can be&lt;br /&gt;&lt;br /&gt;* Rogue process consuming lots of resources&lt;br /&gt;* Denial-of-service attack&lt;br /&gt;* Bad application design causing legitimate requests to suddenly spike in resource usage.&lt;br /&gt;* Operational error (well, anything can be caused by operational error :) )&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;"Zombie" systems or, back from the dead&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;* A system fails in a catastrophic way and can't be remotely recovered&lt;br /&gt;* Operations engineers assume that it's going to be completely dead until physically replaced (They are some distance away and don't raise a "remote hands" request, or are unable to recover it by doing so)&lt;br /&gt;* Another system is provisioned in its place, and takes over its IP address, role etc&lt;br /&gt;* Then one day... the "Zombie" system unexpectedly comes back from the dead to haunt its successsor ... Brraaainss.... &lt;br /&gt;&lt;br /&gt;Of course this could be months later, after many software updates (possibly security updates). The "zombie" system is running an old build and will not carry out correct processing if it is given work to do.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;These are just a few of the annoying types of failures which happen to real systems in production. Expect the unexpected (as if that's not a contradiction!).&lt;br /&gt;&lt;br /&gt;Happy hacking!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-7710649045577135747?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/7710649045577135747/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=7710649045577135747' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/7710649045577135747'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/7710649045577135747'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2011/05/expecting-unexpected.html' title='Expecting the unexpected'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-1518046623054139606</id><published>2011-02-20T08:33:00.003Z</published><updated>2011-02-20T08:38:09.580Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='canvas'/><category scheme='http://www.blogger.com/atom/ns#' term='game-programming'/><title type='text'>HTML 2d Canvas upscaling - really inefficient</title><content type='html'>I started writing some test programs with the HTML canvas element. This is great, as you can actually write games in Javascript - efficiently - in principle.&lt;br /&gt;&lt;br /&gt;My previous attempts have all used the DOM API which is not very convenient, and not very efficient.&lt;br /&gt;&lt;br /&gt;I had assumed the canvas 2d drawing context was basically a software-renderer - it's not extremely efficient, but provided the canvas doesn't have too many pixels, you can still do a lot of work per frame on a modern machine.  &lt;br /&gt;&lt;br /&gt;Which is fine.&lt;br /&gt;&lt;br /&gt;Suppose you have a canvas which is 640x320 pixels, you can then get it upscaled into whatever resolution is in the browser window, making the game appear the same size for everyone. Great.&lt;br /&gt;&lt;br /&gt;Except, the upscaling in web browsers sucks performance. I tried Firefox 3.6 and Chrome 9. Both of them use loads of CPU scaling the canvas on to the screen.&lt;br /&gt;&lt;br /&gt;If we use a canvas element without any scaling (no CSS width etc) then all is fine.&lt;br /&gt;&lt;br /&gt;Scale it up to a large window, Boom! now it's slow as a pregnant snail. Bummer.&lt;br /&gt;&lt;br /&gt;See &lt;a href="http://vectrex.org.uk/mark/canvastest.html"&gt;Example here&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-1518046623054139606?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/1518046623054139606/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=1518046623054139606' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/1518046623054139606'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/1518046623054139606'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2011/02/html-2d-canvas-upscaling-really.html' title='HTML 2d Canvas upscaling - really inefficient'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-6629528762598690058</id><published>2010-05-26T17:59:00.005+01:00</published><updated>2011-02-20T08:38:48.641Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='nonsense'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>MySQL, what are you smoking?</title><content type='html'>There are a lot of weird things which MySQL does to handle its mix of transactional and non-transactional behaviour, but this one was new to me :)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;create table t1 (ID INT NOT NULL PRIMARY KEY, V INT NOT NULL);&lt;br /&gt;&lt;br /&gt;Query OK, 0 rows affected (0.01 sec)&lt;br /&gt;&lt;br /&gt;mysql&gt; insert into t1 (ID,V) VALUES (2,NULL);&lt;br /&gt;ERROR 1048 (23000): Column 'V' cannot be null&lt;br /&gt;&lt;br /&gt;mysql&gt; insert into t1 (ID,V) VALUES (3,1),(4,1);&lt;br /&gt;Query OK, 2 rows affected (0.00 sec)&lt;br /&gt;Records: 2  Duplicates: 0  Warnings: 0&lt;br /&gt;&lt;br /&gt;insert into t1 (ID,V) VALUES (5,1),(6,NULL);&lt;br /&gt;Query OK, 2 rows affected, 1 warning (0.00 sec)&lt;br /&gt;Records: 2  Duplicates: 0  Warnings: 1&lt;br /&gt;&lt;br /&gt;mysql&gt; show warnings;&lt;br /&gt;+---------+------+---------------------------+&lt;br /&gt;| Level   | Code | Message                   |&lt;br /&gt;+---------+------+---------------------------+&lt;br /&gt;| Warning | 1048 | Column 'V' cannot be null |&lt;br /&gt;+---------+------+---------------------------+&lt;br /&gt;1 row in set (0.00 sec)&lt;br /&gt;&lt;br /&gt;select * from t1;&lt;br /&gt;&lt;br /&gt;+----+---+&lt;br /&gt;| ID | V |&lt;br /&gt;+----+---+&lt;br /&gt;|  3 | 1 |&lt;br /&gt;|  4 | 1 |&lt;br /&gt;|  5 | 1 |&lt;br /&gt;|  6 | 0 |&lt;br /&gt;+----+---+&lt;br /&gt;4 rows in set (0.00 sec)&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-size:130%;"&gt;What's going on?&lt;/span&gt;&lt;br /&gt;MySQL does not consider (some) errors to be errors, if they happen on the second or subsequent row of a multi-row insert.&lt;br /&gt;&lt;br /&gt;On the other hand, if it happens on the first row, it's an error.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Why?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;Because non-transactional engines can't rollback to a savepoint. This means that if it's inserted one or more rows already, to generate an error would leave some stuff in the database.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;No, really why?&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;I don't know. This is not consistent with, for example, a unique index violation, which makes it stop half way through a multi-row insert anyway, and non-transactional engines can't rollback.&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;So if you insert a duplicate, THAT still generates an error on the second a subsequent row. It's not even consistent!&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;mysql&gt; insert into t1 (ID,V) VALUES (10,1),(10,2);&lt;br /&gt;ERROR 1062 (23000): Duplicate entry '10' for key 'PRIMARY'&lt;br /&gt;mysql&gt; select * from t1;&lt;br /&gt;+----+---+&lt;br /&gt;| ID | V |&lt;br /&gt;+----+---+&lt;br /&gt;|  3 | 1 |&lt;br /&gt;|  4 | 1 |&lt;br /&gt;|  5 | 1 |&lt;br /&gt;|  6 | 0 |&lt;br /&gt;| 10 | 1 |&lt;br /&gt;+----+---+&lt;br /&gt;5 rows in set (0.00 sec)&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;Of course if we use a transactional engine, it looks better:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mysql&gt; ALTER TABLE t1 ENGINE=InnoDB;&lt;br /&gt;Query OK, 5 rows affected (0.00 sec)&lt;br /&gt;Records: 5  Duplicates: 0  Warnings: 0&lt;br /&gt;&lt;br /&gt;mysql&gt; insert into t1 (ID,V) VALUES (20,1),(20,2);&lt;br /&gt;ERROR 1062 (23000): Duplicate entry '20' for key 'PRIMARY'&lt;br /&gt;mysql&gt; select * from t1 WHERE ID=20;&lt;br /&gt;Empty set (0.00 sec)&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;But that won't change the behaviour of a multi-row insert with NULLs in invalid places.&lt;br /&gt;&lt;br /&gt;This kind of stuff is nonsense and we need it to GO AWAY NOW.&lt;br /&gt;&lt;br /&gt;How?&lt;br /&gt;&lt;br /&gt;SET SQL_MODE='STRICT_ALL_TABLES' &lt;br /&gt;&lt;br /&gt;And now every error is really an error. Yay! Why can't this be the default? (I know the answer; this is a rhetorical question)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-6629528762598690058?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/6629528762598690058/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=6629528762598690058' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/6629528762598690058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/6629528762598690058'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2010/05/mysql-what-are-you-smoking.html' title='MySQL, what are you smoking?'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-190692183671004730</id><published>2010-04-25T11:49:00.002+01:00</published><updated>2010-04-25T12:01:49.279+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql big-data'/><title type='text'>Streaming data from MySQL</title><content type='html'>How do we stream data from MySQL? Traditionally, developers have thought that they can simply use their API's cursor-equivalent object (e.g. resultset) and move through the results.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Unfortunately, this does not do streaming in most cases. Normally most client libraries (which call mysql_store_result) will read the entire result into memory, and you're just going through an already-in-memory data set. This will fail if it doesn't fit in memory.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Enter &lt;a href="http://dev.mysql.com/doc/refman/5.1/en/mysql-use-result.html"&gt;mysql_use_result&lt;/a&gt; - if your library can use this instead of mysql_store_result, you can then skip through the records without needing to keep them all in ram at once.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;However, there is some bad news - while going through a result set in mysql_use_result, you can't do any OTHER queries on the connection. An attempt to do so generally violates MySQL's protocol and fails. So if you need to do other queries while you're processing the large data set, you should open another connection.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We've found that in Perl DBI, mysql_use_result performs significantly worse on small data sets (i.e. ones which fit in ram, say 100M - 1G) than mysql_store_result (which is used by default). It seems that a lot more requests need to be done into the libraries from Perl.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Enter &lt;a href="http://dev.mysql.com/doc/refman/5.1/en/handler.html"&gt;HANDLER&lt;/a&gt;. This is basically an SQL-level object which behaves a bit like a server-side cursor would in some other databases. You can have multiple handlers open on the same connection, and can do other operations while a handler is open. The disadvantage: it cannot be used for arbitrary queries. Only table and index scans of a single table can be done - no joins or sorting. And the client needs to know a bit about the table to choose the best method - the optimiser doesn't get involved.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;However, in the cases where a lot of data are likely to be returned, we can either not require joins, or do joins on the client-side - if there are small "dimension tables" that can be held in-memory on the client.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;None of this is great of course, it would be better if mysql actually supported server-side cursors. Unfortunately it doesn't (except in stored procedures).&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-190692183671004730?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/190692183671004730/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=190692183671004730' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/190692183671004730'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/190692183671004730'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2010/04/streaming-data-from-mysql.html' title='Streaming data from MySQL'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-4344612450871764386</id><published>2009-11-29T13:31:00.004Z</published><updated>2009-11-29T14:44:04.670Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='cassandra'/><title type='text'>What is Cassandra good for</title><content type='html'>The &lt;a href="http://incubator.apache.org/cassandra/"&gt;Cassandra database &lt;/a&gt;has been getting quite a lot of publicity recently. I think this is a good thing in general, but it seems that some people are considering using it for unsuitable purposes.&lt;br /&gt;&lt;br /&gt;Cassandra is a cluster database which uses multiple nodes to provide&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Read-scaling&lt;/li&gt;&lt;li&gt;Write-scaling&lt;/li&gt;&lt;li&gt;High availability&lt;/li&gt;&lt;/ul&gt;Unless you need at least TWO of those things, you should probably not bother.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:180%;"&gt;Good reasons to use Cassandra:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;High availability&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Cassandra tolerates the failure of some nodes and will continue to read data and take writes despite some nodes being offline or unreachable - the exact behaviour depends on its settings and what consistency level of read/write is requested.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Write scaling&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Cassandra allows you to scale writes by just adding more nodes; writes are split between nodes, hence you can generally get better and better write performance by JUST adding more nodes (NB: it doesn't necessarily do load balancing, so you might not in all cases, but this is what it aspires to)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:180%;"&gt;Less good reasons to use Cassandra&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Read scaling&lt;/span&gt;&lt;br /&gt;Cassandra gives you read-scaling in the same way as write-scaling. This is a good thing, but can also be achieved relatively easily* with a conventional database by adding more and more read-only slaves / replicas, or using a cache (if you tend to get a lot of similar requests). Many big MySQL users do both.&lt;br /&gt;&lt;br /&gt;Also Cassandra does NOT create more than the configured number of replicas of any given piece of data, regardless of the amount of traffic on that part, so you could end up having a small number of servers hammered and the rest idle.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:180%;"&gt;Bad reasons to use Cassandra&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Schema flexibility &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;aka "I cannot figure out how to use ALTER TABLE", or at least make a flexible conventional schema ...&lt;br /&gt;&lt;br /&gt;Some people have cited schema flexibility as a good reason to use Cassandra (same argument applies for Voldemort, Couchdb etc).&lt;br /&gt;&lt;br /&gt;However, in practice this is NOT a benefit, because it comes at the cost of EVERYTHING ELSE YOU HAVE IN A TRADITIONAL DATABASE.&lt;br /&gt;&lt;br /&gt;Let's see what Cassandra does NOT do:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Secondary indexes - I'd be really surprised if your app doesn't need any of those!&lt;/li&gt;&lt;li&gt;Sorting, grouping or other advanced queries&lt;/li&gt;&lt;li&gt;Filtering (mostly)&lt;/li&gt;&lt;li&gt;Synchronous behaviour of updates&lt;/li&gt;&lt;li&gt;Bulk updates (UPDATE 10,000 rows in one operation)&lt;/li&gt;&lt;li&gt;Efficient table creation / drop&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;That's quite a big list (and very incomplete) so you'd better have a better reason for using it than "I cannot figure out how to use ALTER TABLE"&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:100%;" &gt;Because X or Y uses it&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;Just because&lt;/span&gt;&lt;span style="font-size:100%;"&gt; &lt;/span&gt;&lt;span style="font-size:100%;"&gt;Digg, Facebook et al &lt;/span&gt;&lt;span style="font-size:100%;"&gt;use Cassandra, doesn't mean you have to. Your data are probably more important than theirs. Your workload is probably different from theirs. In particular, your write/read scale requirements are probably less than theirs.&lt;/span&gt;&lt;span style="font-weight: bold;font-size:100%;" &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;I have a lot of respect for Facebook, Digg developers etc, but I also have a lot of envy:&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;They lose data, nobody cares&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;They lose data, nobody rings up and complains&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;They lose data, and NOBODY DEMANDS THEIR MONEY BACK&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:100%;"&gt;They could get a bit of bad press, their users might desert them in numbers, but they wouldn't lose money directly and immediately.&lt;br /&gt;&lt;br /&gt;Most companies who have big data provide a service, which comes with an SLA. The SLA often says that if we lose their data, they get their money back.&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;font-size:100%;" &gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;* May or may not be easy, depending on the calibre of your developers, ops staff, change control requirements, data structure etc.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-4344612450871764386?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/4344612450871764386/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=4344612450871764386' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/4344612450871764386'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/4344612450871764386'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2009/11/what-is-cassandra-good-for.html' title='What is Cassandra good for'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-1231509183564116288</id><published>2009-10-27T21:15:00.000Z</published><updated>2009-10-27T21:15:42.077Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='robustness'/><category scheme='http://www.blogger.com/atom/ns#' term='transactions'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>When commit appears to fail</title><content type='html'>So you're using explicit transactions. Everything appears to work (every query gives the expected result) until you get to COMMIT.&lt;br /&gt;&lt;br /&gt;Then you get an exception thrown from COMMIT. What happened?&lt;br /&gt;&lt;br /&gt;Usually this would be because the server has been shut down, or you've lost the connection.&lt;br /&gt;&lt;br /&gt;The problem is, that you can't assume that the commit failed, but you also can't assume it succeeded.&lt;br /&gt;&lt;br /&gt;A robust application must make NO ASSUMPTION about whether a failed commit did, indeed, commit the transaction or not. It can safely assume that either all or none of it was committed, but can't easily tell which.&lt;br /&gt;&lt;br /&gt;So the only way to really know is to have your application somehow remember that the transaction MIGHT have failed, and check later.&lt;br /&gt;&lt;br /&gt;Possible solutions:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Ignore it and deal with any inconsistencies manually, or decide that you don't care :)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Write your entire transaction such that if it is repeated having been committed previously, it is a no-op or has no harmful side effects (e.g. change INSERT to INSERT IGNORE to avoid unique index violations). This is rather difficult for complex transactions, but works for some.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Check, in your code, even if the commit apparently succeeded that it really did. If you discover that it didn't, then retry or report failure to the caller / user.&lt;/li&gt;&lt;li&gt;Perform another transaction to "undo" or "cancel" whatever the original transaction did if commit failed (Problem 1: this may itself fail to commit; problem 2: for a short time, an inconsistency exists)&lt;/li&gt;&lt;/ul&gt;None of these is ideal, and I'd like to think that this never happens. But if you do enough transactions, it's going to happen sooner or later (networks and servers do fail, unfortunately)&lt;br /&gt;&lt;br /&gt;In testing, we've found that this happens so infrequently that even deliberate malice (kill -9 of mysql while loads of transactions are processing etc) fails to reproduce it reliably. In the end, a set of iptables rules which blocks the response from a commit was constructed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-1231509183564116288?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/1231509183564116288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=1231509183564116288' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/1231509183564116288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/1231509183564116288'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2009/10/when-commit-appears-to-fail.html' title='When commit appears to fail'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-7086142281747819162</id><published>2009-09-02T13:00:00.004+01:00</published><updated>2009-09-02T13:12:44.891+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='myisam'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>On mysql's myisam_block_size setting</title><content type='html'>There is a little-known setting, myisam_block_size in MySQL. This affects the block size used in the indexes of MyISAM tables stored in .MYI files on disc and in the key buffer.&lt;br /&gt;&lt;br /&gt;The default value is 1k, this is possibly too small for best performance on modern systems; in particular, many filesystems used a bigger block size, so writing a single index block &lt;span style="font-weight: bold;"&gt;requires a read followed by a write.&lt;/span&gt; Random reads are really slow on hard discs (writes are mostly fast as they go into your battery-backed raid controller which has lots of RAM).&lt;br /&gt;&lt;br /&gt;I am currently in the process of experimenting with myisam_block_size, and so far have determined the following:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;myisam_block_size is settable only at server start time, either in my.cnf or on the command-line&lt;/li&gt;&lt;li&gt;myisam_block_size only affects newly created tables or tables rebuilt using ALTER TABLE; existing MyISAM tables keep their old index block size and work no differently.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;There is a second setting, key_buffer_block_size, which is different. This controls the size of blocks in the key buffer, which is independent.&lt;br /&gt;&lt;br /&gt;Advantages of higher myisam_block_size&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Fewer IO requests required to satisfy some requests (e.g. range scans) as fewer blocks are needed&lt;/li&gt;&lt;li&gt;Better index prefix compression, as the blocks are larger - particularly with PACK_KEYS enabled (index compression works on a block-by-block basis)&lt;/li&gt;&lt;li&gt;No read-then-write required by the OS to write to the underlying disc, if the block size is &gt;= the filesystem block size&lt;/li&gt;&lt;/ul&gt;Disadvantages&lt;br /&gt;&lt;ul&gt;&lt;li&gt;More wasted space in partially filled blocks&lt;/li&gt;&lt;li&gt;Possibly less efficient use of key cache, as more of the "wrong" blocks are cached &lt;/li&gt;&lt;li&gt;Larger IOs are required to fulfil the same request often - as it requires the same number of bigger blocks&lt;/li&gt;&lt;/ul&gt;So it could work either way. It may depend on your workload.&lt;br /&gt;&lt;br /&gt;Testing continues.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-7086142281747819162?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/7086142281747819162/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=7086142281747819162' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/7086142281747819162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/7086142281747819162'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2009/09/on-mysqls-myisamblocksize-setting.html' title='On mysql&apos;s myisam_block_size setting'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-835101603105709479</id><published>2009-08-06T18:19:00.004+01:00</published><updated>2009-08-06T18:27:28.275+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cassandra'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>Cassandra database and range scans</title><content type='html'>I've been doing a little more playing with &lt;a href="http://incubator.apache.org/cassandra/"&gt;Cassandra&lt;/a&gt;, an open source distributed database. It has several features which make it very compelling for storing large data which has a lot of writes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Write-scaling - adding more nodes increases write capacity&lt;/li&gt;&lt;li&gt;No single point of failure&lt;/li&gt;&lt;li&gt;configurable redundancy&lt;/li&gt;&lt;/ul&gt;And the most important:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Key range scans&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Key range scans are really important because they allow applications to do what users normally want to do:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;What emails did I receive this week&lt;/li&gt;&lt;li&gt;Give me all the transactions for customer X in time range Y&lt;/li&gt;&lt;/ul&gt;Answering these questions without range scans is extremely difficult; with efficient range scans they become fairly easy (provided you pick your keys right).&lt;br /&gt;&lt;br /&gt;Other distributed-hash-table database (e.g. Voldemort) don't do this. This makes it difficult to do such queries.&lt;br /&gt;&lt;br /&gt;Conventional RDBMSs do range scans all the time, in fact many queries which return more than one row will be implemented as a range scan.&lt;br /&gt;&lt;br /&gt;Cassandra is extremely promising, but still a little bit rough around the edges; I've only done a small amount of research so far, but already found several bugs.&lt;br /&gt;&lt;br /&gt;I can't complain about the service though; the main developer(s) have always looked into any problems I've reported immediately.&lt;br /&gt;&lt;br /&gt;I hope it continues and becomes something really good.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-835101603105709479?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/835101603105709479/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=835101603105709479' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/835101603105709479'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/835101603105709479'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2009/08/cassandra-database-and-range-scans.html' title='Cassandra database and range scans'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-2693441999382203514</id><published>2009-03-29T18:21:00.003+01:00</published><updated>2009-03-29T18:33:47.831+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='data distributed'/><title type='text'>Distributed data storage systems</title><content type='html'>I'm looking for a distributed data storage system. Ideally such a system would have some (or ideally all) of the following characteristics:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Software only, using shared-nothing (must)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Stores arbitrarily large (actually 2G would be enough) items of binary data accessed by a key (a short-ish string would do) specified at store-time. Items would be stored across multiple storage nodes.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;No single point of failure (preferable,  a single point of failure which does not immediately impact the service would be acceptable)&lt;/li&gt;&lt;li&gt;Keeps N copies of each item in different nodes, specifyable either in config or at store-time&lt;/li&gt;&lt;li&gt;Automatic repairer to re-duplicate items following a storage node's demise (or administrative removal)&lt;/li&gt;&lt;li&gt;Automatic expiry of old data after a time specified at store-time&lt;/li&gt;&lt;li&gt;Managability: all nodes would share the same config; nodes can be administratively added and removed without any explicit config changes to other nodes.&lt;/li&gt;&lt;li&gt;Storage management: nodes should be able to be configured to use a given amount of maximum space; nodes should be able to be put into "readonly" mode where new data are not accepted&lt;/li&gt;&lt;li&gt;Automatic balancing of load for storage of new items&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Monitoring: some cluster-aware monitoring tools which could report on the number of available nodes, total space available, which nodes were almost full, how much data is broken and being repaired, etc.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;This is a bit of a tall order. There are some systems which almost do it, or do many of the above.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://allmydata.org/source/tahoe/trunk/docs/about.html"&gt;Tahoe &lt;/a&gt;seems to be the closest so far.&lt;br /&gt;&lt;br /&gt;Of course things like Amazon S3 must do at least most of the above internally, but they aren't open source, indeed you can't even buy it except as a service.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-2693441999382203514?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/2693441999382203514/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=2693441999382203514' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/2693441999382203514'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/2693441999382203514'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2009/03/distributed-data-storage-systems.html' title='Distributed data storage systems'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-2314611426984874962</id><published>2009-03-17T13:16:00.002Z</published><updated>2009-03-17T13:18:21.426Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>REPAIR TABLE does not always upgrade it</title><content type='html'>MySQL 5.0 uses a new MyISAM table format than 4.1. So when upgrading, the tables need to be upgraded.&lt;br /&gt;&lt;br /&gt;This can be done using REPAIR TABLE. Or can it?&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;On 5.0.67 (I've been testing with), REPAIR TABLE does indeed upgrade it&lt;/li&gt;&lt;li&gt;On 5.0.22 (Earlier version, used on a backup slave), REPAIR TABLE does NOT upgrade it&lt;/li&gt;&lt;/ul&gt;Oh dear. That breaks my plan for how to upgrade the slave. Not a disaster, but annoying nonetheless&lt;br /&gt;&lt;br /&gt;NB: this is a non-production system, and this is the kind of thing that we discover by testing it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-2314611426984874962?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/2314611426984874962/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=2314611426984874962' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/2314611426984874962'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/2314611426984874962'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2009/03/repair-table-does-not-always-upgrade-it.html' title='REPAIR TABLE does not always upgrade it'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-1704209081344485941</id><published>2008-12-21T11:42:00.002Z</published><updated>2008-12-21T11:48:33.223Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='robustness'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>MySQL running out of disc space</title><content type='html'>Running out of disc space is not a good situation. However, if it does happen, it would be nice to have some control over what happens.&lt;br /&gt;&lt;br /&gt;We use MyISAM. When you run out of disc space, MyISAM just sits there and waits. And waits, and waits, apparently forever, for some space to become available.&lt;br /&gt;&lt;br /&gt;This is not good, because an auditing/logging application (which ours is) may have lots of available servers which it could send its data to - getting an error from one would simply mean that the data could be audited elsewhere.&lt;br /&gt;&lt;br /&gt;But if the server just hangs, and waits, the application isn't (currently) smart enough to give up and try another server, so it hangs the audit process too. Which means that audit data starts to back up, and customers wonder why they can't see recent data in their reports etc.&lt;br /&gt;&lt;br /&gt;There has to be a better way. I propose&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A background thread monitors the disc space level every few seconds&lt;br /&gt;&lt;/li&gt;&lt;li&gt;When it falls below a critical level (still more than can reasonably be filled up in a few seconds), force the server to become read-only&lt;/li&gt;&lt;li&gt;When in this mode, modifications to the data fail, quickly, with an error code which tells the process (or develope) exactly what the problem is (Out of disc space)&lt;/li&gt;&lt;li&gt;When the disc space falls below some threshold, the read-only mode is turned back off.&lt;/li&gt;&lt;/ul&gt;That way, clients get what they expect - either quick service for inserts, or a fast error telling them what's wrong (Go away, I'm full, audit your data somewhere else)&lt;br /&gt;&lt;br /&gt;Drizzle etc, should do this.&lt;br /&gt;&lt;br /&gt;Or perhaps, it's a job for the storage engine?&lt;br /&gt;&lt;br /&gt;Happy Christmas.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-1704209081344485941?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/1704209081344485941/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=1704209081344485941' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/1704209081344485941'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/1704209081344485941'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2008/12/mysql-running-out-of-disc-space.html' title='MySQL running out of disc space'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-5387214188516903529</id><published>2008-09-16T10:25:00.003+01:00</published><updated>2008-09-16T10:35:45.502+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>Migrating from MySQL 4.1 to 5.x</title><content type='html'>Suppose I have around 30 servers with MySQL 4.1 each with up to 1Tb of MyISAM tables. We need to upgrade their OS to support new hardware. We want to upgrade to 64-bit architecture to use the hardware efficiently.&lt;br /&gt;&lt;br /&gt;Upgrading to 5.x seems like a good idea, as the additional validation work will be the same as would have to be done anyway - so we could effectively upgrade our MySQL version "for free", i.e. no additional QA work would be required than would be needed anyway.&lt;br /&gt;&lt;br /&gt;Upgrading at any other time would involve a repetition of the validation exercise, which is not trivial as we must test at least:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;All features of the complex application which use these databases still work without errors&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The behaviour of the new version is consistent with the old one (e.g. results returned)&lt;/li&gt;&lt;li&gt;Performance needs to be measured to ensure that performance regressions haven't happened (or are acceptable)&lt;/li&gt;&lt;li&gt;Soak / stress testing needs to be done to ensure that the new version can perform under sustained high load.&lt;/li&gt;&lt;/ul&gt;Of course some changes to our software are inevitable, but these are likely to be minor. Testing is the main work.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Migration and rollback&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;My initial plan was to leave the MyISAM table files exactly as they were - after all, 5.0.x can read them fine (apparently) - so that if we need to rollback, we simply move back to the old version and everything works as before (including data created while we were on the new version).&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;However, this looks tricky. Load testing confirms that 5.0.x has frequent crashes when dealing with "old" tables (i.e. those which have not been upgraded). The MySQL manual insists that mysql_upgrade is run to upgrade all existing MyISAM tables.&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;This is fine if your database is small and noncritical, but ours are large and important. So what to do?&lt;br /&gt;&lt;br /&gt;It is possible to upgrade tables using CHECK TABLE FOR UPGRADE and REPAIR TABLE. However, &lt;span style="font-weight: bold;"&gt;NO ROLLBACK METHOD exists. &lt;/span&gt;This is a problem.&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;I've estimated (using performance data from a production-spec test system) that in production, migration can be performed in 2-3 hours. This is an acceptable level of planned downtime. The only way of rolling BACK the table versions is to mysqldump the database and reload it. I estimate that this could take 16 hours, which is not acceptable&lt;span style="font-weight: bold;"&gt;.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;It is not acceptable to apply a change without a working (i.e. tested) rollback procedure. Neither is it particularly acceptable to have 16 hours of downtime to perform one.&lt;br /&gt;&lt;br /&gt;This makes the upgrade tricky. We're working on it.&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-5387214188516903529?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/5387214188516903529/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=5387214188516903529' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/5387214188516903529'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/5387214188516903529'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2008/09/migrating-from-mysql-41-to-5x.html' title='Migrating from MySQL 4.1 to 5.x'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-5684488073160249125</id><published>2008-07-13T10:10:00.005+01:00</published><updated>2008-07-13T10:21:31.386+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scm'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='change control'/><title type='text'>Versioning your schema</title><content type='html'>How do you version your database schemas?&lt;br /&gt;&lt;br /&gt;There are quite a lot of different options. On some very small / trivial applications developed by just one person, it might be acceptable to use ad-hoc queries (maybe with a GUI) to manage the schema - but this runs the risk of development and production schemas becoming out of sync (or rather, out of sync with their respective versions of code).&lt;br /&gt;&lt;br /&gt;Requirements are typically&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Schema changes are held with the rest of the software in a SCM system&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Schema changes can be made in one development environment and will be correctly propogated with code to other development and production environments as required&lt;/li&gt;&lt;li&gt;Schema changes can be rolled back (if for example, a deployment contains a serious bug)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;There doesn't seem to be an easy answer for this one.&lt;br /&gt;&lt;br /&gt;Our teams do something like:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Have a base create.sql which generates the original schema&lt;/li&gt;&lt;li&gt;In each revision where a schema change is required, write an apply.sql and remove.sql script to apply / roll back the change&lt;/li&gt;&lt;li&gt;These scripts need to be hand written as there are often special cases - sometimes several ALTER statements are required to make a single change, sometimes in combination with UPDATEs etc. Rollback scripts are even more tricky and sometimes need to restore from a backup table.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Have a system of scripts which works out which apply.sql / remove.sql is/are required (when deploying a new version of the code) and runs them, in order to update the schema&lt;/li&gt;&lt;/ul&gt;The problem with this system is that it relies on developers correctly writing apply.sql and remove.sql - it is fairly error prone. Although our validation process is fairly rigorous, it still lets errors slip through occasionally.&lt;br /&gt;&lt;br /&gt;The problem is compounded by having multiple teams working on different code branches, not knowing necessarily which order their releases will go out in (one team would ultimately be responsible for merging the other's changes into their branch prior to release).&lt;br /&gt;&lt;br /&gt;Ideas on a postcard please :)&lt;br /&gt;&lt;br /&gt;Mark&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-5684488073160249125?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/5684488073160249125/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=5684488073160249125' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/5684488073160249125'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/5684488073160249125'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2008/07/versioning-your-schema.html' title='Versioning your schema'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-3256222045969138774</id><published>2008-05-10T13:23:00.004+01:00</published><updated>2008-05-10T13:56:57.248+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cluster linux coding'/><title type='text'>Linux IP load balancing without a load balancer</title><content type='html'>I've been investigating load balancing without a load balancer. I'm building my own implementation of a high availability IP load balancer / failover system.&lt;br /&gt;&lt;br /&gt;This will essentially work like the netfilter CLUSTERIP target, except that it will also be self-configuring and self-monitoring / repairing - thus not requiring other tools (such as the complicated LinuxHA tools) to work. Some other efforts to do this have been:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Saru http://www.ultramonkey.org/papers/active_active/ - seems abandoned&lt;/li&gt;&lt;li&gt;Microsoft's "network load balancing" does something similar&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;An author known as "flavio" wrote an &lt;a href="http://flaviostechnotalk.com/wordpress/index.php/2005/06/12/loadbalancer-less-clusters-on-linux/"&gt;article about load balancer-less clusters here&lt;/a&gt; but it seems to have disappeared although it's &lt;a href="http://web.archive.org/web/20070125103802/http://flaviostechnotalk.com/wordpress/index.php/2005/06/12/loadbalancer-less-clusters-on-linux/"&gt;still on the wayback machine&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;How IP load balancing works without a dedicated load balancer host is:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;ARP requests for the cluster IP address are responded to by a multicast ethernet address&lt;/li&gt;&lt;li&gt;All the hosts join the ethernet multicast group&lt;/li&gt;&lt;li&gt;Hosts selectively accept / ignore traffic based on whether they want to handle it or not, by some hashing algorithm.&lt;/li&gt;&lt;/ul&gt;I've started work on the &lt;a href="http://code.google.com/p/fluffy-linux-cluster/"&gt;implementation on google code&lt;/a&gt;. Most parts of it can be done in user-space (A kernel implementation might be necessary for performance later):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;I use arptables to block the kernel's own ARP responses on the load balanced IP, otherwise it would give out its own unicast link address.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;A small userspace daemon responds to ARP requests, giving out a multicast address.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The IP address is configured normally with "ip addr add ..."&lt;/li&gt;&lt;li&gt;Iptables is used to filter out the traffic we don't want and accept traffic we do want. It uses connection tracking to ensure that established connections are always kept, invalid ones ignored, and new connections passed to a userspace daemon using NFQUEUE&lt;br /&gt;&lt;/li&gt;&lt;li&gt;A userspace daemon reads the packets from NFQUEUE and uses a hashing algorithm to determine whther to accept them or not. Each host in the cluster receives the same packets and does the same hash - so reaches the same conclusion about who should receive the packet - thus EXACTLY ONE host will accept each new connection.&lt;/li&gt;&lt;/ul&gt;Load balancing can be done fairly (all nodes equal weight) or unfairly (different weights). Also, when administratively shutting down a node, we can set its weight to zero and existing connections will be allowed to finish (new ones will then be given to other nodes).&lt;br /&gt;&lt;br /&gt;I've created a very sketchy design, it's all basically completely do-able. The userspace daemon uses UDP multicast packets to talk to the other nodes, will organise a "leader" which will then tell the other nodes which hash values to accept/reject, ensuring that there is no overlap and no gaps.&lt;br /&gt;&lt;br /&gt;There are a lot of possibilities for race conditions during a reconfiguration due to a node weight change / failure / recovery. I haven't thought about these yet.&lt;br /&gt;&lt;br /&gt;This principle works well for TCP-based services such as web and email, but may not be good for some UDP-based services because conntrack cannot ensure that the packets continue going to the same node for the lifetime of the connection (as it does for TCP).&lt;br /&gt;&lt;br /&gt;---&lt;br /&gt;Problems / disadvantages:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Apparently, an ARP reply indicating a link-layer multicast address is &lt;a href="http://archives.free.net.ph/message/20061026.114112.69c57336.en.html"&gt;forbidden by RFC1812&lt;/a&gt;&lt;/li&gt;&lt;li&gt;The Linux kernel ignores TCP packets which have a link-layer multicast destination. I've worked around this with a really small kernel module (the same as what CLUSTERIP does)&lt;/li&gt;&lt;li&gt;Interoperability with other network OSs might not be good as this isn't a very official technique. Apparently some routers ignore these ARP packets.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-3256222045969138774?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/3256222045969138774/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=3256222045969138774' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/3256222045969138774'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/3256222045969138774'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2008/05/linux-ip-load-balancing-without-load.html' title='Linux IP load balancing without a load balancer'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-5573836006103337262</id><published>2008-04-20T18:04:00.006+01:00</published><updated>2008-11-13T14:04:43.317Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>MySQL engines space usage comparison - continued</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_wU9G21gJNKk/SAt5vQkUHZI/AAAAAAAAAAM/WTsIpyCBEnU/s1600-h/space_usage_by_engine.png"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_wU9G21gJNKk/SAt5vQkUHZI/AAAAAAAAAAM/WTsIpyCBEnU/s320/space_usage_by_engine.png" alt="" id="BLOGGER_PHOTO_ID_5191376848282721682" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Since Oracle announced the &lt;a href="http://www.innodb.com/innodb_plugin/"&gt;InnoDB plugin&lt;/a&gt; which provides compressed row formats, I thought I'd do this comparison again.&lt;br /&gt;&lt;br /&gt;Here are some more data that I've cobbled together.&lt;br /&gt;&lt;br /&gt;I've created a 2M row table with a schema that we use for audit data. It has 47 columns and 17 indexes.&lt;br /&gt;&lt;br /&gt;I copied the table into new empty tables using INSERT ... SELECT with each of these engines:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;InnoDB row_format=compressed (from the InnoDB plugin)&lt;/li&gt;&lt;li&gt;InnoDB row_format=Compact&lt;/li&gt;&lt;li&gt;InnoDB row_format=Redundant&lt;/li&gt;&lt;li&gt;MyISAM pack_keys=1&lt;br /&gt;&lt;/li&gt;&lt;li&gt;MyISAM packed with myisampack (NOTE: these tables are readonly)&lt;/li&gt;&lt;li&gt;PBXT&lt;/li&gt;&lt;li&gt;Falcon&lt;/li&gt;&lt;/ul&gt;Here are the data:&lt;br /&gt;&lt;br /&gt;&lt;table border="1"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Engine&lt;/th&gt;&lt;th&gt;Space (MB)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;InnoDB compressed&lt;/td&gt;&lt;td&gt;636&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;InnoDB compact&lt;/td&gt;&lt;td&gt;1946&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;InnoDB redundant&lt;/td&gt;&lt;td&gt;2253&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;MyISAM pack_keys=1&lt;/td&gt;&lt;td&gt;738&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;MyISAM packed&lt;/td&gt;&lt;td&gt;476&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;PBXT&lt;/td&gt;&lt;td&gt;3379&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Falcon&lt;/td&gt;&lt;td&gt;999&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;I am very impressed with how good Falcon is. I am very impressed with InnoDB's compressed row format's space usage.&lt;br /&gt;&lt;br /&gt;I don't think it's very reasonable to compare the loading times for the different engines as they all work in different ways, but some were a LOT faster than others. Take this with a pinch of salt:&lt;br /&gt;&lt;br /&gt;Fast: MyISAM, Falcon, InnoDB except compressed&lt;br /&gt;Slow: InnoDB compressed, PBXT&lt;br /&gt;&lt;br /&gt;InnoDB compressed and PBXT both took well over an hour to complete what the others did in a few minutes. I can't explain this - maybe it's a tuning issue.&lt;br /&gt;&lt;br /&gt;In each case, the copy was done in an INSERT ... SELECT which uses a single transaction. I tried to tune the buffers as best as I could (the server has enough ram) but I'm no expert in PBXT and Falcon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-5573836006103337262?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/5573836006103337262/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=5573836006103337262' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/5573836006103337262'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/5573836006103337262'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2008/04/mysql-engines-space-usage-comparison.html' title='MySQL engines space usage comparison - continued'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_wU9G21gJNKk/SAt5vQkUHZI/AAAAAAAAAAM/WTsIpyCBEnU/s72-c/space_usage_by_engine.png' height='72' width='72'/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-2874380844753879085</id><published>2008-04-13T12:29:00.006+01:00</published><updated>2008-04-13T12:59:56.467+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>Improve reliability and maintainability by using stateless connections</title><content type='html'>MySQL application developers have some problems with connection state. There is a lot of state which can be used associated with a single connection&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Transactions (autocommit, uncommitted work, when a transaction was started, isolation level)&lt;/li&gt;&lt;li&gt;Temporary tables&lt;/li&gt;&lt;li&gt;Session variables which affect behaviour&lt;/li&gt;&lt;li&gt;Other session variables&lt;/li&gt;&lt;/ul&gt;If you can avoid &lt;span style="font-weight: bold;"&gt;all&lt;/span&gt; of these, then you can benefit from transparent auto-reconnection and transparent connection reuse / pooling etc.&lt;br /&gt;&lt;br /&gt;If you can't, then you will have to deal with reconnection, retry at a higher level. This is complicated and difficult to test.&lt;br /&gt;&lt;br /&gt;I'll discuss ways of avoiding these things one at a time:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Transactions&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I'm not advocating shunning transactions completely, just avoiding keeping any transaction state in the connection:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Keep autocommit on. Execute everything you want in a transaction in a single statement (in MySQL it is possible to delete from several tables in one statement, insert several rows etc).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Transactional engines will still use transactions if autocommit is on - a single statement becomes a single transaction - you will see a consistent view within that statement - no other thread will see parts of a batch insert, update or delete.&lt;/li&gt;&lt;li&gt;Non-transactional engines behave the same anyway.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Alternative: do everything that needs to be in a transaction inside a stored procedure - if these are retried, they will be retried as a whole.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:130%;"&gt;Temporary tables&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;These are best avoided. They also cause problems for statement-level replication.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Use short-lived permanent tables with unique names instead (perhaps in a separate database which is automatically cleaned up when tables get old)&lt;/li&gt;&lt;li&gt;Wrap things which need temporary tables inside stored procedures&lt;/li&gt;&lt;li&gt;Use derived tables instead - in some cases this is sufficient.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:130%;"&gt;Session variables which affect behaviour&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Sometimes you can't get away from having to set these variables. Common examples are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;SET NAMES utf8&lt;/li&gt;&lt;li&gt;SET SQL_MODE = 'TRADITIONAL'&lt;/li&gt;&lt;/ul&gt;If an autoreconnect happens while these are in effect, you could accidentally get rubbish data inserted into the database if a connection reverts to latin1 unexpectedly or SQL_MODE reverts to its default "anything goes".&lt;br /&gt;&lt;br /&gt;As these are unavoidable, they always have to be set - but there is a solution - the &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/mysql-options.html"&gt;MYSQL_INIT_COMMAND&lt;/a&gt; option. As these commands won't actually have any effect on their own, we can have them automatically re-done when the library reconnects to the database.&lt;br /&gt;&lt;br /&gt;The only problem with this approach is that not all client library interfaces allow the setting of this option - but in some cases it can be set in my.cnf (for client libraries which read this)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Other session variables&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Quite simply - don't use them. There is no reason I know of for having another session variable.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Don't select @@LAST_INSERT_ID or last_insert_id() - use your API method to get it instead. An auto-reconnect happening between the insert and the select @@LAST_INSERT_ID will cause it to return zero, which is probably not what you wanted or expected.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Don't define your own variables - keep them on the client instead (or if you absolutely must store state server-side, put it in a table!)&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:130%;"&gt;Conclusions&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It is possible to enable automatic reconnect for greater resilience at the MySQL API level&lt;/li&gt;&lt;li&gt;To do so safely you must avoid keeping session state in the connection&lt;/li&gt;&lt;li&gt;Variables which need to be reset on connect can be reset with mysql_init_command&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-2874380844753879085?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/2874380844753879085/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=2874380844753879085' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/2874380844753879085'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/2874380844753879085'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2008/04/improve-reliability-and-maintainability.html' title='Improve reliability and maintainability by using stateless connections'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-7221292438195240914</id><published>2008-03-22T09:21:00.000Z</published><updated>2008-03-22T14:59:25.695Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>MySQL engines and space usage</title><content type='html'>A lot of people seem to spend a lot of effort comparing storage engine in MySQL - chiefly focusing on the difference between MyISAM and InnoDB.&lt;br /&gt;&lt;br /&gt;People normally compare:&lt;br /&gt;&lt;br /&gt;Feature sets:InnoDB: better durability, transactions, MVCC, foreign key constraints, row-level locking. MyISAM: fulltext, spatial indexes, table-level locking&lt;br /&gt;Run-time performance: (see your favourite benchmark)&lt;br /&gt;&lt;br /&gt;But few compare actual storage space usage. As this is very important to our application, I decided to run some tests.&lt;br /&gt;&lt;br /&gt;I'm testing here with a realistic-sized table for our application (we partition data into daily partitions and spread them across many servers anyway, so this is just a small piece). We currently use MyISAM, and this is a typical table with approximately 4 million rows.&lt;br /&gt;&lt;br /&gt;I can't dump the schema or content of this table here for confidentiality reasons, but it has:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;47 columns, many of which are VARCHARs&lt;br /&gt;&lt;/li&gt;&lt;li&gt;17 indexes&lt;/li&gt;&lt;li&gt;Typical row size of 300 bytes (as reported by "SHOW TABLE STATUS")&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Method&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;First I generated 4 Million rows of dummy data using a test tool (this is extremely useful as it avoids having to take real, potentially sensitive and very large data from production). Then I measured the space usage using SHOW TABLE STATUS, and successively ALTERed the table to different engines.&lt;br /&gt;&lt;br /&gt;Note that ALTERing a table causes its indexes to be rebuilt - they will be a good deal smaller than if the rows had been inserted normally. Therefore, just an ALTER which doesn't change anything could make a big space saving.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Results&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;table border="1"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Engine&lt;/th&gt;&lt;th&gt;Data size&lt;/th&gt;&lt;th&gt;Index size&lt;/th&gt;&lt;th&gt;Notes&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;MyISAM, PACK_KEYS=1 (initial)&lt;/td&gt;&lt;td&gt;1152854640&lt;/td&gt;&lt;td&gt;682459136&lt;/td&gt;&lt;td&gt;Initial data load&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;MyISAM, PACK_KEYS=0&lt;/td&gt;&lt;td&gt;1152854640&lt;/td&gt;&lt;td&gt;2396920832&lt;/td&gt;&lt;td&gt;ALTER&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;MyISAM, PACK_KEYS=DEFAULT&lt;/td&gt;&lt;td&gt;1152854640&lt;/td&gt;&lt;td&gt;677806080&lt;/td&gt;&lt;td&gt;ALTER&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;MyISAM, PACK_KEYS=1 (alter)&lt;/td&gt;&lt;td&gt;1152854640&lt;/td&gt;&lt;td&gt;433162240&lt;/td&gt;&lt;td&gt;ALTER&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;InnoDB&lt;/td&gt;&lt;td&gt;1973420032&lt;/td&gt;&lt;td&gt;2442166272&lt;/td&gt;&lt;td&gt;ALTER&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;sizes are in bytes.&lt;br /&gt;&lt;br /&gt;In case that's not clear from the numbers above, here is a summary of the results&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In MyISAM, the data are always the same size regardless of options&lt;/li&gt;&lt;li&gt;The initial (PACK_KEYS=1) index file was  650M&lt;/li&gt;&lt;li&gt;PACK_KEYS=0 makes the index 251% bigger&lt;/li&gt;&lt;li&gt;PACK_KEYS=DEFAULT makes the index marginally smaller (but it's been ALTERed which improves things)&lt;/li&gt;&lt;li&gt;Altering the table with PACK_KEYS=1 makes the index around 36% smaller than it originally was&lt;/li&gt;&lt;li&gt;InnoDB leaves the data 71% bigger and the index 257% bigger&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Moving from MyISAM to InnoDB in this case would see an overall increase in space usage of 140% - that's making the disc requirement nearly 2.5 x more.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Conclusions&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Before making a switch from MyISAM to InnoDB, plan your space usage carefully.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Measure your actual data, not mine. Yours may be different. We have a lot of indexes compared to some people.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Future work&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;I hope to be able to repeat this test with Falcon, Maria and PBXT some time soon.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-7221292438195240914?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/7221292438195240914/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=7221292438195240914' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/7221292438195240914'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/7221292438195240914'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2008/02/mysql-engines-and-space-usage.html' title='MySQL engines and space usage'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-7212017647295821018</id><published>2008-01-02T11:24:00.000Z</published><updated>2008-01-02T14:43:23.771Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>mysql address space</title><content type='html'>Address space can be a significant problem on 32-bit mysql installations. After several IRC discussions attempting to explain this to some people, I decided to write this post about it.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;What address space is&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Each process running on a multitasking virtual memory OS has its own private address space. This is a range of addresses of a fixed size. The exact size depends on the CPU architecture, but on most 32-bit processors it is 32-bits. Intel/AMD chips address memory in bytes individually, so the maximum capacity of the address space is 2^32 bytes.&lt;br /&gt;&lt;br /&gt;Address space has nothing whatsoever to do with the physical amount of RAM installed on a machine; you may have more address space than ram, but critically, you may also have less.&lt;br /&gt;&lt;br /&gt;It is not possible to modify or upgrade the address space of a machine or software program, except by recompiling it for a different architecture.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;How much space is available&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Unfortunately, not all of the approximately 4Gb of address space is usable by mysql for buffers; some is used by:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The operating system kernel reserving address space for its own use&lt;/li&gt;&lt;li&gt;Code from the mysql executable file and its libraries etc &lt;/li&gt;&lt;li&gt;Fixed data allocated by mysql and its libraries&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Thread stacks&lt;/li&gt;&lt;li&gt;etc&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;The amount that these use up is extremely system dependent, but it is normally safe to assume that on a 32-bit Linux OS, roughly 1.5Gb is available for mysql's buffers. On some systems there may be a bit more than this, but it's not likely to exceed 3Gb on any platform.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;What impact running out has on mysql&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;MySQL is a multithreaded server process - each instance of mysql running on a machine (there is typically only one; several applications or databases can use the same server). This means that all its threads share the same, limited address space.&lt;br /&gt;&lt;br /&gt;The database server can be tuned to use a lot of memory. There are two kinds of buffer - per thread and global buffers. The MyISAM key buffer is an example of a global buffer, the packet read buffer is an example of a per thread buffer. This is explained in more detail in the manual here:&lt;a href="http://dev.mysql.com/doc/refman/5.0/en/memory-use.html"&gt; How MYSQL uses memory&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;All this memory comes from the same, limited address space. Running out of address space is very bad. When you run out of address space, the following happens:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A memory allocation attempt will fail. How gracefully this happens depends on where the allocation attempt was made. It could be: in the mysql core, in a storage engine, in an underlying library (e.g. the C or C++ runtime libraries) or in some other extension.&lt;/li&gt;&lt;li&gt;MySQL will probably crash. If a crash happens:&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The server will restart, kicking all clients off.&lt;/li&gt;&lt;li&gt;InnoDB will start its recovery process. This may take a long time during which the server will be unusable.&lt;/li&gt;&lt;li&gt;Eventually all transactions will be rolled back and normal service will resume.&lt;/li&gt;&lt;li&gt;MyISAM tables which were in use at the time will be marked as crashed and needing a repair - this can take a very long time on large tables, during which time those tables are unusable.&lt;/li&gt;&lt;/ul&gt;This is very inconvenient for everyone concerned (the app developer, the sysadmin/DBA and especially the end user).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Mitigation: How to prevent running out&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;The most obvious and convenient solution is to run a 64-bit mysql server on a 64-bit OS. These have a lot more address space and are very unlikely to run out.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;If, for some reason, you cannot upgrade to 64-bit, it is a very good idea to ensure that you do not run out of address space. This is mostly done by &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/server-parameters.html"&gt;tuning the server parameters.&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Do not set max_connections higher than necessary.&lt;/li&gt;&lt;li&gt;Keep per-thread buffers to a minimum&lt;/li&gt;&lt;li&gt;Tune other buffers reasonably&lt;/li&gt;&lt;/ul&gt;Monitoring the address space in use by mysql is a good idea. This is normally possible under Linux with "top" or "ps", as the "virt" or "VSZ" columns show the amount of address space used.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Final notes&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Run a 64-bit OS if possible and for all new development. Run a 64-bit mysql if you run a 64-bit OS.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Running out of address space is not the same as running out of memory- it will probably happen a lot sooner (your server &lt;span style="font-weight: bold;"&gt;does&lt;/span&gt; have more than 2Gb of RAM, right?)&lt;/li&gt;&lt;li&gt;You can still run out of address space if you have less  than 2Gb of RAM, because virtual memory is included&lt;br /&gt;&lt;/li&gt;&lt;li&gt;None of this is Linux-specific, it will happen on other OSs too (but the amount of space reserved by the kernel may be less)&lt;/li&gt;&lt;li&gt;Avoiding address space exhaustion is important in production systems&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-7212017647295821018?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/7212017647295821018/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=7212017647295821018' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/7212017647295821018'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/7212017647295821018'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2008/01/mysql-address-space.html' title='mysql address space'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-5960844663855118858</id><published>2007-12-29T15:07:00.001Z</published><updated>2007-12-31T11:49:30.405Z</updated><title type='text'>On web application configuration</title><content type='html'>There are two common approaches for configuring web applications:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Configuration file(s) in the web application's directory&lt;/li&gt;&lt;li&gt;Storing configuration data in the database&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;What sort of data am I talking about:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Email setup (where to send response form, error emails etc, whether to send mail to customers etc)&lt;/li&gt;&lt;li&gt;Setup of things which need to vary between development and production - payment service provider configuration, setup of other third party integration things&lt;/li&gt;&lt;li&gt;Error handler configuration (whether to display errors, where to log them, email them etc)&lt;/li&gt;&lt;li&gt;Enabling / disabling features (for example, debug things)&lt;/li&gt;&lt;li&gt;Visual stuff or branding (labels etc)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Clearly the database connection string needs to be stored somewhere too, but that can't be in the database for obvious reasons.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Database configuration&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;pros: does not need to be configured on each web server of a farm; not accidentally overwritten by new deployments&lt;br /&gt;cons: bad for hierarchical or structured data; performance overhead; difficult to maintain configuration data stored in a database in SCM. Modifications require apply/ rollback scripts&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Configuration files&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;pros: Easy to manage in SCM (like all your other files, even if not in the same place); less runtime performance overhead; better for hierarchical data; database connection string can be held with other items.&lt;br /&gt;cons: Needs to be synchronised across a web farm; care should be taken that the right versions are always deployed everywhere.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ideas&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For a larger application with a web farm, using a database for some configuration is clearly beneficial. If you have many servers and/or servers of other types (non-web servers), they can store all their configuration in a central database. If performance is a problem, copies can be cached locally.&lt;br /&gt;&lt;br /&gt;For a small simple application, configuration files are better- they're more manageable generally. I prefer to put most configuration in a file if possible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-5960844663855118858?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/5960844663855118858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=5960844663855118858' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/5960844663855118858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/5960844663855118858'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2007/12/on-web-application-configuration.html' title='On web application configuration'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-3368254781052920115</id><published>2007-12-03T14:36:00.000Z</published><updated>2007-12-03T14:44:34.949Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>mysql dumps are not text files</title><content type='html'>Today's pearl of wisdom:&lt;br /&gt;&lt;br /&gt;MySQL dump files, as produced by mysqldump, are not text files.&lt;br /&gt;&lt;br /&gt;A common mistake is to think that mysqldump files are text files. While they superficially look like text files, they in fact are not. This leads to confusion and problems if people try to edit them in a text editor application, which will in many cases cause corruption.&lt;br /&gt;&lt;br /&gt;MySQL dump files contain the SQL commands required to recreate the contents of a database (or subset thereof). However, these are SQL commands in arbitrary (binary) encoding and can consist of a mixture of different encodings (e.g. if you have BLOBs). This mixture is not safe to edit with a text editor, which expects a text file to contain character data in exactly one encoding (and will typically guess, ask the user or just use its default to determine which).&lt;br /&gt;&lt;br /&gt;Suppose you use utf8 data in your database - then your mysqldump file will probably contains all of its strings in utf8. This is fine, until you get to a BLOB. This is a binary lump of data and will be full of sequences of bytes which are NOT LEGAL in utf8, therefore any editor attempting to make sense of them in utf8 won't succeed. It will therefore be forced to change them into something else (i.e. different from the original BLOB data) in order to load them into its buffers.&lt;br /&gt;&lt;br /&gt;Saving this modified (or even unmodified) file back out will result in loss of data.&lt;br /&gt;&lt;br /&gt;Summary&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;MySQL dump files aren't text files even though they look like them&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Editing one in a text editor will probably lose data&lt;/li&gt;&lt;br /&gt; &lt;li&gt;They are often also too big for a text editor&lt;/li&gt;&lt;br /&gt; &lt;li&gt;And have lines too long&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-3368254781052920115?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/3368254781052920115/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=3368254781052920115' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/3368254781052920115'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/3368254781052920115'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2007/12/mysql-dumps-are-not-text-files.html' title='mysql dumps are not text files'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-1237391357948241222</id><published>2007-11-26T20:50:00.000Z</published><updated>2007-11-26T22:52:18.781Z</updated><title type='text'>Some MySQL hacking projects</title><content type='html'>I have two ideas for hacking MySQL in a useful manner, in response to issues I've had using it:&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;InnoDB tablespace usage monitor&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;A really simple tool that would report the amount of innodb tablespace used by specific tables and, ideally, indexes too.&lt;br /&gt;&lt;br /&gt;This is required as I'm fairly sure that there isn't a tool to do this at the moment.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;MyISAM mmap() its index files&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;I'm having trouble tuning MyISAM's key_buffer for production use. We want to use delay_key_write to reduce the IO of a lot of inserts, but at the same time, I'd prefer it not to get too out of hand, as large flushes create a lot of I/O in one go.&lt;br /&gt;&lt;br /&gt;Anyway perhaps that was a poor explanation of my motivation, however, the idea would be:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;mmap() the whole of each index file when the table is opened. &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Leave the memory mapping in place as long as the table is open. resize the file and the memory mapping if necessary, e.g. when the index file gets bigger.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Have all reads/writes go via the memory mapping.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;msync() as necessary (e.g. following a write)&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I'm confident that this will have a performance benefit, but mostly, it will remove the onus on the DBA to tune key_buffer correctly, and allow the OS to use its memory how it wants to.&lt;br /&gt;&lt;br /&gt;The downside? It won't work very well on 32-bit systems as they'll rapidly run out of address space. But do we care any more?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-1237391357948241222?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/1237391357948241222/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=1237391357948241222' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/1237391357948241222'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/1237391357948241222'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2007/11/some-mysql-hacking-projets.html' title='Some MySQL hacking projects'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-7182768826167113052</id><published>2007-10-14T16:25:00.001+01:00</published><updated>2007-11-27T22:50:08.467Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>mysql - ENUMs and sql_mode</title><content type='html'>I've been doing a few experiments with ENUMs and SQL_MODE.&lt;br /&gt;&lt;br /&gt;Not a lot of people realise that in MySQL, an ENUM can actually contain one other value as well as the specified ones - no, not NULL, but ANOTHER value, specifically, the empty string ''.&lt;br /&gt;&lt;br /&gt;This can lead to trouble. The empty string is entered by MySQL when it tries to insert an invalid value into the column. It does also give a warning, but nobody takes any notice of those right?&lt;br /&gt;&lt;br /&gt;Demo schema:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CREATE TABLE enumtest (&lt;br /&gt; id int not null auto_increment,&lt;br /&gt; name varchar(100) NOT NULL,&lt;br /&gt; status ENUM('ok','broken','decommissioned','narnia'),&lt;br /&gt; PRIMARY KEY(id),&lt;br /&gt; KEY(name),&lt;br /&gt; KEY(status)&lt;br /&gt;);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now let's try some inserts...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;INSERT INTO enumtest (name,status) VALUES&lt;br /&gt; ('Mark','ok'),&lt;br /&gt; ('Fred','broken'),&lt;br /&gt; ('Bob','decommissioned');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So far so good. Now how about:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mysql&gt; insert into enumtest (name,status) VALUES ('bust','wibble');&lt;br /&gt;Query OK, 1 row affected, 1 warning (0.00 sec)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ok, so it inserted something, and we got a warning. What's there now?&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; SELECT * FROM enumtest;&lt;br /&gt;+----+------+----------------+&lt;br /&gt;| id | name | status         |&lt;br /&gt;+----+------+----------------+&lt;br /&gt;|  1 | Mark | ok             | &lt;br /&gt;|  2 | Fred | broken         | &lt;br /&gt;|  3 | Bob  | decommissioned | &lt;br /&gt;|  4 | bust |                | &lt;br /&gt;+----+------+----------------+&lt;br /&gt;4 rows in set (0.00 sec)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So what's happened is, we've attempted to insert an invalid value (wibble) and it's put an empty string in instead (empty string is not a null, as we defined the column NOT NULL).&lt;br /&gt;&lt;br /&gt;So what's the fix? SET sql_mode to traditional:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mysql&gt; SET SQL_MODE='TRADITIONAL';&lt;br /&gt;Query OK, 0 rows affected (0.00 sec)&lt;br /&gt;&lt;br /&gt;mysql&gt; INSERT INTO enumtest (name,status) VALUES ('breakme','wibble');&lt;br /&gt;ERROR 1265 (01000): Data truncated for column 'status' at row 1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Much better. We can now actually get ENUMs that only contain values they're supposed to. Good.&lt;br /&gt;&lt;br /&gt;Conclusion: use sql_mode TRADITIONAL whenever possible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-7182768826167113052?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/7182768826167113052/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=7182768826167113052' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/7182768826167113052'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/7182768826167113052'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2007/10/mysql-enums-and-sqlmode.html' title='mysql - ENUMs and sql_mode'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-2531185593534229114</id><published>2007-10-06T09:08:00.000+01:00</published><updated>2007-10-06T09:21:46.988+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>MySQL: delay_key_write is good</title><content type='html'>If you're using MySQL with the MyISAM engine for a high-writes application, delay_key_write is usually very good.&lt;br /&gt;&lt;br /&gt;Let me explain further: delay_key_write is a table option which causes the database NOT to flush the MyISAM key file after every write. This is a really good thing, as if you're doing another write very soon anyway, this is likely to just waste I/O time.&lt;br /&gt;&lt;br /&gt;This doesn't sound like a good idea right, because it means that if the power fails (or mysql crashes, or something), then you'll be left with a broken index file? No, it's still a very good idea:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;delay_key_write does NOT appear to affect the MyISAM data file - that will still be flushed according to the normal policy.&lt;/li&gt;&lt;li&gt;If you had delay_key_write off, then a power failure or crash during the index write would cause the same level of corruption.&lt;/li&gt;&lt;li&gt;Broken MyISAM index files need to be rebuilt, regardless of how little or how much "brokenness" they actually have.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-weight: bold;"&gt;How do you turn delay_key_write on?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;It's a table option, so you have to do it at create time. It's possible to do ALTER TABLE, but that behaves how it normally does under MySQL - i.e. it rebuilds the entire table, just to change one flag. This is of course necessary if you're changing the schema, but delay_key_write shouldn't - sadly, it does.&lt;br /&gt;&lt;br /&gt;This means that on a large existing table, the ALTER TABLE needs to be done with care (i.e. out-of-hours or during scheduled down time, if at all).&lt;br /&gt;&lt;br /&gt;Another option is to set the variable delay_key_write to "ALL" - however, this is not necessarily a good idea as it applies to all tables (in the entire server) whether you want it to or not.&lt;br /&gt;&lt;br /&gt;On infrequently written tables, delay_key_write could increase the chance of index corruption.&lt;br /&gt;&lt;br /&gt;Our performance testing cycle for delay_key_write is now over - quite frankly we're amazed by how much performance it gives us for so little effort - it will definitely be going into production.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Our servers are being hurt with heavy write-load which is keeping (at least one thread of) mysql almost permanently in I/O-wait - delay_key_write makes almost all of this go away making inserts much quicker and leaving the server more time for other things. It's likely to become essential once our server workload grows even higher.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-2531185593534229114?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/2531185593534229114/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=2531185593534229114' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/2531185593534229114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/2531185593534229114'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2007/10/mysql-delaykeywrite-is-good.html' title='MySQL: delay_key_write is good'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-8393774781449368943</id><published>2007-06-30T10:14:00.000+01:00</published><updated>2007-06-30T10:24:48.151+01:00</updated><title type='text'>Critical Mass</title><content type='html'>Last night I went to &lt;a href="http://www.myspace.com/oxfordcriticalmass"&gt;&lt;span style="text-decoration: underline;"&gt;Critical Mass&lt;/span&gt;&lt;/a&gt; - it was fairly quiet but regulars were there. Didn't get rained on, which was good.&lt;br /&gt;&lt;br /&gt;No bike polo however (Which is moderately dangerous and I'm rubbish at) - but we did see an interesting, erm, thing for &lt;a href="http://www.eastoxford.com/carnival/"&gt;Cowley Road Carnival.&lt;/a&gt; Some chaps in East Oxford have built a spider-bike - giant spider on top of a frame with three bikes underneath. It animates as it goes along. The middle rider operates the spider and steering, the two back riders provide power.&lt;br /&gt;&lt;br /&gt;Upcoming events: Apparently there is a Perl programmers meeting in Reading on 10th July somewhere - I'll post more details when I have them.&lt;br /&gt;&lt;a href="http://www.myspace.com/oxfordcriticalmass"&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-8393774781449368943?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/8393774781449368943/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=8393774781449368943' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/8393774781449368943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/8393774781449368943'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2007/06/critical-mass.html' title='Critical Mass'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4622775563416752930.post-5297123767786179974</id><published>2007-06-24T08:51:00.000+01:00</published><updated>2007-06-24T08:54:38.052+01:00</updated><title type='text'>Introduction</title><content type='html'>Well, here we are. My first blog post on Blogger. Shows that I'm really with the times, starting several years after blogging became passé.&lt;br /&gt;&lt;br /&gt;Some topics which I may post on:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Computing generally&lt;/li&gt;&lt;li&gt;IT Security&lt;/li&gt;&lt;li&gt;Linux and/or other operating systems&lt;/li&gt;&lt;li&gt;Music&lt;/li&gt;&lt;li&gt;Beer&lt;/li&gt;&lt;li&gt;The weather (unlikely)&lt;/li&gt;&lt;/ul&gt;I'll put some links in soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4622775563416752930-5297123767786179974?l=marksverbiage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://marksverbiage.blogspot.com/feeds/5297123767786179974/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4622775563416752930&amp;postID=5297123767786179974' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/5297123767786179974'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4622775563416752930/posts/default/5297123767786179974'/><link rel='alternate' type='text/html' href='http://marksverbiage.blogspot.com/2007/06/introduction.html' title='Introduction'/><author><name>Mark Robson</name><uri>http://www.blogger.com/profile/15864507044869250062</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
