185
architectural scalability distributed systems

Shared-Nothing

Reference Wikipedia ↗
Shared-Nothing — sequence diagram
Plate 185 sequence diagram

The Shared-Nothing architecture is a distributed computing architecture where each node in the system has its own dedicated resources – CPU, memory, and disk – and does not share these resources with any other node. Nodes communicate with each other via a network, typically using message passing. This contrasts with shared-disk or shared-memory architectures where multiple nodes access the same storage or memory.

This pattern is crucial for building highly scalable and fault-tolerant systems. By eliminating resource contention, it allows for near-linear scalability as more nodes are added. It’s commonly used in large-scale data processing, databases, and cloud computing environments where handling massive datasets and high traffic volumes is essential. The lack of shared state simplifies failure handling, as a node failure doesn’t directly impact others.

Usage

The Shared-Nothing architecture is widely used in:

  • Massively Parallel Processing (MPP) Databases: Systems like Amazon Redshift, Snowflake, and Google BigQuery leverage this architecture to distribute data and query processing across many nodes.
  • Cloud Computing: Cloud providers like AWS, Azure, and Google Cloud use shared-nothing principles to isolate virtual machines and containers, ensuring that one tenant’s activity doesn’t affect others.
  • Distributed Caching: Systems like Memcached and Redis (in clustered mode) can be deployed in a shared-nothing configuration to distribute cached data across multiple servers.
  • Big Data Processing: Frameworks like Apache Spark and Hadoop (with HDFS) are designed to operate on clusters of machines with independent resources.

Examples

  1. Amazon Redshift: Redshift is a fully managed, petabyte-scale data warehouse service. It employs a shared-nothing architecture with a cluster of compute nodes, each having its own CPU, memory, and storage. Data is distributed across these nodes, and queries are processed in parallel, enabling fast analysis of large datasets. There is no shared disk between nodes.

  2. Snowflake: Snowflake is another cloud data platform built on a shared-nothing architecture. It separates storage, compute, and services layers. Compute nodes (virtual warehouses) are independent and scale independently of storage. Each virtual warehouse has its own resources, and data is accessed via shared storage but processed in isolation.

  3. Apache Cassandra: Cassandra is a NoSQL distributed database designed to handle large amounts of data across many commodity servers, providing high availability with no single point of failure. Each node in a Cassandra cluster manages a portion of the data and operates independently, communicating with other nodes to replicate data and handle requests.

Specimens

15 implementations
Specimen 185.01 Dart View specimen ↗

The Shared-Nothing pattern emphasizes independent processes that don’t share any global state. Each process has its own dedicated resources – memory, disk – and communicates exclusively through message passing. This avoids contention and simplifies concurrency.

This Dart example models a simple distributed counter using isolates. Each isolate represents a worker with its private counter. The main isolate spawns multiple worker isolates and sends them increment/get requests via ReceivePort and SendPort. Isolates communicate by copying messages, enforcing the “shared-nothing” principle. Dart’s isolates are a natural fit for this pattern as they enforce memory isolation, and use message passing for communication, making it relatively straightforward to implement.

import 'dart:isolate';

void counterIsolate(SendPort sendPort) {
  int count = 0;
  sendPort.listen((message) {
    if (message == 'increment') {
      count++;
    } else if (message == 'get') {
      sendPort.send(count);
    }
  });
}

void main() async {
  final numIsolates = 3;
  final List<Isolate> isolates = [];
  final List<ReceivePort> receivePorts = [];

  for (int i = 0; i < numIsolates; i++) {
    final receivePort = ReceivePort();
    final isolate = await Isolate.spawn(counterIsolate, receivePort.sendPort);
    isolates.add(isolate);
    receivePorts.add(receivePort);
  }

  // Increment each counter a few times
  for (int i = 0; i < 5; i++) {
    for (var port in receivePorts) {
      port.send('increment');
    }
  }

  // Get the counts from each isolate
  for (int i = 0; i < numIsolates; i++) {
    final count = await receivePorts[i].first;
    print('Isolate $i count: $count');
  }

  // Shutdown isolates
  for (var isolate in isolates) {
    isolate.kill();
  }
}