摘要: 【博客园Android客户端发布】最开始我是想做一个博客园的客户端自己用用,因为我发现m.cnblogs.com在Android手机上的确体验并不好,而www.cnblogs.com在Android手机上体验更差而且相当耗费流量,于是开始我写了一个简单的读取博客的程序,后来dudu给了我一些接口,在做的时候发现很多地方其实可以做得更好,于是继续边学边做,遇到不懂的就问同事和Google,界面也请了一个同事专门优化了一下。于是利用几个星期的晚上和周末时间,完成了这个版本的开发。阅读全文
posted @ 2011-12-30 08:42 walkingp 阅读(4997) 评论(124) 编辑

我们经常在网站上看到“分享到”这种工具,该服务提供网页地址收藏、分享及发送的按钮工具,网站的浏览者可以方便的分享到微博和SNS网站,网站主只要添加一段JavaScript即可实现。这也就是SMO—社会化媒体优化(Social media optimization),即通过某种社会化传播方式,运用社会化媒体资源:网络社区、主题论坛等社会化网站(如:开心网,人人网,搜狐白社会,新浪微博)将所浏览的信息分享到各大社会化网络媒体从而带回更多的社会化流量,同时获得高效、快速、广泛的公共传播效果的一整套方法。目前国内比较大的SMO服务提供商包括国内最大网站统计工具CNZZ旗下的Jiathis和bShare,前一段时间百度也内测了百度分享。

这些工具使用起来还是比较方便的是,自定义也很方便,但是部分工具会收集用户发布的信息,比如Jiathis,它会将用户分享的数据先记录到自己的服务器上,这样你的信息就轻易被这些网站所收集。那么从这个方面考虑,自己做手做一个分享工具还是很有必要的。

准备工具

首页确定你的网站数据用户可能会有哪些分享去向,目前国内比较大的就是包括新浪微博、QQ空间、开心网、人人网、豆瓣、腾讯微博,其他所占比例较小,可根据需要选择。

之后要准备这些网站的ICON图标,方便用户变辨识。我这里使用的是豆瓣分享里的原图片。

从上到下依次是QQ空间、新浪微博、MSN、人人网、开心网、腾讯微博、搜狐微博,从优化前端性能考虑,全部合并到同一个图片,新加的ICON也可以合并到该图片中。

各网站都提供了通过url参数来提交分享的功能,以新浪微博为例:

http://service.t.sina.com.cn/share/share.php?url=http://www.cnblogs.com/walkingp/&title=自己做手做一个分享工具&pic=http://ww4.sinaimg.cn/bmiddle/703a0732jw1di6m7hqphjj.jpg

这里共三个参数:url代表网页地址,title是要分享的内容,pic参数为图片地址。跳转到该地址。 

由于分享成功后一般会将此网页关闭,所以建议使用window.open打开要分享的网页。

新浪微博就会显示成功了:

代码很简单,如下:

function $(id){return document.getElementById(id);}
var share = {
	url: "http://walkingp.com/",
	title: "【快速息怒的7个小方法】1、降低你的音量和语速;2、闭上你的的眼睛,可以快速浇灭怒火;3、转转你的脖子,放松心情,缓解僵硬;4、拥抱你自己,用双臂交叉紧紧拥抱自己一下;5、闻闻植物,离健康的绿色植物10厘米左右,深呼吸5次;6、发扬阿Q精神,自我解嘲;7、击掌、跺脚,两三分钟即可。",
	pic:"http://ww4.sinaimg.cn/bmiddle/6617a6f4jw1di6x9ghsv7j.jpg"
};
var sina_links = 'http://service.t.sina.com.cn/share/share.php?url='+encodeURI(share.url)+'&title='+encodeURI(share.title)+'&pic='+share.pic;
$('sina').href = sina_links;			
var kaixin001_links = 'http://www.kaixin001.com/repaste/share.php?rurl='+encodeURI(share.url)+'&rtitle='+encodeURI(share.title);
$('kaixin001').href = kaixin001_links;
var renren_links = 'http://share.renren.com/share/buttonshare.do?link='+encodeURI(share.url)+'&title='+encodeURI(share.title);
$('renren').href = renren_links;
var douban_links = 'http://www.douban.com/recommend/?url='+encodeURI(share.url)+'&title='+encodeURI(share.title);
$('douban').href = douban_links;
var tqq_links = 'http://v.t.qq.com/share/share.php?url='+encodeURI(share.url)+'&title='+encodeURI(share.title);
$('tqq').href = tqq_links;	
var qzone_links = 'http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url='+encodeURI(share.url);
$('qzone').href = qzone_links;	
var msn_links='http://profile.live.com/badge?url=' + encodeURI(share.url) + '&screenshot=' + encodeURI(share.pic) + '&title=' +encodeURI(share.title); 
$('msn').href = msn_links;
//var baidu_links = 'http://cang.baidu.com/do/add?it=' + encodeURI(share.title) + '&iu=' + encodeURI(share.url) + '&dc=&fr=ien#nw=1';
//$('baidu').href = tqq_links;//百度是个流氓公司~~~


