缓存在何时有必要?

Laputa_Island 2010-11-29 10:18:59
最近公司做一个项目,要大量用到 拼音码 与 五笔码 检索数据。

举个例子,加入数据库中有一条名为“你好”的数据,用户敲“NH”两个字符就能够得到所有的首字母拼音为N,H的数据,包括你好这条数据.

反之,用户添加一条新数据的时候,假如数据为“中国” 这个时候,我们也必须将中国这两个字转换成“ZG”(通过查询大约有5K条的数据库进行单汉字的逐一比对得到结果)然后存储到相应地方以备查询使用


现在是这样的,我想对单独汉字转换到单独拼音码的过程进行缓存,也就是说缓存一组组形如 我->W ,你->N 的内容。

可是我的老大说不需要,他说这个足够简单,就像1+1一样简单,缓存反而徒增复杂性。我经验不足,不知道如何缓存选择的时机与实现方式上取舍,请各位大牛不吝赐教,在下感激不尽 :)

随帖附上我的缓存代码,其中粗鄙之处也拜请一并指出 :)



//代码拼音码与自定义码组合的引用类型
sealed class CodePairRef
{
public Char PinyinCode;
public Char UserDefineCode;
public Int64 Times;//被访问的次数
}


//用于将字符串翻译成拼音码与自定义码的类
static class Encode
{
private static Dictionary<Char, CodePairRef> cache = new Dictionary<Char, CodePairRef>(); //缓存字典
public static Int32 Capacity //缓存最大容量
{
get { return _capacity; }
set { _capacity = value; }
}static Int32 _capacity = 997;// :)
public static String sql = "get_py_wb";//存储过程
private static Object lockHelper = new Object();
private static String connectionString = ConfigurationManager.ConnectionStrings["connectionString"].ToString();//公共的连接字符串

public static CodePairRef Process( Char ch)
{
if (cache.ContainsKey(ch)) //命中缓存
{
CodePairRef pair = cache[ch];
++pair.Times;
return pair;//返回缓存数据
}

SqlParameter[] sqlParams =
{
new SqlParameter("@chinese",SqlDbType.NVarChar,1),
new SqlParameter("@py_result",SqlDbType.NVarChar,1),
new SqlParameter("@wb_result",SqlDbType.NVarChar,1)
};
sqlParams[0].Value = ch;
sqlParams[1].Direction = ParameterDirection.Output;
sqlParams[2].Direction = ParameterDirection.Output;
try
{
SqlHelper.ExecuteNonQuery( connectionString , CommandType.StoredProcedure, sql, sqlParams);//参数化查询进行解码
}
catch
{
return null;
}

String py = sqlParams[1].Value as String;
String udef = sqlParams[2].Value as String;

if (String.IsNullOrEmpty(py) || String.IsNullOrEmpty(udef))
{
return null;
}

try
{
CodePairRef pair = new CodePairRef(); //构建返回值
pair.PinyinCode = py[0];
pair.UserDefineCode = udef[0];



cache.Add(ch, pair);//将新查询到的结果添加进缓存

#region 自动扩容与删除不常用数据
if (cache.Count >= Capacity)//当缓存数据超过最大容量的时候进行扩容与删除不常用数据
{
lock (lockHelper)
{
if (cache.Count >= Capacity)//2+1组合确保以下程序块在条件满足时只被执行一次
{
foreach (KeyValuePair<Char, CodePairRef> value in cache)
{
if (value.Value.Times < (Capacity/2))
{
cache.Remove(value.Key);
}
}

if (cache.Count >= Capacity)
{
Capacity *= 2;
}
}
}
}
#endregion

return pair;






}
catch
{
return null;
}


}
}
...全文
318 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
Laputa_Island 2010-11-29
  • 打赏
  • 举报
回复
吃饭结束,前来瞻仰一番
貓哥是個傳說 2010-11-29
  • 打赏
  • 举报
回复
很精彩,學習。。
taz01 2010-11-29
  • 打赏
  • 举报
回复
p哥挺热心的啊 ,收益不少!顶!
Laputa_Island 2010-11-29
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 sp1234 的回复:]
System.Web.Caching.Cache命名空间下的类不但可以用在asp.net,也可以用在.net桌面、服务器的各个程序中。这是.net类库中缓存控制的现成的类,应该去了解它的机制和使用它,不必重新发明你的缓存类。

有许多人说什么memcache之类的就是缓存解决方案了。其实这些人懒得自己写代码,甚至专门找一些大的服务器系统来应付差事。而数据缓存是在我们自己的应用程序中的,它不是那……
[/Quote]

不是盲目的把数据库中的东西放到内存中,而是缓存查询结果,嗯明白了
  • 打赏
  • 举报
