cassandra中的SecondaryIndex的应用场景主要在于没有带着rowkey的查询。虽然带着rowkey的条件进行检索式cassandra这种非关系型数据库的主流检索,但是使用nosql DB的时候,往往一些统计功能,或者公共的功能,(例如关注度最高的微博,操作次数最多的操作等等)这些不需要和用户绑定,但是又是很多应用不可少的检索功能。并且在cassandra这种海量级的nosql DB中,如果没有Secondary Index这种功能提升没有带rowkey的查询性能的功能,那么就会丧失很多机会,让很多系统发展以后,都没有办法扩展自己的能力。
1、SecondaryIndex的结构
Secondary Index和所有的数据库的index一样是一个牺牲容量,换取某种查询性能的功能。系统会新建一个index表,来存放便于检索的数据,从而加快检索。而cassandra数据库的检索最成熟的条件,就是rowkey。所以Secondary index就是一个以index字段为rowkey,以数据表中的rowkey作为Column的StandCF。
在1.2版本的cassandra中实现的KeyIndex和CompositeIndex都是继承AbstractSimplePerColumnSecondaryIndex简单单列index的实现类。
public void init()
{
assert baseCfs != null && columnDefs != null && columnDefs.size() == 1;
ColumnDefinition columnDef = columnDefs.iterator().next();
init(columnDef);
AbstractType indexComparator = SecondaryIndex.getIndexComparator(baseCfs.metadata, columnDef);
CFMetaData indexedCfMetadata = CFMetaData.newIndexMetadata(baseCfs.metadata, columnDef, indexComparator);
indexCfs = ColumnFamilyStore.createColumnFamilyStore(baseCfs.table,
indexedCfMetadata.cfName,
new LocalPartitioner(columnDef.getValidator()),
indexedCfMetadata);
// enable and initialize row cache based on parent's setting and indexed column's cardinality
CFMetaData.Caching baseCaching = baseCfs.metadata.getCaching();
if (baseCaching == CFMetaData.Caching.ALL || baseCaching == CFMetaData.Caching.ROWS_ONLY)
{
/*
* # of index CF's key = cardinality of indexed column.
* if # of keys stored in index CF is more than average column counts (means tall table),
* then consider it as high cardinality.
*/
double estimatedKeys = indexCfs.estimateKeys();
double averageColumnCount = indexCfs.getMeanColumns();
if (averageColumnCount > 0 && estimatedKeys / averageColumnCount > 1)
{
logger.debug("turning row cache on for " + indexCfs.getColumnFamilyName());
indexCfs.metadata.caching(baseCaching);
indexCfs.initRowCache();
}
}
}
2、SecondaryIndex表的属性
SecondaryIndex的CF的属性有:
属性名 | 属性值 |
keyspaceName | 源CF的keyspaceName |
CFType | Standard CF |
Rowkey的类型 | index字段的类型 |
读修复概率 | 0 |
GC清理时间 | 0 |
keycache开启配置 | 如果源CF开启则开启 |
RowCache开启配置 | 否 |
compact策略 | 与源CF一致 |
compress策略 | 与源CF一致 |
public static CFMetaData newIndexMetadata(CFMetaData parent, ColumnDefinition info, AbstractType> columnComparator)
{
// Depends on parent's cache setting, turn on its index CF's cache.
// Here, only key cache is enabled, but later (in KeysIndex) row cache will be turned on depending on cardinality.
Caching indexCaching = parent.getCaching() == Caching.ALL || parent.getCaching() == Caching.KEYS_ONLY
? Caching.KEYS_ONLY
: Caching.NONE;
return new CFMetaData(parent.ksName, parent.indexColumnFamilyName(info), ColumnFamilyType.Standard, columnComparator, null)
.keyValidator(info.getValidator())
.readRepairChance(0.0)
.dcLocalReadRepairChance(0.0)
.gcGraceSeconds(0)
.caching(indexCaching)
.compactionStrategyClass(parent.compactionStrategyClass)
.compactionStrategyOptions(parent.compactionStrategyOptions)
.reloadSecondaryIndexMetadata(parent);
}
3、SecondaryIndex创建的条件
在普通的CF创建的时候,如果Column定义的时候,indexType属性可以为KEYS(即为KeyIndex),COMPOSITES(即为CompositeIndex),CUSTOM(即为用户自己二次开发的index功能)。
在1.2版本的cassandra中,目前只支持KeyIndex,主要是针对StandCF的普通Column的和CompositeIndex主要是针对CompositeCF的普通Column。
/**
* Drops and adds new indexes associated with the underlying CF
*/
public void reload()
{
// figure out what needs to be added and dropped.
// future: if/when we have modifiable settings for secondary indexes,
// they'll need to be handled here.
Collection indexedColumnNames = indexesByColumn.keySet();
for (ByteBuffer indexedColumn : indexedColumnNames)
{
ColumnDefinition def = baseCfs.metadata.getColumn_metadata().get(indexedColumn);
if (def == null || def.getIndexType() == null)
removeIndexedColumn(indexedColumn);
}
for (ColumnDefinition cdef : baseCfs.metadata.getColumn_metadata().values())
if (cdef.getIndexType() != null && !indexedColumnNames.contains(cdef.name))
addIndexedColumn(cdef);
Set reloadedIndexes = Collections.newSetFromMap(new IdentityHashMap());
for (SecondaryIndex index : indexesByColumn.values())
{
if (reloadedIndexes.add(index))
index.reload();
}
}
4、SecondaryIndex的应用
5、SecondaryIndex与源数据表的关系
Secondary IndexCF都是每个节点自己的本地表,没有副本,所以当数据迁移的时候,需要重新生成对应的index数据。
(1)重启的时候,需要在加载数据表的时候,加载出对应的indexCF结构。这样就根据indexInfo系统表中有没有对应的indexCF的记录,判断indexCF是否需要生成index数据,如果IndexInfo系统表有对应的indexCF数据,则不需要重新生成indexCF的数据,如果没有,则需要重新生成。
问题:如果indexCF在生成对应的数据的时候,此时进行的index条件的查询,由于index数据还没有生成完全导致极大可能差不多数据。