查看效果

posted @ 2011-06-14 22:51 walkingp 阅读(413) 评论(1) 编辑

个人ASP.NET程序性能优化心得系列:

个人ASP.NET程序性能优化心得(1):数据库篇

个人ASP.NET程序性能优化心得(1):数据库篇(外一篇)

个人ASP.NET程序性能优化心得(2):ASP.NET代码优化

个人ASP.NET程序性能优化心得(3):前端性能优化

------------------------------------------------------------------------------

对于影响ASP.NET程序性能的因素,重要性并非按数据库、ASP.NET代码、前端这样来排序,不同的应用环境可能重要的影响因素都不一样,除去外在硬件、网络等因素,用户在地址栏里输入地址,到整个网页完整显示在用户的浏览器中,这个时间等于所有因素之和。一般情况来讲,在正常的情况下,处理这三种因素都需要不同的角色:DBA负责数据库优化,程序员优化ASP.NET代码、前端工程师优化前端性能,如果分工没有这么明细,就会出现分工出现交叉的情况;最坏的情况是,只有一个人,这个人既是DBA,又是程序员,还要书写全部前端部分。——这可能是件坏事,你在一个抠门的老板手里,老板把你当驴子来用;也会是一件好事,Web开发本身就是一件需要掌握众多知识的工作,你会从中学习到更多的知识,这样对理解网络程序会有更全面的认识(偶就是这么过来的…………TAT)。

一、慎用服务器控件

ASP.NET中的服务器控件一部分是对html控件的扩展,包括Button、Label、Literal、TextBox、DropDownList、CheckBox、RadioButton等,ASP.NET对这些控件进行了封装,同时对控件进行了委托事件的绑定,这样使得ASP.NET程序员就可以像VB、WinFornm那样拖控件的方式来去对界面进行布局和事件的处理。这类控件生成的HTML控件基本不会生成多余的代码,如果对Web标准不是很在意的话,影响不会很大。值得一提的是Label生成的是<span>标签,CheckBox(List)、RadioButton(List)生成的标签往往是这样的:

<span class="checkBox"><input id="chkSel" type="checkbox" name="chkSel"  /><label for="chkSel">选择</label></span>

这种代码从Web标准上来看是要被前端工程师骂的,既无语义性又浪费代码。

另一类服务器控件就是微软引以为傲的大数据展示控件,包括GridView、DataList、DetailsView、ListView、Repeater等,这类控件除了Repeater控件外,其他不推荐使用,这类傻瓜控件会把程序员变得更加傻瓜,更重要的是会影响代码性能,以GridView为例,在绑定DataSource后,所有的数据都会在服务器的内存上一次加载,程序员最爱它的分页功能——瞧,点几下就可以了。但如果数据比较多,比如几百页、几千页,点一次等待的时间可以上一次厕所。Repeater控件推荐使用,因为它不会生成任何代码,仅是将数据源的内容重复显示出来。对于最最常用的分页功能建议是存储过程+Repeater,这样无论是从性能还是从HTML代码整洁度来看都是合适的。

HTML算是Web开发者最最基本的能力,如果不能手写HTML很难胜任Web开发这项工作,使用Visual Studio拖控件得来的代码是非常混乱的,建议还是打开源视图一行一行地写HTML,即使它是服务器控件。

二、ViewState的问题

ViewState本身是一个非常好的想法,微软的创新能力在业界是公认的,起因是这样的:用户向服务器提交数据,比如姓名、年龄、性别等,服务器会对这些数据的合法性进行验证,如果格式不正确或者输入为空这样服务器就会将一些错误信息返回给用户,但问题是用户刚刚提交的表单内容都已经没有了,如果表单中要填写的内容比较多,用户会发疯的。这时ASP.NET就非常好心地把这些提交的内容都还存储在这个网页里,即使数据提交未通过验证,这些数据依然保持在它本来的位置上 ,这个做法非常的讨好用户和开发者,对了,它往往和验证控件一起使用,看似非常人性化,但它却是会生成大量臃肿代码的罪魁祸首。

个人ASP.NET程序性能优化心得(2):asp.net代码优化

这种乱七八糟的代码是对已有的数据进行Base64加密后产生的结果,它会让用户的浏览器在下载你的网页时感觉慢了一些。

我的观点是,如果是互联网类产品(包含网站类)要全局把EnableViewState设为false,可以在Web.Config里设置的。解决方案是使用Javascript进行验证,现在的表单验证库使用起来是很方便的。

