Data modeling and schema tuning for Apache Cassandra databases
Data models and the resulting schemas (keyspaces, tables, and indexes) influence the performance of your Apache Cassandra cluster and associated applications that you develop. Optimal data modeling typically leads to better performance, scalability, and stability for your Apache Cassandra cluster and applications. Suboptimal data models can lead to poor performance, even with small amounts of data.
This guide introduces important data modeling concepts, strategies you can use to evaluate your data model, and considerations for properly tuned schemas.
Prepare your data model
Data modeling includes theoretical designs as well as the practical outcomes of those designs. For example:
-
Logical and physical models
-
Data access patterns (queries)
-
Keyspaces and replication
-
Indexes
-
Table properties like compaction and compression
-
Table schema elements like columns and primary keys
-
Hot data and caches
To get started with data modeling, see the following:
|
If you are migrating from a relational database, be aware that there are significant differences between RDBMS and Cassandra-based data models. Notably, data models are based on query patterns, not entities and relationships. Additionally, distributed databases scale horizontally by adding more nodes to the cluster rather than relying on a single machine. Often, developers migrating from relational databases are attracted to materialized views, secondary indexes, and user-defined functions (UDFs). However, these patterns aren’t ideal for heavy usage in distributed NoSQL databases due to performance impacts at scale. Review this guide and the related information to understand the purpose and requirements for different data types and auxiliary indexes. |
Tune table properties
When you create or alter a table, you can specify table configuration properties, including the compaction strategy and data compression options. Understanding how to use and tune these parameters is important for preparing your data model and planning your clusters.
To check the properties for an existing table, use the DESCRIBE TABLE command.
Compaction
The UnifiedCompactionStrategy (UCS) is recommended for all Apache Cassandra versions that support it. For earlier versions, the default SizeTieredCompactionStrategy (STCS) is acceptable for most use cases.
|
If you intend to use TimeWindowCompactionStrategy (TWCS) for time series data and expiring time-to-live (TTL) workloads, you must enable TWCS when you create the table. You cannot apply TWCS to existing tables that weren’t created with the proper time windowing layout. |
For information about the compaction process, supported compaction strategies, and table properties for compaction, see the following:
In addition to the table properties for compaction, there are settings in cassandra.yaml that influence compaction behavior and performance for the entire node:
-
compaction_throughputorcompaction_throughput_mb_per_sec -
concurrent_compactors -
memtable_*parameters, particularly when using LCS -
commitlog_*parameters
|
You can use write survey mode to test compaction and compression configurations on an isolated node. |
Many pending compactions is a strong indicator of suboptimal compaction configuration. Use metrics retrieval and observability tools to get pending compaction statistics. For example, to get the current number of pending compactions for a node, run one of the following commands:
nodetool sjk mx -f Value -mg -b org.apache.cassandra.metrics:type=Compaction,name=PendingTasks
nodetool compactionstats
Compression
To decrease disk consumption, Cassandra stores data on disk in compressed form by default. For this reason, misconfigured compression can significantly degrade performance.
|
If you alter a table’s compression settings, the new configuration only applies to new SSTables. To apply the new settings to all existing SSTables, use
|
For more information about compression, see the following:
Disable background read repair in earlier versions
Read repair happens when Cassandra detects data inconsistencies during a read operation. By default, it blocks application operations until the repair process is complete. Read repair behavior and options depend on your Cassandra version.
Earlier Cassandra versions included a background read repair option that was set with read_repair_chance and dclocal_read_repair_chance.
This behavior was removed in Cassandra 4.0, and this version of background read repair isn’t recommended in earlier versions.
If you are running an earlier version that includes background read repair, set read_repair_chance and dclocal_read_repair_chance to 0 to disable background read repair.
You can use a script to disable background read repair on all tables on a node, for example:
for i in `cqlsh -e 'describe schema;' |grep 'CREATE TABLE'|sed -e 's|CREATE TABLE \([^ ]*\) .*|\1|'`; do
echo "alter table $i with read_repair_chance = 0.0 and dclocal_read_repair_chance = 0.0;"
done| tee alters.cql
cqlsh -f alters.cql
For more information, see CASSANDRA-9753 and CASSANDRA-13910.
Repair down nodes to avoid resurrected deletes (zombies)
|
It’s important that you understand how tombstones work and their impact on database performance. Specifically, you need to know how to address down nodes to avoid resurrecting deletes that a down node missed. |
When a mutation deletes data, the database marks the data with a tombstone. This helps ensure consistency across replicas and proper reconciliation during compaction. For more information, see About Deletes and Tombstones in Apache Cassandra and Tombstones in Apache Cassandra.
The gc_grace_seconds property sets the length of time that a tombstone is retained in SSTables.
When this time period expires, deleted data and tombstones are cleared from the SSTables during compaction.
If any nodes in a cluster go down when data is being deleted, those nodes must be returned to the cluster and repaired before gc_grace_seconds expires.
Otherwise, deleted data can be resurrected because the desynchronized nodes don’t have a tombstone for the deleted data.
If a node is down or not repaired for longer than gc_grace_seconds, you must clear all Cassandra data from the node and replace the node.
If a cluster is performing poorly due to too many tombstones, you can reduce gc_grace_seconds to clear tombstones more frequently.
However:
-
After changing
gc_grace_secondsyou must run a full token range repair within the newgc_grace_secondsperiod. -
If
gc_grace_secondsis extremely low, tombstones might be removed before all replicas have seen the deletion, resulting in resurrected data (zombies). -
If
gc_grace_secondsis too low, it can impact hinted handoff by preventing collection and replaying of hints. If this occurs, repair is required.
Get the schema
After creating your keyspaces and tables in your Cassandra cluster, get the schema to evaluate your data model.
The schema is included in a diagnostic tarball generated by the DataStax diagnostic collection scripts.
You can also get the schema with cqlsh:
-
Run the
DESCRIBE SCHEMAcommand and pipe the output to a file:cqlsh -e 'DESCRIBE SCHEMA;' > schema.cql
The examples in this guide assume you have a running Cassandra cluster with keyspaces and tables already created, and you have output the schema definition to a file named schema.cql.
If you use a different file name, edit the example commands accordingly.
For diagnostic tarballs, schema.cql is located at /driver/schema for each node.
|
The schema is retrieved from a single node. Typically, this is acceptable because schema changes are propagated to all nodes in the cluster. In situations where you need to compare nodes or investigate desynchronized nodes, use |
Evaluate keyspace replication
-
Use NetworkTopologyStrategy for production.
-
Use an odd number for the replication factor, typically 3 or 5. Higher replication is usually not necessary. Even-numbered replication makes clusters less resistant because there are no redundant nodes to serve requests in the case of a node failure. Additional duplication and preservation of data for failover or disaster recovery are better handled through multi-datacenter clusters and backups.
-
When you add or move a datacenter, you must manually reconfigure replication for the
system_authkeyspace.
Fix replication problems
To fix problems with replication, you can either run the ALTER KEYSPACE command manually, or use a script to perform these actions automatically.
|
For example, the following script can help you adjust the replication factor of system and user-provided keyspaces based on the current cluster topology. It generates a file with CQL commands that you can run after reviewing them.
adjust-keyspaces.shscript-
#!/bin/bash rep_factor=3 fix_system=1 keyspaces=("system_auth" "system_distributed" "dse_security" "solr_admin" "dse_perf" "dse_leases" "dse_analytics" '"HiveMetaStore"' "system_traces" "dse_advrep" "dse_system") cqlsh_options="" nodetool_options="" function usage() { echo "Usage: $0 [-n replication_factor] [-c cqlsh_options_as_a_string] [-o nodetool_options_as_a_string] [keyspace1 keyspace2 ...]" echo "Defaults:" echo " replication_factor = $rep_factor" echo " keyspace1.. = name(s) of keyspace(s) to fix replication factor." echo " fix all system keyspaces if not specified" } while getopts ":hc:o:n:" opt; do case $opt in n) rep_factor=$OPTARG ;; c) cqlsh_options=$OPTARG ;; o) nodetool_options=$OPTARG ;; h) usage exit 0 ;; *) usage exit 1 ;; esac done shift "$((OPTIND -1))" if [ -z "$(command -v nodetool)" ]; then echo "Can't find nodetool in the PATH" exit 1 fi if [ -z "$(command -v cqlsh)" ]; then echo "Can't find cqlsh in the PATH" exit 1 fi if [ $# -ne 0 ] ; then keyspaces=() fix_system=0 for ks in "$@" ; do keyspaces+=("$ks") done fi my_pid=$$ NODETOOL_FILE=/tmp/nodetool-out-${my_pid}.txt SCHEMA_FILE=/tmp/cqlsh-schema-${my_pid}.txt # File with commands to execute CQL_FILE=/tmp/fix-keyspaces-${my_pid}.cql rm -f $CQL_FILE touch $CQL_FILE nodetool $nodetool_options status > $NODETOOL_FILE RES=$? if [ $RES -ne 0 ] ; then echo "Can't execute 'nodetool status'! Exit code: $?" exit 1 fi if grep -e '^[UD][NJLM] ' "$NODETOOL_FILE"|grep -v -e '^UN ' > /dev/null 2>&1 ; then echo "Cluster has nodes with non-UN status, can't adjust replication factor!" exit 1 fi cqlsh $cqlsh_options -e 'DESCRIBE FULL SCHEMA;' > $SCHEMA_FILE RES=$? if [ $RES -ne 0 ] ; then echo "Can't get schema via cqlsh! Exit code: $?" exit 1 fi declare -A all_ks for i in $(grep 'CREATE KEYSPACE' "$SCHEMA_FILE"|grep -e 'SimpleStrategy\|NetworkTopologyStrategy'|sed -e 's|^CREATE KEYSPACE \([^ ]*\).*$|\1|'); do all_ks[$i]=1 done #echo "All keyspaces=${!all_ks[@]}" declare -A all_dcs curr_dc='' cnt=0 while read -d $'\n' line ; do # echo "$line" if echo "$line"|grep -e '^Datacenter: ' > /dev/null 2>&1 ; then new_dc=$(echo "$line"|sed -e 's|^Datacenter: \([^ ]*\).*$|\1|') # echo "new_dc=$new_dc" if [ -z "$new_dc" ] ; then echo "Can't extract DC from line '$line'" exit 1 fi if [ -z "$curr_dc" ] ; then curr_dc=$new_dc else max_rf=$rep_factor if [ $rep_factor -gt $cnt ]; then max_rf=$cnt fi echo "$curr_dc has $cnt nodes max RF=$max_rf" all_dcs[$curr_dc]=$max_rf curr_dc=$new_dc cnt=0 fi fi if echo "$line"|grep -e '^[UD][NJLM] ' > /dev/null 2>&1 ; then ((cnt++)) fi done < $NODETOOL_FILE # push the last DC as well max_rf=$rep_factor if [ $rep_factor -gt $cnt ]; then max_rf=$cnt fi echo "$curr_dc has $cnt nodes max RF=$max_rf" all_dcs[$curr_dc]=$max_rf #echo "All DCs=${!all_dcs[@]}" if [ ${#all_dcs[@]} -eq 0 ]; then echo "Can't identify data centers!" exit 1 fi to_repair=() for i in "${keyspaces[@]}"; do # echo "Processing $i" if [ -z "${all_ks[$i]}" ]; then if [ $fix_system = "0" ]; then echo "$i not in the list of existing keyspaces!" fi continue fi echo -n "ALTER KEYSPACE $i WITH replication = {'class': 'NetworkTopologyStrategy'" >> $CQL_FILE for key in "${!all_dcs[@]}"; do echo -n ", '$key': ${all_dcs[$key]}" >> $CQL_FILE done echo "};" >> $CQL_FILE to_repair+=("$i") done ret_code=0 if [ ${#to_repair[@]} -eq 0 ]; then rm -f $CQL_FILE echo "No keyspaces processed!" ret_code=1 else echo "Please execute command 'cqlsh -f $CQL_FILE $cqlsh_options' to adjust replication factor for keyspaces" echo "After that, execute following commands on each node of the cluster:" for i in "${to_repair[@]}" ; do echo "nodetool $nodetool_options repair -pr $i" done fi # remove already processed files rm -f $SCHEMA_FILE $NODETOOL_FILE exit $ret_code - Usage
-
The script must be executed on one of the nodes in your cluster because it uses
cqlshandnodetoolto find cluster topology and extract a list of keyspaces.The script accepts several command line parameters, and it supports clusters with no authentication, as well as clusters with non-default configuration:
-
-h: Get usage information. -
-n replication_factor: Specify the desired replication factor for keyspaces. If this parameter is greater than the number of nodes in a specific datacenter, then the number of nodes is used instead. -
-c cqlsh_options: All options that must be passed tocqlsh, such as authentication. -
-o nodetool_options: All options that must be passed tonodetool. -
Optional list of keyspaces for which adjustment should be done. If not specified, a hardcoded list of system keyspaces is used.
-
- Output
-
The script generates a file with CQL commands for changing replication factors of keyspaces, and it prints usage instructions, including a
nodetool repaircommand for each keyspace that would be changed. You must runnodetool repairon all keyspaces where a change is made. For example:SearchAnalytics has 3 nodes max RF=3 Please execute command 'cqlsh -f /tmp/fix-keyspaces-7111.cql' to adjust replication factor for keyspaces After that, execute following commands on each node of the cluster: nodetool repair -pr system_auth nodetool repair -pr system_distributed nodetool repair -pr dse_security nodetool repair -pr dse_perf nodetool repair -pr dse_leases nodetool repair -pr dse_analytics nodetool repair -pr "HiveMetaStore" nodetool repair -pr system_tracesThe script adjusts replication for the
system_auth,system_distributed, andsystem_traceskeyspaces.
Evaluate the number of tables
Using your schema.cql file, use the following command to check how many tables and keyspaces are in your cluster:
grep 'CREATE TABLE' schema.cql |wc -l
Having too many tables in a cluster can cause substantial performance degradation. Inherently, throughput drops as the number of tables increases.
Because performance is influenced by a range of other factors, it is difficult to determine a universal table threshold. You can use the following guidelines as a starting point:
-
Set the
tables_warn_thresholdguardrail to 500 or less, depending on your cluster resources and workloads. For SAI-heavy and vector search workloads, set this guardrail lower (100 to 200).Clusters can support more tables, but this warning alerts you that you might need to audit underutilized tables or start planning to rearchitect your data model and schemas.
-
Set the
tables_fail_thresholdguardrail to 1000 or less, depending on your cluster resources and workloads. For SAI-heavy and vector search workloads, set this guardrail much lower (200 to 500).Distributed database clusters almost universally experience performance degradation issues at or before 1000 tables, such as excessive memory usage, long-running compactions, and query failures. While some clusters might remain stable, this many tables is usually evidence of an inefficient data model.
For example, a Cassandra data modeling best practice is one table per major query pattern; however, if your applications use 1000 or more substantially different query patterns, your query patterns could probably be optimized through prepared statements or indexes.
-
Understand how JVM heap and byte count can reduce a cluster’s table threshold:
-
Each table uses a small amount of memory for metadata.
-
All tables produce memtables and, through compaction, SSTables.
-
The number of columns and rows in a table can increase pressure on memory by storing more auxiliary data, such as bloom filters, caches, and indexes.
-
Each keyspace causes additional overhead in JVM memory. Having many keyspaces can reduce your cluster’s actual table capacity, regardless of the number of tables per keyspace.
-
-
Tune table properties for appropriate resource usage and performance.
Evaluate keys and partitions
Primary keys, partitions, and clustering columns are fundamental to query performance. For an introduction to these concepts, see the following:
Partition size
For efficient operation, partitions must be sized within certain limits. Partition size can be measured by the number of values in the partition and the partition size on disk.
A cell is a stored value in one column in one row. In addition to the actual data value, each cell has associated metadata, such as timestamp, optional TTL, and additional data for more complex data types, such as collections.
Cassandra has a practical limit of 2 billion (231) cells per partition. It is difficult to exactly predict the required disk space because this depends on the number of rows, columns, primary key columns, and static columns in the table. However, for most use cases, DataStax recommends fewer than 100,000 items per partition and partitions smaller than 100 MB.
The presence of large partitions (greater than 100 MB) is a sign of a suboptimal data model, and they create an additional load on the database. For example:
-
Low cardinality of partition keys.
-
Non-uniform spread of data between partitions.
-
Too many columns and rows in the table, especially when every row contains data for all or most columns.
-
Storing large BLOBs or long text strings in cells.
The performance impacts of large partitions include:
-
Imbalanced partitions cause some nodes to run more operations (hotspots) and trigger compaction more often.
-
More data must be transferred when reading the whole partition.
-
Performance degradation in external systems. For example, a partition is the minimal object mapped into a Apache Spark™ partition, so any imbalance in the database partitions can lead to an imbalance when processing data with Spark.
For more information, see the following:
Get information about partitions
|
If your tables have large partitions, low cardinality partitions, or other issues with partitions, the only long term solution is to change your data model and recreate the tables with more performant partition keys and clustering columns. |
Use the following tools to get information about partitions, including the size and cardinality:
nodetool tablehistograms-
Use the
nodetool tablehistogramscommand to find the partition sizes for75,95,99, and100percentiles:nodetool tablehistograms KEYSPACE_NAME TABLE_NAMEIf the output reports significantly different
Partition SizeandCell Countvalues for each partition, then it is likely that the partition key values are unevenly distributed across partitions, there are too many columns in the table, or too many elements in non-frozen collection columns.test/widerows histograms Percentile SSTables Write Latency Read Latency Partition Size Cell Count (micros) (micros) (bytes) 50% 1.00 126.93 1310.72 545791 103 75% 1.00 152.32 1310.72 545791 124 95% 1.00 785.94 1310.72 545791 124 98% 1.00 1629.72 1310.72 545791 124 99% 1.00 2346.80 1310.72 545791 124 Min 1.00 73.46 1310.72 454827 87 Max 1.00 2346.80 1572.86 545791 124Similar information can be obtained from the
sstablepartitionscommand. nodetool tablestats-
Information about maximum partition size is available through
nodetool tablestats.In the output, check the following:
-
Compacted partition maximum bytes: Make sure the values are smaller than the recommended 100 MB. -
Number of partitions: This is an estimate that can indicate low cardinality partition keys.
-
dsbulk-
You can use DataStax Bulk Loader (DSBulk) to identify partition keys that have the largest number of rows if your cluster has a non-uniform spread of partition key values. The main advantage of DSBulk is that it works with the whole cluster. For example, to find the largest partitions in a table:
dsbulk count -k KEYSPACE_NAME -t TABLE_NAME --log.verbosity 0 --stats.modes partitionsResult'29' 106 51.71 '96' 99 48.29 sstablemetadata-
In Cassandra 4.0 and later, you can use the
sstablemetadatautility with the-scommand line parameter to identify the largest partitions in specific SSTables. For Cassandra 3.x, use the sstable-tools project.sstablemetadataprovides information about the largest partitions as both row count and size in bytes. However, it runs on individual SSTable files, so it could be difficult to analyze partitions that are split across multiple files.sstablemetadata -u -s path_to_file/mc-1-big-Data.dbResultSSTable: /Users/ott/var/dse-5.1/data/cassandra/data/datastax/vehicle-8311d7d14eb211e8b416e79c15bfa204/mc-1-big Size: 58378400 Partitions: 800 Tombstones: 0 Cells: 2982161 WidestPartitions: [266:20180425] 534 [202:20180425] 534 [232:20180425] 534 [187:20180425] 534 [228:20180425] 534 LargestPartitions: [266:20180425] 134568 (131.4 kB) [202:20180425] 134568 (131.4 kB) [232:20180425] 134568 (131.4 kB) [187:20180425] 134568 (131.4 kB) [228:20180425] 134568 (131.4 kB) ... SELECT DISTINCT-
To check for low cardinality partition keys, you can run a
SELECT DISTINCTquery on the partition key columns, and then check thecountin the output for the number of distinct values returned.SELECT DISTINCT partition_key_list, count(*) FROM table
Evaluate the number of columns per table
DataStax doesn’t recommend creating tables with hundreds or thousands of columns for the following reasons:
-
It is easy to exceed the recommended maximum number of cells per partition and columns per row.
-
Every cell has a storage cost.
-
Range scans perform poorly.
If a table has too many columns, analyze access patterns for that table, and then consider the following solutions:
-
If multiple columns are always read together, combine those columns into one frozen user-defined type (UDT) where all data in the UDT is written as one cell per row.
-
Handle serialization and deserialization of data at the application level, not within the table schema.
-
Where appropriate, store values as BLOBs.
-
Split data into separate tables.
Evaluate data types
Cassandra provides a rich set of data types that can be used for table columns. Because so many data types exist, it can be easy for new users to choose an incorrect or suboptimal data type. Using appropriate data types helps ensure efficient storage and accurate query results.
The following list describes notable antipatterns to avoid when choosing data types. It doesn’t describe all data types or all possible antipatterns.
- Avoid overreliance on the
texttype -
Don’t use the
texttype when another specialized type is more appropriate. It can use excess storage or produce incorrect query results.For example, don’t use
textto store timestamps. A timestamp encoded astextusing ISO-8601 notation occupies 28 bytes, while the specializedtimestamptype uses only 8 bytes. - Choose numeric types with appropriate precision
-
Don’t use numeric types that have larger value ranges than are necessary for the data because this stores values at an unnecessary precision, wasting space on irrelevant values. For example, don’t use
bigint(8 bytes) whenint(4 bytes) is sufficient. The excess storage is even worse whendecimalandvarinttypes are used incorrectly because they don’t have a fixed size; they are sized based on the actual value. - Range queries can fail silently when inappropriate types are used for sorted data
-
Don’t use string types when numeric or chronological ordering is needed. For example, storing numbers or timestamps as
textcan cause range queries (greater than, less than, equal to) to fail because the text representation of those values doesn’t support the required ordering for comparison. If a sortable type is used, but it isn’t the correct type for the data, then range queries can run successfully but return incorrect results. - Prefer collections (maps, lists, sets) over UDTs and tuples
-
To store multiple values in a single column, you can use collections (maps, lists, sets), user-defined types (UDTs), and tuples:
-
Collections are preferred when possible, particularly frozen collections.
Performance degradation is possible when a database has large collections and non-frozen collections. This includes read amplification, non-idempotent writes, increased storage overhead, and extra read-before-write operations. To learn when to use collections, how to create efficient collections, and how to reduce performance impacts, see Collections in CQL.
-
UDTs are useful because they are customizable. However, they are more complex to manage and prone to becoming overloaded with too many elements. Consider collections before falling back to UDTs. When UDTs are used, use them deliberately and implement organizational policies that reduce the likelihood of uncontrolled alterations to UDTs.
-
DataStax recommends collections and UDTs over tuples. Tuples are always frozen, requiring a full rewrite for each update with explicit
nullvalues for unset/empty elements, and they require additional handling during upgrade and migrations to avoid data loss.
Collections, UDTs, and tuples all have practical and literal size limits that must be monitored and preemptively addressed to avoid performance degradation.
-
- Prefer frozen collections when using collections
-
Collection types (
map,list,set) can be either frozen or non-frozen. This is an important distinction that can impact application performance and data model designs. - Counters are non-idempotent and restrict the table schema
-
The
countertype is useful for integers that are modified by increments or decrements. However, because counter operations are non-idempotent, they cannot be retried automatically without risking overcounts or undercounts. Additionally, thecountertype places restrictions on the table schema and operations likeALTER TABLEandINSERT. For example, tables withcountercolumns can have onlycountercolumns and the primary key columns. If you choose to usecountercolumns, be aware of the limitations, and make sure your application logic accounts for the non-idempotent nature of counter operations. For more information, see CQL data types: Counter. - BLOBs have a practical limit of 2 GB
-
Storing large BLOBs, such as images or videos, can lead to unbalanced partitions and degraded performance. Instead, for large binary content, store the data in an object store or content delivery network (CDN), and then store the URI in your database.
Evaluate indexes
By default, you can query tables using partition key columns, including the primary key and clustering columns. To query on non-partition key columns, use secondary (auxiliary) indexes:
-
Storage-Attached Indexing (SAI) (recommended)
-
Original secondary indexing (2i)
-
SSTable-Attached Secondary Indexing (SASI)
-
Materialized views (enables non-primary key queries but isn’t a literal index)
For more information about index types, use cases, and CQL commands for creating and querying indexes, see Index types and use cases and Prepare to use materialized views.
Follow these best practices to ensure your indexes are performant and not creating unnecessary load on the cluster:
- Indexing isn’t a replacement for an effective data model
-
Indexes can enable specific query patterns that aren’t otherwise possible, but they aren’t a replacement for best practices like one table per query. Each index has a maintenance and storage cost in the database, and poorly designed indexes lead to poor query performance.
- Avoid superfluous indexes and audit unused indexes
-
Because the database needs resources to build and maintain secondary indexes, DataStax recommends limiting the number of indexes and removing unused indexes. Using your
schema.cqlfile, use the following command to check how many indexes are in your cluster:grep 'CREATE INDEX' schema.cql|wc -l - Storage-attached indexing (SAI) is recommended
-
SAI is recommended for most workloads but these indexes require careful planning and preparation of hardware capacity. For more information, see the following:
- Original secondary indexing (2i, index lookups) isn’t recommended
-
2i isn’t recommended except for edge cases that aren’t addressed by other indexing strategies. This type of indexing is prone to performance degradation and only suitable for specific schemas and query patterns. For more information, see the following:
- Don’t use SSTable-attached Secondary Indexes (SASI)
-
SASI is deprecated in Cassandra 5.0 and not recommended in earlier versions. If you are migrating or upgrading from an earlier version, remove SASI indexes from your schema and data model. Using your
schema.cqlfile, use the following command to check for SASI indexes in your cluster:grep 'CREATE CUSTOM INDEX.*SASIIndex' schema.cql|wc -l - Understand performance implications and limitations of materialized views
-
Materialized views (MVs) aren’t a literal index type, but they can be used to enable queries that aren’t possible with a table’s primary key. For information about how MVs work, known limiations, and performance impacts, see Prepare to use materialized views.
MVs are considered experimental and not recommended for production use.
They can be useful for data modeling because you can write data to one table, and then use MVs to test different primary keys, query patterns, and slices of data from that one table.
MVs are usually less performant than duplicate writes to multiple tables that have different primary keys. Although these duplicate writes add complexity to your application logic, this approach has advantages over MVs:
-
Complete flexibility in defining the primary key for each table, whereas MVs must respect the base table’s primary key.
-
Avoids performance impacts, such as extra read-before-write cycles when iterating writes to MVs.
-
Avoids repair issues known to occur with materialized views.
Both tables and MVs consume storage space in the cluster because MV data is stored in each view, similar to an index or table. Each MV increases the total size of the data stored. This can be significant when there are many MVs or the base table is large. Depending on your workloads, separate tables or secondary indexes can be more efficient to store.
If you choose to use MVs, plan your data model to minimize the number of MVs that you create. Using your
schema.cqlfile, use the following command to check the number of MVs in your cluster:grep 'CREATE MATERIALIZED VIEW' schema.cql|wc -l -
Evaluate query patterns
DataStax recommends that you design your application to use idempotent operations whenever possible. Minimize non-idempotent and expensive read-before-write operations, such as lightweight transactions (LWTs) and certain collection mutations, because they can degrade performance and throughput.
In some cases, these operations are unavoidable. If you must use non-idempotent and read-before-write operations, follow industry best practices for these patterns. For example, make sure your applications have appropriate retry logic and your clusters have adequate resources to handle the additional load.