Nowadays, many technologies allow data partitioning — SQL and NoSQL distributed databases, in-memory data grids, etc.

The basic idea is to split the data into smaller parts and distribute them across multiple computers to achieve horizontal scalability. This way, we can accomplish a couple of things:

  • Storing large datasets that won’t fit a single server space. This is particularly important for in-memory databases — even the most advanced commodity servers usually provide not more than several hundreds of gigabytes of RAM.
  • Virtually unlimited throughput. Different requests are processed by multiple computers in parallel, utilizing the combined power of their CPUs.

With this in mind, let’s build a partitioned database!

Below is our sample data. We will store the information about credit card accounts, where every row consists of the credit card number (used as the primary key), owner’s name, and current balance.

Plain Text

1

cc_number           | first_name | last_name | balance

2

--------------------+------------+-----------+---------

3

3018-6500-3832-7967 | Salem      | Haseman   | $15.04

4

3535-8042-9707-9496 | Issi       | Rosin     | $26.05

5

3580-9535-1215-0386 | Mikaela    | Llopis    | $30.99

6

3559-3211-4085-9306 | Rosemarie  | Myers     | $17.12

7

3547-8357-5853-7362 | Gaspar     | Fall      | $61.65

8

5020-9153-4497-1698 | Ellsworth  | Kairns    | $71.24

9

3026-0087-8643-6365 | Dolli      | Corriea   | $61.02

10

5010-1226-0854-0174 | Ozzy       | Kiddye    | $79.76

11

3578-1820-2518-6448 | Hal        | Le Fevre  | $88.88

There are only nine rows, but that’s only for the sake of simplicity, of course. In a real dataset that you will want to partition, you will have many more elements.

Now, let’s see how we can split this table into three smaller pieces — to store them on three independent servers.

Attempt #1: Naive

So we have nine rows and three servers; therefore, the most optimal distribution would be to have three rows on each server. The first thing that comes to mind is to simply go through the rows one by one, putting the first three to server #1, second three to server #2, and the last three to server #3. Like this:

Server 1:

Plain Text

1

cc_number           | first_name | last_name | balance

2

--------------------+------------+-----------+---------

3

3018-6500-3832-7967 | Salem      | Haseman   | $15.04

4

3535-8042-9707-9496 | Issi       | Rosin     | $26.05

5

3580-9535-1215-0386 | Mikaela    | Llopis    | $30.99

Server 2:

Plain Text

1

cc_number           | first_name | last_name | balance

2

--------------------+------------+-----------+---------

3

3559-3211-4085-9306 | Rosemarie  | Myers     | $17.12

4

3547-8357-5853-7362 | Gaspar     | Fall      | $61.65

5

5020-9153-4497-1698 | Ellsworth  | Kairns    | $71.24

Server 3:

Plain Text

1

cc_number           | first_name | last_name | balance

2

--------------------+------------+-----------+---------

3

3026-0087-8643-6365 | Dolli      | Corriea   | $61.02

4

5010-1226-0854-0174 | Ozzy       | Kiddye    | $79.76

5

3578-1820-2518-6448 | Hal        | Le Fevre  | $88.88

Woohoo, we partitioned the data! Mission accomplished!

Well, not so fast. Although we successfully created a partitioned dataset, we didn’t think about how to use it going forward. Imagine that there is an incoming transaction for one of the credit cards, and you want to update the balance of the corresponding account. Which server should you send the request to? Currently, there is no way to know.

One of the ways to solve this is to memorize the mapping between credit card numbers and corresponding servers. As long as your application can get a hold of this mapping, it will be able to access a particular row knowing the credit card number.

The question is — where to store the mapping? Presumably, we will have to set up an additional server specifically for this purpose, which will have multiple implications:

  1. Such a server will be a single point of failure. If it goes down, the whole system will become non-functional.
  2. Every request from every client will have to go through this server, so it will quickly become a bottleneck. You will lose all the scalability that you expect from a distributed system.

Although we successfully partitioned the data across three servers, we didn’t create an efficient mechanism for a client application to find out where a particular row is stored. How can we achieve this and avoid having a shared state, effectively creating a shared-nothing architecture?

Attempt #2: Hashing

If you’ve worked with hash-based data structures, you probably have already thought that hashing might be a solution here. That’s exactly how a typical hash table puts entries into buckets, so why not use it here? Let’s try.

Instead of storing the mapping, we can consistently calculate it like this:

Plain Text

1

server_number = (abs(hash(cc_number)) mod 3)

Here are the steps in more detail:

  1. Calculate an integer hash value based on the credit card number.
  2. Apply the absolute value function to discard negative values.
  3. Apply the modulo operation to the value, using the number of servers as the divisor.

If we apply this formula to all the credit card numbers that we’ve got, here is what we get:

Plain Text

1

3018-6500-3832-7967 -> 1

2

3535-8042-9707-9496 -> 2

3

3580-9535-1215-0386 -> 2

4

3559-3211-4085-9306 -> 0

5

3547-8357-5853-7362 -> 1

6

5020-9153-4497-1698 -> 0

7

3026-0087-8643-6365 -> 2

8

5010-1226-0854-0174 -> 1

9

3578-1820-2518-6448 -> 0

#big data #scalability #distributed systems #algorithms

Shared-Nothing Data Partitioning - DZone Big Data
1.10 GEEK