Durability is something that you normally want somewhere in a system: where the data will survive reboots, crashes, and other sorts of things that routinely happen to real world systems.
Over the many years that I have worked in system design, there has been a recurring thorny problem of how to handle this durable data. What this means in practice when building a new system is the question “what should we use as our backing store?”. Backing stores are often called “databases”, but everyone has a different view of what database means, so I’ll try and avoid it for now.
In a perfect world a backing store would be:
- Always available
- Geographically distributed
- Highly scalable
While we can do these things quite easily these days with the stateless parts of an application, doing them with durable data is non-trivial. In fact, in the general case, it’s impossible to do all of these things at once (The CAP theorem describes this quite well).
This has always been a challenge, but as applications move onto the Internet, and as businesses become more geographically distributed, the problem has become more acute.
Relational databases (RDBMSes) have been around a very long time, but they’re not the only kind of database you can use. There have always been other kinds of store around, but the so-called NoSQL Movement has had particular prominence recently. This champions the use of new backing stores not based on the relational design, and not using SQL as a language. Many of these have radically different designs from the sort of RDBMS system that has been widely used for the last 30 years.
When and how to use NoSQL systems is a fascinating question, and I put forward our thinking on this. As always, it’s kind of complicated. It certainly isn’t the case that throwing out an RDBMS and sticking in Mongo will make your application awesome.
Although they are lumped together as “NoSQL”, this is not actually a useful definition, because there is very little that all of these have in common. Instead I suggest that there are these types of NoSQL backing store available to us right now:
- Document stores – MongoDB, XML databases, ZODB
- Graph databases – Neo4j
- Key/value stores – Dynamo, BigTable, Cassandra, Redis, Riak, Couch
These are so different from each other that lumping them in to the same category together is really quite unhelpful.
Graph databases have some very specific use cases, for which they are excellent, and probably a lot of utility elsewhere. However, for our purposes they’re not something we’d consider generally, and I’ll not say any more about them here.
I am pretty firmly in the camp that Document stores, such as MongoDB, should never be used generally either (for which I will undoubtedly catch some flak). I have a lot of experience with document databases, particularly ZODB and dbxml, and I know whereof I speak.
These databases store “documents” as schema-less objects. What we mean by a “document” here is something that is:
- always required in it’s entirety
- more valuable than the links between documents or it’s metadata.
My experience is that although often you may think you have documents in your system, in practice this is rarely the case, and it certainly won’t continue to be the case. Often you start with documents, but over time you gain more and more references between documents, and then you gain records and and all sorts of other things.
Document stores are poor at handling references, and because of the requirement to retrieve things in their entirety you denormalise a lot. The end result of this is loss of consistency, and eventually doom with no way of recovering consistency.
We do not recommend document stores in the general case.
These are the really interesting kind of NoSQL database, and I think these have a real general potential when held up against the RDBMS options. However, there is no magic bullet and you need to choose when to use them carefully.
You have to be careful when deciding to build something without an RDBMS. An RDBMS delivers a huge amount of value in a number of areas, and for all sorts of reasons. Many of the reasons are not because the RDBMS architecture is necessarily better but because they are old, well-supported and well-understood.
For example, PostgreSQL (our RDBMS of choice):
- has mature software libraries for all platforms
- has well-understood semantics for backup and restore, which work reliably
- has mature online backup options
- has had decades of performance engineering
- has well understood load and performance characteristics
- has good operational tooling
- is well understood by many developers
These are significant advantages over newer stores, even if they might technically be better in specific use cases.
All that said, there are some definite reasons you might consider using a key/value store instead of an RDBMS.
Reason 1: Performance
Key/value stores often naively appear more performant than RDBMS products, and you can see some spectacular performance figures in direct comparisons. However, none of them really provide magic performance increases over RDBMS systems, what they do is provide different tradeoffs. You need to decide where your performance tradeoffs lie for your particular system.
In practice what key/value stores mostly do is provide some form of precomputed cache of your data, by making it easy (or even mandatory) to denormalize your data, and by providing the performance characteristics to make pre-computation reasonable.
If you have a key/value store that has high write throughput characteristics, and you write denormalized data into it in a read-friendly manner then what you are actually doing is precomputing values. This is basically Just A Cache. Although it’s a pattern that is often facilitated by various NoSQL solutions, it doesn’t depend on them.
RDBMS products are optimised for correctness and query performance and write performance takes second place to these. This means they are often not a good place to implement a pre-computed cache (where you often write values you never read).
It’s not insane to combine an RDBMS as your master source of data with something like Redis as an intermediate cache. This can give you most of the advantages of a completely NoSQL solution, without throwing out all of the advantages of the RDBMS backing store, and it’s something we do a lot.
Reason 2: Distributed datastores
If you need your data to be highly available and distributed (particularly geographically) then an RDBMS is probably a poor choice. It’s just very difficult to do this reliably and you often have to make some very painful and hard-to-predict tradeoffs in application design, user interface and operational procedures.
Some of these key/value stores (particularly Riak) can really deliver in this environment, but there are a few things you need to consider before throwing out the RDBMS completely.
Availability is often a tradeoff one can sensibly make. When you understand quite what this means in terms of cost, both in design and operational support (all of these vary depending on the choices you make), it is often the right tradeoff to tolerate some downtime occasionally. In practice a system that works brilliantly almost all of the time, but goes down in exceptional circumstances, is generally better than one that is in some ways worse all of the time.
If you really do need high availability though, it is still worth considering a single RDBMS in one physical location with distributed caches (just as with the performance option above). Distribute your caches geographically, offload work to them and use queue-based fanout on write. This gives you eventual consistency, whilst still having an RDBMS at the core.
This can make sense if your application has relatively low write throughput, because all writes can be sent to the single location RDBMS, but be prepared for read-after-write race conditions. Solutions to this tend to be pretty crufty.
Reason 3: Application semantics vs SQL
NoSQL databases tend not to have an abstraction like SQL. SQL is decent in its core areas, but it is often really hard to encapsulate some important application semantics in SQL.
A good example of this is asynchronous access to data as parts of calculations. It’s not uncommon to need to query external services, but SQL really isn’t set up for this. Although there are some hacky workarounds if you have a microservice architecture you may find SQL really doesn’t do what you need.
In addition, it is a practical case that we’re not all SQL gurus. Having something that is suboptimal in some cases but where we are practically able to exploit it more cheaply is a rational economic tradeoff. It may make sense to use a key/value store just because of the different semantics it provides – but be aware of how much you are losing without including an RDBMS, and don’t be surprised if you end up reintroducing one later as a platform for analysis of your key/value data.
Reason 4: Load patterns
NoSQL systems can exhibit very different performance characteristics from SQL systems under real loads. Having some choice in where load falls in a system is sometimes useful.
For example, if you have something that scales front-end webservers horizontally easily, but you only have one datastore, it can be really useful to have the load occur on the application servers rather than the datastore – because then you can distribute load much more easily.
Although this is potentially less efficient, it’s very easy and often cheap to spin up more application servers at times of high load than it is to scale a database server on the fly.
Also, SQL databases tend to have far better read performance than write performance, so fan-out on write (where you might have 90% writes to 10% reads as a typical load pattern) is probably better implemented using a different backing store that has different read/write performance characteristics.
Which backing store to use, and how to use it, is the kind of decision that can have huge ramifications for every part of a system. This post has only had an opportunity to scratch the surface of this subject and I know I’ve not given some parts of it the justice they deserve – but hopefully it’s clear that every decision has tradeoffs and there is no right answer for every system.
About us: Isotoma is a bespoke software development company based in York and London specialising in web apps, mobile apps and product design. If you’d like to know more you can review our work or get in touch.