回复
System.Web.Caching.Cache命名空间下的类不但可以用在asp.net,也可以用在.net桌面、服务器的各个程序中。这是.net类库中缓存控制的现成的类,应该去了解它的机制和使用它,不必重新发明你的缓存类。

有许多人说什么memcache之类的就是缓存解决方案了。其实这些人懒得自己写代码,甚至专门找一些大的服务器系统来应付差事。而数据缓存是在我们自己的应用程序中的,它不是那种把整个数据库放到内存中的所谓缓存概念,它并不盲目地把所有低级数据记录都放到内存中,而是仅仅缓存业务逻辑查询结果(这些数据因为缓存依赖的关系,经常自动清空)。
Laputa_Island 2010-11-29
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 sp1234 的回复:]

这完全可以按照机械的做法来设计,首先不要考虑任何缓存机制,把程序写好,测试好,然后过1、2个月才考虑缓存,并且随时应该可以注释掉(删除掉,或者用条件编译取消掉)涉及数据缓存的语句。
[/Quote]

收到,获益良多 :)

帖子挂在这一段时间,希望有更多人看到 :)
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 laputa_island 的回复:]
这就把我的缓存代码给删除掉,老老实实的写代码

先搭建好基本逻辑,然后根据逻辑来设计缓存,不能依赖于缓存,即使没有缓存程序也必须能够正常运行。
[/Quote]

这完全可以按照机械的做法来设计,首先不要考虑任何缓存机制,把程序写好,测试好,然后过1、2个月才考虑缓存,并且随时应该可以注释掉(删除掉,或者用条件编译取消掉)涉及数据缓存的语句。
  • 打赏
  • 举报
回复
更具体地举例来说,假设有个查询是返回“你好”,另外有个查询是返回“我好”,还有个查询是返回“大家好”,这三个查询中都有一个“好”字,那么这个好字要单独缓存?完全是思路的方向性错误!这三个查询结果分别缓存起来,而根本不用关心有个重复的“好”字。

在比如说查询一个“在线用户列表(第3页内容)”,另外有个查询是返回“用户xxx的好友”,还有个查询是返回“频道cccc的管理员”,这三个查询中可能都有相同的用户资料,但是它们是分别缓存的,此时根本不用关心如何将单个用户资料缓存的问题。

如果你把这些查询中涉及的具体的一条一条数据库记录,看作所谓缓存的单元,那么就本末倒置了。缓存控制的关键是懂得控制缓存依赖,保证缓存中的时候不是脏数据,假设系统中有上万个缓存单元,当某个缓存依赖条件发生改变时(例如修改了数据库中某类数据)那么依赖它的几十个缓存单元自动清空。因此数据经常快速地进出内存,并且是针对大粒度的业务查询功能(这样才不琐碎),根本不是那种把静态数据逐一丢到内存里的概念。

缓存是为了处理高效地处理动态变动的数据的。那种认为静态数据可以从数据库移动到内存中的思路,不了解缓存依赖控制技术,所以也就根本不涉及多少缓存技术的。

另外,你应该去首先学习.net类库中的缓存类,其具体的机制。应该是用类库来扩展成自己的强大的缓存系统,而不是自己手写、重复其一点皮毛方面的功能。
Laputa_Island 2010-11-29
  • 打赏
  • 举报
回复
这就把我的缓存代码给删除掉,老老实实的写代码

先搭建好基本逻辑,然后根据逻辑来设计缓存,不能依赖于缓存,即使没有缓存程序也必须能够正常运行。

sp大神,是这个意思不?
Laputa_Island 2010-11-29
  • 打赏
  • 举报
回复
缓存是用来处理经常进行的业务逻辑查询时使用的,不是把什么数据库表放到内存里。

明白了,是我自以为是太想表现了:),新手初入职。。 。
Laputa_Island 2010-11-29
  • 打赏
  • 举报
回复

thany you :)
  • 打赏
  • 举报
回复
缓存是用来处理经常进行的业务逻辑查询时使用的,不是把什么数据库表放到内存里。根本是如何查询还没有想好,或者根本不是一个大粒度面向业务的查询,这时候你去想缓存的事就很盲目了。