三、ADO.NET中的问题

 1、SqlDataReader和DataSet的选择:

SqlDataReader是一种快速向前读取数据的类,它对于从Sql Server数据源检索的数据采用只进数据流的方式,DataSet会将数据库的内容以逻辑数据库的方式存放在服务器的内存中,如果数据量比较大,对服务器的性能影响就比较大了。因此在实际项目中应该合理选择。

2、ExecuteNonQuery和ExecuteScalar的选择:

ExecuteScalar会返回执行结果后的第一行第一列数据,ExecuteNonQuery只会返回影响的行数,在一些情况下我们可能只需要知道是否已经执行成功了,也就是ExecuteNonQuery返回的行数是否大于0;有的时候我们还想知道返回结果的数据,比如在执行INSERT后我们需要使用SCOPE_IDENTITY()来得到返回的主键ID。可以根据实际需要选择使用ExecuteNonQuery和ExecuteScalar。

四、合理使用缓存

使用缓存算是属于重中之重,这个可以这么来解释:当有一百个人都需要到服务器取一个数据,那么程序每次都会到SQL Server数据库中查询取出数据,这样将对性能造成很大的影响。可以将这个数据暂时缓存到服务器上,这样第一个人查询时会将这个数据存到服务器的内存上,其余的用户在缓存时间内都会直接从服务器上取出这个数据,避免多次请求数据源,这样就会提升程序的性能。当然,如果是这个缓存数据是动态变化的话,可以适当的设置缓存时间,或者在更新数据时清除缓存,这种情况比如在文章系统中会遇到。

我这里提供了我个人使用的缓存类:

using System;
using System.Collections;
using System.Web;
using System.Web.Caching;

namespace Cute.Utility
{
    /// <summary>
    /// @ 缓存处理
    /// @ walkingp
    /// @ http://www.cnblogs.com/walkingp
    /// @ http://www.walkingp.com/
    /// @ walkingp@126.com
    /// @2011-05-01
    /// </summary>
    public class CacheHelper
    {
        private Cache webCache = HttpContext.Current.Cache;
        private int defaultCacheTime = 60;
        /// <summary>
        /// 获取某项
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public object Get(string key)
        {
            return this.webCache.Get(key);
        }
        /// <summary>
        /// 判断某项是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Exists(string key)
        {
            return this.webCache.Get(key) != null;
        }
        /// <summary>
        /// 增加某项到缓存中
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool Add(string key, object value)
        {
            return this.Add(key, value, defaultCacheTime);
        }
        /// <summary>
        /// 增加某项到缓存中设置缓存时间为minute分钟
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="minute">要缓存的分钟数</param>
        /// <returns></returns>
        public bool Add(string key, object value, int minute)
        {
            return this.Add(key, value, TimeSpan.FromMinutes(minute));
        }
        /// <summary>
        /// 增加某项到缓存中,并设置某项的缓存时间
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="span">要缓存的时间片</param>
        /// <returns></returns>
        public bool Add(string key, object value, TimeSpan span)
        {
            this.webCache.Insert(key, value, null, DateTime.Now.Add(span), System.Web.Caching.Cache.NoSlidingExpiration);
            return true;
        }
        /// <summary>
        /// 增加某项到缓存中,并设置要过期的缓存时间
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expireTime">缓存的过期时间</param>
        /// <returns></returns>
        public bool Add(string key, object value, DateTime expireTime)
        {
            this.webCache.Insert(key, value, null, expireTime, System.Web.Caching.Cache.NoSlidingExpiration);
            return true;
        }
        /// <summary>
        /// 把某项从缓存中移除
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Remove(string key)
        {
            return this.webCache.Remove(key) != null;
        }
        /// <summary>
        /// 移除所有缓存
        /// </summary>
        public void RemoveAll()
        {
            System.Web.Caching.Cache _cache = HttpRuntime.Cache;
            IDictionaryEnumerator CacheEnum = _cache.GetEnumerator();
            ArrayList al = new ArrayList();
            while (CacheEnum.MoveNext()) {
                al.Add(CacheEnum.Key);
            }
            foreach (string key in al) {
                _cache.Remove(key);
            } 
        }
    }
}

五、其他优化

1、避免拆箱装箱:

我们知道C#中的值类型和引用类型,C#基础数据类型和Struct、Enum属于值类型,引用类型包括类、数组、接口、委托以及字符串(String),不错,字符串也属于引用类型;值类型会以栈(Stack)的形式存储在内存,引用类型会以堆(Heap)的形式存储(这个概念也就是C语言的指针)。值类型转换为引用类型称为装箱,反之称为拆箱。如下:

int i=0;
Syste.Object obj=i;//装箱
int j=(int)obj;//拆箱

