<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;C04NSX0-eip7ImA9WhJWEk0.&quot;"><id>tag:blogger.com,1999:blog-8614766039092762057</id><updated>2012-08-17T04:13:18.352-07:00</updated><title>Sergei Tsarev</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://sergei.clustrix.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://sergei.clustrix.com/" /><author><name>Sergei</name><uri>http://www.blogger.com/profile/12505242855655371767</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://3.bp.blogspot.com/_VHQJkYQ5-dY/SoixbUYDYOI/AAAAAAAAAro/so2zLlfjOuc/S220/profile.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>7</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/SergeiTsarev" /><feedburner:info uri="sergeitsarev" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;DEQEQX48cSp7ImA9WhRTFEg.&quot;"><id>tag:blogger.com,1999:blog-8614766039092762057.post-5674260733881387205</id><published>2011-11-04T18:25:00.000-07:00</published><updated>2011-11-04T18:25:00.079-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-04T18:25:00.079-07:00</app:edited><title>Distributed Database Architectures: Distributed Storage / Centralized Compute</title><content type="html">In my previous post I wrote about &lt;a href="http://sergeitsar.blogspot.com/2011/11/distributed-database-architectures.html"&gt;shared disk architectures&lt;/a&gt; and the problems they introduce. It's common to see comparisons between shared disk and shared nothing architectures, but that distinction is too coarse to capture the differences between various shared nothing approaches.&lt;br /&gt;
&lt;br /&gt;
Instead, I'm going to characterize the various "shared-nothing" style systems by their query evaluation architectures. Most systems fall into one of the following buckets:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Centralized compute&lt;/li&gt;
&lt;li&gt;Limited distributed compute&lt;/li&gt;
&lt;li&gt;Fully distributed compute&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;b&gt;Centralized Compute: MySQL Cluster &lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.mysql.com/products/cluster/"&gt;MySQL Cluster&lt;/a&gt; consists of two basic roles used for servicing user queries: a compute role and a storage/data role. The compute node is the front end which takes in the query, plans it, and executes it. The compute node will communicate with the storage nodes remotely to fetch any data relevant to the query.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-9Blp95pXStI/TrRqMqbvF7I/AAAAAAAAB6s/Osizuu0oBv0/s1600/dist+storage+cent+compute+1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-9Blp95pXStI/TrRqMqbvF7I/AAAAAAAAB6s/Osizuu0oBv0/s1600/dist+storage+cent+compute+1.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
In the distributed storage model, data is no longer shared between the nodes at the page level. Instead, the storage nodes expose a higher level API which allows the compute node to fetch row ranges based on the available access paths (i.e. indexes).&lt;br /&gt;
&lt;br /&gt;
In such a system, storage level locks associated with the data are now managed exclusively by the storage node itself. A compute node does not cache any data; instead, it always asks the set of storage nodes responsible for the data. The system solved the cache coherence overhead problem. &lt;br /&gt;
However, it still suffers from extensive data movement and centralized query evaluation.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;strike&gt;Cache coherence overhead&lt;/strike&gt;&lt;/li&gt;
&lt;li&gt;Extensive data movement&lt;/li&gt;
&lt;li&gt;Centralized query evaluation&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;b&gt;MySQL Query Evaluation in Action&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Consider the following example:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="text-align: center;"&gt;&lt;i&gt;SELECT count(*) FROM mytable WHERE acol = 1 and bcol = 2&lt;/i&gt;&lt;/div&gt;&lt;br /&gt;
Assumptions:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;an index over &lt;i&gt;acol&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;10% of the rows in the table match &lt;i&gt;acol = 1&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;3% of the rows match &lt;i&gt;acol = 1 and bcol = &lt;/i&gt;2&lt;/li&gt;
&lt;li&gt;total table size 1 Billion rows&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-7Hs9p8lrWMI/TrSF3fFLHgI/AAAAAAAAB68/l6-OfQvPKjw/s1600/dist+storage+cent+compute+2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;br /&gt;
&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-Hn8mR3U2VKM/TrSJM0DHF0I/AAAAAAAAB7U/r_6xxc4xBic/s1600/dist+storage+cent+compute+2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-Hn8mR3U2VKM/TrSJM0DHF0I/AAAAAAAAB7U/r_6xxc4xBic/s1600/dist+storage+cent+compute+2.png" /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;In the diagram above, the arrows represent the flow of data through the system. As you can see, most of the query evaluation in the example is done by a single compute node.&amp;nbsp; The system generated a &lt;b&gt;data movement of 100 million rows&lt;/b&gt;, and only &lt;b&gt;a single node performed additional filtering and aggregate count&lt;/b&gt;. &lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;It's an improvement over a shared disk system, but it still has some serious limitations. Such a system could be well suited for simple key access (i.e. query touches a few specific rows), but any more complexity will generally result in poor performance&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;As with the shared disk system, adding more nodes will not help improve single query execution, and queries which operate over large volumes of data have the potential to saturate the message bus between the nodes.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SergeiTsarev/~4/0YowL9UuUIg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sergei.clustrix.com/feeds/5674260733881387205/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://sergei.clustrix.com/2011/11/distributed-database-architectures_04.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/5674260733881387205?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/5674260733881387205?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SergeiTsarev/~3/0YowL9UuUIg/distributed-database-architectures_04.html" title="Distributed Database Architectures: Distributed Storage / Centralized Compute" /><author><name>Sergei</name><uri>http://www.blogger.com/profile/12505242855655371767</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://3.bp.blogspot.com/_VHQJkYQ5-dY/SoixbUYDYOI/AAAAAAAAAro/so2zLlfjOuc/S220/profile.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-9Blp95pXStI/TrRqMqbvF7I/AAAAAAAAB6s/Osizuu0oBv0/s72-c/dist+storage+cent+compute+1.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://sergei.clustrix.com/2011/11/distributed-database-architectures_04.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMNRX8_cSp7ImA9WhRTFEk.&quot;"><id>tag:blogger.com,1999:blog-8614766039092762057.post-8413059794996044465</id><published>2011-11-04T14:51:00.000-07:00</published><updated>2011-11-04T14:51:34.149-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-04T14:51:34.149-07:00</app:edited><title>Distributed Database Architectures: Shared Disk</title><content type="html">One of the most common questions I get about &lt;a href="http://www.clustrix.com/"&gt;Clustrix&lt;/a&gt; is "How is Clustrix different than Database X?" Depending on who asks the question, Database X tends to be anything from Oracle RAC to MySQL Cluster. So I decided to put together a primer on different types of Distributed Database Architectures, and what each one means for real world applications. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Shared Disk Databases&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Shared disk databases fall into a general category where multiple database instances share some physical storage resource. With a shared disk architecture, multiple nodes coordinate access to a shared storage system at a block level.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-ABKLHvI2pz8/TrMvECJwiII/AAAAAAAAB5E/NLS98-fFHgM/s1600/shared+disk+1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-ABKLHvI2pz8/TrMvECJwiII/AAAAAAAAB5E/NLS98-fFHgM/s1600/shared+disk+1.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Adding clustering to a stand alone database through a shared disk cache.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
Several of the older generation databases fall into this category, including &lt;a href="http://www.oracle.com/technetwork/database/clustering/overview/index.html"&gt;Oracle RAC&lt;/a&gt;, &lt;a href="http://www-01.ibm.com/software/data/db2/linux-unix-windows/editions-features-purescale.html"&gt;IBM DB2 pureScale&lt;/a&gt;, &lt;a href="http://www.sybase.com/manage/shared-disk-clustering"&gt;Sybase&lt;/a&gt;, and others.&lt;br /&gt;
&lt;br /&gt;
These systems all started out as single instance databases. The easiest way to add clustering to an existing database stack would be to share the storage system between multiple independent nodes. All of these databases already did paged disk access through a buffer manager cache. Make the caches talk to each to manage concurrence, and bam, you have a distributed database!&lt;br /&gt;
&lt;br /&gt;
Most of the database stack remains the same. You have the same planner, the same optimizer, the same query execution engine. It's a low risk way to extend the database to multiple nodes. You can add more processing power to the database, keep data access transparency in the application layer, and get some amount of fault tolerance.&lt;br /&gt;
&lt;br /&gt;
But such systems also have serious limitations which prevent them from getting very wide spread adoption, especially for applications which require scale. They simply don't scale for most workloads  &lt;span style="font-size: x-small;"&gt;(footnote: see Scale w/ Sharing below)&lt;/span&gt;, and they are extremely complex to administer.&lt;br /&gt;
&lt;br /&gt;
Almost every new distributed database system built in the last 10 years has embraced a different architecture, mainly because the shared disk model has the following problems:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Cache coherence overhead&lt;/li&gt;
&lt;li&gt;Extensive data movement across the cluster&lt;/li&gt;
&lt;li&gt;Centralized query execution (only a single node participates in query resolution)&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;b&gt;Cache Coherence and Page Contention&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Let's assume the following workload as the first example:&lt;br /&gt;
&lt;div style="text-align: center;"&gt;&lt;i&gt;DB 1: UPDATE mytable SET mycol = mycol + 1 WHERE mykey = &lt;span style="color: red;"&gt;X&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;div style="text-align: center;"&gt;&lt;i&gt;DB 2: UPDATE mytable SET mycol = mycol + 1 WHERE mykey =&lt;span style="color: blue;"&gt; Y&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;
Now let's further assume that some of the keys for that update statement will share a page on disk. So we don't have contention on the same key, but we do have some contention on the same disk page.&lt;br /&gt;
&lt;br /&gt;
&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-s24ipE1npMI/TrM3mo4CObI/AAAAAAAAB5c/a6AF0p0XTuQ/s1600/shared+disk+2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-s24ipE1npMI/TrM3mo4CObI/AAAAAAAAB5c/a6AF0p0XTuQ/s1600/shared+disk+2.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Page contention in shared-disk systems.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
Both nodes receive similar update queries. Both nodes must update the same physical page. But in order to do it safely, they must insure that &lt;i&gt;all copies of the page are consistent across the cluster&lt;/i&gt;. &lt;br /&gt;
&lt;br /&gt;
Managing such consistency comes at a cost. Consider some of the requirements which have to be satisfied:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Every node must acquire a page lock in order to read or write from the page. In a clustered environment, such locking results in communication between nodes.&lt;/li&gt;
&lt;li&gt;If a node acquires a write lock on a page and modifies it, it must notify any other node to invalidate their caches.&lt;/li&gt;
&lt;li&gt;If a node gets a page invalidation request, it must re-fetch the latest copy of the page, which also results in more network communication.&lt;/li&gt;
&lt;li&gt;Each node caches the contents of the entire data set. It means that the effective cache size of the cluster is as large as the cache on any of the nodes. Adding more nodes does not scale cache efficiency. &lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Now imagine adding more nodes to your cluster. You end up with a system which has&lt;b&gt; non-linear scaling in message complexity and&lt;/b&gt;&lt;b&gt; data movement&lt;/b&gt;.&amp;nbsp; It's common to see such systems struggle beyond 4 nodes on typical OLTP workloads.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Data Movement&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Consider another example which poses problems for shared disk systems:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="text-align: center;"&gt;&lt;i&gt;SELECT sum(mycol) FROM mytable WHERE something = x&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="text-align: left;"&gt;Let's assume that &lt;i&gt;mytable&lt;/i&gt; contains 1 Billion rows and that the &lt;i&gt;something = x&amp;nbsp;&lt;/i&gt;&lt;b&gt;&lt;i&gt; &lt;/i&gt;&lt;/b&gt;leaves us 1,000 rows. With a shared disk system, if the predicate results in a table scan, &lt;b&gt;the entire contents of the 1B row table must be transferred &lt;/b&gt;to the node evaluating the query!&lt;b&gt; &lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="text-align: left;"&gt;And from the previous example, we see that it's not just a matter of data movement across the SAN infrastructure. The system must also maintain cache coherence, which means &lt;b&gt;lots of cache management traffic between all the nodes.&lt;/b&gt; Such queries on large data sets can bring the whole cluster to its knees.&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;Centralized Query Resolution&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div style="text-align: left;"&gt;It's also worth while to point out that &lt;b&gt;only a single node within the cluster can participate in query evaluation&lt;/b&gt;. So even if you throw more nodes at your system, it's not generally going to help you speed up that slow query. In fact, adding more nodes may slow down the system as the cost of managing cache coherence increases in a larger cluster.&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;Scaling with Sharding&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
It's possible to scale with shared disk, but it requires an extensive engineering effort. The application is written (a) to keep affinity of data accesses to nodes and (b) to keep each shard within its own data silo. You &lt;b&gt;lose transparency of data access&lt;/b&gt; at the application level. The app can no longer send any query to any one of the nodes, or it will result in a catastrophic performance problem.&lt;br /&gt;
&lt;br /&gt;
While you end up with a very expensive means of implementing sharding, there is some advantage to this approach over regular sharding. In case of node failure, you can more easily bring in a host standby without having to keep a dedicated slave for each shard. But in practice, getting fault tolerance right with a SAN based shared disk infrastructure is no easy task -- just ask anyone who manages SANs for a living.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-vCW-H3tCXy0/TrRZE-R3kRI/AAAAAAAAB6k/9kLi7H6ubnE/s1600/shared+disk+3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-vCW-H3tCXy0/TrRZE-R3kRI/AAAAAAAAB6k/9kLi7H6ubnE/s1600/shared+disk+3.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;ul&gt;&lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/SergeiTsarev/~4/Aell0TvwnLo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sergei.clustrix.com/feeds/8413059794996044465/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://sergei.clustrix.com/2011/11/distributed-database-architectures.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/8413059794996044465?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/8413059794996044465?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SergeiTsarev/~3/Aell0TvwnLo/distributed-database-architectures.html" title="Distributed Database Architectures: Shared Disk" /><author><name>Sergei</name><uri>http://www.blogger.com/profile/12505242855655371767</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://3.bp.blogspot.com/_VHQJkYQ5-dY/SoixbUYDYOI/AAAAAAAAAro/so2zLlfjOuc/S220/profile.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-ABKLHvI2pz8/TrMvECJwiII/AAAAAAAAB5E/NLS98-fFHgM/s72-c/shared+disk+1.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://sergei.clustrix.com/2011/11/distributed-database-architectures.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0QCQ3w-eCp7ImA9Wx9bGUw.&quot;"><id>tag:blogger.com,1999:blog-8614766039092762057.post-6879943174643658111</id><published>2011-02-28T09:49:00.000-08:00</published><updated>2011-02-28T09:49:22.250-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-28T09:49:22.250-08:00</app:edited><title>Profile Driven Performance Optimization</title><content type="html">Recently, one of our support engineers noticed that a customer cluster got a little sluggish during a routine maintenance operation. In looking back at historical data, we noticed that the cluster saw a decrease in transaction throughput. The system should have done its cleanup in the background at a low priority, but that didn't happen. I thought it would be interesting to describe our process for addressing a performance problem like this one. I will also share the tools we developed and used.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Reproduce, Measure, and Automate&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The symptom our support engineer noticed was a "sluggish" system. More accurately, we saw that query latencies increased and overall system throughput dropped. So our first step was to reproduce the problem under some approximation of the customer's workload. It was a bit tricky to get all of the prerequisite conditions just right, but we finally landed on the right set of circumstances.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://lh4.googleusercontent.com/-RTmHUI63NQw/TWhi4McDktI/AAAAAAAABrI/wRKFUMHg220/s1600/test4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://lh4.googleusercontent.com/-RTmHUI63NQw/TWhi4McDktI/AAAAAAAABrI/wRKFUMHg220/s1600/test4.png" /&gt;&lt;/a&gt;&lt;/div&gt;Once we reproduced the issue, the next step was to automate the test to ensure that we could consistently demonstrate the problem. It boiled down to using one of our standard performance harnesses with a couple of tweaks.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;The Profile&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Our most commonly used profiling tool is based on the Linux oprofile infrastructure. But we had to modify it to better suit our system. Why? Because substantial portions of our codebase are written in an event driven/&lt;a href="http://en.wikipedia.org/wiki/Continuation-passing_style"&gt;continuation passing style&lt;/a&gt; programming model. Call graph reporting in ofprofile is highly dependent on the stack, and the C stack often does not contain enough information to provide a valuable analysis in our system.&lt;br /&gt;
&lt;br /&gt;
To get around this problem, we modified oprofile to gather additional information from the system. The key concept was adding a method for tagging a series of continuation calls and getting oprofile to sample these tags. The tag itself includes enough information to map the executing code to a logical system module hierarchy. The screen shot below shows an example output from one of our reporting tools.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://lh6.googleusercontent.com/-9A87ylb1nIA/TWhtMYtM-gI/AAAAAAAABrM/B6dudqgLeZg/s1600/gopd-for-blog.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="308" src="https://lh6.googleusercontent.com/-9A87ylb1nIA/TWhtMYtM-gI/AAAAAAAABrM/B6dudqgLeZg/s640/gopd-for-blog.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Coming back to our original problem, the screen shot shows the output of the profile analysis tool during the dip in performance. The tool allows us to isolate the report to a specific processor. In this case we're looking at the output for our management core. &lt;br /&gt;
&lt;br /&gt;
Interpreting the results we see that:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;The execution engine dominates performance, and most of that is in the storage engine&lt;/li&gt;
&lt;li&gt;The storage engine spends 63% of all its cycles on CRC32 calculations&lt;/li&gt;
&lt;li&gt;Our particular maintenance task was erroneously bound to the management core &lt;/li&gt;
&lt;li&gt;The task should have a lower priority&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;b&gt;CRC Performance&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The root cause of the performance degradation was a scheduling issue. However, it caused me to take a closer look at our crc algorithm. We use the crc implementation from zlib, and it should be reasonably well optimized. Whipping up a quick benchmark, I we see that the zlib implementation takes about 41us to checksum a 32k block. In the test, we were reading about 224MB/s per node. That's 293ms of checksums in a second. No wonder we saw a performance drop.&lt;br /&gt;
&lt;br /&gt;
I looked around and found a paper from Intel describing their &lt;a href="ftp://download.intel.com/technology/comms/perfnet/download/slicing-by-8.pdf"&gt;slice-by-eight crc approach&lt;/a&gt;. A quick benchmark revealed that the same 32k checksum could be done in 27us -- a 37% percent improvement over zlib's version.&lt;br /&gt;
&lt;br /&gt;
With Nehalem-based processors, Intel introduced an SSE instruction for a hardware based crc computation. I haven't had a chance to conduct my own benchmarks, but&amp;nbsp; the following &lt;a href="http://www.strchr.com/crc32_popcnt"&gt;results&lt;/a&gt; suggest that we could get a 2-3x speedup over slice by eight.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Source Code&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
I'm releasing the &lt;a href="http://www.clustrix.com/wp-content/uploads/2011/02/opd.tgz"&gt;source for the tools&lt;/a&gt; referenced in this post. The user space oprofile daemon replacement requires our database runtime which I am not making public.&lt;br /&gt;
&lt;ul&gt;&lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/SergeiTsarev/~4/UlpPVdZ6ddg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sergei.clustrix.com/feeds/6879943174643658111/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://sergei.clustrix.com/2011/02/profile-driven-performance-optimization.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/6879943174643658111?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/6879943174643658111?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SergeiTsarev/~3/UlpPVdZ6ddg/profile-driven-performance-optimization.html" title="Profile Driven Performance Optimization" /><author><name>Sergei</name><uri>http://www.blogger.com/profile/12505242855655371767</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://3.bp.blogspot.com/_VHQJkYQ5-dY/SoixbUYDYOI/AAAAAAAAAro/so2zLlfjOuc/S220/profile.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://lh4.googleusercontent.com/-RTmHUI63NQw/TWhi4McDktI/AAAAAAAABrI/wRKFUMHg220/s72-c/test4.png" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://sergei.clustrix.com/2011/02/profile-driven-performance-optimization.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU8ERHY8eCp7ImA9Wx9UEEQ.&quot;"><id>tag:blogger.com,1999:blog-8614766039092762057.post-8886412606209160584</id><published>2011-02-07T07:50:00.000-08:00</published><updated>2011-02-07T07:50:05.870-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-07T07:50:05.870-08:00</app:edited><title>Clustrix as a Document Store: Blending SQL and JSON Documents</title><content type="html">Many responses to my previous post claimed that my comparisons to MongoDB were unfair because MongoDB is a "document store" and Clustrix is a SQL RDBMS.&amp;nbsp; Somehow that distinction made almost any comparison invalid. In response, I spent my weekend coding up a prototype document store interface for Clustrix to demonstrate that &lt;b&gt;a different data model is not in itself an architectural differentiator&lt;/b&gt;. &lt;br /&gt;
&lt;br /&gt;
At first I thought of just creating a different front end for Clustrix; our &lt;a href="http://sergeitsar.blogspot.com/2011/02/sierra-and-clustrix-database-stack.html"&gt;architectural model&lt;/a&gt; makes this easy. However, I quickly decided against it because in many ways, it would be much more limiting than a SQL based interface (e.g. joins, flexibile aggregation, subqueries, etc.)&lt;br /&gt;
&lt;br /&gt;
Instead, I extended our SQL syntax to support native operations on JSON objects. So now you can do the following:&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;clustrix&amp;gt; create table files (id int primary key auto_increment, doc json);&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Query OK, 0 rows affected (0.04 sec)&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;clustrix&amp;gt; insert into files (doc) values ('{"foo": {"bar": 1}, "baz": [1,2,3,4]}');&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Query OK, 1 row affected (0.00 sec)&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;clustrix&amp;gt; select id, files.doc::foo.baz from files where files.doc::foo.bar = 1;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;+----+--------------------+&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;| id | files.doc::foo.baz |&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;+----+--------------------+&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;|&amp;nbsp; 1 | [1,2,3,4]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;+----+--------------------+&lt;/span&gt;&lt;br /&gt;
&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;1 row in set (0.00 sec)&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;clustrix&amp;gt; create index foo_bar on files (doc::foo.bar);&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Query OK, 0 rows affected (0.08 sec)&lt;/div&gt;&lt;br /&gt;
The database has native support for dealing with JSON documents. We're not simply storing text blobs inside of some column and getting them back. We're exposing the contents of the JSON document to the underlying planner and execution engine. Immediately, we get the following advantages:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Ability to do joins across document collections&lt;/li&gt;
&lt;li&gt;Extremely powerful and flexible query language&lt;/li&gt;
&lt;li&gt;Ability to index into JSON objects&lt;/li&gt;
&lt;li&gt;Transactional semantics built-in&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Taking it alittle further, I added a select clause modifier which instructs the database to return all row data as a JSON document, including fields which come from "a relational column." The following example shows how the database can seamlessly join between our json data type and other data types in the system, and then return the result as a JSON objects.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;clustrix&amp;gt; select _json_ f.doc, u.doc::username&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; -&amp;gt;&amp;nbsp;&amp;nbsp; from files f&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; -&amp;gt;&amp;nbsp;&amp;nbsp; join users u on f.doc::user_id = u.user_id&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; -&amp;gt;&amp;nbsp; where f.doc::foo.bar = 1\G&lt;br /&gt;
*************************** 1. row ***************************&lt;br /&gt;
json: {"f.doc": {"foo": {"bar": 1}, "baz": [1,2,3,4]}, "u.doc::username": "sergei"}&lt;br /&gt;
1 row in set (0.01 sec)&lt;/div&gt;&lt;br /&gt;
We can continue to extend the syntax. For example, adding operators to manipulate lists within JSON. Or adding optional schema checking for contents of the JSON (i.e. something along the lines of DTD for XML). I'm sure you can think of more.&lt;br /&gt;
&lt;br /&gt;
An any case, one can build a system which combines the best characteristics of a document store with the power of SQL. Both models can coexist in the same database, allowing the devloper to trully choose&amp;nbsp;the data model which best suits his or her needs.&lt;img src="http://feeds.feedburner.com/~r/SergeiTsarev/~4/6ZjSShkRkE8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sergei.clustrix.com/feeds/8886412606209160584/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://sergei.clustrix.com/2011/02/clustrix-as-document-store-blending-sql.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/8886412606209160584?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/8886412606209160584?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SergeiTsarev/~3/6ZjSShkRkE8/clustrix-as-document-store-blending-sql.html" title="Clustrix as a Document Store: Blending SQL and JSON Documents" /><author><name>Sergei</name><uri>http://www.blogger.com/profile/12505242855655371767</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://3.bp.blogspot.com/_VHQJkYQ5-dY/SoixbUYDYOI/AAAAAAAAAro/so2zLlfjOuc/S220/profile.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://sergei.clustrix.com/2011/02/clustrix-as-document-store-blending-sql.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkAMRX87fyp7ImA9Wx9VFkQ.&quot;"><id>tag:blogger.com,1999:blog-8614766039092762057.post-5826281085561225618</id><published>2011-02-02T15:53:00.000-08:00</published><updated>2011-02-02T15:53:04.107-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-02T15:53:04.107-08:00</app:edited><title>Sierra and the Clustrix Database Stack</title><content type="html">A lot of the responses to my previous posts criticized my choice of comparing a document database like MongoDB to a relational database like Clustrix. I tried to examine aspects of the database architecture which have nothing to do with the data model, but somehow the data model would always come up. There is a set of concerns that's common to all database systems, whether you are a document store or a relational database.&lt;br /&gt;
&lt;br /&gt;
But first, think about how your database would handle the following workload. Chose whatever data model you find best suited for my use case.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;get me all the records from foo where a = ? and b = ?&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;foo has an index over A and another index over&amp;nbsp; B.&lt;/li&gt;
&lt;li&gt; A and B have non-uniform data distributions. A is 90% value "X" and B is 90% value "Y".&lt;/li&gt;
&lt;li&gt;We have 1 billion records in foo.&lt;/li&gt;
&lt;li&gt; The database has a choice: index A, index B, or scan and filter.&lt;/li&gt;
&lt;li&gt;50% of the queries are a = X and b = Z, the other 50% are a = Z and b = Y&lt;/li&gt;
&lt;/ul&gt;&lt;b&gt;What does your database do?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
If the database always chooses index A or index B for all queries, then it ends up examining 900,000 rows 50% of the time.&lt;br /&gt;
&lt;br /&gt;
We'll come back to my question later. First, I will briefly describe what the database stack in Clustrix looks like. I need to set a foundation so that my next set of posts makes sense.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_VHQJkYQ5-dY/TUnZW_lhjKI/AAAAAAAABq8/iF_iwZ-UZUo/s1600/clustrix-stack.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_VHQJkYQ5-dY/TUnZW_lhjKI/AAAAAAAABq8/iF_iwZ-UZUo/s1600/clustrix-stack.png" /&gt;&lt;/a&gt;&lt;/div&gt;To anyone who has experience with DBMS systems, the stack should look very familiar. We have fairly strict abstraction of interfaces between the various portions of the stack.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Protocol Handler and Query Parser are responsible for taking in user connections and translating SQL into our intermediate representation called &lt;b&gt;Sierra&lt;/b&gt;. We can actually support multiple type of dialects at these two layers; the constraint is that we must be able to express the query language in Sierra. And &lt;b&gt;Sierra is much more expressive than SQL&lt;/b&gt;. The value of Sierra is that it provides an extensive planner framework for reasoning about distributed database queries.&lt;br /&gt;
&lt;br /&gt;
So the Planner/Optimizer only accepts Sierra. It runs through a search space of possible plans and prunes them based on cost estimates. After coming up with the best plan candidate, it translates into another intermediate representation used by the Distributed Compiler, which reasons out the physical execution plan for our query. Finally, we compile the query into machine code and execute it.&lt;br /&gt;
&lt;br /&gt;
As a performance optimization, we cache the compiled programs and plans. Clustrix does not need to optimize and compile every query, and you don't need to use prepared statements to get this behavior.&lt;br /&gt;
&lt;br /&gt;
Back to my question. What did you come up with? Well, I can tell you what happens in Clustrix. During the planning phase of Sierra, we examine the various statistics that the database keeps about the data distribution in our indexes. The statistics include tracking:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Number of distinct values across index columns&lt;/li&gt;
&lt;li&gt;Quantile distributions&lt;/li&gt;
&lt;li&gt;Hotlist tracking top n values within a column&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Clustrix correctly chooses the plan which will result in the least number of rows examined for all input parameters.&lt;br /&gt;
&lt;br /&gt;
Now, don't get me wrong. I am not claiming that data distribution statistics are unique to Clustrix. On the contrary, they are very common in any modern RDBMS. I'm using it as an example of a requirement that's independent of any data model, and it's actually a very important feature to have.&lt;img src="http://feeds.feedburner.com/~r/SergeiTsarev/~4/GCLkhxIcRl4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sergei.clustrix.com/feeds/5826281085561225618/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://sergei.clustrix.com/2011/02/sierra-and-clustrix-database-stack.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/5826281085561225618?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/5826281085561225618?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SergeiTsarev/~3/GCLkhxIcRl4/sierra-and-clustrix-database-stack.html" title="Sierra and the Clustrix Database Stack" /><author><name>Sergei</name><uri>http://www.blogger.com/profile/12505242855655371767</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://3.bp.blogspot.com/_VHQJkYQ5-dY/SoixbUYDYOI/AAAAAAAAAro/so2zLlfjOuc/S220/profile.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_VHQJkYQ5-dY/TUnZW_lhjKI/AAAAAAAABq8/iF_iwZ-UZUo/s72-c/clustrix-stack.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://sergei.clustrix.com/2011/02/sierra-and-clustrix-database-stack.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0IMQ3s6cCp7ImA9Wx9VFkg.&quot;"><id>tag:blogger.com,1999:blog-8614766039092762057.post-9133926021899300611</id><published>2011-02-01T10:01:00.001-08:00</published><updated>2011-02-02T07:13:02.518-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-02T07:13:02.518-08:00</app:edited><title>MongoDB vs. Clustrix Comparison: Part 2</title><content type="html">&lt;b&gt;Introduction&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
In Part 1 of my comparison, I ran some performance benchmarks to  establish that relational systems can scale performance. In this post I  would like to focus more on the High Availability and Fault Tolerance  aspects of the two systems. The post will go over the approach of each  system and what it means for fault tolerance and availability. I will  also conduct a test of the claims: I'm going to fail a node by pulling  power to see what happens.&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;
A Primer on Clustrix Data Distribution&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Clustrix has a fine grained approach to data distribution. The following graphic demonstrates the basic concepts and terminology used by our system. Notice that unlike MongoDB (and many other systems for that matter), Clustrix applies a per-index distribution strategy.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_VHQJkYQ5-dY/TUi4Xrcoz6I/AAAAAAAABq4/hDv1CsjWrLU/s1600/slices-intro.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_VHQJkYQ5-dY/TUi4Xrcoz6I/AAAAAAAABq4/hDv1CsjWrLU/s1600/slices-intro.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;There are many interesting implications for query evaluation and execution in our model, and the topic deserves its own set of posts. For the curious, you can get a brief &lt;a href="http://www.clustrix.com/wp-content/uploads/2010/04/clustrix-whitepaper-01-no-on-sql-mysql-object-key-value-store-database-scaling.pdf"&gt;introduction to our evaluation model&lt;/a&gt; from our white paper on the subject. For this post, I'm going to stick to how our model applies to fault tolerance and availability.&lt;br /&gt;
&lt;br /&gt;
You can find documentation for &lt;a href="http://www.mongodb.org/display/DOCS/Sharding"&gt;MongoDB's distribution approach&lt;/a&gt; on their website. In brief, MongoDB chooses a single distribution key for the collection. Indexes are co-located with the primary key shard.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Clustrix Fault Tolerance and Availability Demo&lt;/b&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/L4PAo_du6fI/0.jpg" height="266" width="320"&gt;&lt;param name="movie" value="http://www.youtube.com/v/L4PAo_du6fI?f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="320" height="266" src="http://www.youtube.com/v/L4PAo_du6fI?f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;b&gt;Fault Tolerance&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Both Clustrix and MongoDB rely on replicas for fault tolerance. A loss of a node results in a loss of some copy of the data which we can find elsewhere in the system. The MongoDB team put together a good set of documentation &lt;a href="http://www.mongodb.org/display/DOCS/Replication"&gt;describing their replication model&lt;/a&gt;. Perhaps one of the most salient differences between the two approaches is the granularity of data distribution.&amp;nbsp; The unit of recovery on Clustrix is the replica (a small portion of an index), while the unit of recovery in MongoDB is a full instance of a Replication Set.&lt;br /&gt;
&lt;br /&gt;
For Clustrix, this means that the reprotection operation happens in a many-to-many fashion. Several nodes copy small portions of data from each of their disks to several other nodes onto many disks. The advantages of this approach are:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;No single disk in the system becomes overloaded with writes or reads&lt;/li&gt;
&lt;li&gt;No single node hotspot for driving the reprotect work&lt;/li&gt;
&lt;li&gt;Incremental progress toward full protection&lt;/li&gt;
&lt;li&gt;Independent replica factors for each index (e.g. primary key 3x, indexes 2x)&lt;/li&gt;
&lt;li&gt;Automatic reprotection which doesn't require operator intervention&lt;/li&gt;
&lt;li&gt;All replicas are always consistent&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
One of the interesting aspects of the system is the &lt;b&gt;complete automation of every recovery task&lt;/b&gt;. It's built in. I don't have to do anything to make that happen. So if I have a 10 node system, and a node fails, in an hour or so I will have a completely protected&amp;nbsp; 9 node system without any operator intervention at all. When the 10th node comes back, the system will simply perceive a distribution imbalance and start moving data back onto that node.&lt;br /&gt;
&lt;br /&gt;
While the Replica Sets feature in MongoDB is nicer than replication in say MySQL, it's still highly manual. So in contrast with the above list for Clustrix, for MongoDB we have:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Manual intervention to recover from failure&lt;/li&gt;
&lt;li&gt;The data is moved in a one-to-one fashion&lt;/li&gt;
&lt;li&gt;All data within a Replica Set has the same protection factor&lt;/li&gt;
&lt;li&gt;Failures can lead to inconsistency&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;b&gt;Availability&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Both systems rely on having multiple copies of data for Availability. I've seen a lot of interesting discussion recently about the CAP theorem and what it means for real-world distributed database systems. It's another deep topic which really deserves its own set of posts, so I'll simply link to a couple posts on the subject which I find interesting and illuminating:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://voltdb.com/blog/clarifications-cap-theorem-and-data-related-errors"&gt;Stonebraker of VoltDB on partition tolerance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://perspectives.mvdirona.com/2010/02/24/ILoveEventualConsistencyBut.aspx"&gt;James Hamilton of Amazon/AWS on consistency&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;At Clustrix, we think that Consistency, Availability, and Performance are much more important than Partition tolerance. &lt;b&gt;Within a cluster, Clustrix keeps availability in the face of node loss while keeping strong consistency guarantees.&lt;/b&gt; But we do require that more than half of the nodes in the cluster group membership are online before accepting any user requests. So a cluster provides fully ACID compliant transactional semantics while keeping a high level of performance, but you need majority of the nodes online.&lt;br /&gt;
&lt;br /&gt;
However, Clustrix also offers a lower level of consistency in the way of asynchronous replication between clusters. So if you want to setup a disaster recovery target in another physical location over high-latency link, we're able to accommodate that mode. It simply means that your backup cluster may be out of date by some number of transactions.&lt;br /&gt;
&lt;br /&gt;
MongoDB has relaxed consistency all around. The Replication Set itself uses an asynchronous replication model. The MongoDB guys are &lt;a href="http://www.mongodb.org/display/DOCS/Replica+Set+Design+Concepts"&gt;upfront about the kinds of anomalies&lt;/a&gt; they expose. The end user gets the equivalent of &lt;b&gt;read uncommitted&lt;/b&gt; isolation. Mongo's claim is that they do this because they (1) can achieve higher performance, and (2) "&lt;i&gt;merging back old operations later, after another node has accepted writes, is a hard problem.&lt;/i&gt;" &lt;span style="color: black;"&gt;Yes. Distributed protocols are a hard problem, but it doesn't mean you should punt on them.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Availability Continued&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
There's also a more nuanced discussion to availability. One of the principal design features of Clustrix has been to aim for lock-free operation whenever possible. We have Multi-Version Concurrency Control (MVCC) deeply ingrained in the system. It allows a transaction to see a consistent snapshot of the database without interfering with writes. So a read in our system will not block a write.&lt;br /&gt;
&lt;br /&gt;
Building on top of MVCC, Clustrix has implemented &lt;b&gt;a transactionally safe, lockless, and fully consistent method for moving data in the cluster without blocking any writes to that data&lt;/b&gt;. All of this happens completely automatically. No administrator intervention required. So when the Rebalancer decides to move a replica from Node 1 to Node 3, the replica can continue to take writes. We have a mechanism to sync changes to the source replica with the target replica without limiting the replica availability.&lt;br /&gt;
&lt;br /&gt;
Compare that to what many other systems do: read lock the source to get a consistent view for a replica copy. You end up locking out writers for the duration of the data copy. So while your data is available for reads, it is not available for writes.&lt;br /&gt;
&lt;br /&gt;
After a node failure (or to be more precise, a replica failure within a set), MongoDB advocates the following approach:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Quiesce the master (read lock)&lt;/li&gt;
&lt;li&gt;Flush dirty buffers to disk&amp;nbsp; (fsync)&lt;/li&gt;
&lt;li&gt;Take an LVM snapshot of the resulting files&lt;/li&gt;
&lt;li&gt;Unlock the master&lt;/li&gt;
&lt;li&gt;Move the data files over to the slave&lt;/li&gt;
&lt;li&gt;Let the slave catch up from the snapshot&lt;/li&gt;
&lt;/ol&gt;So a couple of points (a) the &lt;b&gt;MongoDB is not available for writes&lt;/b&gt; during steps (1) and (2),&amp;nbsp; and (b) it's a &lt;b&gt;highly manual process&lt;/b&gt;. It reminds me very much of the MySQL best practices for setting up a slave. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
I've seen a lot of heated debates about Consistency, Availability, Performance, and Fault Tolerance. These issues are deeply interconnected and it's difficult to write about any of them in isolation. Clustrix maintains a high level of performance without sacrificing consistency and very high degree of availability. I know that it's possible to build such a system because we actually built it. And you shouldn't sacrifice these features in your application because you believe it's the only way to achieve good performance.&lt;img src="http://feeds.feedburner.com/~r/SergeiTsarev/~4/SWWV6rjHzI4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sergei.clustrix.com/feeds/9133926021899300611/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://sergei.clustrix.com/2011/02/mongodb-vs-clustrix-comparison-part-2.html#comment-form" title="10 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/9133926021899300611?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/9133926021899300611?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SergeiTsarev/~3/SWWV6rjHzI4/mongodb-vs-clustrix-comparison-part-2.html" title="MongoDB vs. Clustrix Comparison: Part 2" /><author><name>Sergei</name><uri>http://www.blogger.com/profile/12505242855655371767</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://3.bp.blogspot.com/_VHQJkYQ5-dY/SoixbUYDYOI/AAAAAAAAAro/so2zLlfjOuc/S220/profile.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_VHQJkYQ5-dY/TUi4Xrcoz6I/AAAAAAAABq4/hDv1CsjWrLU/s72-c/slices-intro.png" height="72" width="72" /><thr:total>10</thr:total><feedburner:origLink>http://sergei.clustrix.com/2011/02/mongodb-vs-clustrix-comparison-part-2.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0QGRnY5fCp7ImA9Wx9VFUo.&quot;"><id>tag:blogger.com,1999:blog-8614766039092762057.post-1130784329641309411</id><published>2011-01-30T19:55:00.000-08:00</published><updated>2011-02-01T07:48:47.824-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-01T07:48:47.824-08:00</app:edited><title>MongoDB vs. Clustrix Comparison: Part 1 -- Performance</title><content type="html">&lt;span style="font-weight: bold;"&gt;UPDATE:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
You can now &lt;a href="http://www.clustrix.com/wp-content/uploads/2011/01/mongo-bench.tgz"&gt;download the benchmark&lt;/a&gt; code. As mentioned in my post, I populated both databases with 300M rows. I played around with the best client/host/thread ratio for each database to achieve peak throughput.  I used MongoDB v1.6.5.&lt;br /&gt;
&lt;br /&gt;
For the MongoDB read tests, I used the read-test.cpp harness. I didn't have time to do a proper getopt parsing for it, so I would modify it by hand for test runs. But it's very straight forward.&lt;br /&gt;
&lt;br /&gt;
The C version of the MySQL/Clustrix harness is not included because I used a Clustrix internal test harness framework -- to save time. It doesn't impart Clustrix any advantage in the test, and it relies too much on our infrastructure to be of any value. You can still use the Python based test harness -- it just requires a lot of client cpu power.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;UPDATE 2:&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
There's an &lt;a href="http://news.ycombinator.com/item?id=2161753"&gt;interesting conversation&lt;/a&gt; over at Hacker News about this post. &lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt; &lt;/span&gt;&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Introduction&lt;/span&gt; &lt;br /&gt;
&lt;br /&gt;
With all the recent buzz about NoSQL and non-relational databases, the marketing folks at Clustrix asked a question: Do we have the right solution for today's market? It's a fair question, especially since Clustrix is a fully featured RDBMS with a SQL interface. And we all heard that SQL doesn't scale, right?&lt;br /&gt;
&lt;br /&gt;
So that brings us around to the next question: What do people actually want out of their database? Surely it's not simply the absence of a SQL based interface. Because if that's the case,&amp;nbsp; &lt;a href="http://en.wikipedia.org/wiki/Berkeley_DB"&gt;Berkeley DB&lt;/a&gt; would be a lot more popular than say &lt;a href="http://www.sqlite.org/"&gt;SQLite&lt;/a&gt;. Over the years, we've had many conversations with people about their database needs. Over and over, the following has always come up a list of must have features for any modern system:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li style="font-weight: bold;"&gt;Incrementally Scalable Performance&lt;/li&gt;
&lt;li&gt;&lt;span style="font-weight: bold;"&gt;High Availability and Fault Tolerance&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Ease of Overall System Management&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Interestingly enough, we never heard that SQL or the relational model was the root of all their problems. It appears that the anti-SQL sentiment came around with this sort of false reasoning.&lt;br /&gt;
&lt;blockquote&gt;I have a SQL based RDBMS.&lt;br /&gt;
I can't seem to scale my database beyond a single system.&lt;br /&gt;
Therefore SQL is the problem.&lt;/blockquote&gt;&lt;br /&gt;
The NoSQL movement embraced this reasoning. The NoSQL proponents began to promote all sorts of "scalable" systems at the expense of venerable DBMS features like durability. And they kept going. What else don't we need? Well, we don't need Consistency! Why? Because that's really hard to do and keep performance.&amp;nbsp; Slowly but surely, these systems would claim to have a panacea for all of your scalable database needs at the expense of cutting features we've come to expect from 40 years of database systems design.&lt;br /&gt;
&lt;br /&gt;
Well, that's just bullshit. There is absolutely nothing about SQL or the relational model preventing it from scaling out. &lt;br /&gt;
&lt;br /&gt;
Over the next set of posts, I'm going to compare &lt;a href="http://mongodb.org/"&gt;MongoDB&lt;/a&gt; and &lt;a href="http://www.clustrix.com/"&gt;Clustrix&lt;/a&gt; using the above evaluation criteria: Scalable Performance,  Availability and Fault Tolerance, and Ease of Use.  I am going to start with Performance because no one believes that you can grow a relational database to Internet Scale. And to put the results into context, I chose to compare Clustrix to MongoDB because (1) it doesn't support SQL, (2) it can transparently scale to multiple nodes, and (3) it seems to be the new poster child for NoSQL.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Performance&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Conducting performance benchmarks is always challenging. First, you have to decide on a model workload. Next, you have to accurately simulate that workload. The system under test should be as close as possible to your production environment. The list gets long. In the end, no benchmark is going to be perfect. Best you can hope for is reasonable.&lt;br /&gt;
&lt;br /&gt;
So I looked at some common themes in the workloads from &lt;a href="http://gigaom.com/cloud/clustrix-lifts-the-curtain-on-early-database-customers/"&gt;some of our customers&lt;/a&gt;, and decided that I would simulate a basic use case of keeping metadata about a collection of 1 Billion files. Whether you're a cloud based file storage provider or a photo sharing site, the use case is familiar. The test would use the appropriate access patterns for the database. Since MongoDB does not support joins, I'm not going to put it at a disadvantage by moving join logic into the application. Instead, I'm going to make full use of the native document centric interface.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Benchmark Overview&lt;/span&gt;&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;10 node cluster&lt;/span&gt; of dedicated hosts (SSD, 8 cores/node, 32GB ram/node)&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style="font-weight: bold;"&gt;2x replication&lt;/span&gt; factor&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;A data set containing information about &lt;strike&gt;&lt;span style="font-weight: bold;"&gt;1 Billion files&lt;/span&gt;&lt;/strike&gt;&lt;span style="font-weight: bold;"&gt; &lt;span style="color: red;"&gt;300 Million&lt;/span&gt; &lt;/span&gt;(see bellow)&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;A read only performance test&lt;/li&gt;
&lt;li&gt;A read/write performance test&lt;/li&gt;
&lt;li&gt;Both databases will use the exact same hardware &lt;/li&gt;
&lt;/ul&gt;The test uses the following schema:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;CREATE TABLE files (&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; id &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;                  bigint     NOT NULL,&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; path&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;                 varchar    NOT NULL,&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; size&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;                 bigint     NOT NULL,&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; server_id&amp;nbsp;&amp;nbsp;&amp;nbsp;            int        NOT NULL,&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; deleted&amp;nbsp; &amp;nbsp; &amp;nbsp;              smallint   NOT NULL,&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; last_updated         datetime   NOT NULL,&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; created&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;              datetime   NOT NULL,&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; user_id&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;              bigint     NOT NULL,&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PRIMARY KEY (id),&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; KEY server_id_deleted (server_id, deleted),&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; KEY user_id_updated (user_id, last_updated),&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; KEY user_id_path (user_id, path)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;);&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;
Additionally, the data set has the following characteristics:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;path&lt;/span&gt; is a randomly generated string between the length of 32 and 128 characters&lt;/li&gt;
&lt;li&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;server_id&lt;/span&gt; has a distribution of 0-32&lt;/li&gt;
&lt;li&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;deleted&lt;/span&gt; has 1% value 1, and the rest 0 (lumpy data distributions tests)&lt;/li&gt;
&lt;li&gt;for MongoDB, we use &lt;span style="font-style: italic; font-weight: bold;"&gt;user_id&lt;/span&gt; as the shard key&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Test 1: Loading the Initial Data Set&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The test harness itself is a multi-host, multi-process, and multi-threaded python application. Because of the &lt;a href="http://wiki.python.org/moin/GlobalInterpreterLock"&gt;GIL&lt;/a&gt; in python, I ended up designing the test harness so that it forks off multiple processes, with each process having some number of threads. It also turns out that I needed more than 10 client machines to saturate the cluster with reads using python, so I rewrote the read tests suing C++ for MongoDB and C for Clustrix.&lt;br /&gt;
&lt;br /&gt;
While populating the dataset into MongoDB,&amp;nbsp; I kept on running into a huge drop off in performance at around 55-60M rows. A 10 node cluster has an aggregate of 320GB or ram and 80 160GB SSD drives. That's more than enough iron to handle that much data. As I started to dig in more, I saw that 85% of the data was distributed to a single node. MongoDB had split the data into multiple chunks, but its balancer could not (would not?) move the data to other nodes. Once the database size exceeded that node's available memory, everything went to shit. The box started thrashing pretty badly. It seems that under a constant high write load, MongoDB is unable to automatically redistribute data within the cluster.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;span id="goog_1576347085"&gt;&lt;/span&gt;&lt;span id="goog_1576347086"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_VHQJkYQ5-dY/TUOsCf5fduI/AAAAAAAABqk/6SHSqvlasZE/s1600/mongo-write-fail.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_VHQJkYQ5-dY/TUOsCf5fduI/AAAAAAAABqk/6SHSqvlasZE/s1600/mongo-write-fail.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
To get the test going, I split the files collection into an even distribution. Without any load on the cluster, I watched MongoDB move the chunks onto the 10 replica sets for an even layout. Now I was finally getting somewhere.&lt;br /&gt;
&lt;br /&gt;
Immediately, I noticed that MongoDB had a highly variable write throughput. I was also surprised at how low the numbers were. Which led me to discover Mongo's concurrency control: a single mutex over the database instance. Furthermore, in tracking the insert performance along with memory utilization, I could see that getting to more than 300 million records would spill some of the data set to disk. While that's a reasonable benchmark for a database, I decided that I would keep the data set memory resident for Mongo's sake.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_VHQJkYQ5-dY/TUOt52JGdmI/AAAAAAAABqo/ADMuOUMrKxc/s1600/insert.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_VHQJkYQ5-dY/TUOt52JGdmI/AAAAAAAABqo/ADMuOUMrKxc/s1600/insert.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
The drop-off on the Clustrix happened because not all of the load scripts finished at the same time. A couple of the client nodes were slower, so they took a bit longer to finish up their portion of the load.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
For a system which eschews consistency and durability, the write performance on MongoDB looks atrocious. Initially, I thought that Mongo completely trashing Clustrix on the write performance.&amp;nbsp; The result was a complete surprise. Here's why I think Clustrix did so much better:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Highly concurrent b-tree implementation with fine grained locking&lt;/li&gt;
&lt;li&gt;Buffer manager and transaction log tuned for SSD&lt;/li&gt;
&lt;li&gt;Completely lock-free Split and Move operations (very cool stuff, another post)&lt;/li&gt;
&lt;/ul&gt;Conversely, Mongo did so poorly because it: &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Has a big fat lock, severely limiting concurrency&lt;/li&gt;
&lt;li&gt;It relies entirely on the kernel buffer manager&lt;/li&gt;
&lt;/ul&gt;Total time to load was &lt;b&gt;0:37 &lt;/b&gt;(hh:mm)&lt;b&gt; on Clustrix&lt;/b&gt; and &lt;b&gt;4:47 on MongoDB&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b style="color: #990000;"&gt;Clustrix was 775% faster for writes than MongoDB!&lt;/b&gt;&lt;br /&gt;
And that's with fully durable and fully consistent writes on the Clustrix side.&lt;br /&gt;
&lt;ul&gt;&lt;/ul&gt;&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Test 2: Read Only&lt;br /&gt;
&lt;br /&gt;
&lt;/span&gt;The read test consists of the following basic workloads:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;get the 10 latest updated files for a specific user&lt;/li&gt;
&lt;li&gt;count the number of deleted files on a given server id&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;/ul&gt;&lt;br /&gt;
I chose these queries because they are representative of the types of queries our example application would generate, and they are not simple point selects. Getting a distributed hash table working is easy. But DHTs tend to fall apart fairly quickly when queries start introducing ordering, examining multiple rows,&amp;nbsp; or other non key-value lookups. In other words, real-world use.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;MongoDB&lt;/b&gt;&lt;br /&gt;
C++ test harness. Peak throughput at 64 concurrent client threads.&lt;br /&gt;
&lt;div style="color: #444444;"&gt;db.files.find({user_id: user_id}).sort({last_updated: -1}).limit(10)&lt;/div&gt;&lt;div style="color: #990000;"&gt;55,103 queries/sec&lt;/div&gt;&lt;div style="color: #444444;"&gt;db.files.find({'server_id': server_id, 'deleted': 1}).count()&lt;/div&gt;&lt;div style="color: #38761d;"&gt;675 queries/sec&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;Clustrix&lt;/b&gt;&lt;br /&gt;
C test harness. Peak throughput at 256 concurrent client threads. &lt;br /&gt;
&lt;div style="color: #444444;"&gt;select * from benchmark.files where user_id = .. order by last_updated desc limit 10 &lt;/div&gt;&lt;div style="color: #38761d;"&gt;56,641 queries/sec&amp;nbsp;&lt;/div&gt;&lt;div style="color: #444444;"&gt;select count(1) from benchmark.files where server_id = .. and deleted = 1&lt;/div&gt;&lt;div style="color: #990000;"&gt;625 queries/sec&lt;/div&gt;&lt;br /&gt;
So on a read-only test, MongoDB and Clustrix are within 1% of each other for test1. Clustrix is faster on test 1 and MongoDB is 7% faster on test2. I captured a profile of Clustrix during a test1 run, and saw that the the execution engine dominates CPU time (as opposed to say SQL parsing or query planning). In looking at the profiles during test2 runs on Clustrix, I saw that we  had a bunch of idle time in the system, so there's room for  optimization.&lt;br /&gt;
&lt;br /&gt;
But real-world loads tend to be read/write, so let's see how Mongo does when we add writes to the equation.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Test 3: Read/Write &lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
My initial plan called for a combination of read-centric and write-centric loads. It seems that most web infrastructures are heavier on the reads than writes, but there are many exceptions. In Clustrix, we use &lt;a href="http://en.wikipedia.org/wiki/Multiversion_concurrency_control"&gt;Multi-Version Concurrency Control&lt;/a&gt;, which means that readers are never blocked by writers. We handle both read heavy and write heavy workloads equally well. Since MongoDB seems to do much better with reads than writes, I decided to stick to a read-centric workload.&lt;br /&gt;
&lt;br /&gt;
The Clustrix test shows show very little drop off in performance for reads. On the Mongo side, I expected to see a drop off in performance directly proportional to the amount of write load.&lt;br /&gt;
&lt;br /&gt;
However, what I saw mind blowing: &lt;b&gt;Mongo completely starved the readers&lt;/b&gt;! The following graph shows the query load on one of the 10 shards during the write portion of the test. I simply started up &lt;b&gt;a single write thread&lt;/b&gt; while letting the read test run. The write was active for all of 60 seconds, and it took Mongo an additional 15 seconds to recover after the writer stopped.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_VHQJkYQ5-dY/TUO3RAn8SNI/AAAAAAAABqs/FJKgl_HgBWA/s1600/mongo-rw-fail.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_VHQJkYQ5-dY/TUO3RAn8SNI/AAAAAAAABqs/FJKgl_HgBWA/s1600/mongo-rw-fail.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
MongoDB&lt;br /&gt;
&lt;span style="color: red;"&gt;FAIL&lt;/span&gt; &lt;br /&gt;
&lt;br /&gt;
Custrix&lt;br /&gt;
test1: 4,425 writes/sec and 49,099 reads/sec (total 53,524 queries/sec) (92% read / 8% write) &lt;br /&gt;
test2: 4,450 writes/sec and 625 reads/sec&lt;br /&gt;
&lt;br /&gt;
The test2 aggregate query is much more computationally expensive compared to an insert. So the read/write ratio for test2 became very skewed. Note that Clustrix did not drop in read throughput at all. &lt;br /&gt;
&lt;br /&gt;
Overall, you can see why every modern DBMS choose to go with the MVCC model for concurrency control.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The SQL relational model can clearly scale. The only place where MongoDB could compete with Clustrix was on pure read-only workloads. But that's just not representative of real world application loads &lt;br /&gt;
&lt;br /&gt;
Building a scalable distributed system is more about good architecture and solid engineering. Now that we have scale and performance out of the way, I'm going to review the other important aspects of a DBMS in my upcoming posts.&lt;img src="http://feeds.feedburner.com/~r/SergeiTsarev/~4/4goGUWlLOhk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sergei.clustrix.com/feeds/1130784329641309411/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://sergei.clustrix.com/2011/01/mongodb-vs-clustrix-comparison-part-1.html#comment-form" title="27 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/1130784329641309411?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8614766039092762057/posts/default/1130784329641309411?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SergeiTsarev/~3/4goGUWlLOhk/mongodb-vs-clustrix-comparison-part-1.html" title="MongoDB vs. Clustrix Comparison: Part 1 -- Performance" /><author><name>Sergei</name><uri>http://www.blogger.com/profile/12505242855655371767</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://3.bp.blogspot.com/_VHQJkYQ5-dY/SoixbUYDYOI/AAAAAAAAAro/so2zLlfjOuc/S220/profile.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_VHQJkYQ5-dY/TUOsCf5fduI/AAAAAAAABqk/6SHSqvlasZE/s72-c/mongo-write-fail.png" height="72" width="72" /><thr:total>27</thr:total><feedburner:origLink>http://sergei.clustrix.com/2011/01/mongodb-vs-clustrix-comparison-part-1.html</feedburner:origLink></entry></feed>
