oscache的默认缓存实现是由4个类组成的,如下图所示:
首先来看一下是如何放入缓存的操作吧,也就是AbstractConcurrentReadCache类的#put()方法:

public
Object put(Object key, Object value)
{
return
put(key, value,
true
);
}

//
这里的第三个参数代表是否持久化缓存

private
Object put(Object key, Object value,
boolean
persist)
{
if
(value
==
null
)
{
//
默认是不支持空值的
throw
new
NullPointerException();
}

//
计算hash
int
hash
=
hash(key);
//
hash表,其实Entry本身是一个链表的结构,也就是hash桶
Entry[] tab
=
table;
//
将hash值与hash表的长度按位与得到初始位置
int
index
=
hash
&
(tab.length
-
1
);
//
first指的是hash表中Entry链表的第一个元素
Entry first
=
tab[index];
Entry e
=
first;

for
(;;)
{
if
(e
==
null
)
{
//
如果哈希表当前位置是空位

synchronized
(
this
)
{
tab
=
table;
Object oldValue
=
null
;
//
Remove an item if the cache is full

if
(size()
>=
maxEntries)
{
//
如果缓存已满,需要挑选一个Entry移出
//
part of fix CACHE-255: method should return old value
//
挑选要移出的key的方法#removeItem()是由之类去实现的
oldValue
=
remove(removeItem(),
false
,
false
);
}


if
(first
==
tab[index])
{
//
这里是对可能存在的并发更新的处理
//
Add to front of list
Entry newEntry
=
null
;

if
(memoryCaching)
{
newEntry
=
new
Entry(hash, key, value, first);
}
else
{
newEntry
=
new
Entry(hash, key, NULL, first);
}

tab[index]
=
newEntry;
//
通知具体实现值已经放入缓存的回调
itemPut(key);
//
Persist if required
//
这里如果配置文件中cache.memory=false并且cache.persistence.overflow.only=true程序就进入了一个混乱的状态了
//
因为内存中的Entry值为NULL,并且不会调用持久化存储
//
所以这两个配置项配合的话只有3种情况了
//
(1) memory=true, overflow=true:使用内存缓存,溢出的数据持久化
//
(1) memory=true, overflow=false:使用内存缓存,溢出的数据不处理
//
(1) memory=false, overflow=false:使用持久化缓存

if
(persist
&&
!
overflowPersistence)
{
//
如果需要持久化保存
persistStore(key, value);
}

//
If we have a CacheEntry, update the group lookups

if
(value
instanceof
CacheEntry)
{
//
更新缓存的分组信息,其实AbstractConcurrentReadCache
//
用一个HashMap保存了分组名和各个key之间的一个映射 groupname -> Set<Key>
updateGroups(
null
, (CacheEntry) value, persist);
}

//
如果数量大于threshold(capacity * 装填因子(loadfactor))

if
(
++
count
>=
threshold)
{
//
是否rehash
rehash();
}
else
{
recordModification(newEntry);
}

return
oldValue;
}
else
{
//
如果当前hash表发生了变化,即发生了并发插入缓存的操作,此时需要进入这个分支
//
#sput()里边的逻辑和#put()是类似的
return
sput(key, value, hash, persist);
}
}

}
else
if
((key
==
e.key)
||
((e.hash
==
hash)
&&
key.equals(e.key)))
{
//
如果当前的key已经存在了,更新值
//
synch to avoid race with remove and to
//
ensure proper serialization of multiple replaces

synchronized
(
this
)
{
tab
=
table;
Object oldValue
=
e.value;
//
[CACHE-118] - get the old cache entry even if there's no
//
memory cache
//
oldValue为NULL代表了是磁盘缓存

if
(persist
&&
(oldValue
==
NULL))
{
//
在磁盘里去的缓存值
oldValue
=
persistRetrieve(key);
}


if
((first
==
tab[index])
&&
(oldValue
!=
null
))
{
if
(memoryCaching)
{
//
缓存更新值
e.value
=
value;
}

//
Persist if required

if
(persist
&&
overflowPersistence)
{
//
如果缓存溢出需要持久化,在缓存持久化处移除这个值
//
因为现在内存中已经有这个值了,不能再持久化了
//
这里因为是更新,所以按理说不会有它对应的overflow缓存的啊?
persistRemove(key);
}
else
if
(persist)
{
//
持久化保存
persistStore(key, value);
}

updateGroups(oldValue, value, persist);
itemPut(key);
return
oldValue;
}
else
{
return
sput(key, value, hash, persist);
}
}

}
else
{
//
将e指向Entry链表的下一个项目
e
=
e.next;
}
}
}
整个的流程用代码的注释其实就可以写清楚了,注意,在更新缓存后会调用给之类的回调函数#itemPut(),另外还有参数cache.memory和cache.persistence.overflow.only对流程的影响。
下面看下#get(),这里#remove()就不写了其实过程反倒和#get()也差不多:

public
Object get(Object key)
{
if
(log.isDebugEnabled())
{
log.debug(
"
get called (key=
"
+
key
+
"
)
"
);
}

//
计算hash
int
hash
=
hash(key);

/**/
/*
* Start off at the apparently correct bin. If entry is found, we need
* to check after a barrier anyway. If not found, we need a barrier to
* check if we are actually in right bin. So either way, we encounter
* only one barrier unless we need to retry. And we only need to fully
* synchronize if there have been concurrent modifications.
*/
//
计算在hash表中的位置
Entry[] tab
=
table;
int
index
=
hash
&
(tab.length
-
1
);
//
Entry链表中的第一个数据
Entry first
=
tab[index];
Entry e
=
first;

for
(;;)
{
if
(e
==
null
)
{
//
If key apparently not there, check to
//
make sure this was a valid read
//
key没找到,再次查看hash表确定是否真的找不到了
tab
=
getTableForReading();

if
(first
==
tab[index])
{
//
Not in the table, try persistence
//
试着在持久化处找
Object value
=
persistRetrieve(key);

if
(value
!=
null
)
{
//
Update the map, but don't persist the data
//
在持久化处找到数据的话需要更新hash表,但不去重新持久化
put(key, value,
false
);
}

return
value;
}
else
{
//
Wrong list -- must restart traversal at new first
e
=
first
=
tab[index
=
hash
&
(tab.length
-
1
)];
}
}
//
checking for pointer equality first wins in most applications

else
if
((key
==
e.key)
||
((e.hash
==
hash)
&&
key.equals(e.key)))
{
//
找到了数据
Object value
=
e.value;

if
(value
!=
null
)
{
if
(NULL.equals(value))
{
//
Memory cache disable, use disk
//
需要去缓存找数据
value
=
persistRetrieve(e.key);

if
(value
!=
null
)
{
//
调用回调
itemRetrieved(key);
}

return
value;
//
fix [CACHE-13]

}
else
{
//
调用回调
itemRetrieved(key);
return
value;
}
}

本文解析了 oscache 的核心缓存实现原理,包括如何放入和获取缓存数据,以及缓存更新流程。重点关注内存缓存与持久化缓存之间的交互机制。
241

被折叠的 条评论
为什么被折叠?