装箱拆箱会影响程序性能,我们在程序中应该尽可能避免。因此程序这样写会提升那么一点点的性能:

String info="My Age is" + 24.ToString();

2、使用StringBuilder

如果字符串拼接过多,可能会影响程序性能,这样因为每次++都会创新一个新的字符串,也就是说会在内存中多开辟了一个空间给这个新的字符串。使用StringBuilder可以减少这种消耗。建议在连接字符串过多的情况下使用。

另外记得关于StringBuiler的问题曾经在博客园争吵过一次,如果有问题欢迎大家指出来。

3、避免不必要的ToLower()和ToUpper()

同上一样,都会创建新的字符串对象,如果没有必要尽量减少使用,也可以使用Compare方法来比较字符串。

4、避免不必要的异常捕获

也许你对事物的考虑过于复杂,对于异常的捕获也过于敏感,于是Try...Catch就用得多一点,那么建议还是在该用的时候才用,不该用的时候不用。

5、使用存储过程

频繁的SQL语句在Web服务器传递到数据库服务器上来回传输会影响程序执行的效率,更为有效的方式就是把语句比较复杂和需要比较频繁使用的SQL语句封装成存储过程进行调用。

总结

以上是我个人总结的ASP.NET代码优化的一点方法和心得,这个层面的优化需要更多的是对C#语言、ADO.NET机制和ASP.NET运行机制掌握得更多和更加深入。优化无止境,欢迎完善和补充。

推荐书籍

CLA Via C#》:这本书对c#进行了最深入的讲解。

ASP.NET本质论》:园子里的朋友写的一本书,上次跟dudu借看了(偶经常能看到dudu,羡慕不?),强烈推荐,谁说国内没有好的IT图书?

C#图解教程》:老外也玩标题党,差点毁了一本好书,对于感觉自己对C#似懂非懂的同学一定要去买下这本书去认真读一下,看的时候会有一种“so simple”的感觉。

-------------------------------------------------------

本文同时发表在我的个人主页上:http://www.walkingp.com/

posted @ 2011-06-10 00:47 walkingp 阅读(1793) 评论(13) 编辑

个人ASP.NET程序性能优化心得系列:

个人ASP.NET程序性能优化心得(1):数据库篇

个人ASP.NET程序性能优化心得(1):数据库篇(外一篇)

个人ASP.NET程序性能优化心得(2):ASP.NET代码优化

个人ASP.NET程序性能优化心得(3):前端性能优化

------------------------------------------------------------------------------

在上一篇文章《个人ASP.NET程序性能优化心得(1):数据库篇》里,不少园友对我其中的一些观点提出了一些质疑,这里我认真查阅了一些资料,更正了一些存在错误的地方,另外对一些存在争议的地方加上更详细的说明,并会对一些将于性能方面的问题以实例数据表现出来。

表结构:

News(NewsId,NewsTitle,Content,CateId,CateTitle,PostUserId,PostUserName,AddTime,DateNum,Hits,CommentNum)//Hits:点击数;CommentNum:评论次数

Cate(CateId,CateTitle)
只有主键,未建外键,暂时未建其他索引;其中News表插入了100万条测试数据,Cate共有三条数据。

1、对于外键及相关键是否比严格的范式型效率高

这两张表是显著的胖瘦表查询,这种情况也比较常见,假设使用严格的范式型表结构,也就是News表不存在CateTitle,这里进行连接查询:

select n.*,c.* from news n inner join Cate c on c.CateId=n.CateId and c.CateId=1

执行计划如下:

个人ASP.NET程序性能优化心得(1):数据库篇(外一篇)

另一种情况查询语句如下:

select * from News where CateId=1

执行计划如下:

从执行计划里可以看到第一种情况在两次聚焦索引查找后再进行一次嵌套循环将结果合并,而第二种情况只有一次聚焦索引扫描,因此会在一定程序上减少性能的消耗,下图是SQL Server Profiler的对比:

个人ASP.NET程序性能优化心得(1):数据库篇(外一篇)

这里Reads大致相同,由于语句又进行了一次合并去处,会对CPU有一定的性能消耗。

结论:将外键及相关键合并到主表上会在这种简单查询中提升一定的性能,但是它却是靠数据冗余来达到提升性能的目的,而实际上由于查询条件是在聚集索引上进行的,因此如果是数据量不大的情况可以不必考虑这种情况。

2、DateTime类型问题

上一篇文章我提到了一个观点,DateTime比Int性能要高,这是很武断的结论,而且那个例子里我仅是以排序来去说明。事实上两者类型都是BigInt类型来存储在数据库中的,只不过DateTime占用8个字节,Int占用4个字节,在这种简单的排序中性能基本没有任何差别,DateTime具有强大的时间运算函数,Int类型当然达不到这些功能,这时候使用DateTime是必须的,但如果是类似ORDER BY AddTime DESC这种情况,假设添加时间默认值是GETDATE(),那么这个排序与主键排序应该是一致的,这时建议使用ORDER BY NewsID DESC来进行排序。

