GAE Memcache
Problem= Fetching an
entity from the datastore by key can take time on the order of tens of milliseconds. (scale!!!)
Solution = A memory cache uses a
volatile storage medium, usually the RAM of the cache machines, for very fast read and
write access to values.
GAE Solution = A distributed memory cache provides scalable, consistent temporary
storage for distributed systems, so many processes can access the same data.
Don't Use Memory Cache for
NOT for long term storage = it gets erased during an outage
NOT for short term storage of IMPORTANT data = again can be erased in outage
Google's Memory Cache = Memcache
-
named after original memcached system that it resembles
-
stores key-value pairs.
-
key size = 250 bytes
-
value = 1 MByte
Google's Memcache -- use it in "front" of datastore
cache datastore entities
by their keys.
IDEA
1)Prior to a datastore Fetch ----first check the memcache
for a value with that key
2) if FOUND (Cache hit) then use it
3) if NOT FOUND (Cache miss) then fetch from datastore AND put in Memcache
CONCEPT: At small expense of for cache storage in #3 follow on fetches will be faster.
BEST Practice:
1) When Data Entity changes, delete it from Memcache (note possible that delete fails and get old data
2) To minimize this possiblity give expiration time to memcache entry
Google Memcache and Python ---see official api
Google Memcache Java low level api
-
key = 250 bytes
-
value = serializable object max 1 MByte
-
OPTION 1 w/JPA : Entity class use @Serializable annotation
-
OPTION 2 w/Low Level Java Entity: Entity class and all of the property value classes implement the
Serializable interface.
-
Classes: MemcacheServiceFactory and MemcacheService
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
// ...somewhere in your webapp code
List<String> headlines;
MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();
//put a new item in cache associated with string "headlines"
memcache.put("headlines", headlines);
headlines = (List<String>) memcache.get("headlines");
//remove item associated with string "headlines" in cache
memcache.delete("headlines");
-
MemcacheServiceObject.put(key, value)
-
key = serializable key
-
value = serializable object
-
MemcacheServiceObject.put(key, value, expiration)
-
memcache.put("headlines", headlines,
Expiration.byDeltaSeconds(300)); //done in milliseconds
-
memcache.put("headlines", headlines,
Expiration.onDate(instance_java_util_Date)); //based on date
-
MemcacheServiceObject.put(key, value, expiration, policy)
-
memcache.put("headlines", headlines, null,
SetPolicy.ADD_ONLY_IF_NOT_PRESENT); //add only if not present
-
SetPolicy.* =
-
SET_ALWAYS (the default)
-
ADD_ONLY_IF_NOT_PRESENT
(only create, do not overwrite)
-
REPLACE_ONLY_IF_PRESENT (only overwrite, do not
create)
GAE low level Memcahce API -- checking to see if in cache
boolean headlinesAreCached = memcache.contains("headlines"); //based on key="headlines"
GAE low level Memcahce API -- removing from cache
memcache.delete("headlines"); //based on key="headlines"
GAE low level Memcahce API -- batching multiple fetch/set/delete operations
Multiple sets /adding to memcache
import java.util.HashMap; import java.util.Map; // ... Map<Object, Object> articleSummaries = new HashMap<Object, Object>(); articleSummaries.put("article00174", "..."); articleSummaries.put("article05234", "..."); articleSummaries.put("article15820", "...");
memcache.putAll(articleSummaries);
Multiple Fetches
import java.util.List;
// ...
List<Object> articleSummaryKeys = Arrays.<Object>asList(
"article00174",
"article05234",
"article15820");
Map<Object, Object> articleSummaries = memcache.getAll(articleSummaryKeys);
Multiple deletes
import java.util.List;
// ...
List<Object> articleSummaryKeys = Arrays.<Object>asList(
"article00174",
"article05234",
"article15820");
memcache.deleteAll(articleSummaryKeys);
Avoiding collisions using Java low level cache API by adding NAMESPACE
Namespaces let you segment the
key space by the kind of thing you are storing
To use namespaces in the low-level Java
API, you set the namespace on the MemcacheService by calling its setNamespace()
memcache.setNamespace("News");
memcache.put("headlines", headlines);
memcache.setNamespace("User");
memcache.put("headlines", userHeadlines);
// Get User:"headlines".
userHeadlines = (List<String>) memcache.get("headlines");
// Get News:"headlines".
memcache.setNamespace("News"); //will get a memcache hit for key="headlines" only if in Newspace = "News"
headlines = (List<String>) memcache.get("headlines");
Google Memchache Java low level api --- STATISTICS
import com.google.appengine.api.memcache.Stats;
// ...
Stats stats = memcache.getStatistics();
int ageOfOldestItemMillis = stats.getMaxTimeWithoutAccess();
other statistics methods
= num cache hits counted.
= number of cache misses counted.
= number of items currently in the cache.
= total size of items currently in the cache.
= total of bytes returned in response to cache hits, including keys and values.
= age of the least recently accessed item in the cache, in seconds.
Google AsyncMemcacheService -- perform memcache operations asynchronously
Google Memcache Java with JCache api -- see official api
Why use low level Memcache API over JCache API?
-
there are automatic increment and decrement integer counter values
-
more cache statistics, such as the amount of time since the least-recently-used entry was accessed, and the total size of all items in the cache.
-
Check and set operations to conditionally store data
-
Perform memcache operations asynchronously, using the AsyncMemcacheService.
|