With such a plethora of choices, how do you pick the right database solution to provide optimal performance to your workload?
Well, you need to start with your workload requirements. What type of data do you need to handle? What volume of data will you need to store? How will the data be queried? How many concurrent requests (on average and max) do you expect? Do you have latency constraints when accessing the data (reads and/or writes)? What are your availability requirements (such as uptime)? In the case of disaster, how fast do you need to be back up and running for your Recovery Time Objective (RTO), and how much data can you afford to lose for your Recovery Point Objective (RPO)? What is the lifecycle of your data (for instance, if you have regulatory or operational constraints)? Do you have a predilection for a given technology (for instance, if your team is used to working with a specific database engine)?
Answering those questions will rapidly narrow down your choices. Perhaps you cannot easily isolate a single choice following your initial rationale; say you are left with two alternatives that could potentially both do the job (for instance, Amazon RDS PostgreSQL or Amazon Aurora PostgreSQL). In that case, simply benchmark them for your use cases. Don’t take anything for granted: collect metrics using AWS services such as CloudWatch and AWS X-Ray to trace end-to-end performance, including your database transactions. You can easily deploy any of the previously mentioned database services to test them out on your most demanding use cases, collect some solid evidence of how they perform under stress, and validate the best choice.
When ensuring good database performance, you typically have two primary areas of focus:
If you need ultra-low latency, such as sub-millisecond data access, you have to consider storing the data, or part of it, in memory. The choice of solution for that will depend on whether you need it for the entire dataset or just a portion of it, for reads or for writes, without forgetting the previous questions that already narrowed your database choice based on the type of data, the type of queries, and so on.
There are essentially two caching patterns used commonly. The first is cache-aside, or lazy caching; it entails loading the cache when reading data. If the data is not in the cache, it gets pulled out of the data store and then copied into the cache. The other pattern is called write-through and loads the cache in the reverse order; when the data is written to the data store, the cache gets populated. For instance, as illustrated in the following diagram, ElastiCache can be used either in a cache-aside or write-through configuration, or both at the same time, depending on what makes more sense to meet your workload requirements.
Figure 8.2: Cache patterns: cache-aside and write-through
Some database engines provide native caching mechanisms. For instance, Aurora has a built-in write-through cache to handle fast writes, and DynamoDB offers DynamoDB Accelerator (DAX), which is an in-memory cache add-on that transparently speeds up database requests.
To reduce resource contention between reads and writes, you need to split the database resources serving reads, on the one hand, from those serving writes, on the other hand. Either you have a native mechanism to do it, such as read replicas for RDS or Aurora, for instance, or you need to rely on an external mechanism, such as a caching layer to serve reads.
Don’t forget, you don’t always need a Ferrari: sometimes a more mundane car with a less powerful engine is good enough for your needs so you can save yourself some money.