Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 1

Scalability Essentials: Vertical, Horizontal, Load Balancing, and Data Partitioning

Recommended resources

Welcome to your first lesson in the Machine Learning System Design course! Your extensive background in data science and machine learning provides a fantastic foundation for building models, and this course is designed to bridge the gap between developing a model and deploying it as a robust, large-scale service. We'll start by tackling the foundational principles of system design that enable applications to serve millions of users.

This lesson addresses a fundamental challenge in production systems: scalability. As user traffic or data volume grows, a system must be able to handle the increased load gracefully. We will explore the core concepts that make this possible. Specifically, you will learn to explain vertical and horizontal scaling, the role of load balancers in distributing traffic, and the concept of data partitioning for managing large datasets. These are the absolute cornerstones of distributed systems.

What is Scalability?

In system design, scalability refers to a system's capacity to handle a growing amount of work by adding resources. A system that works for 100 users might grind to a halt with 100,000 users unless it was designed to scale. For ML applications, this could mean handling a surge in prediction requests or processing an ever-increasing volume of training data.

To get a high-level overview of this concept, let's start with a short video from ByteByteGo.

Scalability Simply Explained in 10 Minutes

This video provides a concise and clear explanation of what scalability is and why it's more than just "handling more work."

Please watch the first two minutes of the video, from the beginning until the discussion of scaling bottlenecks. Focus on the definition of scalability and the concept of the response vs. demand curve, which is a useful mental model for evaluating a system's performance under load.

As the video explains, our goal is to design systems where the performance degrades as slowly as possible when demand increases. The two primary strategies to achieve this are vertical and horizontal scaling.

This diagram illustrates the fundamental difference between vertical scaling (making a single machine more powerful) and horizontal scaling (adding more machines to the system).

Vertical vs. Horizontal Scaling

At its core, the choice between scaling "up" and scaling "out" is one of the first major decisions in system architecture. An excellent article from Clarifai provides a detailed comparison. We'll use it to explore both approaches.

Horizontal vs Vertical Scaling | Which Strategy Fits Your AI ...

This article offers a comprehensive look at both scaling strategies, complete with their advantages, limitations, and decision criteria, which are especially relevant for AI/ML workloads.

First, please read the introductory sections, Quick Digest and Introduction to Scalability, for a solid definition. Next, read the section "Understanding Vertical Scaling (Scaling Up)". Pay attention to the advantages and limitations. Then, move on to "Understanding Horizontal Scaling (Scaling Out)". Focus on the benefits and the challenges it introduces. Finally, review the table in the comparison section for a concise summary.

To summarize the key points:

  • Vertical Scaling (Scaling Up): This involves increasing the resources of a single server—more CPU, more RAM, faster storage.

    • Pros: It's simple. The application logic doesn't need to change, and since everything is on one machine, communication between components is extremely fast. This is often a good starting point.
    • Cons: There's a hard physical limit to how much you can upgrade a single machine. High-end hardware gets exponentially expensive, and crucially, it creates a single point of failure (SPOF). If that one server goes down, your entire service is offline.
  • Horizontal Scaling (Scaling Out): This involves adding more servers to your resource pool and distributing the load among them.

    • Pros: It offers near-infinite scalability, high availability, and fault tolerance. If one server fails, the others can pick up the slack. You can also scale elastically, adding or removing machines in response to demand.
    • Cons: It introduces significant complexity. Your application must be designed to run in a distributed environment, which brings challenges like network latency, data consistency, and service discovery.

In the context of ML systems, you often see a hybrid approach. A stateful component, like a database holding model metadata or user profiles, might be scaled vertically to maintain strong data consistency. Meanwhile, the stateless prediction service, which just takes in data and returns a prediction, is a perfect candidate for horizontal scaling behind a load balancer.


Load Balancing: Distributing the Workload

Horizontal scaling is powerful, but it immediately raises a new question: if you have a fleet of servers, how do client requests know which one to talk to? Sending all traffic to the first server would defeat the purpose. This is where a load balancer comes in.

A load balancer is a dedicated server or service that acts as a "traffic cop." It sits in front of your application servers and distributes incoming requests across them, ensuring no single server is overwhelmed.

