As commitment to our database literacy campaign, we're offering our Database Foundations course—for FREE!

Skip to main content
Completion requirements

1. Schema Design Best Practices

A. Schema-on-Write (SQL) vs. Schema-on-Read (NoSQL)

  • Schema-on-Write in SQL:

    • Definition: Schemas are rigidly defined, and every record must conform to the predefined structure. This ensures data integrity.
    • Example: In a relational database like PostgreSQL, a table for customer orders has fixed columns (order_id, customer_id, order_date, total_amount) and every insert operation must adhere to this structure.
    • Pros and Cons: Excellent for transactional consistency, but less flexible when requirements change.
  • Schema-on-Read in NoSQL:

    • Definition: Data can be stored without a predetermined schema. The structure is defined when reading the data. This allows flexibility and rapid iterations.
    • Example: In MongoDB, documents within a collection (say, orders) can have varying fields and structures. One document might include an order_date, while another may have an additional delivery_date.
    • Pros and Cons: Offers flexibility and scalability which are ideal for evolving applications but can lead to inconsistencies if not managed with care.

B. Designing Schemas for Document Stores versus Column-Family Stores

  • Document Stores (e.g., MongoDB, Couchbase):

    • Structure: Documents (typically in JSON or BSON format) are self-contained records that can store nested objects and arrays.
    • Modeling: Design schemas by thinking of documents as aggregates that bring related data together.
    • Example: A blog application might store an entire article with embedded comments in a single document.
      {
        "article_id": "a1",
        "title": "NoSQL Design Patterns",
        "content": "Detailed discussion on NoSQL design practices.",
        "comments": [
            {"user": "alice", "comment": "Great article!", "date": "2023-09-01"},
            {"user": "bob", "comment": "Very informative.", "date": "2023-09-02"}
        ]
      }
      
    • Strategy: Embedding related data (comments) keeps queries simple but might lead to large document sizes if the embedded array grows significantly.
  • Column-Family Stores (e.g., Cassandra, HBase):

    • Structure: Data is stored in rows and column families where rows may have dynamic columns suited to each entry.
    • Modeling: Design focuses on denormalizing data to optimize read performance.
    • Example: In a messaging application using Cassandra, user messages may be stored in a denormalized table where each row represents a conversation thread, and columns are dynamically added for each message.
    • Strategy: Denormalization is used to partition data effectively; however, careful consideration must be given to column family design and partition keys to ensure performance and scalability.

C. Common Pitfalls and Strategies to Avoid Data Redundancy

  • Pitfall: Over-denormalizing Data

    • Issue: Duplicating too much data can lead to inconsistencies during updates and high storage costs.
    • Strategy: Strike a balance between denormalization for read performance and normalization to avoid multiple copies of the same data. For instance, in a document database, instead of embedding an entire user profile in every order record, store a reference (like a user_id) and use a caching strategy when the profile details are needed frequently.
  • Pitfall: Ignoring Access Patterns

    • Issue: Designing schemas without understanding the query patterns and application workload can result in suboptimal performance.
    • Strategy: Begin by identifying the primary use cases. For example, if frequently accessing aggregated order information, precompute and store necessary fields rather than always performing complex queries on the fly.
  • Pitfall: Poor Partitioning Strategy in Column-Family Stores

    • Issue: Without a thoughtful design of partition keys and clustering columns, data might lead to hotspots and uneven data distribution.
    • Strategy: Analyze query patterns to choose effective partition keys. For example, in a time-series application, partition by a combination of geographic region and time bucket (e.g., month) to ensure that writes are distributed evenly across the cluster.

2. Modeling Relationships in a Denormalized World

A. Techniques for Embedding Versus Referencing Data

  • Embedding:

    • Description: Incorporate related data directly into the parent document.
    • When to Use: When the relationship is “contains” or “has-a”, and the embedded data is not expected to grow unboundedly.
    • Example: In a content management system, a blog post might embed its comments directly:
      {
        "post_id": 1001,
        "title": "Understanding NoSQL",
        "content": "An in-depth look at NoSQL databases...",
        "comments": [
          {"user": "john", "text": "Very useful!", "date": "2023-08-20"},
          {"user": "doe", "text": "I learned a lot.", "date": "2023-08-21"}
        ]
      }
      
    • Trade-Offs: Simplifies data retrieval but can lead to challenges if comments become too voluminous or need individual updates/frequent access.
  • Referencing:

    • Description: Store related data in separate documents or tables, using references (like unique IDs) to link them.
    • When to Use: When the related data is voluminous, shared among many entities, or subject to independent updates.
    • Example: In MongoDB, a user profile might reference an order document:
      {
        "user_id": "u123",
        "name": "Jane Doe",
        "orders": ["order_456", "order_789"]
      }
      
      And a separate collection holds order details.
    • Trade-Offs: Enhances data normalization and prevents bloating of parent documents, but may result in additional database lookups (join-like operations) which can impact performance.

