阅读准备
阅读本文前,请先通过下面两篇文章了解该缓存依赖的设计思路和程序实现。
1、 为memcached增加缓存依赖的初步设想。
2、 为memcached增加缓存依赖的程序实现。
测试环境
环境:本机测试,memcached和站点都搭建在本地。 下面是我机器的配置。
工具:测试工具是WAS。使用介绍请参考之前写的这篇文章。
测试结果
一、未使用memcached
对一个空白的aspx页面进行测试。
运行时间 |
结果 |
2分钟 |
Run length: 00:02:00 Number of threads: 200 Requests per Second: 1906.39 |
5分钟 |
Run length: 00:05:00 Number of threads: 200 Requests per Second: 1860.21 |
二、使用memcached
(一)、正常使用memcached,一次创建一个key
运行时间 |
结果 |
2分钟 |
Run length: 00:02:00 Requests per Second: 651.16 Number of threads: 200 |
5分钟 |
Run length: 00:05:00 Requests per Second: 670.73 Number of threads: 200 |
(二)、使用缓存依赖
keyA: key1+六位随机数。
不依赖其他cache,创建的cache有:DATA_keyA和CTIME_keyA。
keyB:keyN+六位随机数[N>1]。
依赖于keyA,创建的cache有:DATA_keyB、CTIME_keyB、DEPEND_keyB和DEPCTIME_keyB。
1、创建keyA
运行时间 |
结果 |
2分钟 |
Run length: 00:02:00 Requests per Second: 505.45 Number of threads: 200 |
5分钟 |
Run length: 00:05:00 Requests per Second: 530.68 Number of threads: 200 |
2、keyA已经存在,创建keyB。 所有的keyB都依赖于同一个keyA。
运行时间 |
结果 |
2分钟 |
Run length: 00:02:00 Requests per Second: 290.03 Number of threads: 200 |
5分钟 |
Run length: 00:05:00 Requests per Second: 308.27 Number of threads: 200 |
3、创建keyA和一个keyB。
运行时间 |
结果 |
2分钟 |
Run length: 00:02:00 Requests per Second: 266.01 Number of threads: 200 |
5分钟 |
Run length: 00:05:00 Requests per Second: 275.95 Number of threads: 200 |
4、创建keyA和两个keyB。
运行时间 |
结果 |
2分钟 |
Run length: 00:02:00 Requests per Second: 188.12 Number of threads: 200 |
5分钟 |
Run length: 00:05:00 Requests per Second: 199.03 Number of threads: 200 |
通过测试数据来看,【651.16-670.73】VS 【505.45-530.68】 。使用缓存依赖后并发数会下降20%左右。所以是否使用该依赖方案,请根据项目实际情况进行取舍。
下面是使用缓存依赖的测试代码。
/// 创建keyA
/// </summary>
private void AddTest1()
{
CacheContext context = CacheContext.GetCacheService();
string key1 = RandomKey("key1");
string vaule1 = "我是" + key1;
context.AddObject(key1, vaule1);
}
/// <summary>
/// 使用前请先创建 key1
/// keyA已经存在,创建keyB。 所有的keyB都依赖于同一个keyA。
/// </summary>
private void AddTest2()
{
CacheContext context = CacheContext.GetCacheService();
string key2 = RandomKey("key2");
string value2 = "我是" + key2;
ICacheDependency dep = new MemCacheDependency("key1");
context.AddObject(key2, value2, dep);
}
/// <summary>
/// 创建keyA和一个keyB。
/// </summary>
private void AddTest3()
{
CacheContext context = CacheContext.GetCacheService();
string key1 = RandomKey("key1");
string key2 = RandomKey("key2");
string vaule1 = "我是" + key1;
string value2 = "我是" + key2;
context.AddObject(key1, vaule1);
ICacheDependency dep = new MemCacheDependency(key1);
context.AddObject(key2, value2, dep);
}
/// <summary>
/// 创建keyA和两个keyB。
/// </summary>
private void AddTest4()
{
CacheContext context = CacheContext.GetCacheService();
string key1 = RandomKey("key1");
string key2 = RandomKey("key2");
string key3 = RandomKey("key3");
string vaule1 = "我是" + key1;
string value2 = "我是" + key2;
string value3 = "我是" + key3;
context.AddObject(key1, vaule1);
string[] depkeys = { key1 };
context.AddObject(key2, value2, depkeys);
ICacheDependency dep = new MemCacheDependency(key1);
context.AddObject(key3, value3, dep);
}
/// <summary>
/// 生成随机key
/// </summary>
/// <param name="prefix"></param>
/// <returns></returns>
public string RandomKey(string prefix)
{
Random r = new Random();
string number = r.Next(999999).ToString();
string temp = "000000";
string item = temp + number;
item = item.Substring(item.Length - 6, 6);
return prefix + item;
}
补充说明
同生同灭
该依赖方案基于这样一个假设,姑且叫“同生同灭”特性:多份cache同时创建同时移除。
keyA对应DATA_keyA和CTIME_keyA。这两份cache同生同灭。
keyB对应DATA_keyB、CTIME_keyB、 DEPEND_keyB和DEPCTIME_keyB。这四份cache同生同灭。
memcached中的LRU
memcached会优先使用已超时的记录的空间,但即使如此,也会发生追加新记录时空间不足的情况,此时就要使用名为Least Recently Used(LRU)机制来分配空间。即删除“最近最少使用”的记录。因此,当memcached的内存空间不足时就从最近未被使用的记录中搜索,并将其空间分配给新的记录。
该依赖方案存在的问题
看出问题了吧:
1、如果内存空间不足,就会启动LRU(当然可以禁止LRU),这样就没法保证“同生同灭”了。
2、如果某份cache出现不可预见的异常,也可能没法保证“同生同灭”特性。
对于问题1来说可以禁止LRU或者保证足够用的内存。问题2目前没什么办法。