SQL语句如下:

select top 5000 * from News order by addtime desc
select top 5000 * from News order by NewsId desc

在SQL Server Profiler中执行结果如下:

因此结论是:DateTime类型还是继续保留使用吧,如果存储的实际上是SmallDateTime建议还是使用smalldatetime来存储数据。

3、SELECT TOP 1问题

搞清楚聚集索引就一定会明白这个问题,主键一定是聚集索引,在聚集索引上进行查询性能其实影响不大,下图分别是带TOP 1和不带TOP 1对查询条件在主键上的分析:

个人ASP.NET程序性能优化心得(1):数据库篇(外一篇)

而在复杂查询条件下我们就需要更多的参数进行查询,这个时候条件列往往假设在没有索引的情况下,就会进行全表扫描。这个时候性能就会受到影响,不使用TOP 1情况下:

select * from News where NewsId=500000 and PostUserName='walkingp'

执行计划如下:

个人ASP.NET程序性能优化心得(1):数据库篇(外一篇)

个人ASP.NET程序性能优化心得(1):数据库篇(外一篇)

使用TOP 1进行条件约束情况下:

select top 1 * from News where NewsId=500000 and PostUserName='walkingp'

可以看到在查询到结果后符合TOP数目即返回了结果,这样就节省了全表扫描的时间:

详情对比如下图:

因此结论是对于查询不全部在(聚集)索引上的查询,如果仅是需要返回某几条,建议采用TOP进行约束,这种性能上的差异在嵌套查询IN等会体现得更加明显。

4、Hits、UpdateTime字段是否应该从表中分开

这类字段属于主表中更新最为频繁的字段,频繁对一张大数据量进行更新数据,显示会造成性能下降,因此在数据量较大时建议将这类数据分离到另一张表中,并对该表中逻辑外键列建立索引以提升性能。

新表结构如下:

News(NewsId,NewsTitle,Content,CateId,CateTitle,PostUserId,PostUserName,AddTime,DateNum,CommentNum)//Hits:点击数;CommentNum:评论次数
Hits(NewsId,Hits)
Cate(CateId,CateTitle)

5、外键问题

外键问题影响性能是不言的事实,我们这里也是主要以性能为最主要考察点,当然具体情况其实更要以具体情况来考虑,外键是维护数据完整性重要的一个手段,在某些应用场合下数据的完整性可能要比性能更加的重要,这种情况下建议还是要建立外键。这种性能上的消耗相对于业务上的重要性要小得多,另外也可以通过其他优化方式来进行性能的优化。

对于互联网应用,数据增长极其快速,另外设计不合理、编码不严谨等方面都会造成运行中不可预料的问题(除了CSDN上那位老湿,谁敢保证0 bug?),相对来讲,使用外键的成本要高很多。当然对于企业网站这种小型系统,建议还是使用外键,最起码可以规范自己的编码规范。

6、Scan都是会进行全表扫描吗?

在物理上,SQL Server使用三种方法来组织其分区中的数据:1、用B树存储有聚集索引的表数据页;2、使用堆来存储没有存储组织的表;3、非聚集索引使用与聚集索引相类似的B树来存储索引结构。针对这三种不同结构,SQL Server使用的数据检索方法也会不一样:

这其实已经是一个相当深奥的知识点了,我本人现在也是一知半角,上图来自《Microsoft SQL Server企业级平台管理实践》一书,结论如下:SCAN并非都会进行全表扫描;在某些情况下,Scan并非比Seek性能差。想要完全搞明白它,需要更清楚SQL Server核心的一些查询机制。

但是值得注意的是,一般情况下我们的查询以简单查询为主,这种情况下要尽量避免Scan,尤其是类似多重嵌套查询这种复杂的场景。

7、见识索引的强大

索引的概念相信不用我来讲了,下面以实例来说明索引的强大作用。

首先是在不建立索引的情况下对CommentNum(评论次数)进行倒序排序:

select top 5000 * from News order by CommentNum desc

然后对CommentNum建立索引:

重新执行SQL语句:

可以看出,在没有建立索引前,时间主要花费在排序上,而建立索引后时间就基本上全部都在查找了。两者对比如下图:

针对查询中可能出现的复杂条件可对其进行分析适当建立索引,一定会让性能大大提升。

最后,最重要的一点是,在实际的数据库应用中,应当灵活运用,不能拿理论去死套,在存在疑惑的地方可以自己去测试一下,多去查阅一些相对权威的资料,这样技术就在不断的慢慢进步了。

