红俊's profile蝈蝈俊的共享空间PhotosBlogListsMore Tools Help

Blog


    September 28

    SQL2005的SQL Server Management Studio对大数据库操作超时解决方法

    SQL2005 中,我们使用 SQL Server Management Studio对大数据库操作,不象 SQL2000 的企业管理器那样,作一些大数据库的修改操作时候,不会超时。

    如何解决呢? 很简单,在SQL Server Management Studio的 Tool --〉 Options 菜单设置即可。

    需要设置的项如下:

    配置数据库的超时

    “为表设计器更新重写连接字符串的超时值”这个选项必须选中。

    同时设置事务超时时间为最大值 即 65535 秒

    这个事务设置的范围只能是1到65535 ,不能是0,也不能更大。

    September 21

    从HTTP状态 301,302,200 来看页面跳转

    301和302 Http状态有啥区别?

    301,302 都是HTTP状态的编码,都代表着某个URL发生了转移,不同之处在于:

    301 redirect: 301 代表永久性转移(Permanently Moved),

    302 redirect: 302 代表暂时性转移(Temporarily Moved ),

    当然 Http 状态 200 标示没有任何问题发生。

     

    这两种转移在使用的时候有啥好处或者问题?

    301 重定向是网页更改地址后对搜索引擎友好的最好方法,只要不是暂时搬移的情况,都建议使用301来做转址。

    302 重定向是临时性转移。

    在前些年,不少Black Hat SEO曾广泛应用这项技术作弊,目前,各大主要搜索引擎均加强了打击力度,象Google前些年对Business.com以及近来对BMW德国网站的惩罚。即使网站客观上不是spam,也很容易被搜寻引擎容易误判为spam而遭到惩罚。

    研究搜索引擎优化(SEO)的人,应该都知道,301,302 使用不当,或者灵活使用会有不错的效果的,比如参看下面文章:

    302转向与网址劫持

    301转向和网址规范化

    301重定向的实现方法
    51window提供的301转向的代码
    一个网站十几个域名的重定向经验谈
    几个常用的301重定向

     

     

    如果是运维人员,配置WEB服务器的一些设置就可以实现跳转,比如下面一些关于IIS实现重定向的文章

    将请求重定向到文件、目录或程序 (IIS 6.0)

    http://www.microsoft.com/technet/prodtechnol/windowsserver2003/zh-chs/library/iis/fbcccfce-6695-4f92-bd55-869d85ff49fb.mspx?mfr=true

    重定向参考 (IIS 6.0)

    http://www.microsoft.com/technet/prodtechnol/windowsserver2003/zh-chs/library/iis/41c238b2-1188-488f-bf2d-464383b1bb08.mspx?mfr=true

    其他WEB服务器也可以通过设置实现跳转,我就不罗列了。

     

     

    从技术人员来说我们比较常用的跳转方法有以下几种:

    我们使用 Fiddler 工具来监控的下面提到的几种方法,看跳转过程中HTTP状态码是301?302?200?:

     

    方法1:Response.Redirect("Test.aspx");

    状态码 301  临时性跳转

     

    方法2:<meta http-equiv="refresh" content="5; URL=Test.aspx" />

    meta fresh: 这在2000年前比较流行,不过现在已很少见。其具体是通过网页中的meta指令,在特定时间后重定向到新的网页,如果延迟的时间太短(约5秒之內),会被判断为spam。这种跳转方式,整个跳转过程中,页面Http状态都是200,即,页面没有任何错误发生。

     

    方法3:Server.Transfer("Test.aspx");

    这种跳转方式,整个跳转过程中,页面Http状态都是200,即,页面没有任何错误发生。

     

    方法4:URLRewrite

    这种跳转方式,整个跳转过程中,页面Http状态都是200,即,页面没有任何错误发生。

     

    没有一种是301跳转。也就是上面几种跳转方式都用不到301跳转的好处。

     

    使用301跳转有啥好处呢?

    SEO(搜索引擎优化)中提到一点:如果我们把一个地址采用301跳转方式跳转的话,搜索引擎会把老地址的PageRank等信息带到新地址,同时在搜索引擎索引库中彻底废弃掉原先的老地址。

     

    如何编码实现301跳转呢?

    Response.Status = "301 Moved Permanently";
    Response.AddHeader("Location","...");

    这里的 ... 表示你要跳转去的页面。

     

     

    参考资料:

    301永久重定向实现方式及302重定向

    生成 RSS2.0 文档可能犯错误的一些地方

    手下有个人在创建一个RSS2.0文档时,对RSS2.0的一些项的规范不是很了解,把数据放错了地方,我下面就整理一些他犯错误的地方。避免其他人也犯这个错误。

    完整的RSS2.0规范可以看以下链接地址:

    http://tonyqus.cnblogs.com/archive/2005/01/30/93684.aspx

     

    <item>的节点下的guid节点

    item节点下的guid节点的作用在于:可以唯一确定item的字符串。我们经常把这一项设置成URL,跟 link 节点是一样的。但是也是可以设置成不一样的。而且guid

    节点可以设置任意格式的字符串。只要保证这个字符串是不重复的。

    当设置的guid节点不是一个URL时,需要isPermaLink标志。isPermaLink标志就是标志是否guid节点是一个URL。示例如下:

    <guid isPermaLink="true">http://inessential.com/2002/09/01.php#a2</guid>
    isPermaLink是可选属性,默认值为真。如果值为假,guid将不会被认为是一个url或指向任何对象的url。

    我的手下就是一直误以为这一项只能设置成一个URL,结果在需要在RSS中显示一个Guid类型的成员时,找不到地方放,随便放到了comments 中了。

    用RSS.net 来实现这个定义,就可以简单的写成下面的代码:

    RssGuid rg = new RssGuid();
    rg.PermaLink = DBBool.False;
    rg.Name = Guid.NewGuid().ToString();
    item.Guid = rg;

     

    <item>节点下<comments> 节点

    comments 节点不是注释文本节点,而是 与item相关的评论的地址。这是一个URL。

    <comment>是<item>的可选节点。 如果出现,它指向该item评论的url

     

    <item>节点下分类节点<category>节点

    这是表示分类的节点。

    如果我们有多种分类,可以使用这个节点的 Domain  属性。

    生成的节点如下:

    <category domain="ParentRoomID">分类名字1</category>

    用 RSS.net 的代码就是:

    RssCategory c2 = new RssCategory();
    c2.Domain = "ParentRoomID";
    c2.Name = "分类名字1";
    item.Categories.Add(c2);

     

    参考资料:

    中文的RSS2.0 规范

    September 16

    心态

    继续说女足世界杯的中国姑娘们,昨晚的比赛0:4输了。

    由于上半场最后时刻被巴西队进了一个球,从下半场刚开始时,中国队的心态就是准备加强攻势,但是一减轻防守,就被巴西的足球小姐抓住机会,两个助攻得分。从此中国队就难以组织起象样的进攻。

    为何比分变成0:3后,中国队为何难以再组织起象上半场那样的进攻势头?

    我觉得是象足球这种比赛,它需要的是场上11个人统一目标,而且心态一致的落实某个具体的进攻方案。在0:3后,队员们很难全部都统一心态。有人想防守,有人想进攻,有的人时不时有点犯晕。更多的人脑子一热,不考虑战术了,只想着进球。这种情况下,很难组织一个团队进行有效的战术配合。从而下半场组织不起上半场的攻势。

    一个团队的工作结果是依靠整个团队的所有成员目标一致的努力结果。中间只要有一个人走神,或者犯晕,或者不遵循团队制定的战术原则。会影响整个团队的结果的。这点足球比赛的团队表现的比其他团队更明显。足球比赛的结果能数倍的放大这种问题。

    September 13

    走神

    昨天晚上看了女足世界杯中国对丹麦。结果是3:2中国赢了。给我感觉最深的是下半场的4个进球的过程。

    第50分钟的时候,中国队一个世界波,2:0领先。

    当时明显就能看到中国队的姑娘们满脸幸福,有点分心,结果不到1分钟,丹麦队扳回一球,2:1 。这个失球典型是还没有从哪个世界波进球中兴奋过来,就被泼了一盆冷水。

    87分钟的时候,中国的一次进攻未果,给丹麦队获得一个后场任意球,中国队的姑娘们都还在丹麦队的后场。丹麦队非常快的发出任意球,造成中国队的后场竟然丹麦队的人比中国队的人还多。结果丹麦队2:2扳平比分。很显然这个失球是脑子还没转过弯,还在想自己进攻中,结果被对方扳平。

    扳平后,丹麦队异常兴奋,从教练到队员有点兴奋过度,犯了跟中国队第二个进球一样的问题,结果不到一分钟,被中国队又一个世界波3:2拿下。给了丹麦队闷头一棍。

     

    这几个进球有个共同特点,都是对手由于各种原因走神的时候进的。

    足球场上走神一下,一失足成千古恨。其他场景也是类似的。

    比如我所参与的项目中,经常会出问题的项目一般都不是难的项目,而是非常容易,没有任何技术含量的项目。轻敌,走神就是造成的根源。而难的项目,复杂的项目由于重视,反而很少出问题。

    看完昨天的足球,对此感觉颇深,特写此篇博客发表自己的感触。

    September 12

    装 Visual Studio 2005 的用户请赶快打补丁

    今天早上微软发布了一个补丁,修复了Visual Studio 2005的一个安全漏洞

    下面是我的安装信息

    Microsoft Visual Studio 2005 Service Pack 1 安全更新 (KB937061)

    安装日期: ‎2007/‎9/‎12 8:40

    安装状态: 成功

    更新类型: 重要

    最近发现了一个安全问题,攻击者可能会利用该问题来破坏您运行 Microsoft Visual Studio 2005 Service Pack 1 的 Windows 系统,进而对其进行完全控制。您可以通过安装此 Microsoft 更新来保护您的计算机。安装完此产品后,您可能必须重启动计算机。

    详细信息:
    http://go.microsoft.com/fwlink/?LinkId=91233

    帮助和支持:
    http://support.microsoft.com

    这是因为我的VS2005是SP1版本,你可以在下面地址看到更多信息

    Vulnerabilities in .NET Framework Could Allow Remote Code Execution (931212)

    http://www.microsoft.com/technet/security/bulletin/ms07-040.mspx

     

    而且这个安装更新可以无限次的打,比如下面我一台Win2003的电脑,已经打了三次这个补丁。

    kb937061

    September 05

    WPF中展开一个TreeView控件的所有树节点

           在 Windows Form 应用中,我们碰到需要展开一个TreeView 控件的所有树节点的时候很简单,微软已经替我们提供了ExpandAll 方法,我们只要简单的一行代码tv_QTree.ExpandAll();就可以了。即 System.Windows.Forms 命名空间的 TreeView.ExpandAll 方法 。

           在WPF中,我们会发现,System.Windows.Controls.TreeView 中没有了 ExpandAll 方法。唯一跟展开有关系的属性和方法就是每一个TreeViewItem 中有一个属性IsExpanded 属性。这个属性定义这个节点是否打开。MSDN帮助如下:

           Gets or sets whether the nested items in a TreeViewItem are expanded or collapsed. This is a dependency property.

           这时候如何办呢? 很简单,自己写个递归,遍历这个树的每一个子节点,然后把每一个子节点的 IsExpanded 设置为 true.

           你可以通过以下几个链接看到这个解决方案:

           http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=976861&SiteID=1

           http://shevaspace.spaces.live.com/blog/cns!FD9A0F1F8DD06954!463.entry

           http://blogs.msdn.com/okoboji/archive/2006/09/20/764019.aspx

     

           我们可以在上面解决方案基础上进一步发展。

           用扩展方法来给 System.Windows.Controls.TreeView 类扩展个 ExpandAll方法方法。有关扩展方法的一些基础知识可以参看我之前的博客:C#3.0 中的扩展方法 (Extension Methods)

          我的扩展方法代码如下:

    /// <summary>
    /// 郭红俊的扩展方法
    /// </summary>
    public static class ExtensionMethods
    {
        /// <summary>
        ///
        /// </summary>
        /// <param name="treeView"></param>
        public static void ExpandAll(this System.Windows.Controls.TreeView treeView)
        {
            ExpandInternal(treeView);
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="targetItemContainer"></param>
        private static void ExpandInternal(System.Windows.Controls.ItemsControl targetItemContainer)
        {
            if (targetItemContainer == null) return;
            if (targetItemContainer.Items == null) return;
            for (int i = 0; i < targetItemContainer.Items.Count; i++)
            {
                System.Windows.Controls.TreeViewItem treeItem = targetItemContainer.Items[i] as System.Windows.Controls.TreeViewItem;
                if (treeItem == null) continue;
                if (!treeItem.HasItems) continue;

                treeItem.IsExpanded = true;
                ExpandInternal(treeItem);
            }

        }

    }

     

    扩展方法的使用方法也请参看C#3.0 中的扩展方法 (Extension Methods) 提到的注意事项。

    September 03

    GridSplitter 的 ResizeBehavior 属性介绍

    我们先来看一个我们在使用 GridSplitter 时,经常可能会碰到的问题:

    把一个窗体分成2部分,这两部分中间是一个GridSplitter来支持这左右两部分的宽度变动。类似下面的效果图:

    GridSplitter 的 ResizeBehavior 属性介绍01

    中间红色就是分割线,这个分割线支持左右两部分的宽度变更,一般我们会把这个需求写成类似下面XAML文件的方式:

    <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Class="WpfApplication_GridSplitter.Window1"
        Title="Window1" Height="100" Width="300">
        <Grid Width="Auto" Height="Auto" >
            <Grid.ColumnDefinitions >
                <ColumnDefinition Width="*"  />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="5*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions />
            <StackPanel Grid.Column="0"  Background="DarkBlue" />
            <GridSplitter Grid.Column="1"  Width="3" Background="Red" />
            <StackPanel Grid.Column="2"  Background="#FFAEA130" />
        </Grid>
    </Window>

    但是仅仅上面的代码,我们在拖动分割线的时候,会发现,分割线右边的尺寸发生变化了,而左边的尺寸没有发生变化,类似下面效果图:

    GridSplitter 的 ResizeBehavior 属性介绍02

    中间出现了一个空白区域。

    这是为何呢??

    原因很简单, GridSplitter 默认的 ResizeBehavior 属性为:BasedOnAlignment

    ResizeBehavior 属性是一个枚举类型:GridResizeBehavior ,这个枚举类型包含以下枚举项:

    BasedOnAlignment    根据VerticalAlignment 和 HorizontalAlignment 属性来确定分割线移动后,有哪些项被移动
    CurrentAndNext        Grid 当前所在的区域以及下一个区域被影响
    PreviousAndCurrent  前一个和当前区域被影响
    PreviousAndNext      前一个和后一个区域被影响

    我们把这个XAML文件改成下面方式就可以左右联动了。注意,只改动了加粗,加红的这一个属性。

    <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Class="WpfApplication_GridSplitter.Window1"
        Title="Window1" Height="100" Width="300">
        <Grid Width="Auto" Height="Auto" >
            <Grid.ColumnDefinitions >
                <ColumnDefinition Width="*"  />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="5*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions />
            <StackPanel Grid.Column="0"  Background="DarkBlue" />
            <GridSplitter Grid.Column="1"  Width="3" Background="Red"

    ResizeBehavior="PreviousAndNext" />
            <StackPanel Grid.Column="2"  Background="#FFAEA130" />
        </Grid>
    </Window>

    改动后的效果图:

    GridSplitter 的 ResizeBehavior 属性介绍03

    总结:

    GridSplitter 的默认 ResizeBehavior 属性导致我们无法实现左右联动,我们只需要简单的修改这个属性就实现左右联动。

    这个问题,由于是第一次接触WPF,让我花了好几个星期,才发现是这里简单的问题所导致的。所以特写此篇博客,希望对碰到这个问题人有所帮助。

    参考资料:

    MSDN GridResizeBehavior Enumeration
    September 02

    焦油坑与激情

    先来听我说几个真实的故事:

         上周面试了一个开发人员,这个人所有的面试题都答出来了。各方面我们需要的知识也掌握了,但是在初试中,这个人就被我们三个面试官一并否决了。 原因很简单,这个面试者提供的答案都是能解决问题,但几乎都是效率最差的方案;另外,从一些面试题中,可以看出这人很多时候,把开发工作当成一个应付差事的工作来做,而不是作为自己的兴趣来做。缺乏激情,工作只是应付差事,仅仅是由于有几年工作经验,才能答出我们的面试题。这样的人不要也罢。

         另外一个故事,公司有个开发组,在他们组发生过这样一件事情:有次在一个界面布局文档中,有几个很明显的错别字。提交到某个开发人员那里后,开发人员在知道是错别字的前提下,不跟其它相关人员沟通,在WEB页面上,仍然采用了这个错别字。

         再一个面试中听到的故事:在问面试者为何离开前一个公司的时候,有个面试者说了这样一种情况,一个10个多人的开发团队,忙了一年,产品还没有上线,需求还在因为Boss的灵感不断,还在不断修改中,最后他觉得对这个项目和这个公司失去了激情,为此他决定寻找新的工作机会。

         种种原因,没能或不再把编程工作当作一种乐趣,没有成就感,这是上面三个故事产生的根源。这是我的看法。

         不仅仅程序,编程系统产品在焦油坑中苦苦挣扎,程序员们也在焦油坑中苦苦挣扎。下图是人月神话第一章的插图:拉布雷阿的焦油坑壁画(Mural of La Brea Tar Pits)。我们每个人都象其中的动物一样,时时刻刻陷身在焦油坑中,都在苦苦的挣扎中。

    人月神话 焦油坑

         说自己陷身焦油坑,有这个,哪个苦恼。谁没有苦恼,人在各个阶段都会有大量的苦恼存在,在苦恼中仍然有激情,这才是重要。

         有时候,我就在关注身边的程序员,去想他们为何会有这样、那样的行为发生。分析的其中一个结果就是:程序员的水平差别很大程度是看他是否有编程的激情。

         比如:刚毕业的大学生,差的,好的我都看到过,好的一个在还有半年才毕业的时候,就被我拉到公司工作了(这个人后来由于想自己创业,离职了,不过这个人的激情我非常佩服)。而差的,在面试中一些非常简单问题的答题,让我差点吐掉。要知道,我面试刚毕业者,一般不是考具体应用,而是重点考察算法、数据结构的简单知识,而这些知识,大学里是必修的。

         有哪些方面可能成为程序员有激情的原因?

         在人月神话的第一章《焦油坑》中提到编程行业“满足我们内心深处的创造渴望和愉悦所有人的共有情感”提供了五种乐趣:

    • 创造事务的快乐; 
    • 开发对其它人有用的东西的乐趣;
    • 将可以活动、相互啮合的零部件组装成类似迷宫的东西,这个过程所体现出令人神魂颠倒的魅力;
    • 面对不重复的任务,不断学习的乐趣;
    • 工作在如此易于驾驭的介质上的乐趣--纯粹的思维活动--其存在、移动和运转方式完全不同于实体物件;

     从我的角度来理解,就是以下两个方面:

    • 编程的结果导致的激情,包括创造事务,以及创造的事务被其它人认可。
    • 编程的过程,把一行行代码组装成一个功能强大的产品,组装中引入自己正在关注的一些技术,算法,模式。这个算法是我设计的,为此带来的一系列乐趣。

    编程的结果和过程,很多时候受制于其它人的。这时候必然会对你在这方面的乐趣产生限制。

    越是大公司,越是大型的系统,这种限制越多。另外,日企的严格开发规范,我觉得是对这些限制达到极致的表现。所以我个人肯定不会去日企。也不建议去日企。有些技术牛人,没有去大企业,可能的一个原因是因为他想把自己这些编程乐趣保留在一个更大的范围。

    另外,微软开发流程我知道的可能比较多点。我就说说我对微软开发中的乐趣说说我的看法,毕竟我不在微软,可能不一定对。

    1、我觉得在微软开发团队,测试人员的开发乐趣比开发人员的开发乐趣要更多。

    开发人员需要有产品经理的各种文档作限制,他的最大开发乐趣可能是在设计各种更优秀的算法来保证界面功能可用。

    而测试人员则需要用各种工具(包括自己开发的,使用脚本等)来测试各种场景。比如我挺佩服的施凡,有段时间,他为了测试Live Meeting,自己在设计写个语音机器人,来完成这个测试。

    2、工作产品被很多人使用的成就感,这点大家应该都很明白,就不细说了。

    上面说的是针对个人的编程激情,很多时候,周围人的习惯会影响你也会慢慢具有这个习惯,这就是团队,环境在潜移默化的感染者您。

    如何让团队保持激情?

    以下只是我个人的看法,并不一定使用于你的环境。

    1、制度化,并以身作则。

    比如我带领的CSDN论坛相关产品开发团队,有个制度就是每周四,组内一个成员介绍自己最近关注的技术。这个制度我没有往整个公司推广,是因为我觉得推广到整个公司,平均分配到每个人身上,就是4、5个月才轮到一个人一次,对于个人学习的督促作用一点用都没有了。

    2、工作节奏松劲轮换,不能一直以一个节奏来进行。

    该加班的时候就加班,该放松的时候就放松。注意不能一直是一个节奏,如果一直是一个节奏,那么人很容易懒散。

    总结:

    最近在看《人月神话》,其中的很多知识点感触很深,很浅显的一个道理,如果让自己一个人去慢慢悟的话,就不知何时才能出来。

    回到本文讨论的主题,如何让自己保持激情?如何让团队保持激情?这是每一个程序员,每一个项目经理都要考虑的事情,只有有激情的团队才能产生伟大的作品。才能跟上时代的步伐。

    September 01

    理解缓存

      最近公司一直在招人,我作为主考官之一 。经常会提问的一个问题,就是让用户介绍自己在缓存方面的经验和心得。绝大多数的面试者只能说ASP.net的页面缓存和局部缓存,稍稍有点经验的,会提到企业库的缓存,只有很少的人会知道Memcached(一个分布式的缓存)。而对于缓存的一些基本思想,却没有一个人能说出来。

      现在的技术人员,很多时候,不管三七二十一,把一个个实体丢到缓存中,然后在用的时候,就从缓存中去找这个实体。而不会考虑缓存的其他方面因素。所以他们在提到缓存时,想到的才只能是一个个的缓存实现的方法,而不是缓存的思想。

      那么,肯定就有人问,蝈蝈俊,那你理解的缓存思想是如何的呢?下面我就一一来说出我理解的缓存。

     

    Q:什么样的缓存才是好缓存?

      能解决问题的缓存就是好缓存。这句话简直就是废话,相当于白猫、黑猫,抓住老鼠的就是好猫。

      那在解决问题前提下,哪个缓存才是好缓存呢? 这个问题我的答案是:缓存命中率高的缓存是好缓存。

      在解决问题前提下,命中率高的缓存比命中率低的缓存,在硬件投入上可能会比较小,同时缓存的数量比命中率低的缓存数量也可能少,这样寻址的速度肯定比较快。所以命中率高的缓存是好缓存。

     

    缓存的命中率

      一个缓存的实体在被丢到缓存中后,在这个实体被缓存的期间(这个实体被缓存的生命周期内),如果外部一次都没有使用过它,这个缓存实体的命中率就是0。这个实体被请求的次数越多,它的缓存命中率越高。

      上面说的是缓存中一个实体的命中率。对于缓存整体来说,它的命中率则是上面各个被缓存的个体的命中率分布图。

      对于缓存来说:通常最常使用的个体之占总体的很小一部分。最不常使用的占整体的很大一部分。如下图所示:

    缓存的命中率一般分布图

      所以我们经常会看到类似这样的数据:

      缓存的1万个元素中,有100个被频繁的使用,几乎每分钟都会被使用一次。2000个数据,每小时被请求一次。3000个数据,每天被请求一次,剩下的数据,被丢到缓存中后一次都没有被使用过。

      现在硬件发展很快,如果我们只是需要缓存1万个数据的话,我们完全可以做到不管这1万个数据是否被使用到,全部丢到缓存,这样只要找数据,肯定缓存中有这个数据。而不需要作额外的运算,或者不需要向数据库发出请求。

      但是:硬件发展快,数据量发展也快。小型的网站,缓存1万条数据,也就全部缓存了。但是大型网站最少也是上百万的数据量或者上T级别的数据,这些数据量显然不能都丢到缓存。这时候设计一个合理的缓存方案,提高缓存的命中率,就非常重要。而且是必须的。

     

    提高缓存命中率的一些常见方法

      纯技术的角度来说,我们只有记录了用户的单位时间的请求数,并依照这个信息来把最常被使用的数据缓存起来。

      但更多的时候,我们是根据业务逻辑来提高缓存命中率的。比如:去年,前年发表的博客,这类文章的浏览请求,一般一天至少可怜的几次。一般不应该缓存到内存中。

      又比如,回复数多的帖子,一般被请求数会比回复数少的帖子会被更多人次看到。 

      我们应该通过上面逻辑,根据我们实际业务逻辑,提供一个缓存算法,提高缓存的命中率。让在我们硬件允许的条件下,缓存适当的数据,而不是所有数据。

     

      一个反面的例子就是:不管三七二十一,一个大型的博客站点,一篇文章被用户请求的时候,发现不在内存缓存中,就从数据库中读出,然后丢到缓存。

      要知道,现在爬虫程序很多的。另外,博客这类搜索引擎友好的站点,决大多数的访问压力是搜索引擎搜索过来的。而这些访问一般都是1小时,或者1天之内,对某篇文章只有几次甚至1次请求,之后再也没有了。上面作缓存的方法,命中率会非常低的。

      这里也许就有人会问,郭红俊,既然你不建议我缓存这些博客的内容,但是我如何提高我站点的性能呀,我至少得保证我博客站点不会速度慢的无法响应用户请求呀。

      这个问题的解决方案有很多,一个最简单的方法就是把这些博客做成静态Html页面,也就是文件系统的缓存,文件系统因为硬盘的原因,可以简单理解成可以无限扩容,这样就可以把很多命中率低的内容进行缓存。

      如果你的页面需要一些动态逻辑判断,你可以把数据缓存成XML文件,然后服务器段整合这些XML文件,或者是包含文件。这也是种不错的方法。

     

    说了这么多缓存命中率的问题,简单汇总一下缓存命中率的观点

    1. 小型网站可以全部数据缓存,一般压力也不会很大,可以忽略缓存命中率问题。
    2. 大型服务无法全部数据缓存,只能部分数据缓存,这时候就需要架构师设计出对该业务逻辑适用的缓存方法,尽可能的提高缓存的命中率。
    3. 提高命中率的方法大多是跟业务逻辑捆绑的,需要跟具体问题具体分析
    4. 对于不能被内存缓存的数据,最简单的提高性能方法就是使用文件缓存。
    5. 文件缓存可以整个内容缓存成一个静态文件;也可以是整个页面的一个区域被缓存成一个文件,然后被包含;也可以是把一个实体序列化成XML文件进行缓存。

     

    下面我们看看缓存的其他几个不那么重要的方面:

     

    缓存的生命周期内的活动

      永久不过期,永久不变更的内容,这类东西就不应该放在缓存。缓存是临时的存储,而不是永久的,所以缓存的生命周期是有限的。

      它依次可能会经历如下活动:

    1. 进入缓存。(进入缓存的时候,可能需要指定它以后的过期策略,如果不指定,需要使用系统默认的过期策略)
    2. 从缓存中获得它,注意,这时候需要处理线程安全的问题。
    3. 更新缓存,注意,也需要考虑线程安全问题
    4. 离开缓存,这个可能是外部请求,也可能是缓存根据过期策略把它清理掉。

     

    缓存的过期策略

      一般我会问,你所接触的缓存中,碰到过那些缓存过期策略?

      最常见的几种过期策略如下:

      多长时间没有被请求,则过期,最典型的就是ASP和ASP.net 提供的 Section 功能。其实它就是一个缓存。

      依赖于文件变更的缓存,一旦文件被修改,缓存则过期,典型的是 WEB站点的 Web.config ,一旦这个文件变更,不但缓存重起,IIS进程也会进行一次释放工作。

      在此基础上,可能看到很多依赖关系的缓存过期策略。比如依赖于数据库的缓存过期策略。

      当然,业务逻辑里可能会有更复杂的过期策略,必须CSDN新版积分制论坛中,帖子列表缓存会在列表数据缓存达到600时,把它清理到550条数据。

      又比如新积分制论坛帖子的缓存过期,则是没有任何列表引用这个帖子后,则这个帖子过期。

     

    缓存的同步问题

      使用缓存,则意味着同样的数据,可能有多份并存。如果你的代码没有考虑某种情况,导致了这两份数据不一致了。这时候就会有问题发生。

      解决方法很简单,把你的业务逻辑,代码触发情况都考虑清楚,不要遗留没有触底的地方。

      简单的方法会导致你的代码逻辑变得非常复杂。

      这也就是有些人,在非必要的时候,建议你不要用缓存的原因。一旦开始使用缓存,你就应该准备增加大量的代码来处理数据同步的问题。

     

    初始化填充缓存数据

      有时候在缓存被初始化后,还需要预先填充一些数据到缓存中。这就是缓存数据的初始化操作。

      缓存数据的初始化操作需要考虑以下问题:

    1. 需要多长时间进行初始化,一般如果是站点的话,我们可能在 Global.asa 的 Application_OnStart 中处理这个初始化工作。初始化的一般不能太久,这时候就是考验我们代码优化的能力了。
    2. 初始化的时候,一般是批量导入数据,而不是我们正常使用的时候,一次处理一个数据。

     

     总结:

      本文介绍了我对缓存的一些观点,而没有深入涉及到具体的缓存技术。希望通过本文的讲述,让只会缓存用法不懂缓存思想的人有初步的了解。