= Introduction

Apache Cassandra stores data in tables, with each table consisting of
rows and columns. CQL (Cassandra Query Language) is used to query the
data stored in tables. Apache Cassandra data model is based around and
optimized for querying. Cassandra does not support relational data
modeling intended for relational databases.

== What is Data Modeling?

Data modeling is the process of identifying entities and their
relationships. In relational databases, data is placed in normalized
tables with foreign keys used to reference related data in other tables.
Queries that the application will make are driven by the structure of
the tables and related data are queried as table joins.

In Cassandra, data modeling is query-driven. The data access patterns
and application queries determine the structure and organization of data
which then used to design the database tables.

Data is modeled around specific queries. Queries are best designed to
access a single table, which implies that all entities involved in a
query must be in the same table to make data access (reads) very fast.
Data is modeled to best suit a query or a set of queries. A table could
have one or more entities as best suits a query. As entities do
typically have relationships among them and queries could involve
entities with relationships among them, a single entity may be included
in multiple tables.

== Query-driven modeling

Unlike a relational database model in which queries make use of table
joins to get data from multiple tables, joins are not supported in
Cassandra so all required fields (columns) must be grouped together in a
single table. Since each query is backed by a table, data is duplicated
across multiple tables in a process known as denormalization. Data
duplication and a high write throughput are used to achieve a high read
performance.

== Goals

The choice of the primary key and partition key is important to
distribute data evenly across the cluster. Keeping the number of
partitions read for a query to a minimum is also important because
different partitions could be located on different nodes and the
coordinator would need to send a request to each node adding to the
request overhead and latency. Even if the different partitions involved
in a query are on the same node, fewer partitions make for a more
efficient query.

== Partitions

Apache Cassandra is a distributed database that stores data across a
cluster of nodes. A partition key is used to partition data among the
nodes. Cassandra partitions data over the storage nodes using a variant
of consistent hashing for data distribution. Hashing is a technique used
to map data with which given a key, a hash function generates a hash
value (or simply a hash) that is stored in a hash table. A partition key
is generated from the first field of a primary key. Data partitioned
into hash tables using partition keys provides for rapid lookup. Fewer
the partitions used for a query faster is the response time for the
query.

As an example of partitioning, consider table `t` in which `id` is the
only field in the primary key.

....
CREATE TABLE t (
   id int,
   k int,
   v text,
   PRIMARY KEY (id)
);
....

The partition key is generated from the primary key `id` for data
distribution across the nodes in a cluster.

Consider a variation of table `t` that has two fields constituting the
primary key to make a composite or compound primary key.

....
CREATE TABLE t (
   id int,
   c text,
   k int,
   v text,
   PRIMARY KEY (id,c)
);
....

For the table `t` with a composite primary key the first field `id` is
used to generate the partition key and the second field `c` is the
clustering key used for sorting within a partition. Using clustering
keys to sort data makes retrieval of adjacent data more efficient.

In general, the first field or component of a primary key is hashed to
generate the partition key and the remaining fields or components are
the clustering keys that are used to sort data within a partition.
Partitioning data improves the efficiency of reads and writes. The other
fields that are not primary key fields may be indexed separately to
further improve query performance.

The partition key could be generated from multiple fields if they are
grouped as the first component of a primary key. As another variation of
the table `t`, consider a table with the first component of the primary
key made of two fields grouped using parentheses.

....
CREATE TABLE t (
   id1 int,
   id2 int,
   c1 text,
   c2 text
   k int,
   v text,
   PRIMARY KEY ((id1,id2),c1,c2)
);
....

For the preceding table `t` the first component of the primary key
constituting fields `id1` and `id2` is used to generate the partition
key and the rest of the fields `c1` and `c2` are the clustering keys
used for sorting within a partition.

== Comparing with Relational Data Model

Relational databases store data in tables that have relations with other
tables using foreign keys. A relational database’s approach to data
modeling is table-centric. Queries must use table joins to get data from
multiple tables that have a relation between them. Apache Cassandra does
not have the concept of foreign keys or relational integrity. Apache
Cassandra’s data model is based around designing efficient queries;
queries that don’t involve multiple tables. Relational databases
normalize data to avoid duplication. Apache Cassandra in contrast
de-normalizes data by duplicating data in multiple tables for a
query-centric data model. If a Cassandra data model cannot fully
integrate the complexity of relationships between the different entities
for a particular query, client-side joins in application code may be
used.

== Examples of Data Modeling

As an example, a `magazine` data set consists of data for magazines with
attributes such as magazine id, magazine name, publication frequency,
publication date, and publisher. A basic query (Q1) for magazine data is
to list all the magazine names including their publication frequency. As
not all data attributes are needed for Q1 the data model would only
consist of `id` ( for partition key), magazine name and publication
frequency as shown in Figure 1.

image::Figure_1_data_model.jpg[image]

Figure 1. Data Model for Q1

Another query (Q2) is to list all the magazine names by publisher. For
Q2 the data model would consist of an additional attribute `publisher`
for the partition key. The `id` would become the clustering key for
sorting within a partition. Data model for Q2 is illustrated in Figure
2.

image::Figure_2_data_model.jpg[image]

Figure 2. Data Model for Q2

== Designing Schema

After the conceptual data model has been created a schema may be
designed for a query. For Q1 the following schema may be used.

....
CREATE TABLE magazine_name (id int PRIMARY KEY, name text, publicationFrequency text)
....

For Q2 the schema definition would include a clustering key for sorting.

....
CREATE TABLE magazine_publisher (publisher text,id int,name text, publicationFrequency text,  
PRIMARY KEY (publisher, id)) WITH CLUSTERING ORDER BY (id DESC)
....

== Data Model Analysis

The data model is a conceptual model that must be analyzed and optimized
based on storage, capacity, redundancy and consistency. A data model may
need to be modified as a result of the analysis. Considerations or
limitations that are used in data model analysis include:

* Partition Size
* Data Redundancy
* Disk space
* Lightweight Transactions (LWT)

The two measures of partition size are the number of values in a
partition and partition size on disk. Though requirements for these
measures may vary based on the application a general guideline is to
keep number of values per partition to below 100,000 and disk space per
partition to below 100MB.

Data redundancies as duplicate data in tables and multiple partition
replicates are to be expected in the design of a data model , but
nevertheless should be kept in consideration as a parameter to keep to
the minimum. LWT transactions (compare-and-set, conditional update)
could affect performance and queries using LWT should be kept to the
minimum.

== Using Materialized Views

[WARNING]
.Warning
====
Materialized views (MVs) are experimental in the latest (4.0) release.
====
Materialized views (MVs) could be used to implement multiple queries
for a single table. A materialized view is a table built from data from
another table, the base table, with new primary key and new properties.
Changes to the base table data automatically add and update data in a
MV. Different queries may be implemented using a materialized view as an
MV's primary key differs from the base table. Queries are optimized by
the primary key definition.