针对本文中有任何问题的,欢迎回复讨论。

参考资料:

Microsoft SQL Server企业级平台管理实践

本文同时发在我的个人主页:http://www.walkingp.com/?p=1136

posted @ 2011-05-30 23:37 walkingp 阅读(3681) 评论(43) 编辑

个人ASP.NET程序性能优化心得系列:

个人ASP.NET程序性能优化心得(1):数据库篇

个人ASP.NET程序性能优化心得(1):数据库篇(外一篇)

个人ASP.NET程序性能优化心得(2):ASP.NET代码优化

个人ASP.NET程序性能优化心得(3):前端性能优化

------------------------------------------------------------------------------

前言

相信园子里有不少程序员同学都是在做着xx管理系统这样的中小型项目,这种项目往往是一种工作量的代码,程序员同学就将青春耗费在这样的项目中,不断改变需求,不断地加班赶工,于是就开始怀疑这个行业,对developer充满厌恶,想学新东西,可是周围同事的水平都是差不多;想买书学平时加班根本没有自己的时间。这种状况相信大多数情况都在我们身边发生,我之前就是处于这种状态,使用的是asp.net语言,不过很难界定所做的项目是网站还是软件,因为它很复杂,开发周期和传统软件开发没有什么区别,但它确实是部署在IIS上可以通过浏览器访问。或者又是专门给企业做网站的程序员,一套程序内核不变,只是每个网站换个壳,新闻系统、留言系统、下载系统等等。……为什么我要说这些呢?因为在前面我要说的是,这些并不是真正的互联网公司,这种公司往往追求利益最大化、最快化、最直接化,签单给钱,整个流程程序员的作用几乎可以忽略不计,因为随便招几个毕业生带几一两个月就可以继续把项目做下去。

而在互联网公司里,尤其是中大型网站,性能绝对是跃居非常重要的位置,试想一下日IP过百万上千万,并发成千上万的网站,如果首页每节省1k的流量,那么一天下来就为企业节省相当可观的支出;页面加载每减少1秒的时间,就会减少可观的用户流失。我之前是在上段说讲到的小公司做项目,现在在一家算是中型互联网公司里,日PV几十万的网站,前端、代码、数据库设计的影响对网站的影响是如此之重大,是我之前从没有亲身体会到的。在这近一年的时间里,总结了一些关于asp.net性能优化的一些经验与大家分享,由于个人水平的限制,难免有一些不准确、不完善的地方,欢迎大家拍砖o(∩_∩)o 

这一系列文章我计划花三个大的方向来讲解一些性能方面的东西,包括数据库性能优化、asp.net程序优化和前端优化,这里我将数据库性能优化放在前面,代表了它的重要性。我个人认为影响一个网站性能从程序上来说最主要就是这三个方面,从这三个方面逐一进行优化,将对网站性能的提升会有较大的帮助。

数据库性能优化

一、字段的建立

1、减少跨表查询

需求确定后往往就开始建立数据库,那么建立数据库,对数据库的操作离不开增删改查这些最基本的操作,其中查询应该是频繁的操作,提升查询操作的一个基本的原则是尽量减少跨表查询,也就是JOIN、UNION和子查询等,这种情况往往最为常见,往往是A表中其中一个字段是B表的外键,查询时往往需要同时将A表中的数据全部查询出来,同时再把匹配A表外键的字段查询出来,这样就会大大增加查询的成本。这里我们以文章系统为例,一般表结构如下:

News(NewsId,NewsTitle,Content,CateId,PostUserId,Hits,AddTime)--文章
NewsCate(CateId,CateTitle)--文章分类

这样的表结构肯定就需要使用两次查询或者连接查询等方法来取得两张表的数据。如果统计一个分类下的文章数量,还需要SELECT COUNT(*)来进行News表的全表扫描来完成,这样就会在性能上受到严重影响。

对于这种本身表结构设计欠合理的情况,优化SQL语句基本作用不大,改进方法是将表中需要跨表查询的地方减少到最小,可以将表结构建立如下:

News(NewsId,NewsTitle,Content,CateId,CateName,PostUserId,PostUserName,Hits,AddTime)
NewsCate(CateId,CateTitle,NewsNum)

这里进行了两个地方的改进,一是将CateName同时并入到News表中,这样可以避免跨表连接查询带来的性能损耗(PostUserName与此类似);二是将News表中的总记录数存入NewsCate表中的NewsNum字段中,可以避免SELECT COUNT(*)带来的性能损耗(在大数据量的情况下效果是非常明显的)。由此产生的冗余字段带来的性能影响与之前的性能影响相比较,可以很明显的对比出来。这种优化可以称为以空间换时间。

2、排序问题

