晚上用什么洗脸最好:一种基于内存的快速查询的解决方案。

来源:百度文库 编辑:偶看新闻 时间:2024/04/29 16:28:37
一种基于内存的快速查询的解决方案。

 转载请注明出处(http://www.cnblogs.com/goldentime,作者:黄金年代。文章虽差也属原创,苍蝇虽小也是块肉!)

被网上小将们骂怕了。但是该写还是要写,而且坚持原创继续毁人不倦。我们的口号是(没有蛀牙!!!)

本垃圾文章描述了一种类似于缓存的查询方式,将数据放入内存中进行查询,从而达到提高响应速度增加吞吐量的目的。方法简单,属于原创。缺点等待大家来评论。

本文技术内容其实很简单,但是我前边会写一些技术之外的东西,因为不仅在中国就是在世界上很多东西比技术更重要。毕竟技术是为了更好的提供服务。往往是最末的一环,请各位程序员和准程序员清醒这一点。另外还简单介绍了设计的过程欢迎有同样小型项目管理经验的同道评论,也给初学者参考。

正文开始:

之前我们参考了南方兄弟招办已有系统(运行过一年,我们本来想去年搞,但是没搞起来,人家先做了)地运行情况。算是学习了经验。最后我们认为整个系统的压力应当集中在填报志愿过程中(废话)。

填报方案选型过程:

一、如果考生直接选择院校名称和专业的话,对服务器和网络压力太大,该方案淘汰。(在后期征集志愿时由于院校数目少可以考虑)

二、志愿翻译:简单说来就是:考生输入报考院校的学校代号和专业代号,然后系统翻译为现实的名称,考生检查是否正确即可。

例如10003:01,02 翻译为:清华大学:工程力学与航天航空工程,艺术设计学。

选择:方案二,考生填写代码,系统翻译志愿。

设计实现过程

设计目标:

翻译过程很简单但是主要是次数比较多。每位考生一次填写正确需要翻译9批次*2-5院校*1院校名称+6专业名称=250次左右的翻译。共55万考生。一共需要翻译1.25亿次。平均到2天的工作时间16小时,平均每秒翻译2170次。再用正态分布估算。我们觉得每秒1万次翻译作为设计目标应当能够满足实际应用的情况。

说起来简单,实际上每个学校翻译的时候还需要判断科类、考生类别、限报条件等,所以sql查询语句效率不会太高。

翻译方案选型:

一、Sql存储过程

已有系统的查询使用存储过程来完成查询,将web服务器和数据库服务器之间的通信减到最小。然后通过对数据库进行优化将性能最大化。

这种方法比较成熟,也是大家首先能够想到的,实施起来风险也较小,数据流量也可很容易的分布到多个数据库上(不要给我提什么群集,挖掘一类的,我们有2台F5,根本不需要那么麻烦再烂的系统也能够运行的差不多。但是不能那样做事。第一设备再先进,也要你的底层架构设计合理才可以。第二、设备技术再先进是人家微软、IBM的又不是自己的,要想进步,要自己去努力。我实在不明白为什么有些人使用起那些名词来那么理直气壮好像是自己发明的一般。)

为了规避风险我们首先实现了该方法,但是并没有在数据优化时下功夫因为我们认为,这种方法效率不高。比如:我们仅仅是只读的查询。一共的3万记录进行1.25亿次查询相当于每条平均查重复4000多次,这是巨大的性能浪费。

而且sql查询时作负载均衡我们嫌麻烦,我们这时就是想要试验去做一种没有试过的高效的内存查询算法。看看到底能要出多少性能来

二、专有数据库查询:实际上无论是平易近人的mssql还是走下神坛的ORCAL由于太大,求全涉及到方方面面。他的数据查询效率并不如专有数据库效率高。就像CE不如PALM快,虽然PALM不先进但是运行快。但是时间长了,由于CE的通用性和兼容性还是占据了比较大的市场。

但是专有的数据库用起来一方面技术比较冷门,另外我们没有时间去进行评估也就无法去选择。单独为一个系统去学习一个冷门数据库不划算。

三、内存数据库,目前内存数据库上网查了查有一个韩国的厂商在工控领域做的不错。另外好像微软的Microsoft SQL Server 2005 Compact Edition

(这个不确认是内存的,但是好像支持,而且好像对多用户支持不好或不支持)做的也不错。感兴趣的去这个地址:http://www.microsoft.com/downloads/details.aspx?FamilyId=E6BC81E8-175B-46EA-86A0-C9DACAA84C85&displaylang=en#filelist

四、私有算法。我们最后选择了自动开发一种查询算法。因为:

1、 创新的需求。做东西技术人员总要有所追求创新,有个东西是你自己的,总是用别人的有啥意思?

2、 我们的查询比较简单,没有必要实现到数据库的层次。为了我们简单的查询,没有必要去实现Codd十二法则?这样我们就可以借着这个项目研究新技术,同时还可以控制风险。

3、 时间充裕:我们设计完之后,用2天就实现了基于内存查询的并进行了评估(虽然网上没有现成的,但是只要想到了确实很简单,就和原来我们写汉字字库实际上一样),结论是值得一试。

具体实现:(请注意本文的实现与现实严格关联,因为实际工作中这些政策年年变,不可能复用,所以也就没有完全面向对象,而只是抽了出来做成类方便维护而已。)

系统具体实现部分

系统实现的原理:

原理很简单将数据放入数组或哈希表中,然后查找即可,关键是如何实现。

关键在于:

第一、       如何将表放入内存中。

第二、       如何对外提供查询。

对于第一个问题我们可以很简单的通过将数组和哈希定义为静态变量从而常驻内存。

请原谅我的代码中有拼音因为。。。教育部的表就是拼音缩写的,因为广大招办有很多老同志,都换成英文不可能。

 public static string[,] stryxs = new string[3200, 4];//前边是学校数,后边是每组结构 学校名称+kldm+pc+zsfw 科类代码、批次、招生范围
  public static Hashtable htzys = new Hashtable();

  然后在构造函数中填充数据例如:


/**////首先加载院校代码表
        jihuas dsjihua = new jihuas();
        jihuasTableAdapters.tjhyxTableAdapter jhapt = new jihuasTableAdapters.tjhyxTableAdapter();        
        //将指定院校(YXBM从01开始排)放入数组
        foreach(jihuas.tjhyxRow dr in dsjihua.tjhyx)
        {
//填充数据
    }
然后在全局文件Global.asax中将该类实例化即可
 void Application_Start(object sender, EventArgs e) 
    {
        // 在应用程序启动时运行的代码        
        cache ap = new cache();//加载全局类
    }

对于第二个问题,就好说多了,有了数据,查询、就是了。。

好了不说废话,看程序。。

系统代码实现:

/**////本模块功能是:
///1、构建全局缓存,在全局缓存中构建键列,供志愿查找使用
///2、提供刷新全局缓存功能。(调用构造函数)
///构建全局缓存,应用一次构造函数即可
///提供的全局缓存包括
///1、志愿:院校、专业名称
///2、区域对照表:地市、县区、报名点代码对照表
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;


/**//// 
/// cachetest 的摘要说明
/// 

public class cache
{
   //str??? 代表字符串数组 ht???代表哈希表 
    //public static ArrayList alyxs=new ArrayList ();//测试中使用过的方式
public static string[,] stryxs = new string[3200, 4];
/**////前边是学校数,后边是每组结构 学校名称+kldm+pc+zsfw共4项, 
///其中学校编码为从0001开始的4位编码,正好作为数组的下表stryxs[1]=0001院校的数据
    public static Hashtable htzys = new Hashtable();/**////志愿表
    public static Hashtable htdishi = new Hashtable();/**////地市
    public static Hashtable htxq = new Hashtable();/**////县区
    public static Hashtable htbmd = new Hashtable();/**////报名点
    /// 
    /// 构造函数,加载缓存
    /// 

    public cache()
    {
        /**////首先加载院校代码表
        jihuas dsjihua = new jihuas();
        jihuasTableAdapters.tjhyxTableAdapter jhapt = new jihuasTableAdapters.tjhyxTableAdapter();
        jhapt.Fill(dsjihua.tjhyx);
        /**////没有0院校,需要处理一下
        stryxs[0, 0] = "院校填写错误";
        stryxs[0, 1] = "";        
        //将指定院校(YXBM从01开始排)放入数组
        foreach(jihuas.tjhyxRow dr in dsjihua.tjhyx)
        {
      int yxbm = Convert.ToInt16(dr.YXBM);
            /**////07年有一个院校 编号为9001,将其转化为3199,后来临时加的,
            ///各部门没有协调好以至于编号从9001开始,其实要是使用3200以内闲置的代码号就不用这样处理了
            ///再有新增的也这样处理。
            if (yxbm == 9001)
            {
                yxbm = 3199;
            }
            stryxs[yxbm, 0] = dr.YXMC.Trim();
            //累加该院校的kldm
            //此处没有判断科类是否会重复,不需要判断,重复不影响例如AABBCCDD,只要有即可。此处可以改用STRINGBULIDER,
            string kldm = stryxs[yxbm, 1];
            string newkldm=dr.KLDM.Trim();
            kldm = kldm + newkldm;
            stryxs[yxbm, 1] = kldm;

            //累加该院校的pc省略
            //记录招生范围省略
        }
       
        //加载专业代码表
        /**////SELECT DISTINCT yxbm, yxmc, jhxz, kldm, pc, cc
        ///ORDER BY yxbm
        jihuasTableAdapters.tjhzyTableAdapter zyapt = new jihuasTableAdapters.tjhzyTableAdapter();
        zyapt.Fill(dsjihua.tjhzy);
        foreach (jihuas.tjhzyRow dr in dsjihua.tjhzy)
        {
            /**////追加的hash表格式为 keys  专业名称
            ///其中keys 为 yxbm+ZYBM+kldm+pc
            ///使用keys查找 
            htzys.Add(dr.YXBM.Trim() + dr.ZYBM.Trim()+dr.KLDM.Trim()+dr.PC.Trim(), dr.ZYMC);            
        }
        /**////SELECT YXBM, ZYBM, zymc, jhxz, KLDM, pc, cc 
        ///ORDER BY jhxz, KLDM, pc, cc, YXBM, ZYBM

        //加载报名点代码
        jihuasTableAdapters.baomingdianTableAdapter bmdapt = new jihuasTableAdapters.baomingdianTableAdapter();
        bmdapt.Fill(dsjihua.baomingdian);
        foreach (jihuas.baomingdianRow dr in dsjihua.baomingdian)
        {
            /**////追加的hash表格式为 keys  报名点名称
            ///其中keys 为 dmddm
            htbmd.Add(dr.ZXDM.ToString().Substring(1,6), dr.ZXMC.ToString().Trim());
        }

        //加载区县代码
        jihuasTableAdapters.td_xqdmTableAdapter xqdapt = new jihuasTableAdapters.td_xqdmTableAdapter();
        xqdapt.Fill(dsjihua.td_xqdm);
        foreach (jihuas.td_xqdmRow dr in dsjihua.td_xqdm)
        {
            /**////追加的hash表格式为 keys  县区名称
            ///其中keys 为 xqdm
            htxq.Add(dr.xqdm.ToString().Substring(1,4), dr.xqmc.ToString().Trim());
        }

        //加载地市代码
        jihuasTableAdapters.td_dsdmTableAdapter dsdapt = new jihuasTableAdapters.td_dsdmTableAdapter();
        dsdapt.Fill(dsjihua.td_dsdm);
        foreach (jihuas.td_dsdmRow dr in dsjihua.td_dsdm)
        {
            /**////追加的hash表格式为 keys  地市名称
            ///其中keys 为 dsdm
            htdishi.Add(dr.dsdm.ToString().Substring(1, 2), dr.dsmc.ToString().Trim());
        }        
    }
    //将数据表读入缓存中。然后使用
    /**//// 
    /// 志愿转换
    /// 

    /// 志愿信息
    /// 序号     0       1       2       3       4       5       6          7       8        9       10      11      12          13          14          15              16
    /// 有效     yxdh    zydh1   zydh2   zydh3   zydh4   zydh5   zydh6      tj     laiyuan      ksh     pcdm    zyh     zyhanyi     tiaoji      validstr     bmddm          kldm
    /// 
    public static string tranzhiyuan(ArrayList zyxx)
    {
        /**////翻译步骤
        ///1、检查翻译合法性,是否能够找到(还未考虑)
        ///2、翻译院校名称
        ///3、翻译专业名称
        ///4、翻译调剂
        ///
        //检查cache是否存在
        if (!cache.testcache())
        {
            return "错误!请联系系统管理员,重新启动应用程序以加载系统缓存!"; 
        }
        StringBuilder retstr = new StringBuilder();
        retstr.Append("");
        //首先判断院校编号是否填写,并且不越界
        //07年有一个院校编号为9001放入了3199需要处理与前边呼应,被临时变动打乱了
        if (zyxx[0].ToString().Trim() == "9001")
        {
            zyxx[0]= "3199";
        }

        if (zyxx[0].ToString().Trim() == "")
        {
            retstr.Append("未填写志愿!");
            return retstr.ToString();
        }
        if (!cache.IsNumeric(zyxx[0].ToString().Trim()))
        {
            retstr.Append("错误输入!必须是数字!");
            return retstr.ToString();
        }
        if (zyxx[0].ToString().Trim().Length != 4)
        {
            retstr.Append("错误输入!院校是4位数字!");
            return retstr.ToString();
        }
        
        if ((Convert.ToInt16(zyxx[0].ToString().Trim()) < 3200))
        {
            //首先需要取出院校编码,判断院校编码的范围,可能超出,如果超出,则返回院校代码错误,
            //然后判断该院校是否有所填科类,返回院校没有符合条件的科类
            int yxdh = Convert.ToInt16(zyxx[0].ToString().Trim());
            string yxmc = stryxs[yxdh, 0].Trim();
            //处理错误院校,该错误为院校编号不连续,用户可能恰巧输入错误的编号,错误编号返回值应为null。另外长度也需要检查
            if (yxmc != null)
            {
                if (yxmc.Length < 2)
                {
                    retstr.Append("无此院校,必须是招生计划中的正确院校编号");
                    return retstr.ToString();
                }
            }
            else
            {
                retstr.Append("无此院校,必须是招生计划中的正确院校编号");
                return retstr.ToString();
            }
            //已有的院校,需要判断pc和KLDM
            string pcs = stryxs[yxdh, 2].Trim();
            string kldms = stryxs[yxdh, 1].Trim();
            if (pcs.IndexOf(zyxx[10].ToString().Trim()) == -1)
            {
                retstr.Append("无此院校,批次错");
                return retstr.ToString();
            }
            if (kldms.IndexOf(zyxx[16].ToString().Trim()) == -1)
            {
                retstr.Append("无此院校,报考科类错");
                return retstr.ToString();
            }
            //判断招生范围 专业术语部分不用考虑含义
            if ((stryxs[yxdh, 3].Trim() != "0") && (stryxs[yxdh, 3].Trim() != "81") && (stryxs[yxdh, 3].Trim() != "82")&&(stryxs[yxdh, 3].Trim() != "83"))
            {
                if (stryxs[yxdh, 3].ToString().Replace("0","").Trim()!="")//有时可能会有多个0,所以不能REMOVE
                {
                    if (zyxx[9].ToString().Trim().Substring(4, 2) != stryxs[yxdh, 3].Trim())
                    {
                        retstr.Append("无此院校,请检查该院校招生范围");
                        return retstr.ToString();
                    }
                }
            }
             
            

            //都正确返回院校名称
            retstr.Append(zyxx[0].ToString() + stryxs[yxdh, 0].Trim() + " ");
            /**////***************************************************
            ///***************************************************
            /// 下边的进程延迟语句发布时一定要去掉。在测试Ajax刷新效果时使用
            ///***************************************************
            //////***************************************************
            //System.Threading.Thread.Sleep(1000);//延迟??毫秒再返回
            //开始翻译专业
            for (int i = 1; i < 7; i++)
            {//翻译专业:

                //首先判断是否为空
                if (zyxx[i].ToString().Trim() != string.Empty)
                {
                    //首先生成keys
                    /**////追加的hash表格式为 keys  专业名称
                    ///其中keys 为 YXBM+ZYBM+kldm+pc
                    string key = zyxx[0].ToString().Trim() + zyxx[i].ToString().Trim() + zyxx[16].ToString().Trim() + zyxx[10].ToString().Trim();
                    if (htzys.Contains(key))
                    {
                        retstr.Append(zyxx[i].ToString() + htzys[key].ToString().Trim() + " ");
                    }
                    else
                    {//如果专业找不到
                        retstr.Append(zyxx[i].ToString().Trim() + "(无此专业)" + " ");
                    }
                }
                else
                {//此处else分支为只要有空白不再向下翻译//已经修改为空白也继续翻译,但是需要注意如果后边还有数据才翻译,如果后边的都为空则不再翻译
                    //if (zyxx[7].ToString() == "1")
                    //{ retstr.Append("服从分配"); }
                    //else { retstr.Append("不服从分配"); }
                    //return retstr.ToString();
                    bool transflag = false;
                    for (int j = i; j < 7; j++)//判断该空位专业后边是否还有专业,有则翻译为空白专业,没有则本条为最后一条空白专业,不需要翻译
                    {
                        if (zyxx[j].ToString().Trim() != string.Empty)
                        {
                            transflag = true;
                        }
                    }
                    if (transflag)
                    { retstr.Append(zyxx[i].ToString() + "空白专业" + " "); }
                }
            }
            //最后附加是否服从分配
            if (zyxx[7].ToString() == "1")
            { retstr.Append("服从专业调剂"); }
            else { retstr.Append("不服从专业调剂"); }
            return retstr.ToString();
        }
        else
        {
            retstr.Append("无此院校,必须是招生计划中的正确院校编号!");
            return retstr.ToString();
        }
       // return retstr.ToString();
    }

    /**//// 
    /// 测试当前系统缓存是否存在
    /// 

    /// 
    private static bool testcache()
    {
        //仅仅是测试了一个表是否存在而已,要有都有,要没都没,这里没有用try因为每次查询try系统消耗太大。而且如果表不存在,静态方法也就不存在了。
        if (htzys.Count > 0)
        { return true;}
        else { return false; }
    }

    //从学院字符串数组中取值,调试用
    public static string getyxs(int i)
    {
        return stryxs[i, 0] + stryxs[i, 1];
    }

    /**//// 
    /// 翻译地市代码
    /// 传入字符串代码 返回结果
    /// 

    /// 
    /// 
    public static string transdsdm(string key)
    {
        if (htdishi.Contains(key))
        {
            return htdishi[key].ToString().Trim();
        }
        else
        {//如果专业找不到
            return "地市代码错误!";
        }        
    }

    /**//// 
    /// 翻译县区代码
    /// 传入字符串代码 返回结果
    /// 

    /// 
    /// 
    public static string transxqdm(string key)
    {
        if (htxq.Contains(key))
        {
            return htxq[key].ToString().Trim();
        }
        else
        {//如果专业找不到
            return "县区代码错误!";
        }
    }

    /**//// 
    /// 翻译报名点代码
    /// 传入字符串代码 返回结果
    /// 

    /// 
    /// 
    public static string transbmddm(string key)
    {
        if (htbmd.Contains(key))
        {
            return htbmd[key].ToString().Trim();
        }
        else
        {//如果专业找不到
            return "报名点代码错误!";
        }
    }

    /**//// 
    /// 翻译院校代码
    /// 传入字符串代码 返回结果
    /// 

    /// 
    /// 
    ///public static string transyxdh(string key,string ksh,string pc)
    public static string transyxdh(string hanyi)
    {
        /**////现在不再翻译,直接取出含义字段中的翻译
        ///过来的含义字段有2种格式,
        ///正确的10094河北师范大学
        ///错误的,无此院校,或报考。。。错
360docimg_501_        string[] words=hanyi.Split(new char[]360docimg_502_{' '});
360docimg_503_        if (words.Length >0)
360docimg_504_360docimg_505_        360docimg_506_{
360docimg_507_            //还需要判断前4个字符是否是汉字。因为有可能只填写了学校。
360docimg_508_            string temp = words[0].ToString().Trim();
360docimg_509_            if(IsNumeric(temp.Substring(0,4)))
360docimg_510_360docimg_511_            360docimg_512_{
360docimg_513_                return temp.Substring(4, temp.Length - 4);
360docimg_514_            }
360docimg_515_            return words[0].ToString().Trim();
360docimg_516_        }
360docimg_517_        else
360docimg_518_360docimg_519_        360docimg_520_{
360docimg_521_            return "院校代码格式未知!";
360docimg_522_        }
360docimg_523_   }
360docimg_524_
360docimg_525_360docimg_526_    /**//// 
360docimg_527_    /// 判断是否是数字
360docimg_528_    /// 

360docimg_529_    /// 
360docimg_530_    /// 
360docimg_531_    private static bool IsNumeric(string str)
360docimg_532_360docimg_533_    360docimg_534_{
360docimg_535_        return Regex.IsMatch(str, @"^-?\d+(\.\d)?$");
360docimg_536_}
360docimg_537_
360docimg_538_360docimg_539_/**//// 
360docimg_540_    /// 将院校表输出测试用
360docimg_541_    /// 

360docimg_542_
360docimg_543_    public static string test()
360docimg_544_360docimg_545_    360docimg_546_{
360docimg_547_        StringBuilder strs = new StringBuilder();
360docimg_548_        for (int i = 0; i < 3200; i++)
360docimg_549_360docimg_550_        360docimg_551_{
360docimg_552_            string temp="";
360docimg_553_            try
360docimg_554_360docimg_555_            360docimg_556_{
360docimg_557_                temp = stryxs[i, 3].Trim();
360docimg_558_            }
360docimg_559_            catch
360docimg_560_360docimg_561_            360docimg_562_{ }
360docimg_563_            if (temp.Length != 2)
360docimg_564_360docimg_565_            360docimg_566_{
360docimg_567_                strs.Append(temp + "");
360docimg_568_            }
360docimg_569_        }
360docimg_570_        return strs.ToString();
360docimg_571_
360docimg_572_    }
360docimg_573_}
360docimg_574_

调用方式说明:传入的为一个ARRAYLIST 00001,01,02。。。。

360docimg_575_string zhiyuanhanyi = cache.tranzhiyuan(zyxx);//翻译志愿含义,并放在控件中显示
360docimg_576_        tbxxdm.Text = zyxx[0].ToString();
360docimg_577_        tbzy1.Text = zyxx[1].ToString();
360docimg_578_        tbzy2.Text = zyxx[2].ToString();
360docimg_579_        tbzy3.Text = zyxx[3].ToString();
360docimg_580_        tbzy4.Text = zyxx[4].ToString();
360docimg_581_        tbzy5.Text = zyxx[5].ToString();
360docimg_582_        tbzy6.Text = zyxx[6].ToString();
360docimg_583_ 

终于完了,能够看到这里的都是高手(没有耐心成不了高手)。其实给出这么长的代码段并没有必要,但是如果修改太多又需要时间太多,而且其中有一个修改的地方能够很好的诠释一些不在计划中的意外的处理。所以就直接搬上来了。

原理很简单,实现也不麻烦。

实际效果很好,能够实现每秒百万次级别的查询。而且仅仅加载时访问数据库。我想这可能是这种特殊环境下的最高效率了。

原因很简单:

第一、翻译院校名称根本就没有查找过程,直接下标访问的。

第二、翻译专业名称时哈希表的算法复杂度与直接下标访问差不多(多一次计算hash值)

希望有人能够告诉我有没有更好更快得方法。我很感兴趣。

我这里有负载评测本方法与数据库查询方法比较的测试数据(不使用存储过程的未经优化的数据库同样查询每秒300-1000次,性能保守快2个数量级以上。),改天整理一下再放上来。

反思:

1、   实现的缺陷,静态方法支持多线程吗?我不大好说,但是负载测试时确实4个cpu都跑起来了。盼望高人指点。

2、   该方法占用内存。我们加载了全部3万条记录后,大概占用60m左右内存。当然对于现在我们动辄4g以上的内存不算什么。

3、   不支持sql标准查询。

4、   如果海量数据,内存盛不下如何操作?还有如何支持数据更新?我目前没事的时候在研究这个。希望在用户和数据库中增加一个中间层作缓冲,从而进一步提高数据库吞吐量。作为高考数据量可能没有这个必要,但是对性能的需求是无止境的。

5、   该方法使用起来可靠吗?实际使用时没有出现问题,不知道有没有高手指出可靠性的缺陷。

6、有没有人能够比较与.net新增的缓存的比较。





























posted on 2007-08-19 22:50 黄金年代 阅读(2916) 评论(38)  编辑  收藏 360docimg_584_ 评论
  • #1楼  progame
    Posted @ 2007-08-19 22:59
    .....
    看楼下的怎样说   回复  引用  查看    
  • #2楼  teana [未注册用户]
    Posted @ 2007-08-19 23:18
    文章很不错,不过代码看上去不怎么舒服。。不习惯楼主的命名方式。。   回复  引用  查看    
  • #3楼  麦舒
    Posted @ 2007-08-19 23:19
    哈哈~~~太巧了,我在某个项目里,也是这种法做缓存。出现的一个问题是,长时间运行(例如几天),垃圾回收会把那个Hashtable给回收而导致“空对象引用”异常。   回复  引用  查看    
  • #4楼  Anders Cui
    Posted @ 2007-08-19 23:25
    建议把代码分为几段
    多写点注释
    这样会更容易理解吧   回复  引用  查看    
  • #5楼  楼主 [未注册用户]
    Posted @ 2007-08-20 00:24
    @麦舒
    呵呵,所见略同真高兴,你不写出来。让我琢磨了好几天,还以为自己原创呢。
    我在测试时发现这个问题,不过对于我们的系统,
    第一,运行时间短,2-3天,运行期间一直在执行,所以实际运行期间没有发生丢失表问题.
    第二,有专人监控,发现了,管理员有一个按纽重新实例话调用一次构造函数从新加载即可.
    总之,后果相对于收益在可控范围内。

    我觉得如果要长期运行的话,有2个方案
    1,过一小段时间自动访问一次该表.(不知道可不可以,对垃圾回收没有研究,不熟悉)
    2,是否c#中是否有能够将该对象不进行垃圾回收的语法?
    3,查错控制中,出错,自动重新加载。会产生性能波动

      回复  引用  查看    
  • #6楼  呵呵 [未注册用户]
    Posted @ 2007-08-20 01:04
    我晕,就这种文章也值得首页来说?一般的情况都是数据库数据会经常发生变化的,读写都有可能,同时还要考虑到多线程和并发问题,楼主还说看到最后的算高手,呵呵,你这文章也就给新手看还凑合。   回复  引用  查看    
  • #7楼  可爱的书记
    Posted @ 2007-08-20 01:37
    呵呵,楼上说的有点过了,只要是原创,还是支持的,至于缓存,可以参考微软企业库的做法,另外,代码太长了最好用Region区分一下,看着的确是有点晕,随便看了一下代码,没细看你的流程,跳点小毛病,ISNUMERIC这个方法貌似.net已经提供了嘛,好像叫ISNUMBER,面向对象的思想,分工合作,拿来主义,有了的东西,若非十分必要,还是调人家微软的好   回复  引用  查看    
  • #8楼  cnlamar
    Posted @ 2007-08-20 02:29
    跑个timeten就完事了的东西,搞这些名堂。   回复  引用  查看    
  • #9楼  布尔
    Posted @ 2007-08-20 06:24
    建议:参考MS的CacheBlock或者现在的EnterpriseApplicationBlock中的Cache支持多种缓存策略和完整的缓存生命周期
      回复  引用  查看    
  • #10楼  Michael.li
    Posted @ 2007-08-20 07:42
    建议你泛型实现一个LRU缓存,这样不必担心内存了.   回复  引用  查看    
  • #11楼  sharelai [未注册用户]
    Posted @ 2007-08-20 08:11
    3、 不支持sql标准查询

    不知是否能以DataTable类型保存在内存中   回复  引用  查看    
  • #12楼  henry
    Posted @ 2007-08-20 08:42
    还是那一句,面对这样的代码无语...   回复  引用  查看    
  • #13楼  补丁
    Posted @ 2007-08-20 08:42
    建议去看看关于MemCache及其应用的文章
      回复  引用  查看    
  • #14楼  补丁
    Posted @ 2007-08-20 08:43
    呵呵,搜索了一下lock,竟然没查到....   回复  引用  查看    
  • #15楼  tc [未注册用户]
    Posted @ 2007-08-20 08:53
    文章不咋的 为什么不用内存datatable+视图来查询?   回复  引用  查看    
  • #16楼  zzHanlder [未注册用户]
    Posted @ 2007-08-20 09:05
    oh my god,这是什么狗屁文章,这是再普通不过的技巧性问题了,经常查询的东西当然会放在内存里,不管是hashtable还是 datatable 还是泛型集合,道理都是一样的。更重要的是,楼主的代码写的太离谱了我实在不想看下去。像是刚学一星期的新手。更离谱的是用"0"\"82"这种数字表示执行结果进行返回   回复  引用  查看    
  • #17楼  zz [未注册用户]
    Posted @ 2007-08-20 09:10
    其实并发量不算大,只渎数据的只要缓存加遍厉即可,
    未看出楼主的search有任何空间 时间复杂度的优化

    public DataTable CacheDataTable
    {
    get
    {
    string key="xxx";
    if(HttpRuntime.Cache[key]==null)
    HttpRuntime.Cache[key]=...;
    else
    return (DataTable) HttpRuntime.Cache[key];
    }
    }
    public DataView Search(string where)
    {
    DataView aDataView = this.CacheDataTable.DefaultView;
    aDataView.RowFilter = where;
    return aDataView;
    }

    估计性能都比楼主的好   回复  引用  查看    
  • #18楼  看不惯你 [未注册用户]
    Posted @ 2007-08-20 09:25
    各位达人,你觉得不爽,你可以提意见,也不用诋毁楼主吧
    好像自己牛B得很!   回复  引用  查看    
  • #19楼  1 [未注册用户]
    Posted @ 2007-08-20 09:29
    我倒是真没有看完,太长,也不是高手了。

    光看前面那个哈希表,好像没有同步吧,而且,微软也提供了缓存的企业库,也很好使的。   回复  引用  查看    
  • #20楼  丢人啊 [未注册用户]
    Posted @ 2007-08-20 09:36
    不好“好像自己牛B得很”
    我看过上面的,觉得这东西没有任何技术含量,最一一般不过的东西了。你不觉得大家都这样说这代码就是垃圾就很能说明问题啊?你看看真正的技术含量的文章,哪个说了那些代码是垃圾。
    垃圾就是垃圾,不要不承认啊。   回复  引用  查看    
  • #21楼  亚历山大同志
    Posted @ 2007-08-20 09:43
    指出缺点就行了,不要动辄垃圾垃圾的定性,人身攻击是不好的   回复  引用  查看    
  • #22楼  抱不平 [未注册用户]
    Posted @ 2007-08-20 09:48
    最简单一点的,别人至少发表了自己的看法.不像你们............   回复  引用  查看    
  • #23楼  sharelai [未注册用户]
    Posted @ 2007-08-20 09:56
    呵呵,每个文章的阅读对象不同,难道DUDU有规定一定得是高手写的文章才能放到首页吗?说实在的,真正的高手才不会来玩这小玩意呢。   回复  引用  查看    
  • #24楼  Nineteen@newsmth
    Posted @ 2007-08-20 10:02
    首先要鄙视那些说这个简单那个简单的人。

    其次,对楼主的需求来说,应该首先看看是不是需要缓存。DB服务器是撑不住了?一般情况下,这种情况不太可能存在,类似楼主需求的查询,加点内存一般就OK了。另外,要知道SQLServer缓存机制是非常彪悍的,在应用程序上做缓存,命中率比SQLServer高吗?

    然后,应该根据用例分析要缓存哪些数据。看楼主的文章,缓存的貌似都是静态数据,如果只是为了提供查询功能,还不如加一台DB服务器,专门存储这些数据,命中率肯定比在应用程序服务器上自己做缓存强。就算是没有机器,直接在应用程序服务器上建个实例,没准也比自己搞强。

    解决方案其实很简单,完全不用写程序的说。呵呵   回复  引用  查看    
  • #25楼  金色海洋(jyk)
    Posted @ 2007-08-20 10:10
    我记得 DataView 就带有查询的功能呀。
    把DataTable放在内存里,然后用 DataView  查询 是不是也可以解决呢?   回复  引用  查看    
  • #26楼  zjfeiye [未注册用户]
    Posted @ 2007-08-20 10:25
    为什么不用Singleton模式呢?还人手操作重载数据???太夸张了吧   回复  引用  查看    
  • #27楼  阿毅 [未注册用户]
    Posted @ 2007-08-20 11:01
    不管怎麽說,博主精神可嘉!
    技術高低,精湛與否不是大問題,關鍵還是態度。   回复  引用  查看    
  • #28楼  Gao.Steven
    Posted @ 2007-08-20 11:43
    唉,楼主还是多学习学习再把文章放到首页吧,下面的代码你能看懂就用吧。
    360docimg_585_        private static CacheWrapItem channelIdentitiesCache;
    360docimg_586_        public List AllIdentitiesList
    360docimg_587_360docimg_588_        360docimg_589_{
    360docimg_590_            get 
    360docimg_591_360docimg_592_            360docimg_593_{
    360docimg_594_                if (channelIdentitiesCache == null)
    360docimg_595_360docimg_596_                360docimg_597_{
    360docimg_598_                    lock (lockObject)
    360docimg_599_360docimg_600_                    360docimg_601_{
    360docimg_602_                        if (channelIdentitiesCache == null)
    360docimg_603_360docimg_604_                        360docimg_605_{
    360docimg_606_                            channelIdentitiesCache = new CacheWrapItem();
    360docimg_607_                            
    360docimg_608_                            channelIdentitiesCache.CachedList = LoadAllIdentities();
    360docimg_609_                        }
    360docimg_610_                    }
    360docimg_611_                }
    360docimg_612_
    360docimg_613_                return channelIdentitiesCache.CachedList;
    360docimg_614_            }
    360docimg_615_            set
    360docimg_616_360docimg_617_            360docimg_618_{
    360docimg_619_                AllIdentitiesList = value;    
    360docimg_620_            }
    360docimg_621_        }
    360docimg_622_    public class CacheWrapItem : ICacheSupport
    360docimg_623_360docimg_624_    360docimg_625_{
    360docimg_626_        private string cacheKey;
    360docimg_627_
    360docimg_628_        private List cackedList;
    360docimg_629_
    360docimg_630_        public List CachedList
    360docimg_631_360docimg_632_        360docimg_633_{
    360docimg_634_            get
    360docimg_635_360docimg_636_            360docimg_637_{
    360docimg_638_                return cackedList;
    360docimg_639_            }
    360docimg_640_            set
    360docimg_641_360docimg_642_            360docimg_643_{
    360docimg_644_                cackedList = value;
    360docimg_645_
    360docimg_646_                CacheManager.Instance.AddToCache(this);
    360docimg_647_            }
    360docimg_648_        }
    360docimg_649_
    360docimg_650_        public CacheWrapItem(string cacheKey)
    360docimg_651_360docimg_652_        360docimg_653_{
    360docimg_654_            this.cacheKey = cacheKey;
    360docimg_655_        }
    360docimg_656_
    360docimg_657_        public CacheWrapItem()
    360docimg_658_360docimg_659_        360docimg_660_{
    360docimg_661_            cacheKey = typeof(T).FullName;
    360docimg_662_        }
    360docimg_663_
    360docimg_664_360docimg_665_        ICacheSupport Members#region ICacheSupport Members
    360docimg_666_        public string CacheManagerName
    360docimg_667_360docimg_668_        360docimg_669_{
    360docimg_670_360docimg_671_            get 360docimg_672_{ return String.Empty; }
    360docimg_673_        }
    360docimg_674_
    360docimg_675_        public string CacheKey
    360docimg_676_360docimg_677_        360docimg_678_{
    360docimg_679_360docimg_680_            get 360docimg_681_{ return cacheKey; }
    360docimg_682_        }
    360docimg_683_
    360docimg_684_        public Microsoft.Practices.EnterpriseLibrary.Caching.ICacheItemExpiration[] Dependency
    360docimg_685_360docimg_686_        360docimg_687_{
    360docimg_688_360docimg_689_            get 360docimg_690_{ return null; }
    360docimg_691_        }
    360docimg_692_
    360docimg_693_        public Microsoft.Practices.EnterpriseLibrary.Caching.CacheItemPriority Priority
    360docimg_694_360docimg_695_        360docimg_696_{
    360docimg_697_360docimg_698_            get 360docimg_699_{ return Microsoft.Practices.EnterpriseLibrary.Caching.CacheItemPriority.High; }
    360docimg_700_        }
    360docimg_701_
    360docimg_702_        public string Description
    360docimg_703_360docimg_704_        360docimg_705_{
    360docimg_706_360docimg_707_            get 360docimg_708_{ return "cache for " + typeof(T).FullName; }
    360docimg_709_        }
    360docimg_710_
    360docimg_711_        public object CacheData
    360docimg_712_360docimg_713_        360docimg_714_{
    360docimg_715_360docimg_716_            get 360docimg_717_{ return this.CachedList; }
    360docimg_718_        }
    360docimg_719_
    360docimg_720_        public bool AreEqual(object targetCacheData)
    360docimg_721_360docimg_722_        360docimg_723_{
    360docimg_724_            if (targetCacheData == null || CacheData == null)
    360docimg_725_                return false;
    360docimg_726_
    360docimg_727_            return CacheData.Equals(targetCacheData);
    360docimg_728_        }
    360docimg_729_        #endregion
    360docimg_730_    }
    360docimg_731_  回复  引用  查看    
  • #29楼  cicjs
    Posted @ 2007-08-20 11:56
    其实在微软的sqlhelper里早有了一个比较好的方法.
    你可以第一次查询时放入内存,再查时就不访问数据库了

    private static Hashtable parmCache = Hashtable.Synchronized(new Hashtable());

    ///
    /// add parameter array to the cache
    ///

    /// Key to the parameter cache
    /// an array of SqlParamters to be cached
    public static void CacheParameters(string cacheKey, params SqlParameter[] cmdParms)
    {
    parmCache[cacheKey] = cmdParms;
    }

    ///
    /// Retrieve cached parameters
    ///

    /// key used to lookup parameters
    /// Cached SqlParamters array
    public static SqlParameter[] GetCachedParameters(string cacheKey)
    {
    SqlParameter[] cachedParms = (SqlParameter[])parmCache[cacheKey];

    if (cachedParms == null)
    return null;

    SqlParameter[] clonedParms = new SqlParameter[cachedParms.Length];

    for (int i = 0, j = cachedParms.Length; i < j; i++)
    clonedParms[i] = (SqlParameter)((ICloneable)cachedParms[i]).Clone();

    return clonedParms;
    }

      回复  引用  查看    
  • #30楼  卖糕的
    Posted @ 2007-08-20 11:56
    你们这个系统一点不人性化,既然有了院校名称,专业等等,为什么不搞一个向导让学生一步步选择,这样出错的几率要小得多,反而是要申请人先找到代码再填,再翻译成院校名称,估计是给每个学生一个申请代码表吧。你提到的直接选择的性能问题,两种方式似乎差别不大,甚至我估计向导的方式开销更小,还可以利用ajax或者js把部分负载转移到客户端来,光这几个方面,你们的系统架构就是有问题的。   回复  引用  查看    
  • #31楼  卖糕的
    Posted @ 2007-08-20 12:00
    上面有人提到datatable 的查询,那种查询效率其实效率也挺高,足够用了,
    datatable.select("id=24") ,不知道谁测试一下和楼主的比谁更快。楼主这个系统不需要什么缓存一类的东西,它就是个全局常量。   回复  引用  查看    
  • #32楼  sumtec@beijing [未注册用户]
    Posted @ 2007-08-20 12:01
    @楼主:
    你的任务本来就应该这么做,根本就不应该用数据库。原因很简单:你的数据在一个应用周期内几乎都是保持不变的,初始化之后所有的任务都是读取读取读取。

    至于说要提高性能的话,要看你到底有多少数据,如果运行的时候不到300MB,那就不需要增加缓存结构了,因为所有数据都已经在内存,再增加结构只会导致额外的性能消耗。这个时候基本上你就应该用提高硬件性能的方式来提高了,比如弄个好一点的CPU(充其量就一个月的人工),或者吞吐量还是不行就用多个机器并发(简单点就弄一个4层交换机,或者你自己写一个前置服务器应用,把这些计算丢给后面若干个后台服务器,负载均衡,减少每台机器的负载)。

    前面还有一些朋友说的东西是不正确的:如果正确书写,是不可能发生你需要用的数据被“垃圾回收”了的。
    这有两种可能性:一是你的对象实际上没有被任何其他对象引用,即,本身被设计成自发调用其它对象(但是又没有任何地方引用这个对象本身)。另外一种可能性更大,就是你把对象放到了HttpContext的Cache里面了,这里面参数设置不正确,导致在其他访问占用内存太多的情况下,系统将这部分的缓存给释放了,以用于其他资源。同时,设计也存在不正确的地方。按道理来讲,放到HttpContext的Cache里面不会出现这样的问题的,因为有至少两种办法可以重新加载这些数据。一种是LazyLoading,就是自己写一个属性,内部首先是图读取被缓存的对象,如果有就直接返回,没有就加载-〉放入缓存-〉返回。还有一种是在添加缓存的时候,指派一个缓存被释放时的回调函数,再该回调函数内实现重新加载的功能。

    至于说线程安全,至少要有这三要素才需要考虑是否有线程安全:
    1、多线程
    2、同时发生读写
    3、读写可能不在同一个线程内

    你这个应用肯定是首先加载写入内存,后面就几乎不再做写操作,然后在开始读取。按道理读写都不是同时发生的,所以应该没有线程安全的因素。

    但是后来看你的回复说,有一个人工重新启动的按钮,如果该按钮是要重新加载内容到内存中的,那就有可能发生线程安全问题。根据你的程序任务要求,实际上重新启动的时候,也应该是加载完毕之后再运行(加载到一半的时候运行,结果一定不正确)。所以,你要考虑的不仅仅是对象锁,而应该还有状态锁,建议使用ManualResetEvent对象。

    private ManualResetEvent _initialized = new ...(false);
    private AutoResetEvent _workingFinishedEvent = new ...();
    private volatile int _workingCount = 0;
    private object _workingCountSync = new object();
    private object _initializedSync = new object();

    private void Initialize()
    {
    lock(_initializedSync)
    {
    // 避免新的工作开始
    _initialized.Reset();
    }

    // 等待已有的所有工作结束
    while(true)
    {
    lock(_workingCountSync)
    {
    if(_workingCount<= 0) break;
    }
    _workingFinishedEvent.Wait();
    }

    .... // 初始化

    // 初始化结束,允许工作开始。
    _intialized.Set();
    }

    private void Worker()
    {
    // 由于_workingCount的增加和等待初始化结束应该是一个原子操作
    // 因此需要一个锁去做同步。
    // 但是不能够使用_workingCountSync,因为会造成和_initialized互相死锁
    lock(_initializedSync)
    {
    // 等待初始化结束,避免在初始化过程中运行。
    _initialized.WaitOne();

    // 开始工作之前,首先标记有一个在运作。
    lock(_workingCountSync)
    {
    _workingCount++;
    }
    }

    // 开始工作
    ...

    // 工作完成,释放
    lock(_workingCountSync)
    {
    _workingCount--;
    _workingFinishedEvent.Set();
    }
    }
      回复  引用  查看    
  • #33楼  sumtec@beijing [未注册用户]
    Posted @ 2007-08-20 12:03
    补充一句:
    如果想要在稍微快一点,可以使用Dictionary来代替HashTable

    不过效果可能有限   回复  引用  查看    
  • #34楼  goldentime [未注册用户]
    Posted @ 2007-08-20 13:50
    感谢后来的几个评论,评之有物,让我很有收获,相信其他人看了也有收获。
    十分感谢和欢迎评论
    静态方法默认和Singleton模式有一点类似,不用再搞成Gao.Steven双检查那么麻烦。这个项目不是商业项目。
    另外OO和设计模式正在学习,范型那里确实我还拿不准,不敢随便用,=我在熟悉一下吧,你的代码我会好好借鉴的,多谢了
    sumtec@beijing谢谢了。
    关于性能评测,对于使用datatable的对比评测我这里有,我想既然很多朋友对这种方式有疑问,我想这几天抽出时间写个测试,大家一看就明白了。

    技术并不先进也不需要先进,那样就曲高和寡了,关键是研究和进步的过程。   回复  引用  查看    
  • #35楼  Satan [未注册用户]
    Posted @ 2007-08-20 15:38
    这个路还长啊,朋友慢慢走,再谦虚一点进步会快一些   回复  引用  查看    
  • #36楼  yaolp [未注册用户]
    Posted @ 2007-08-20 17:37
    数据量如果很大呢?
    最好在内存中有索引功能,这样可以更好。
      回复  引用  查看    
  • #37楼  工业酒精
    Posted @ 2007-08-20 21:34
    建议看看MemCached

    给个链接http://jehiah.cz/projects/memcached-win32/

    客户端:https://sourceforge.net/projects/memcacheddotnet/

    分布式的缓存服务   回复  引用  查看    
  • #38楼  corvallis [未注册用户]
    Posted @ 2007-08-22 08:22
    Several things I think might improve:

    1. Reduce the number of tempory objects. String operation is evil for performance:

    <<<<
    zyxx[0].ToString().Trim()
    <<<<
    appears many times. You should cache it. Otherwise, every time you call it. Two temporary objects generated.


    <<<
    string key = zyxx[0].ToString().Trim() + zyxx[i].ToString().Trim() + zyxx[16].ToString().Trim() + zyxx[10].ToString().Trim();
    <<<
    Use stringbuilder to append. Otherwise many copies and tempory objects are generated/

    It might be good if you convert everything to int at first instead of use string later. Int operation is much faster that string. Especially with "+"


    2. Are major code same for all school? If so, you should consider use
    an array instead of hashtable. It will be much faster than hastable. The computation key takes some time.

    Since your are only read, it is safe for multithread. Basically, it should fit your requirement.   回复  引用  查看    
td { font-size: 12px } .commentTextBox { font-family : Verdana; font-size: 13px; } a.blue:visited, a.blue:active, a.blue:link, a.blue:hover { color:blue; } .userData { BEHAVIOR: url(#default#userdata) } 刷新评论列表  
标题请输入标题姓名请输入你的姓名主页Email(只有博主才能看到)邮件地址无效请输入验证码 验证码*  360docimg_732_内容(请不要发表任何与政治相关的内容) 请输入评论内容
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交        博客园首页  小组  博问  闪存  新闻频道  招聘频道  专题   

统计

  • 随笔 - 5
  • 文章 - 1
  • 评论 - 94
  • 引用 - 0
Powered by:
博客园
Copyright ? 黄金年代