Goal
The purpose of this post is to share observations on why secondary indexes in Cassandra 1.2.X should only be used in specific use cases.Background
For background, here are two good write-ups on secondary indexes in Cassandra:
1) Original DataStax blog entry introducing secondary indexes by Jonathan Ellis
2) Indexing in Cassandra
Our view on secondary indexes is that they provide a good mechanism to short-circuit the restrictions of CQL3 data modeling to retrieve data in very specific situations. Secondary indexes are not a panacea to all data modeling challenges in CQL3 and should be used very selectively.
Check out the 2nd article listed above for the "official" guidelines for when to, and not to, use secondary indexes.
Observations
Our take is that secondary indexes should be used to enable read queries;1) on columns that contain a small set of possible values (low cardinality) that span many partitions
2) and where the indexed column is queried often
3) and data in the table is not often updated or deleted
3) and including the tables (cf's) partition index in the query is undesirable
4) and when explicitly creating another CQL3 table is undesirable
Narrow use-case, huh?
So, why are secondary indexes recommended for very specific use cases? It's because a secondary index creates a second, hidden, table "under the covers". Looking at the contents of the hidden table on disk, using SSTable2Jason, you can clearly see what's happening when you create a secondary index.
Here's an example, using a little POC schema, complete with a type in the table name.
CREATE TABLE ptoducts2 (
country_cd text,
base_div_nbr int,
sub_div text,
store_nbr int,
store_type text,
att1 text,
att2 text,
PRIMARY KEY ((country_cd, base_div_nbr), sub_div, store_nbr, store_type)
) WITH
bloom_filter_fp_chance=0.010000 AND
caching='KEYS_ONLY' AND
comment='' AND
dclocal_read_repair_chance=0.000000 AND
gc_grace_seconds=864000 AND
read_repair_chance=0.100000 AND
replicate_on_write='true' AND
populate_io_cache_on_flush='false' AND
compaction={'class': 'SizeTieredCompactionStrategy'} AND
compression={'sstable_compression': 'SnappyCompressor'};
CREATE INDEX att1 ON ptoducts2 (att1);
total 64
4 -rw-r--r-- 1 root root 46 Feb 10 06:20 demo2-ptoducts2.att1-ic-1-CompressionInfo.db
4 -rw-r--r-- 1 root root 159 Feb 10 06:20 demo2-ptoducts2.att1-ic-1-Data.db
4 -rw-r--r-- 1 root root 16 Feb 10 06:20 demo2-ptoducts2.att1-ic-1-Filter.db
4 -rw-r--r-- 1 root root 37 Feb 10 06:20 demo2-ptoducts2.att1-ic-1-Index.db
8 -rw-r--r-- 1 root root 4355 Feb 10 06:20 demo2-ptoducts2.att1-ic-1-Statistics.db
4 -rw-r--r-- 1 root root 73 Feb 10 06:20 demo2-ptoducts2.att1-ic-1-Summary.db
4 -rw-r--r-- 1 root root 79 Feb 10 06:20 demo2-ptoducts2.att1-ic-1-TOC.txt
4 -rw-r--r-- 1 root root 46 Feb 10 06:20 demo2-ptoducts2-ic-1-CompressionInfo.db
4 -rw-r--r-- 1 root root 300 Feb 10 06:20 demo2-ptoducts2-ic-1-Data.db
4 -rw-r--r-- 1 root root 16 Feb 10 06:20 demo2-ptoducts2-ic-1-Filter.db
4 -rw-r--r-- 1 root root 78 Feb 10 06:20 demo2-ptoducts2-ic-1-Index.db
8 -rw-r--r-- 1 root root 4357 Feb 10 06:20 demo2-ptoducts2-ic-1-Statistics.db
4 -rw-r--r-- 1 root root 96 Feb 10 06:20 demo2-ptoducts2-ic-1-Summary.db
4 -rw-r--r-- 1 root root 79 Feb 10 06:20 demo2-ptoducts2-ic-1-TOC.txt
The bolded, added fro emphasis, files represent the hidden table that was created with the CREATE INDEX statement.
Even more interesting is the content of this hidden table.
Here are the "rows" that exist in the ptoducts2 table:
cqlsh:demo2> select * from ptoducts2;
Here are the rows returned for a specific value of the secondary index:
cqlsh:demo2> SELECT * FROM ptoducts2 WHERE att1 = 'test2';
And here is the output of SSTable2Jason for the hidden table:
[
{"key": "74657374","columns": [["000275730000040000000100:us1:1:test","",1392040302405000], ["000275730000040000000100:us1:2:test","",1392040376314000], ["000275730000040000000100:us1:3:test","",1392040376326000], ["000275730000040000000100:us2:1:test","",1392040376339000], ["00026d780000040000000100:us1:1:test","",1392040377513000]]},
{"key": "7465737432","columns": [["000275730000040000000200:us1:1:test","",1392041431384000]]}
]
Looking at the actual data stored on disk shows that this index contains a partition for each unique value of the secondary index column, Just imagine what the secondary index data file would look like if you create a secondary index on a column that doesn't adhere to the guidelines listed above.
Conclusion
Now that you can see exactly what's happening with secondary indexes in Cassandra 1.2.X be sure to think through the use cases for when to leverage this mechanism. Secondary indexes do enable a lot of "ease of use" query functionality but there's a cost. Be conscious of the cost of this mechanism and use it appropriately. Make good, sound architectural and design trade-off type decisions when deciding to use secondary indexes.
We still prefer creating explicit tables to handle "index" scenarios. Hopefully, you find the right mix for your scenario.
No comments:
Post a Comment