cassandra的一组sstable中虽然index.db已经记录了rowkey所在data.db文件中的位置,但是index.db是磁盘文件,如果每次查询都要通过该文件查询得到,那么查询就必然增加一次IO,所以就有了keycache,这个记录了sstable中rowkey对应的data.db文件中的位置,并且存储在内存中,减少一次IO来加快通过rowkey的检索速度。
在cassandra运行中,cache的总体管理和控制主要由CacheService管理
public final AutoSavingCache<KeyCacheKey, RowIndexEntry> keyCache;
而一组sstable就会对应在内存中映射出一个SSTableReader对象进行对应SSTable的控制。
所以在SSTableReader中就会有相应的指针指向keycache
private InstrumentingCache<KeyCacheKey, RowIndexEntry> keyCache;
1、keycache的初始化,是在CF创建生成内存对象ColumnFamilyStore的时候,根据cache的配置参数而进行开启的。
keycache的相关属性介绍如下:
keycache的内存容量:cassandra.yaml文件中的配置项key_cache_size_in_mb:如果不为空,则keycache在内存中的容量即使这个配置值,否则如果总量内存的5%如果大于100MB,则取100MB,否则取总量内存的5%。
keycache的内存结构:public final AutoSavingCache<KeyCacheKey, RowIndexEntry> keyCache;为一个AutoSavingCache对象。AutoSavingCache是继承父类InstrumentingCache<K, V>,
首先InstrumentingCache<K, V>中有四个属性分别是:
private volatile boolean capacitySetManually;判断是否进行过手动更改cache容量的
private final ICache<K, V> map;主要是内存中存放cache数据的内存结构,keycache的主要实现类就是org.apache.cassandra.cache.ConcurrentLinkedHashCache<KeyCacheKey, RowIndexEntry>.主要是存放KeyCacheKey与 RowIndexEntry的对应关系,而KeyCacheKey主要是rowkey与SStable的信息,而RowIndexEntry则是rowkey对应sstable所在data.db文件中的偏移量。
private final String type;cache的类型,现在cassandra中主要有两种一种是keycache,一种是rowcache。
private CacheMetrics metrics;cache的度量器,主要是cache的命中几率等等记录
然后是AutoSavingCache中有四个属性:
public static final Set<CacheService.CacheType> flushInProgress = new NonBlockingHashSet<CacheService.CacheType>();主要是记录的正在flush的cache的任务,在同一时刻只允许一个cache进行flush
protected volatile ScheduledFuture<?> saveTask; 保存cache的任务,后面详细介绍
protected final CacheService.CacheType cacheType; cache的类型
private CacheSerializer<K, V> cacheLoader; cache数据的序列化器,Keycache的实现主要是KeyCacheSerializer,在keycache进行持久化的时候,进行调用其序列化方法将keycache进行序列化。序列化格式主要是:
rowkey的长度 | rowkey | sstable的标示ssid | 标示是否进行index升级的版本 | 如果升级过index则需要序列化RowIndexEntry,如果没有就返回 |
RowIndexEntry的序列化已经在indexinfo中介绍过了。
其次KeyCacheKey的属性
public final Descriptor desc;sstable的相关属性
public final byte[] key;rowkey信息
/**
* We can use Weighers.singleton() because Long can't be leaking memory
* @return auto saving cache object
*/
private AutoSavingCache<KeyCacheKey, RowIndexEntry> initKeyCache()
{
logger.info("Initializing key cache with capacity of {} MBs.", DatabaseDescriptor.getKeyCacheSizeInMB());
long keyCacheInMemoryCapacity = DatabaseDescriptor.getKeyCacheSizeInMB() * 1024 * 1024;
// as values are constant size we can use singleton weigher
// where 48 = 40 bytes (average size of the key) + 8 bytes (size of value)
ICache<KeyCacheKey, RowIndexEntry> kc;
if (MemoryMeter.isInitialized())
{
kc = ConcurrentLinkedHashCache.create(keyCacheInMemoryCapacity);
}
else
{
logger.warn("MemoryMeter uninitialized (jamm not specified as java agent); KeyCache size in JVM Heap will not be calculated accurately. " +
"Usually this means cassandra-env.sh disabled jamm because you are using a buggy JRE; upgrade to the Sun JRE instead");
/* We don't know the overhead size because memory meter is not enabled. */
EntryWeigher<KeyCacheKey, RowIndexEntry> weigher = new EntryWeigher<KeyCacheKey, RowIndexEntry>()
{
public int weightOf(KeyCacheKey key, RowIndexEntry entry)
{
return key.key.length + entry.serializedSize();
}
};
kc = ConcurrentLinkedHashCache.create(keyCacheInMemoryCapacity, weigher);
}
AutoSavingCache<KeyCacheKey, RowIndexEntry> keyCache = new AutoSavingCache<KeyCacheKey, RowIndexEntry>(kc, CacheType.KEY_CACHE, new KeyCacheSerializer());
int keyCacheKeysToSave = DatabaseDescriptor.getKeyCacheKeysToSave();
logger.info("Scheduling key cache save to each {} seconds (going to save {} keys).",
DatabaseDescriptor.getKeyCacheSavePeriod(),
keyCacheKeysToSave == Integer.MAX_VALUE ? "all" : keyCacheKeysToSave);
keyCache.scheduleSaving(DatabaseDescriptor.getKeyCacheSavePeriod(), keyCacheKeys