首先在考虑数据库表之前,在设计客户端需求时,知道需要有这样的业务逻辑查询机制:
public static class BLL
{
public static string 根据拼音字头查询用户汉字名(string 拼音字头)
{
return 查询数据库返回用户汉字名(拼音字头);
}
......

在数据库中,“你好”的旁边会有另外一个字段,记录了“NH”这个值。也就是说汉字对应的拼音字头都是保存在数据库中的。然后sql查询也不过是使用一条类似“select username from table where pinyin='NH‘”这样的查询来直接得到。这是基本的结构设计和查询设计。这些做完了,程序已经稳定一段时间经过了千百次测试了,这时候等你有空闲的时候可以把原来查询优化一下
public static string[] 根据拼音字头查询用户汉字名(string 拼音字头)
{
var key=string.Format("拼音字头{0}对应的用户名",拼音字头);
var ret=(string[])Cache[key];
if(ret==null)
{
ret=查询数据库返回用户汉字名(拼音字头);
//这里作为一个demo,缓存3分钟(用于应付突发情况)。实际上第三个参数更重要,而不应该是null。
Cache.Insert(key,ret,null,Cache.NoAbsoluteExpiration, new TimeSpan(0, 3, 0));
}
return 查询数据库返回用户汉字名(拼音字头);
}



首先,去掉缓存来看你的程序逻辑,如果它是画蛇添足的,那么你就不必为了炫耀你会缓存而硬要维持原来的程序逻辑。

其次,缓存应该是在程序已经测试了千百遍之后才启用(而不是一开始就花精力写这个代码),并且你应该可以随时删除(注释掉)关于缓存的代码,而程序仍然正常运行。

第三,缓存有现成的机制,其中最重要地是控制缓存依赖,缓存机制会自动淘汰不太需要的数据而释放内存(并且内存不足时也会自动丢弃一些缓存依赖条件没有到期的数据)。缓存编程只需要多写2、3行语句。你的“老大”会告诉你什么时候、用什么方法写缓存语句。如果擅自写得太多了,那么就应该删除。
cxx1997 2010-11-29
  • 打赏
  • 举报
回复
就这个问题而言,你老大是对的
缓存是以空间换性能的一种技术手段
你将 汉字对应拼音的关系缓存起来,下次如果用户输入相同的汉字可以节约时间
那么用不用就取决于2中方案的代价
1:性能节约多少(每个汉字转换需要多长时间、用户使用的频率,占系统性能的百分比)
2:缓存的命中率(用户下次输入时,能用到缓存的概率)
3:空间的占用大小(最坏情况下,所有的汉字都被记录入缓存)
4:其他问题(比如缓存是否要更新)

显然对于这个功能而言,汉字转换的时间可以忽略
而缓存过小的情况下,命中率也很低

如果要优化,完全可以实现不用数据库的汉字翻译方法
Ryan20082009 2010-11-29
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 youyubianyuan 的回复:]

缓存就是用在高速或者处理数据庞大的情况下,用空间换取时间的一种方式.

具体怎么实现看具体应用而定.
[/Quote]+1
Laputa_Island 2010-11-29
  • 打赏
  • 举报
回复
老系统里面有几千行,也就是几千个汉字以及其对应的拼音,五笔码,这是很久前就存在的。我没权利选择另一种实现方法。

各位大大给我讲讲缓存时机选择以及一些注意事项吧 :),这个偶很想知道
Laputa_Island 2010-11-29
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 sp1234 的回复:]
查找一个单汉字对应的拼音码,根本用不着数据库。你的思考的结构方向性的问题。随便google一下:http://www.google.com.hk/search?hl=zh-CN&newwindow=1&safe=strict&q=%E6%B1%89%E5%AD%97+%E6%8B%BC%E9%9F%B3%E5%AD%97%E7%A0%81+%E6%8B%BC%E9%9F%B……
[/Quote]

我知道有类似的公共库存在,但... ..我们的老系统是使用数据库的..在我没来之前就是使用数据库..我也没办法哇
sxmonsy 2010-11-29
  • 打赏
  • 举报
回复
http://blog.sina.com.cn/s/blog_4982b4e5010007ij.html
这个貌似是楼主要的。。。。不过不用数据库的说
  • 打赏
  • 举报
回复
查找一个单汉字对应的拼音码,根本用不着数据库。你的思考的结构方向性的问题。随便google一下:http://www.google.com.hk/search?hl=zh-CN&newwindow=1&safe=strict&q=%E6%B1%89%E5%AD%97+%E6%8B%BC%E9%9F%B3%E5%AD%97%E7%A0%81+%E6%8B%BC%E9%9F%B3%E5%AD%97%E5%A4%B4&aq=f&aqi=&aql=&oq=&gs_rfai=
我奇怪,你们“老大”连这个都讲不明白,你甚至得不到任何具体的信息,不知道是你故意不去问清楚应该怎样写代码,还是他故意保持高调而不让你顺利写出代码?!
兔子-顾问 2010-11-29
  • 打赏
  • 举报
回复
不知道你缓存的是什么。如果是大量数据,你内存够大么?都存内存里当然访问快。可不可以将汉字首字母(拼音、五笔)的都各存一列,直接使用这个列检索?
gomoku 2010-11-29
  • 打赏
  • 举报
回复
[Quote=引用楼主 laputa_island 的回复:]
……
SqlHelper.ExecuteNonQuery(...);
……
[/Quote]
当然用缓存会更好。
本地Dictionary缓存,比数据库查询,几个数量级的快。
加载更多回复(3)

110,502

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