This diagram shows a typical high-level architecture. Notice how the load balancer is positioned after the user request comes in, distributing traffic across a pool of web servers. This is a classic horizontal scaling pattern.

Load balancers use various algorithms to decide where to send the next request. A great, concise explanation of these can be found in the following video.

Load Balancing - The Right Way To Do It | Systems Design Interview 0 to 1 With Ex-Google SWE

This video by an ex-Google engineer clearly connects the concept of horizontal scaling to the need for load balancing and walks through the most common routing algorithms.

Please watch from the beginning for a recap of horizontal vs. vertical scaling. Then, pay close attention to the section on load balancer routing policies. The video covers several key algorithms: Round Robin (and Weighted Round Robin) Lowest Response Time Hashing (Layer 4 vs. Layer 7)

The most common load balancing algorithms include:

  • Round Robin: Requests are distributed sequentially across the group of servers. Simple and effective if all servers are identical.
  • Least Connections: The load balancer sends the next request to the server that is currently handling the fewest active connections. This is a dynamic approach that adapts to the current server load.
  • IP Hash: The client's IP address is used to calculate a hash, which determines which server receives the request. This ensures that requests from a particular user (or, more accurately, a particular IP) always go to the same server. This is useful for maintaining session state without a shared database, a concept known as session affinity or sticky sessions.

The choice of algorithm depends on the specific needs of your application. For a stateless API, Round Robin or Least Connections works well. For an application that stores session data on the server, IP Hashing is often necessary.


Data Partitioning: Scaling the Database

We've covered how to scale the application logic, but what about the data itself? Just like a single application server, a single database server can become a bottleneck. When a dataset grows too large for a single machine to handle efficiently, we need to scale the database layer. While vertical scaling is an option, for massive datasets, we must turn to horizontal scaling for databases.

This is achieved through data partitioning, also known as sharding. The core idea is to break a large database into smaller, more manageable pieces called shards, and spread them across multiple database servers.

Each shard contains a subset of the data, and collectively, the shards make up the entire dataset. When a query comes in, the system must know which shard holds the relevant data. This is determined by a shard key.

There are a few common partitioning strategies:

  1. Key or Hash-based Partitioning: A shard key (e.g., user_id, product_id) is fed into a hash function. The output of the function determines which shard the data belongs to. For example, you could use hash(user_id) % N, where N is the number of shards. This generally leads to a uniform data distribution.
  2. Range-based Partitioning: Data is sharded based on a range of the shard key. For example, user profiles with usernames starting A-F go to Shard 1, G-M to Shard 2, and so on. This can make range queries (e.g., "find all users from A to C") very efficient, but it can also lead to "hotspots" if data is not evenly distributed across ranges.

A significant challenge with simple hash-based sharding (mod N) is what happens when you add or remove servers. Changing N to N+1 or N-1 changes the result of the modulo operation for almost every key, forcing a massive, disruptive reshuffling of data across nearly all shards. This problem is solved by a more advanced technique called consistent hashing, which we will explore in a future lesson.


Conclusion

In this lesson, we've laid the groundwork for designing scalable systems. You've learned about the fundamental trade-offs between making servers bigger (vertical scaling) versus adding more of them (horizontal scaling), and how to manage the complexity that arises from the latter.

Here are the key takeaways:

  • Scalability is a system's ability to handle growing load while maintaining performance.
  • Vertical scaling is simple but limited by cost and a hardware ceiling, and it creates a single point of failure.
  • Horizontal scaling offers near-limitless elasticity and fault tolerance but requires designing for a distributed environment.
  • Load balancers are essential for horizontal scaling, distributing traffic across servers using algorithms like Round Robin, Least Connections, or Hashing.
  • Data partitioning (sharding) is the strategy for horizontally scaling databases, splitting large datasets across multiple servers based on a shard key.

These concepts are not just theoretical; they are the bread and butter of system design interviews and the building blocks of any application meant to operate at scale.

In our next lesson, we will build directly on these ideas. We will compare monolithic vs. microservice architectures and see how horizontal scaling and load balancing are the enabling technologies behind the modern microservices paradigm.