另外一点是针对AddTime字段,一般情况下以它排序的情况较多,这种DateTime类型字段在排序时会进行计算,它的排序比Int类型要慢得多,因此还可以考虑新增加一个DateNum(int)字段来储存日期,比如AddTime为2011-05-27,那么插入到DateNum可以是20110527这个数字,这样在排序时可以通过ORDER BY DateNum DESC来减少排序的时间。当然如果你的AddTime默认是getdate(),并且排序只有一个按时间排序的话,可以ORDER BY NewsID DESC来完成。

对于排序,数据库在查询出满足WHERE所有条件的数据后,然后再进行排序,因此如果没有必要,不要使用复杂的排序,可以根据实际情况考虑是否添加OrderNum来减少相关的排序;对于的确需要复杂的排序我们第二点会讲到索引问题来解决。

3、需要外键吗?

如果你的数据库学得不错的话,一定记得数据库范式,满足三级范式才是标准的数据库设计,在实际情况中,绝不可完全照书本来。对于是否需要外键争论一直较多,我的理解是外键是一个约束,它在避免程序插入异常数据会有一定的帮助,异常的数据会导致程序需要异常处理的地方增加,随之代码增加,程序稳定性降低。但在实际的开发中,外键会导致调试程序的复杂,并且会在一定程序上降低SQL执行的效率,因为数据在插入前引擎会对数据的合法性进行校验,这样在一定程序上也会降低数据库的性能,另外对于外键数据在删除情况下查询主表数据可能会发生不可预料的异常。在网站中我的个人建议是不用外键,但为了避免出现DBNull的情况,“是否为空”这个选项在必要时要选择不允许为空。

二、索引的建立

在项目开始前要确定实际项目中可能哪些会频繁进行查询——一般情况下实际情况是程序员和DBA是同一个人,因此假设你已经知道了这些会频繁查询的地方。以一个文章系统为例来说,可能会有时间、分类和关键字这几种查询比较频繁。那么必要时要建立索引,一般情况需要对其各自建立索引,比如实际的表结构如下:

News(NewsId,NewsTitle,Content,CateId,CateName,PostUserId,PostUserName,Hits,AddTime,CommentNum)

假设以分类和时间排序来进行查询十分频繁,那么需要各自建立CateId和AddTime索引;如果这个查询属于复杂查询,如果SQL语句如下:

SELECT * FROM News WHERE CateId=1 ORDER BY AddTime DESC,CommentNum DESC

那么可以建立多个字段的复合索引,这里可以将CateId,AddTime建立复合索引,如有必要可以将CommentNum也包括在内。

对于搜索如果以关键字查询较为频繁,建议在查询字段上建立全文索引,全文索引是SQL Server内置的搜索算法来进行的查询规则,性能比LIKE就好很多。比如查询标题:

SELECT TOP 10 * FROM News WHERE CONTAINS(News,‘walkingp’)

*注意SQL 2000及以后的版本才有全文索引功能。

三、查询的优化

查询是绝大多数SQL语句优化的用武之地,不同的SQL语句可能会让查询时间有着很大的区别;查询优化最核心的内容就是减少scan,尽量做到seek;scan代表全表扫描,seek代表定位到某一行。使用COUNT、NOT、!=、IN、LIKE等都会引起全表扫描,如果这张表数据足够多,那么性能影响是非常大的。更好的方法是避免这种全表扫描,使用最准确的条件限制来缩小数据库扫描的范围,减少SQL执行的时间。

以上表为例,ASP.NET程序最常使用的DAL功能是根据某一编号取数据然后存储到对象中。

SELECT * FROM News WHERE NewsId=@NewsId

这样即使在查询到需要的数据后,它仍会执行剩余数据的查询进行全表扫描,这样就浪费了大量的资源和程序时间。可以使用TOP 1来进行条件限制:

SELECT TOP 1 * FROM News WHERE NewsId=@NewsId

假设有一百万条数据,实际中NewsId=1,那么我们就节约了查询999999条数据的时间。

一般情况下News表会很大,而NewsCate会很少,对于这种对比非常悬殊的两表,如果进行连接查询,将数据小的NewsCate放到JOIN后面,这样可以提升查询性能。

关于查询优化,相关的资料非常丰富,大家可以自己去搜索一下,对于争执比较多的类似IN和EXISTS等问题,可以在实际数据库中测试其性能,然后决定选用哪一种。

四、SQL Profiler的使用

SQL Profiler是最容易被忽视的工具,而这个工具是数据库性能优化一个非常强大的工具,它与Management Studio在SQL Server安装时都会绑定在一起,选择新建跟踪,然后在跟踪属性中选择相应的事件列,一般选择CPU、Reads、Writes、Duration、StartTime、EndTime即可,它们对应了在物理上SQL语句对CPU的占用、对硬盘读写的次数和起止时间,通过它可以很直观地看出影响SQL性能的地方。

