Partitioning, sometimes also known as sharding, is a scalability strategy that handles large numbers of incoming requests by spreading the workload over non-identical servers.
Partitioning is similar to the strategy used when collecting a name-badge at a conference, seminar or event. One person might help guests with family names starting with A–I, another may help guests with family names J–R, and the final person will help guests with family names S–Z. This partitioning of guests by surname triples the throughput of name-badge collection.
When building distributed systems, there are many ways of partitioning (other than family names). Some examples are as follows:
-
By geography (e.g., store all data for Australian customers in an Australian data center, and US customers in a US data center)
-
By user type (e.g., store all data for staff on one server, and all student data is on another server)
-
By object types (e.g., the user account table on one server and the current order table on another server) [1]
-
By object properties (e.g., sales transactions for 2019 on one server and 2020 on another server) [2]
-
By request (e.g, send requests for
/api/authenticate
to one server and requests for/api/purchase
to a different server)
Understanding partitioning
In mathematics, a partition refers to a grouping of elements of a set into subsets, such that each item in the original set occurs in exactly one subset.
In a distributed system, a partition is a mapping of requests or data requests onto distinct servers. For example, the following table depicts a partition of sales transactions onto four servers:
Partitioning expression |
Server |
---|---|
|
Server A |
|
Server B |
|
Server C |
|
Server D |
Now, suppose a user needs to view sales where month == 'January' and year == 2019
, then the partitioning can be used to direct that request to Server B.
The partitioning expression does not even need to be fixed. In dynamic partitioning, the expression can adjust over time. For example, if the start of 2021 is an unusually busy, then the system might automatically rebalance the partitions to combine 2019 and 2018 onto Server B to dedicate all of 2021’s sales to Server C.
Implementing partitioning
You can partition any layer of your application. Many technologies provide built-in support for partitioning or sharding:
- PostgresSQL
-
PostgreSQL has support for partitioning table storage on a server. However, there are forks of PostgreSQL that support sophisticated partitioning in a cluster of database servers. The foreign data wrapper can also define a table partitioned across multiple servers.
- MongoDB
-
MongoDB has built-in sharding capabilities for clusters.
- Load balancers
-
Load balancers offer simple mechanisms to partition incoming HTTP requests. Most load balancers support partitioning by request path (i.e.,
/api/sales/2020/*
to Server A and/api/sales/2019/*
to Server B). More sophisticated load balancers, such as AWS Elastic Load Balancing allow advanced rules based on inspection of headers, IP addresses and query parameters. - DNS
-
The domain name system (DNS) provides a crude form of partitioning. For example, Amazon’s US customers typically visit
amazon.com
, while Australian customers useamazon.com.au
.
However, partitioning can be as simple as an if
-statement. The following code illustrates partitioning in domain logic. The if
-statement chooses between two different database management systems (database1
and database2
) based on the year:
app.get('/api/sales', async (req,res) => {
// Choose between two separate partitions
let db;
if (req.body.year >= 2020)
db = database1;
else
db = database2;
// Query
let results = await db.query({
text: 'select sum(amount) from sales where year = ?',
values: [req.body.year]
});
...
res.json(...);
});