- Hello. Welcome to 'Back To Basics'.
In this episode,
we are going to talk about database sharding
to horizontally scale your relational databases.
Four years ago,
I was working with a last-mile delivery customer
who were dealing with database scalability challenges.
They had a very good problem.
That is, significant growth in number of customers
in a short amount of time.
They implemented different design patterns
to increase system throughput.
That is, adding more read replicas,
leveraging cache for frequently accessed data,
and partitioning and archiving data.
It worked for a few months,
but the growth of databases were reaching vertical limits
and they needed to plan for additional growth.
This is when they considered horizontal database sharding.
Let's look at a similar customer use case.
Here is an example of a typical e-commerce application
with a three-tier architecture.
The architecture includes web tier,
that interacts with the order management application,
and backend databases to complete the order lifecycle.
As e-commerce companies acquire new users,
database size and number of database requests per second
increase for their order database.
As the number of incoming orders increase,
database size grows
along with increasing number of database requests,
increasing the average response time
until it reaches an inflection point
when the response time goes beyond the client timeout
and client requests start to fail.
In this situation,
you can upgrade database instances from a smaller
to a larger instance to handle additional user traffic,
but you will reach vertical limits eventually.
When the limits are reached,
you still face increased average response time.
This phenomena can also be explained with Little's Law.
It says, as the average number of requests being processed
and average rate of incoming requests increase,
the average time it takes a request increases.
Now let's look at how it works when we do horizontal scaling
instead of vertical scaling.
As the number of orders increases,
you distribute the orders across multiple database shards.
The number of incoming database requests per shard
is still within the limits
and average response time doesn't go beyond client timeout.
As you expect more orders to process,
you can add more shards
without affecting customer experience.
Now, there are a number of sharding techniques
that you can use to shard your database.
Identifying the right technique
requires you to consider first cardinality,
which is possible values of the shard key.
Second, frequency, which is probability of storing
specific information in a particular shard.
There are different types of sharding techniques,
key-based, range-based, hash-based, and geo-based.
Here is a customer order database, which has customer ID,
order ID, and number of other columns.
The cardinality of customer ID in this example is 20.
Let's take average number of orders per customer
to be balanced across the first 10 customers
and the next 10 customers.
We split the customer database using customer ID
as the shard key with range-based partitioning.
Once you have identified sharding method
and database design, the next step is to implement,
which requires some changes on the application layer.
You'll need to build/re-use a proxy layer
that's responsible for properly routing database requests
from the application layer to database shards.
A number of database engines
natively don't support sharding,
so you need to use a proxy layer to do a lookup
and properly request database requests to a shard.
Some database engines such as PostgreSQL
have open source proxy implementation
such as PgCat for you to use.
When choosing database sharding as your scalability design,
it brings additional complexity of maintaining shards
and re-sharding databases in case of hot spotting.
AWS reduces a lot of maintenance operational overhead
by removing heavy lift of database maintenance activities.
You need to watch for hotspots.
It's possible that certain shards
are accessed more frequently than the others,
leading to uneven resource utilization
and possible performance impact.
In this case, Shard-2 gets accessed more frequently
as can be seen from the number of orders.
If hot spotting starts impacting your performance,
you have to re-shard with a new shard,
possibly order ID in this case.
Lastly, your application complexity increases
due to the introduction of a proxy layer.
Now, coming back to our original application design,
let's see how it looks when we have multiple shards
on Amazon RDS.
You introduce another RDS instance
independently functioning as a shard.
The shard leverages high availability
best practices of multi-AZ
and additional read replicas.
Afterwards, you add a proxy layer to map shards to ranges
or hashes or something else
depending upon your sharding technique.
As you may have noticed,
you can add virtually as many shards as possible,
but AWS service limits still apply.
You need to check for those limits
when building your sharding strategy.
After sharding, you comparatively
have a smaller number of rows per shard
and queries will take less time to execute
in comparison to all queries running on the same database.
Along with performance, it provides higher resiliency
as if a database shard experiences any issues.
You can still process customer orders
and avoid complete disruptions.
From a security standpoint,
it reduces blast radius if a database shard is compromised.
In summary, it's a good design pattern
when you have already utilized all other design patterns
and are still facing database scaling challenges.
You will need to spend most of the time
coming up with a scalable sharding strategy
to accommodate for growth for many years to come.
Check out the links below for more details.
Thanks for watching 'Back to Basics'. See you next time.