接上次的话题《缓存方式扩展,缓存 Tag(命名空间) 的实现》,给 cache item 打上了 tag 标签,这个方式是不错,今天再来一个更好的方法。
这个方式是模仿 Ruby on Rails 里面自带的功能
在 Ruby on Rails 里面,我们可以:
1
2
3
4
5
6
7
8
| # 写入将 page1,page2 的数据写入缓存
Rails.cache.write('posts/page/1',@page1)
Rails.cache.write('posts/page/2',@page2)
Rails.cache.write('posts/index',@index)
# 用 posts/page/* 来删除 posts/page/1,posts/page/2
Rails.cache.delete('posts/page/*')
# 用 posts/* 来删除 posts/index,posts/page/1,posts/page/2
Rails.cache.delete('posts/*')
|
从上面的代码中看出这种删除实在是非常的实用,我们可以根据类别层次级为缓存起 key 的名称,如
- home/index - 首页的 index 视图
- posts/show/1 - 查看文章页面 ID 为 1 的文章
然后根据层次级来删除缓存。
这种按 * 号匹配删除一组缓存的方式在实际的应用中需求非常频繁,如《缓存方式扩展,缓存 Tag(命名空间) 的实现》里面提到的列表缓存清除,你不确定具体有多少页,但这些页面都得清除,哪就得用Tag或正则匹配的方式来清除了。
如图:
如上图所示,我们将所有的 Cache Key 存入 CacheKeys 中,CacheKeys 以静态的方式存放于进程内存里面,当 DeleteByMatch 调用的时候,用正则的从 CacheKeys 里面匹配出相关的 Keys 列表,再通过循环删除 Memcached 中的缓存。
.NET 里面实现代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
| /**
* Delete cache by regex match
* Author: Jason Lee
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Memcached;
public class Caches
{
///
/// 用于存放所有 cache keys,以便于用正则的方式删除缓存
/// 如:place/home/page/1,以后用 place/home/page/* 来删除 place/home/page/1,place/home/page/2 ... place/home/page/n
///
private static List<string> cacheKeys = new List<string>();
///
/// 将 Key 存入 cacheKeyStore
///
///
private static void saveKeyToCacheKeys(string key)
{
if (!cacheKeys.Exists(c => c == key))
{
cacheKeys.Add(key);
}
}
///
/// 删缓存
///
///
/// 是否用正则匹配
public static void Remove(string key, bool match)
{
if (!match)
{
// Memcached 虚构的自已实现一个
Memcached.Remove(key);
// 写 Cached 操作日志
Debug.Log("Cache remove " + key);
}
else
{
Regex reg = new Regex(key, RegexOptions.IgnoreCase);
List<string> matchedKeys = cacheKeys.FindAll(c => reg.IsMatch(c));
foreach (var k in matchedKeys)
{
Memcached.Remove(k);
Debug.Log("Cache remove " + key);
}
}
}
}
|
其实整个过程还是比较简单的,这里有个细节要注意!
我在示例代码中所提供的方式是直接静态的方式存放于进程缓存的,这种方式效率很高。但有两个缺陷:
- 空间问题,当 Cache Key 过多时就需要考虑换地方存放这个列表了,不然 IIS 进程内存超支哪就要 crash 掉。
- 多个网站同步问题,当有两个 Web 应用程序使用相同的缓存时,如:news.tmitter.com,share.tmitter.com,IIS 两个网站之间内存不是共享的,这里就需要考虑缓存同步的问题了。
以上两个问题目前我已有解决办法,不在本文讨论范围内,以后再说。