B. Trade-offs in Performance, Maintainability, and Complexity

  • Performance:

    • Embedding improves read performance with fewer database calls, making it ideal for retrieving self-contained documents.
    • Referencing can be slower if multiple lookups are necessary but improves performance for write operations focused on a single record.
  • Maintainability:

    • Embedded documents may become harder to maintain if the embedded data needs independent updates.
    • Referencing keeps schemas clean and modular but may introduce complexity in terms of consistency and data integrity across collections/tables.
  • Complexity:

    • Embedded relationships can simplify queries but may complicate updates and large-scale aggregations.
    • References necessitate additional logic to manage relationships, especially in aggregations and when ensuring referential integrity.

C. Practical Examples of Denormalized Data Models

  • Example in a Document Store:

    • Scenario: E-commerce application.
    • Data Model Options:
      • Embedding: Embed order items in the order document for quick transactional read operations.
        {
          "order_id": "order_1001",
          "customer_id": "cust_789",
          "order_date": "2023-09-15",
          "items": [
            {"product_id": "prod_1", "quantity": 2, "price": 19.99},
            {"product_id": "prod_2", "quantity": 1, "price": 99.99}
          ]
        }
        
      • Referencing: Store order items in a separate collection if items are large, need independent updates, or if there’s a need to track inventory separately.
        {
          "order_id": "order_1001",
          "customer_id": "cust_789",
          "order_date": "2023-09-15",
          "item_ids": ["item_5001", "item_5002"]
        }
        
    • Choosing Strategy: If the typical query is to view the order details as a whole, embedding makes sense. If items need independent processing (such as inventory adjustments), referencing might be more appropriate.
  • Example in a Column-Family Store (Cassandra):

    • Scenario: Time-series sensor data.
    • Denormalized Model:
      • Design: Data is partitioned by a composite key (sensor_id, date_bucket) where each row contains multiple timestamped readings.
        CREATE TABLE sensor_readings (
            sensor_id text,
            date_bucket text,
            reading_time timestamp,
            reading_value double,
            PRIMARY KEY ((sensor_id, date_bucket), reading_time)
        );
        
      • Trade-Off: Denormalizing by pre-aggregating data (e.g., hourly averages) in additional tables can optimize read performance but complicates write processes when multiple tables require synchronization.

D. Understanding Aggregation and Data Duplication as Performance Strategies

  • Pre-Aggregation:

    • Concept: Store computed values (such as totals or averages) alongside raw data to avoid expensive runtime aggregation.
    • Example: In a sales database, instead of computing daily totals from individual transactions on each query, maintain a separate table that holds daily aggregated sales figures.
    • Consideration: Data duplication must be controlled and updated reliably whenever new transactions occur.
  • Data Duplication:

    • Concept: Duplicating data across multiple models or tables to optimize for particular query patterns.
    • Example: In a social media application, user profile data might be duplicated across several documents (e.g., posts, comments) to allow for fast user lookups without joining multiple collections.
    • Trade-Off: While this approach improves performance for specific queries, it increases the challenge of ensuring data consistency and may require background processes to synchronize updates.

3. Case Examples

A. Walkthroughs of Real-World Data Models

  • Case 1: E-commerce Application (Document Store)

    • Scenario: An online store with users, orders, and products.
    • Design Options:
      • Embedding Orders in User Documents: For scenarios where orders are seldom updated after creation and are mostly used for historical viewing.
      • Referencing Products in Orders: Instead of embedding detailed product specifications, reference product IDs from an independent catalog collection to minimize redundancy.
    • Discussion: By applying a mix of embedding (for orders) and referencing (for products), the design leverages fast read operations for historical order retrieval while keeping product data centrally managed.
  • Case 2: IoT Data Management (Column-Family Store)

    • Scenario: Collecting large volumes of sensor data from a distributed network.
    • Design Choices:
      • Denormalization: Store sensor readings with composite keys to optimize writes and queries for recent data.
      • Partitioning Strategy: Use a composite key (sensor_id, date_bucket) to distribute data evenly across nodes.
      • Aggregation: Maintain a separate table for pre-aggregated hourly or daily sensor statistics.
    • Discussion: The denormalized approach ensures high write throughput and efficient time-based queries, while pre-aggregation minimizes expensive read-time computations.

B. Discussion of Different Approaches and When Each is Most Effective

  • Embedding vs. Referencing:

    • Effective Embedding: When the related data is tightly coupled, changes infrequently, and retrieval of the parent object with all its details is common (e.g., order details within a purchase).
    • Effective Referencing: When data is shared across many entities, is too large to embed, or must be updated independently (e.g., user profiles referenced in multiple collections).
  • Schema Variants Based on Access Pattern:

    • Write-Optimized Models: For write-heavy applications, favor designs that minimize the number of write operations per transaction (e.g., denormalize and use pre-aggregation).
    • Read-Optimized Models: When read performance is paramount, redundancy via duplication (with proper update mechanisms) can reduce query complexity.
    • Balanced Models: In cases with mixed workloads, a hybrid strategy that leverages both embedding and referencing can often provide a good compromise.
Last modified: Friday, 11 April 2025, 10:45 AM