个人ASP.NET程序性能优化心得(1):数据库篇

比如我这里测试是对一百万条数据SELECT *和SELECT NewsId的性能测试,可以较直观地看出SELECT * 在CPU损耗、和硬盘读取上会大很多。因此在实际项目中建议“吃多少,拿多少”。

个人ASP.NET程序性能优化心得(1):数据库篇

以上就是关于在数据库性能优化的部分,期待大家有更好的讨论一起来分享,下一篇文章会讲解ASP.NET代码的优化。

这篇文章同时发表在我的个人博客上 http://www.walkingp.com/

(感谢大家的指点和讨论,晚上的时候我会将会更新一下这篇文章的版本,以免误导新手。)

posted @ 2011-05-27 02:35 walkingp 阅读(4921) 评论(74) 编辑
摘要: 关于115网盘115最初的来头是什么呢?相信各位一定了解,当年国内盗版Windows操作系统四大门派,电脑之家、蕃茄花园、雨林木风和深度论坛,其中电脑之家和蕃茄花园实质上是依靠个人的力量在运作,2008年番茄花园事发,很快电脑之家的donghai很快闻风而逃,电脑之家倒闭,深度论坛继而全面转向技术开发,团队相当神秘,论坛管理制度森严,属于国内在操作系统研究较为深入的团队,后来他们推出了Linux Deepin。Ghost XP是他们主要的作品,可以分析当年他们各自作品的特点,Ghost XP在系统优化、个性化设置稍高一筹,蕃茄花园在主题美化方面更加专业,深度论坛主要特色是它的精简版XP,精简后阅读全文
posted @ 2011-05-20 02:18 walkingp 阅读(260) 评论(0) 编辑
摘要: 为何要多服务器存储Web前端优化其中有一个规则就是通过不同的主机并行下载资源(Parallelize downloads across hostnames),IE浏览器对同一个域名的文件,同时只能下载2个文件,这样的话,假设同一个页面站内资源很多,尤其是图片资源过多时,页面加载速度将会受到影响,这时分布式处理文件显得尤为重要,中大型站点往往通过多个资源服务器来存储这些文件,比如博客园本身也是将图片文件保存在imgn.cnblogs.com的服务器上。更大型的网站可能会将一些js、css文件也会从主域中分离处理。跨服务器上传解决方案跨服务器上传其实就是在a站上传文件保存到b站,比较常见的两种方式阅读全文
posted @ 2011-04-29 18:59 walkingp 阅读(510) 评论(2) 编辑
摘要: 关于插入代码博客园吸引我的一个很重要的原因就是因为后台编辑器里的插入代码功能,之前在自己博客里使用的是wordpress,编辑器使用十分不便,只能插入blockquote来当作代码显示,但这样没有代码高亮功能。我们这里讲到的插入代码其实就是在代码外部包裹在<pre></pre>内部,我们知道<pre>标签具有保持原有格式的功能,再使用代码高亮js库SyntaxHighlighter就可以对页面中的代码进行高亮了。我们现在要做的就是在KindEditor加入插入<pre>...</pre>代码的插件。步骤一:外围工作首先找到一个icon阅读全文
posted @ 2011-04-20 13:14 walkingp 阅读(733) 评论(5) 编辑
摘要: 1、显示行号如果数据没有删除的情况下主键与行号是一致的,但在删除某些数据,行号就与主键不一致了,这时需要查询行号就需要用新的方法,在SQL Server2005之前,需要使用临时表,但在SQL Server2005中,使用ROW_NUMBER()非常方便。select row_number() over (order by UseriD) as rowNum,* from UserInfo 查询结果:2、随机查询有的时候我们需要查询出的数据是随机排序的,newid()函数在扫描每条记录时会生成一个随机值,这个随机数没有大小写顺序,因此根据这个数排序,就可以将数据进行随机排序。select *,阅读全文
posted @ 2011-04-09 12:16 walkingp 阅读(1094) 评论(0) 编辑
摘要: 关于Quartz.NETQuartz.NET是一个.net平台下的开源作业调度框架,它最先是从Java平台的Quartz移植过来的。官方网站是http://quartznet.sourceforge.net/,目前最新版本为1.0.3。它可应用于WinForm程序及asp.net程序。作业调度可以理解为计划任务,例如我们工作日内每天9:00打卡,每月10号发工资,类似这种需要自动执行且无须干预的任务,Quartz.NET是最佳应用环境,它既可以满足类似每天打卡的简单应用场景,也能实现复杂作业与触发器对应,又由于它采用正则表达式的方式匹配时间间隔来定义作业调度时间,因此应用复杂时间间隔场景也相对阅读全文
posted @ 2011-04-05 23:28 walkingp 阅读(638) 评论(1) 编辑