最新评论

子时三刻 2011-06-10 10:15
受教了
JerryZeng 2011-02-17 08:49
很好。都总结出来了。啥时候再整个触发器篇什么的?
www.mangago.com 2010-06-18 14:26
can read free manga online (http://www.mangago.com)
loose_went 2008-09-16 22:12
收藏了
小眼睛老鼠 2008-06-19 17:48
不太赞成跨服务器的缓存.
因为InProc缓存的实现比较看重的只是内存的大小, 而对CPU/IO的要求是非常低的.

目前跨服务器的缓存的唯一好处就是集中 , 用于配合Web服务器集群.
这样在数据的版本上维护上会容易一点. 能保证各服务器的信息的准确性.


但是我比较喜欢这样的设计:

1. 具体的数据需要一个唯一的key
2. 具体的结果需要保留一个version
3. 每个服务器都有它自己的缓存. 各服务器的缓存不互相干涉.
4. 独立一台服务器,用于验证version.
5. 当读取结果时,需要发送key到独立服务器去获取version用于验证.
6. 当修改数据时,发送key和version到该独立服务器上.

这种设计,需要各Web服务器有它自己的缓存, 也就是说每个Web服务器都需要购买很大的内存.
因为与独立服务器传递的不是数据,只是key和version,那么就免去了传递和序列化数据导致的性能问题.
小眼睛老鼠 2008-06-19 17:47
文章链接:http://www.25hoursaday.com/weblog/2007/07/05/ASPNETCachingVsMemcachedSeekingEfficientDataPartitioningLookupAndRetrieval.aspx
In my previous post, I mentioned that I'm in the early stages of building an application on the Facebook platform. I haven't yet decided on an application but for now, let's assume that it is a Favorite Comic Books application which allows me to store my favorite comic books and shows me to most popular comic books among my friends.

After investigating using Amazon's EC2 + S3 to build my application I've decided that I'm better off using a traditional hosting solution running either a on the LAMP or WISC platform. One of the things I've been looking at is which platform has better support for providing an in-memory caching solution that works well in the context of a Web farm (i.e. multiple Web servers) out of the box. While working on the platforms behind several high traffic Windows Live services I've learned that you should be prepared for dealing with scalability issues and caching is one of the best ways to get bang for the buck when improving the scalability of your service.

I recently discovered memcached which is a distributed, object caching system originally developed by Brad Fitzpatrick of LiveJournal fame. You can think of memcached as a giant hash table that can run on multiple servers which automatically handles maintaining the balance of objects hashed to each server and transparently fetches/removes objects from over the network if they aren't on the same machine that is accessing an object in the hash table. Although this sounds fairly simple, there is a lot of grunt work in building a distributed object cache which handles data partitioning across multiple servers and hides the distributed nature of the application from the developer. memcached is a well integrated into the typical LAMP stack and is used by a surprising number of high traffic websites including Slashdot, Facebook, Digg, Flickr and Wikipedia. Below is what C# code that utilizes memcached would look like sans exception handling code


public ArrayList GetFriends(int user_id){

ArrayList friends = (ArrayList) myCache.Get("friendslist:" + userid);

if(friends == null){
// Open the connection
dbConnection.Open();

SqlCommand cmd = new SqlCommand("select friend_id from friends_list where owner_id=" + "user_id", dbConnection);

SqlDataReader reader = cmd.ExecuteReader();

// Add each friend ID to the list
while (reader.Read()){
friends.Add(rdr[0]);
}

reader.Close(); dbConnection.Close(); myCache.Set("friendslist:" + userid, friends);
}

return friends;
}

public void AddFriend(int user_id, int new_friends_id){

// Open the connection
dbConnection.Open();


SqlCommand cmd = new SqlCommand("insert into friends_list (owner_id, friend_id) values (" + user_id + "," + new_friend_id ")"; cmd.ExecuteNonQuery(); //remove key from cache since friends list has been updated
myCache.Delete("friendslist:" + userid);

dbConnection .Close();
}

The benefits of the using of the cache should be pretty obvious. I no longer need to hit the database after the first request to retrieve the user's friend list which means faster performance in servicing the request and less I/O. The memcached automatically handles purging items out of the cache when it hits the size limit and also deciding which cache servers should hold individual key<->value pairs.


I hang with a number of Web developers on the WISC platform and I don't think I've ever heard anyone mention memcached or anything like it.In fact I couldn't find a mention of it on Microsoft employee blogs, ASP.NET developer blogs or on MSDN. So I wondered what the average WISC developer uses as their in-memory caching solution.

After looking around a bit, I came to the conclusion that most WISC developers use the built-in ASP.NET caching features. ASP.NET provides a number of in-memory caching features including a Cache class which provides a similar API to memcached, page directives for caching portions of the page or the entire page and the ability to create dependencies between cached objects and the files or database tables/rows that they were populated from via the CacheDependency and SqlCacheDependency classes. Although some of these features are also available in various Open Source web development frameworks such as Ruby on Rails + memcached, none give as much functionality out of the box as ASP.NET or so it seems.

Below is what the code for the GetFriends and AddFriend methods would look like using the built-in ASP.NET caching features

public ArrayList GetFriends(int user_id){

ArrayList friends = (ArrayList) Cache.Get("friendslist:" + userid);

if(friends == null){
// Open the connection
dbConnection.Open();

SqlCommand cmd = new SqlCommand("select friend_id from friends_list where owner_id=" + "user_id", dbConnection);

SqlCacheDependency dependency = new SqlCacheDependency(cmd);
SqlDataReader reader = cmd.ExecuteReader();

// Add each friend ID to the list
while (reader.Read()){
friends.Add(rdr[0]);
}

reader.Close();
dbConnection.Close();

//insert friends list into cache with associated dependency
Cache.Insert("friendslist:" + userid, friends, dependency);
}
return friends;
}

public void AddFriend(int user_id, int new_friends_id){
// Open the connection
dbConnection.Open();

SqlCommand cmd = new SqlCommand("insert into friends_list (owner_id, friend_id) values (" + user_id + "," + new_friend_id ")";
cmd.ExecuteNonQuery();

/* no need to remove from cache because SqlCacheDependency takes care of that automatically */
// Cache.Remove("friendslist:" + userid);

dbConnection .Close();
}

Using the SqlCacheDependency class gets around a significant limitation of the ASP.NET Cache class. Specifically, the cache is not distributed. This means that if you have multiple Web front ends, you'd have to write your own code to handle partitioning data and invalidating caches across your various Web server instances. In fact, there are numerous articles showing how to implement such a solution including Synchronizing the ASP.NET Cache across AppDomains and Web Farms by Peter Bromberg and Use Data Caching Techniques to Boost Performance and Ensure Synchronization by David Burgett.

However, let's consider how how SqlCacheDependency is implemented. If you are using SQL Server 7 or SQL Server 2000, then your ASP.NET process polls the database at regular intervals to determine whether the target(s) of the original query have changed. For SQL Server 2005, the database can be configured to send change notifications to the Web servers if the target(s) of the original query change. Either way, the database is doing work to determine if the data has changed. Compared to the memcached this still doesn't seem as efficient as we can get if we want to eke out every last out of performance out of the system although it does lead to simpler code.

If you are a developer on the WISC platform and are concerned about getting the best performance out of your Web site, you should take a look at memcached for Win32. The most highly trafficked site on the WISC platform is probably MySpace and in articles about how they are platform works such as Inside MySpace.com they extol the virtues of moving work out of the database and relying on cache servers.

小眼睛老鼠 2008-06-19 17:43
双缓存(使用文件与内存)

#region 使用指定的 XmlWriter 序列化指定的 Object 并将 XML 文档写入文件

/// <summary>
/// 使用指定的 XmlWriter 序列化指定的 Object 并将 XML 文档写入文件
/// </summary>
/// <param name="filePath">XML 文档路径</param>
/// <param name="data">对象</param>
public static void XmlSerialize(string filePath, object data)
{
string directoryPath = filePath.Substring(0, filePath.LastIndexOf("\\"));

if ( ! File.Exists(filePath) )
{
try
{
if ( ! Directory.Exists(directoryPath) )
{
Directory.CreateDirectory(directoryPath);
}

using ( TextWriter textWriter = new StreamWriter(filePath) )
{
XmlSerializer xmlSerializer = new XmlSerializer(data.GetType());
xmlSerializer.Serialize(textWriter, data);
}
}
catch ( Exception e )
{
throw e;
}
}
}


#endregion

#region 反序列化指定 XmlReader 说包含的 XML 文档

/// <summary>
/// 反序列化指定 XmlReader 说包含的 XML 文档
/// </summary>
/// <param name="filePath">XML 文档路径</param>
/// <param name="type">对象类型</param>
/// <returns>对象</returns>
public static object XmlDeserialize(string filePath, Type type)
{
object obj = null;

if ( File.Exists(filePath) )
{
FileStream fileStream = new FileStream(filePath, FileMode.Open);

try
{
XmlSerializer xmlSerializer = new XmlSerializer(type);

obj = xmlSerializer.Deserialize(fileStream);
}
catch ( Exception e )
{
throw e;
}
finally
{
fileStream.Close();
}
}

return obj;
}


#endregion

#region 将指定的 Object 放入高速缓存并使用指定的 XmlWriter 序列化指定的 Object 并将 XML 文档写入文件

/// <summary>
/// 将对象放入高速缓存并使用指定的 XmlWriter 序列化指定的 Object 并将 XML 文档写入文件
/// </summary>
/// <param name="filePath">XML 文档路径</param>
/// <param name="data">对象</param>
/// <param name="serialize">是否本地序列化</param>
public static void CacheSerialize(string filePath, object data, bool serialize)
{
paramCache[filePath] = data;

if ( serialize )
{
XmlSerialize(filePath, data);
}
}

#endregion

#region 读取高速缓存并反序列化指定 XmlReader 说包含的 XML 文档

/// <summary>
/// 读取高速缓存并反序列化指定 XmlReader 说包含的 XML 文档
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="type">对象类型</param>
/// <param name="serialize">是否本地序列化</param>
/// <returns>对象</returns>
public static object CacheDeserialize(string filePath, Type type, bool serialize)
{
if ( paramCache[filePath] == null )
{
if ( serialize )
{
paramCache[filePath] = XmlDeserialize(filePath, type);
}
}

return paramCache[filePath];
}

#endregion
小眼睛老鼠 2008-06-19 17:42
转:
大家都应该清楚把实体对象更新到数据库必须经过一系列的转换;特别是SQL语句的生成是比较费资源的,因为中间处里的东西实大是太多了。
在设计的过程中我就想如果一个对象插入数据库后把相应的Command保存在缓存中;下一次同一个类型的对象做这个操作时检测一下缓存如果有就直接拿来用这样效率应该会高些。
抱着这种想法就开始设计了(不过心里还是上上下下的,毕竟第一次尝试)。
因为缓存中的对象处理比较复杂点,在多线程中存在共享的问题,如果两个线程同时调用同一个Command这样一定会产生处理错误的!
为了更好地控制Command对象的共享,特别为Command定义了持久化的接口。
经过一段时间的设计和编写,算有点成果吧,顺把自己做的东西共享一下。


以下是组件测试的情况
P4 2.4 1G
SqlServer sp3

运行的代码大概如下:
Entitys.Customers customer = new Test.Entitys.Customers();
DateTime dt = DateTime.Now;
using(HFSoft.Data.IDataSession session = mapcontainer.OpenSession())
{
session.Open();
for(int i =0;i<2000;i++)
{
customer.CustomerID = Guid.NewGuid().ToString();
customer.CompanyName = "henry";
session.Save(customer);
}
}
tp1 = new TimeSpan(DateTime.Now.Ticks - dt.Ticks);


不开启缓存(5个线程运行时间)
00:00:05.7031250
00:00:06.8281250
00:00:05.0156250
00:00:06.6875000
00:00:06.4218750
--------------------------------------------------------
开启5个命令缓存(5个线程运行时间)
00:00:04.8906250
00:00:03.5625000
00:00:02.8750000
00:00:04.9375000
00:00:05.4843750
---------------------------------------------------------

以下是缓存类的源码
/// <summary>
/// 数据缓存保存信息异步处理委托
/// </summary>
delegate void EventSaveCache(object key,object value);
/// <summary>
/// 对象缓存类
/// </summary>
public class Cache
{
private MappingContainer mContainer;
/// <summary>
/// 获取或设置当前缓存对象所在的关系映象容器
/// </summary>
public MappingContainer Container
{
get
{
return mContainer;
}
set
{
mContainer = value;
}
}
/// <summary>
/// 构造缓存对象
/// </summary>
public Cache()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
/// <summary>
/// 用于缓存数据的Hashtable
/// </summary>
protected System.Collections.Hashtable _Cache = new System.Collections.Hashtable();
protected Object _LockObj = new object();
/// <summary>
/// 获取指定键值的对象
/// </summary>
/// <param name="key">键值</param>
/// <returns>object</returns>
public virtual object GetObject(object key)
{
if(_Cache.ContainsKey(key))
return _Cache[key];
return null;
}
/// <summary>
/// 把对象按指定的键值保存到缓存中
/// </summary>
/// <param name="key">键值</param>
/// <param name="value">保存的对象</param>
public void SaveCaech(object key,object value)
{
EventSaveCache save = new EventSaveCache(SetCache);
IAsyncResult ar = save.BeginInvoke(key,value,new System.AsyncCallback(Results),null);
}
private void Results(IAsyncResult ar)
{
EventSaveCache fd = (EventSaveCache)((AsyncResult)ar).AsyncDelegate;
fd.EndInvoke(ar);
}
/// <summary>
/// 把对象按指定的键值保存到缓存中
/// </summary>
/// <param name="key">键值</param>
/// <param name="value">保存的对象</param>
protected virtual void SetCache(object key ,object value)
{
lock(_LockObj)
{
if(!_Cache.ContainsKey(key))
_Cache.Add(key,value);
}
}
public int Count
{
get
{
return _Cache.Count;
}
}
/// <summary>
/// 在缓存中删除指定键值的对象
/// </summary>
/// <param name="key">键值</param>
public virtual void DelObject(object key)
{
lock(_Cache.SyncRoot)
{
_Cache.Remove(key);
}
}
/// <summary>
/// 清除缓存中所有对象
/// </summary>
public virtual void Clear()
{
lock(_Cache.SyncRoot)
{
_Cache.Clear();
}
}
}

/// <summary>
///针对一条记录操作命令的缓存类
/// </summary>
public class CachePersistentCommand:Cache
{

/// <summary>
/// 把记录操作命令缓存到内存中
/// </summary>
/// <param name="key">标识</param>
/// <param name="value">值</param>
protected override void SetCache(object key, object value)
{
lock(_LockObj)
{
int count=0;
if(Container.Config.CommandsCache.ContainsKey(key))
count=(int) Container.Config.CommandsCache[key];
System.Collections.IList _list;
//如果缓存中已经存在这种命令的列表
if(_Cache.ContainsKey(key))
{
_list = (System.Collections.IList)_Cache[key];

if( count >0)//命令的缓存总数
{
if(_list.Count < count)//缓存数据量少于缓存总数
_list.Add(value);
}
else
{
if(_list.Count < Container.Config.CommandBuffer)//缓存数小于组件的默认列表
_list.Add(value);
}
}
else//如果不存在列表
{
if(count >0 || Container.Config.CommandBuffer >0)//如果组件允许对象缓存
{
_list = new System.Collections.ArrayList();
_list.Add(value);
_Cache.Add(key,_list);
}
}
}
}
/// <summary>
/// 从缓存中获取相关命令对象
/// </summary>
/// <param name="key">标识</param>
/// <returns>IPersistentCommand</returns>
public override object GetObject(object key)
{

if(_Cache.Contains(key))//如果命令存在缓冲中
{
foreach(IPersistentCommand cmd in (System.Collections.IList)_Cache[key])
{
if(!cmd.State)//命令是否可以过行锁定
if(cmd.Lock())//命令锁定
return cmd;
}
}
return null;
}


}
小眼睛老鼠 2008-06-19 17:39
ASP.NET中的数据缓存有许多种,一般来说大型的数据使用 Cache对象缓存,用户身份信息用Session对象缓存,小型数据用Cookie对象缓存在客户端。

其中 Cache和Session应该属于系统级的缓存,Cookie属于用户客户端的缓存

除此之外还有用户控件的缓存,我转载一篇文章来详细介绍用户控件的缓存
<%@ OutputCache Duration="60" VaryByParam="*" %>

如同其他页面指令一样,该指令应该出现在ASPX页面的顶部,即在任何输出之前。它支持五个属性(或参数),其中两个是必需的。
  Duration 必需属性。页面应该被缓存的时间,以秒为单位。必须是正整数。
  Location 指定应该对输出进行缓存的位置。如果要指定该参数,则必须是下列选项之:Any、Client、Downstream、None、Server或ServerAndClient。
  VaryByParam 必需属性。Request中变量的名称,这些变量名应该产生单独的缓存条目。“none”表示没有变动。“*”可用于为每个不同的变量数组创建新的缓存条目。变量之间用“;”进行分隔。
  VaryByHeader 基于指定的标头中的变动改变缓存条目。
  VaryByCustom 允许在global.asax中指定自定义变动(例如,“Browser”)。
  利用必需的Duration和VaryByParam选项的组合可以处理大多数情况。例如,如果您的产品目录允许用户基于categoryID和页变量查看目录页,您可以用参数值为“categoryID;page”的VaryByParam将产品目录缓存一段时间(如果产品不是随时都在改变,一小时还是可以接受的,因此,持续时间是3600秒)。这将为每个种类的每个目录页创建单独的缓存条目。每个条目从其第一个请求算起将维持一个小时。
VaryByHeader和VaryByCustom主要用于根据访问页面的客户端对页面的外观或内容进行自定义。同一个URL可能需要同时为浏览器和移动电话客户端呈现输出,因此,需要针对不同的客户端缓存不同的内容版本。或者,页面有可能已经针对IE进行了优化,针对Netscape或Opera则应取消这种优化功能。后一个例子非常普遍,我们将提供一个说明如何实现此目标的示例:
  示例:VaryByCustom用于支持浏览器自定义
  为了使每个浏览器都具有单独的缓存条目,VaryByCustom的值可以设置为“browser”。此功能已经内置在缓存模块中,并且将针对每个浏览器名称和主要版本插入单独的页面缓存版本。<%@ OutputCache Duration="60" VaryByParam="None" VaryByCustom="browser"%>

示例:
<%@ OutputCache Duration="60" VaryByParam="*" %>
该示例将缓存用户控件60秒,并且将针对查询字符串的每个变动、针对此控件所在的每个页面创建单独的缓存条目。
<%@ OutputCache Duration="60" VaryByParam="none"
VaryByControl="CategoryDropDownList" %>
该示例将缓存用户控件60秒,并且将针对CategoryDrop
  DownList控件的每个不同的值、针对此控件所在的每个页面创建单独的缓存条目。
<%@ OutputCache Duration="60" VaryByParam="none" VaryByCustom="browser"
Shared="true" %>
最后,该示例将缓存用户控件60秒,并且将针对每个浏览器名称和主要版本创建一个缓存条目。然后,每个浏览器的缓存条目将由引用此用户控件的所有页面共享(只要所有页面都用相同的ID引用该控件即可)。

下面的示例说明了如何设置页或用户控件进行输出缓存的持续时间。
<%@ OutputCache Duration="100" VaryByParam="none" %>