[转载]Sql Server查询性能优化之不可小觑的书签查找

mikel阅读(978)

[转载]Sql Server查询性能优化之不可小觑的书签查找 – 懒惰的肥兔 – 博客园.

小小程序猿SQL Server认知的成长

1.没毕业或工作没多久,只知道有数据库、SQL这么个东东,浑然分不清SQL和Sql Server Oracle、MySql的关系,通常认为SQL就是SQL Server

2.工作好几年了,也写过不少SQl,却浑然不知道索引为何物,只知道数据库有索引这么个东西,分不清聚集索引和非聚集索引,只知道查询慢了建个索引查询就快了,到头来索引也建了不少,查询也确实快了,偶然问之:汝建之索引为何类型?答曰:。。。

3.终于受到刺激开始奋发图强,买书,gg查资料终于知道原来索引分为聚集索引和非聚集索引,顿时泪流满面,呜呼哀哉,吾终知索引为何物也。

4.再进一步学习之亦知聚集索引为物理索引、非聚集索引为逻辑索引,聚集索引为数据的存储顺序,非聚集索引是逻辑索引既对聚集索引的索引

5.再往后学会了查看执行计划,通过查询计划终于对查询过程有了大概了解,也知道了聚集索引扫描和表扫描没有用到索引,看到聚集索引、索引查找高兴 的眉飞色舞,看到RID、键查找暗自窃喜,瞧,键查找肯定就是关键字查找了,用着索引呢,效率肯定高,于是每次写完sql都要观看下其执行计划,表扫描的 干货统统不要,俺只要索引查找、键查找。

6.自信满满的过着悠哉的小日子,突然有一天迷茫了,为嘛俺明明在这个字段上建立了索引,它她妹的老给我显示聚集索引扫描的,难道查询优化器发烧了,实际执行下,发现实际的执行计划还是表扫描,这下彻底迷惑了,兴许是查询优化器显示的有问题吧。

7.继续深入学习终发现,数据库这潭水太深了,了解的太片面了,想想从猿到人的进化过程吧,恩恩,现在就是一个灵智初开的程序猿,向着伟大的程序员奋勇前进

恩恩,跑题了,进入我们的主题:数据库的书签查找

认识书签查找

书签查找这个词可能对于很多开发人员比较陌生,很多人都遇到过,但是却没引起足够的重视以至于一直都忽略它的存在了

定义:当查询优化器使用非聚集索引进行查找时,如果所选择的列或查询条件中的列只部分包含在使用的非聚集索引和聚集索引中时,就需要一个查找 (lookup)来检索其他字段来满足请求。对一个有聚簇索引的表来说是一个键查找(key lookup),对一个堆表来说是一个RID查找(RID lookup),这种查找即是——书签查找(bookmark lookup)。简单的说就是当你使用的sql查询条件和select返回的列没有完全包含在索引列中时就会发生书签查找。

书签查找的重要性

1.书签查找发生条件:只有在使用非聚集索引进行数据查找时才会产生书签查找,聚集索引查找、聚集索引扫描和表扫描不会发生书签查找。

2.书签查找发生频率:书签查找发生频率非常高,甚至可以说大部分查询都会发生书签查找,我们知道一个表只能建立一个聚集索引,所以我们的查询更多的会使用非聚集索引,非聚集索引不可能覆盖所有的查询列,所以会经常性产生书签查找。

3.书签查找的影响:导致索引失效的主要原因之一。书签查找 根据索引的行定位器从表中读取数据,除了索引页面的逻辑读取外,还需要数据页面的逻辑读取,如果查询的结果返回数据量较大会导致大量的逻辑读或者索引失 效,这也是为什么我们查看查询计划时有时明明在查询列上建立了索引,查询优化器却依然使用表扫描的原因。

4.如何消除书签查找:
1.使用聚集索引查找,聚集索引的叶子节点就是数据行本身,因此不存在书签查找
2.聚集索引扫描、表扫描,说白了就是啥索引都不建直接全表扫描,肯定不会发生书签查找,不过效率吗。。。
3.使用非聚集索引的键列包含所有查询或返回的列,这个不靠谱,非聚集索引最大键列数为16,最大索引键大小为900字节,就算你有勇气在16列上全部 建立索引,那如果表的列数超过16列了你咋办,还有索引列长度之和不能超过900字节,所以不可能让非聚集索引包含所有列,而且索引涉及到得列越多维护索 引的开销也就越大。
4.使用include,嗯,这是个好东东,索引做到只能包含16列且不能超过900字节,include不受此限制,最 多可以包含1023列怎么也够你用了,而且对长度也没有限制你可以随心所欲的包含nvarchar(max)这也的列,当然了text之流就不要考虑了
5.其它,其它还有神马呢,这个我也不知道了,估计应该、可能、大概木有了吧,若有知道的兄弟可以告诉我声哈

可能上面说的有点抽象,我们开看看具体的例子

一般我们的数据库都会建上聚集索引(一般大家喜欢建表时有用没有肯定先来个自增ID列当主键,这个主键SQL Server默认就给你创建成聚集索引了),故我们这里都假设表上已经建立了聚集索引,不考虑堆表(就是没有聚集索引的表)

1.首先创建表Users、插入一些示例数据并建立聚集索引PK_UserID 非聚集索引IX_UserName

--懒得的肥兔 http://http://www.cnblogs.com/lzrabbit/
--创建表Users
Create table Users
(
UserID int identity,
UserName nvarchar(50),
Age int,
Gender bit,
CreateTime datetime
)
--在UserID列创建聚集索引PK_UserID
create unique clustered index PK_UserID on Users(UserID)
--在UserName创建非聚集索引IX_UserName
create index IX_UserName on Users(UserName)

--插入示例数据
insert into Users(UserName,Age,Gender,CreateTime)
select N'Bob',20,1,'2012-5-1'
union all
select N'Jack',23,0,'2012-5-2'
union all
select N'Robert',28,1,'2012-5-3'
union all
select N'Janet',40,0,'2012-5-9'
union all
select N'Michael',22,1,'2012-5-2'
union all
select N'Laura',16,1,'2012-5-1'
union all
select N'Anne',36,1,'2012-5-7'

.执行以下查询并查看查询计划,可以看到第一个SQL执行聚集索引扫描,第二个SQL执行聚集索引查找都没有使用到书签查找

--懒得的肥兔 http://http://www.cnblogs.com/lzrabbit/
select * from Users select * from Users where UserID=4

3.比较以下几个查询SQL,观察其查询计划,思考下为什么会发生书签查找:

--查询1:使用索引IX_UserName,选择列UserID,UserName,查询条件列为UserName
select UserID,UserName from Users with(index(IX_UserName)) where UserName='Robert'

--查询2:使用索引IX_UserName,选择列UserID,UserName,Age,查询条件列为UserName
select UserID,UserName,Age from Users with(index(IX_UserName)) where UserName='Robert'

--查询3:使用索引IX_UserName,选择列UserID,UserName,查询条件列为UserName,Age
select UserID,UserName from Users with(index(IX_UserName)) where UserName='Robert' and Age=28

--查询4:使用索引IX_UserName,选择列所有列,查询条件列为UserName
select * from Users with(index(IX_UserName)) where UserName='Robert'

分析:

查询1:选择的列UserID是聚集索引PK_UserID的键列,UserName为索引IX_UserName的键列,查询条件列为 UserName,由于索引IX_UserName包含了查询用到得所有列,所以仅需要扫描索引即可返回查询结果,不需要再额外的去数据页获取数据,故不 会发生书签查找

查询2:选择列Age不包含在聚集索引PK_UserID和IX_UserName中,故需要进行额外的书签查找

查询3:查询条件Age列不包含在聚集索引PK_UserID和IX_UserName中,故需要进行额外的书签查找

查询4:包含了所有的列,Age、Gender、CreateTime列均不在聚集索引PK_UserID和IX_UserName中,所以需要书签查找以定位数据

这里解释下:查询中用到的列无论是一列还是多列不在索引覆盖范围查询开销基本上一样,每条记录均只需要一次书签查找开销,不会说因为查询3只有一个Age列,查询4有Age、Gender、CreateTime 3列不在索引覆盖范围而导致额外的开销

书签查找是怎么发生的

和许多人一样看到大神们画的二叉树索引结构图就脑袋大,看得云里雾里,所以这里我们以表Users为例来说聚集索引(PK_UserID)和非聚集索引(IX_UserName)的结构可以简单的表示为下图

首先我们来看聚集索引PK_UserID,对于聚集索引来说数据行就是其叶子节点,故当执行聚集索引查找时找到了具体的键值后就可以直接去叶子节点 获取所有需要的数据不需要进行额外的逻辑读,比如select * from Users where UserID=2,根据值2在索引PK_UserID中找到UserID为2的值后去叶子节点就可以拿到所需数据,然后返回查询结果

然后看非聚集索引IX_UserName,上面我们说过非聚集索引覆盖的列为非聚集索引的键列+包含的列+聚集索引的键列,对于 IX_UserName来说就是如图中所示键列UserName保存在索引的二叉树节点中,聚集索引的列包含在其叶子节点中,这也就形成了对列 (UserName,UserID)的覆盖,对于查询1(select UserID,UserName from Users with(index(IX_UserName)) where UserName=’Robert’)来说查询只用到了UserName,UserID列,这样只需要扫描索引IX_UserName即可拿到所有数据然 后进行结果返回,而对于查询2、查询3来说由于需要用到Age列,而索引IX_UserName中并没有包含Age列,这时就需要个书签查找 (bookmark lookup)根据叶节点中的RowID去定位到具体的数据行获取Age列值,对于示例查询来说先根据索引IX_UserName定位Robert所在 行,然后根据RowID=3去数据表里获取Age值,然后完成查询,对于查询4来说需要更多的列(Age,Gender,CreateTime),同样定 位到Robert所在行RowID=3,去数据表一次性拿到Age,Gender,CreateTime数据然后返回,这样就形成了书签查找(查询计划中 显示为键查找RID查找)

书签查找的对查询性能的影响

 

--这是我们现在使用的索引
create index IX_UserName on Users(UserName)

 

打开IO统计并执行下面两个查询

--set statistics io on
select * from Users where UserName like 'ja%'

select * from Users with(index(IX_UserName))  where UserName like 'ja%'

 

 

两个查询都返回2条数据,聚集索引扫描仅仅2次逻辑读,使用索引IX_UserName却达到了6次的逻辑读

我们示例的数据量比较小,所以感受不明显,不过我们却也看到了我们在UserName列上市建立了索引 IX_UserName,默认情况下查询优化器并没有使用我们的索引,而是选择了表扫描,仅仅需要2次逻辑读就拿到了我们需要的数据,在我们使用索引提示 强制查询优化器使用索引IX_UserName后,同样也是返回2条数据,逻辑读缺达到了惊人的6次,看查询计划使用IX_UserName后发生了书签 查找,而这个开销主要是有书签查找造成的,而且随着我们返回数据量的增加,由书签查找导致的逻辑读将会成直线上升,造成的结果就是查询开销比进行全表扫描 还要大的多,最终导致索引失效

使用覆盖索引避免书签查找

覆盖索引是指非聚集索引上的列(键列+包含列) + 聚集索引的键列包含了查询中用到的所有列,对于索引IX_UserName来说索引覆盖列就是(UserName,UserID)。若查询中只用到了索引 所覆盖的列,那么只需扫描索引即可完成查询,若用到了索引覆盖范围以外的列就需要书签查找来获取数据,当这种查找发生次较多时就会导致索引失效从而导致表 扫描,因为查询优化器是基于开销的优化器,当其发现使用非聚集索引引发的书签查找开销比表扫描开销还大时就会放弃使用索引,转向表扫描。

1.在UserName,Age列上重建索引IX_UserName,这时对于索引IX_UserName来说覆盖列变为(UserName,Age,UserID),再次执行上面的查询SQL可以发现查询计划已经发生变化

--懒得的肥兔 http://http://www.cnblogs.com/lzrabbit/
drop index IX_UserName on Users create index IX_UserName on Users(UserName,Age)

我们可以看到查询2、查询3的书签查找已经消失,因为索引IX_UserName包含了查询中用到得所有列(UserID,UserName,Age),查询4因为选择返回所有列我们的索引没有包含Gender和CreateTime列,故还是会进行书签查找

这时索引IX_UserName结构表示如下


可见对于查询2、查询3仅仅通过索引IX_UserName既可以拿到需要的列UserName,Age,UserID,而对于查询4索引并没有全部覆盖还是需要进行书签查找

2.继续修改我们的索引IX_UserName,使用include包含非键列(键列就是索引上的列,非键列就是索引之外的列,对于include来说就是存放于非聚集索引叶子节点上的列,聚集索引的列也放在非聚集索引的叶子节点上)

--懒得的肥兔 http://http://www.cnblogs.com/lzrabbit/
drop index IX_UserName on Users create index IX_UserName on Users(UserName,Age) include(Gender,CreateTime)

可以看到我们修改索引使用include包含了Gender,CreateTime后,索引IX_UserName达到了对数据表Users的所有列的全覆盖,这时候毫无疑问的查询2、查询3没有出现书签查找,查询4的书签查找也消失了。

此时索引IX_UserName 结构如下

索引IX_UserName已经达到了对Users表的全覆盖,对于我们的查询2、查询3、查询4来说,仅通过索引IX_UserName即可完成查询,不需要进行书签查找。

这时我们再来看一下这两个查询的开销及查询计划,可以看到不需要我们进行索引提示,查询优化器已经自动选择了我们的索引,逻辑读也降至了2次

select * from Users where UserName like 'ja%'

select * from Users with(index(IX_UserName))  where UserName like 'ja%'

关于Include请参考 SQL Server 索引中include的魅力(具有包含性列的索引)

这里说明下书签查找对查询性能有着较大的影响并且基本上不可避免,这并不意味着书签查找就是洪水猛兽,原来我们不是也不知道啥叫书签查找么,查 询性能一样也不差,是吧,呵呵。书签查找也说明了为什么我们不推荐写sql时使用select *,也解释了为什么有时候我们的索引会失效,同时可以作为优化查询性能考虑的一个方面,在设计表和索引时尽量规避书签查找带来的负面影响,比如非聚集索引 尽量选择高选择性的列即返回尽量少的行,需要大批量数据查询时尽量使用聚集索引等。

本文中为了便于演示仅仅使用了有几条数据的表,而且查询中为了使用索引都用了索引提示,实际开发中请不要使用索引提示,查询优化器大多数情况下 会为我们生成最优(最优不代表开销最小,只要开销足够小即认为最优)的执行计划,索引结构里面用到得RowID也仅仅是为了演示虚构出来的,我们只要认为 它是对于数据行的一个标识位就行了。

此文旨在让我们认识书签查找并意识到书签查找的意义,从而对于索引失效原因有清晰的认识,更好的理解查询计划。

 

[转载]php实现长连接的方法和注意事项

mikel阅读(880)

[转载]php实现长连接的方法和注意事项 – brainy – 博客园.

php可以通过set_time_limit(0);来取消php脚步超时限制,从而达到长连接的效果。例子代码如下:

程序代码

<?php

echo “每隔3秒输出一次<br />”;

set_time_limit(0); //保证php程序运行不超时退出

while(1) {

   echo date(“H:i:s”).”<br />”;

   ob_flush();

   flush(); //刷新并输出PHP缓冲数据

   sleep(3); //延迟3

}

?>

示例代码2:

程序代码

set_time_limit(0);

header(“Connection:Keep-Alive”);

header(“Proxy-Connection:Keep-Alive”);

for($i=0;$i<60;$i++) {

   print ‘text’.$i.'<br>’;

   ob_flush();

   flush();

   sleep(1);

   clearstatcache();

}

这里调用了ob_flush();   flush();来强制输出数据到缓冲区,这样就能在脚步返回之前及时返回数据到浏览器。另外不冲下flush和ob_flush的使用上有一些特别容易犯错的地方,造成无法刷新输出缓冲。

一. flushob_flush的正确顺序,正确应是,先ob_flushflush,如下:

ob_flush();

flush();

如果Web服务器的操作系统是windows系统,那顺序颠倒或者不使用ob_flush()也不会出现问题。但是在Linux系统上就无法刷新输出缓冲。

二. 使用ob_flush()前,确保前面的内容大小足够4069字符。

一些Web服务器的output_buffering默认是4069字符或者更大,即输出内容必须达到4069字符服务器才会flush刷新输出缓冲,为了确保flush有效,最好在ob_flush()函数前有以下语句:

print str_repeat(” “, 4096);

以确保到达output_buffering值。

for ($i=10; $i>0; $i–)

{

echo $i.'<br />’;

ob_flush();

flush();

sleep(1);

}

ob_end_flush();

[转载]上周热点回顾(5.14-5.20) - 博客园

mikel阅读(781)

[转载]上周热点回顾(5.14-5.20) – 博客园团队 – 博客园.

热点随笔:

· 软件开发比较好的公司,.NET开发1W到1.5W需要具备的技能webstudy
· .net 开发人员的瓶颈和职业发展听说读写
· 与老大的交谈——估算项目时间川山甲
· Ajax与JSON的一些总结JK_Rush
· 兼职开发悟出的点点滴滴ForEvErNoMe
· 使用分页方式读取超大文件的性能试验野比
· Android, WindowsPhone7, IOS ,vc2010平台40多套图片滤镜开源代震军
· 开发职位经典面试题zhangjing230
· 关于找工作的一些个人看法Shadow.R
· MS SQL,单一语句VS复杂语句 谁快活跃的毛虫
· 软件随想–写牛B的代码啊汉
· 小女也爱葵花宝典—读懂编译原理(1)百合莲子

热点新闻:

· 一名小米手机用户:我为毛不用小米手机了!
· 8家正在赔钱的科技巨头
· 亚马逊Kindle Phone已在深圳代工厂生产
· 乌云爆料:网路岗可截获QQ密码和聊天记录
· 三种东西永远不要放到数据库里
· 猜猜这是什么编程语言写的?
· 谈谈对程序员的培养
· 腾讯微信技术总监周颢:一亿用户增长背后的架构秘密
· 省时利器:代码美化与格式化工具
· 《神秘的程序员们》漫画(36、37)
· 史上最糟糕的两个变量名
· 为什么人们喜欢翻页效果?

知识库热点文章:

· 我是工程师,不是编译器
· 超载的程序员
· QQ空间技术架构之深刻揭秘
· 什么是好代码?
· 软件设计的真谛
· 了解Instagram背后的技术
· 不懂技术的人不要对懂技术的人说这很容易实现
· 12 个有效的提高编程技能的方法
· 简单之美——系统设计黄金法则
· 程序员也要养生
· 十条不错的编程观点
· 腾讯微信技术总监周颢:一亿用户增长背后的架构秘密

[转载]游览器中javascript的执行过程

mikel阅读(1032)

[转载]游览器中javascript的执行过程 – lengyuhong – 博客园.

在讲这个问题之前,先来补充几个知识点,如果对此已经比较了解可以直接跳过

1. 大多数游览器的组件构成如图

在最底层的三个组件分别是网络,UI后端和js解释器。作用如下:

(1)网络- 用来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作

(2)UI 后端- 用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口

(3)JS解释器- 用来解释执行JS代码

ps:上图和知识点主要来自《HOW BROWSERS WORK: BEHIND THE SCENES OF MODERN WEB BROWSERS》 想深入了解的同学可以重点看下。

 

2. 大多数游览器(比如chrome)让一个单线程共用于执行javascrip和更新用户界面。这个线程通常被称为“游览器UI线程”, 每个时刻只能执行其中一种操作,这意味着当JavaScript代码正在执行时用户界面无法响应输入,反之亦然。这样做是因为JavaScript代码的 作用就是操作DOM更新用户界面,用同一个线程来做负责这两件事情可以更高效

 

3. 游览器UI线程的工作基于一个简单的队列系统,任务会被保存到队列中直到进程空闲。一旦空闲,队列中的下一个任务就被重新提取出来并运行。这些任务要么是运行JavaScript代码,要么执行UI更新,包括重绘和重排。

 

4. 重点再强调下,javascript是单线程运行,千万别被setTimeout()和setInterVal()这种函数迷惑而误以为它是多线程。

 

ok,基础点讲解完毕,让我们进入正题,来讲解在游览器中javascript的执行过程。

一、原理

一般而言,<script>标签每次出现都会霸道地让页面等待脚本的解析和执行,无论当前的Javascript是内嵌的还是包含了外链文 件,页面的下载和渲染都必须停下来等待脚本执行完成。这在页面的生存周期中是必要的,因为脚本执行过程中可能修改页面内容,一个典型的例子就是在页面中使 用document.write()。

当javascript代码是内嵌在html里面时,这点还是比较容易理解,但当javascript是外链文件时稍微有点负载,因为存在一个加载过程,而且游览器加载好这个js文件之后往往还对其缓存。

首先,我们用以下这个例子来说明下缓存问题

复制代码
<html>
<head> 
  <script type='text/javascript' src='js/f2.js'></script>
</head>
<body>
</body>
</html>
复制代码

第一次打开页面时:

第二次打开页面时:

从上例中可以明显看出,像chrome之类的高版本游览器会对js文件进行缓存,作用是不言而喻,减少网络请求。

 

其次,第二个问题,当一个javascript文件被加载时是否会阻塞其他javascript文件或者其他文件的加载。《高性能Javascript》 一书中对这个问题做了较好的解答:各种游览器的低版本的处理是当一个javascript文件在加载时,会同时阻塞页面其他文件的加载(包括其他 javascript文件),但IE8,Firfox3.5,Safari 4和Chrome 2都允许并行下载javascript文件,但遗憾的是,javascript下载过程仍然会组舍其他资源的下载,比如图片。尽管javascript脚 本的下载过程不会相互影响,但页面仍然必须等待所有的javascript代码下载并执行完成才能继续。

这里说句题外话:游览器对同一域名下的并发链接数也是有限制的,其他一些参数如下:

二、技巧

1. 脚本位置

由于脚本会阻塞页面其他资源的下载,因此推荐将所有的<script>标签放到<body>标签的底部,已尽量减少对整个页面下载的影响。

2. 将能合并的js文件合并

3. 无阻塞脚本

现在比较常用的方法就是动态加载执行脚本。你的原理是通过DOM,你几乎可以用Javascript动态创建HTML中的所有内容,其根本在 于,<script>标签与页面中其他元素并无差异:都能通过DOM引用,都能在文档中移动,删除和创建。文件在改 该<script>元素被添加到页面时开始现在,它不会阻止其他文件下载,只在执行阶段阻塞渲染。特别强调:《高性能javascript》 一文中说“这种技术的重点在于:无论何时启动下载,文件的下载和执行都不会阻塞页面其他进程”,这并不是说它在执行不会阻塞其他javascript代 码,而是要强调不会阻塞其他资源的下载等其他任务。

具体的代码如下:

复制代码
function loadScript(url){ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = url; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga); }
复制代码

4. 神奇的setTimeout()

这里我不过多的将setTimeout()的原理,有兴趣的读者可以具体去看《高性能javascript》的第六章。我重点强调 下,setTimeout的第二个参数并不是一个精确的时间,二是必须在javascript线程空闲时才能运行。利用这个特性,如下代码简单可以实现等 待其他js代码执行完毕后再执行function里面的代码。

setTimeout(function(){ // do some before other javascripe codes had processed
}, 25)

但在function里面不要使用document.write()方法,因为执行setTimeout里面函数时往往已经到了页面onload之后,此 时再执行 document.write 将导致当前页面的内容被清空,因为它会自动触发 document.open 方法。

参考文章:

《高性能Javascript》

HOW BROWSERS WORK: BEHIND THE SCENES OF MODERN WEB BROWSERS

Google Chrome源码剖析【一】:多线程模型

javascript异步加载详解

 

[转载]11 个最常用的 AJAX 开发框架汇总

mikel阅读(1172)

[转载]11 个最常用的 AJAX 开发框架汇总_IT新闻_博客园.

  AJAX(Asynchronous JavaScript and XML,异步 JavaScript 和 XML),是创建交互式 Web 应用的主要开发技术。互联网中也有大量的关于 AJAX 的框架,本文汇总了最常用的 11 个框架。

1.   jQuery

JQuery 是一个轻量级的 Javascript 库,兼容 CSS3,还兼容各种浏览器。JQuery 使用户能更方便地处理 HTML documents、events、实现动画效果,并且方便地为网站提供 AJAX 交互。

2.   MooTools

MooTools 是一个简洁、模块化、面向对象的 JavaScript 库。它能够帮助你更快、更简单地编写可扩展和兼容性强的 JavaScript 代码。Mootools 跟 prototypejs 相类似,语法几乎一样。但它提供的功能要比 prototypejs 多,而且更强大。比如增加了动画特效、拖放操作等等。

3.   Prototype

Prototype 是 Sam Stephenson 写的一个非常优雅的 JavaScript 基础类库,对 JavaScript 做了大量的扩展,旨在简化动态 Web 应用程序的开发。Prototype 很好的支持 AJAX,国内外有多个基于此类库实现的效果库,也做得很棒。

4.   ASP.NET AJAX

ASP.NET AJAX 是一个完整的开发框架,容易与现有的 ASP.NET 程序相结合,通常实现复杂的功能只需要在页面中拖几个控件,而不必了解深层次的工作原理,除此之外服务器端编程的 ASP.NET AJAX Control Toolkit 含有大量的独立 AJAX 控件和对 ASP.NET 原有服务器控件的 AJAX 功能扩展,实现起来也非常简单。

5.   Apache Wicket

Apache Wicket 是一个针对 Java 的 Web 开发框架,与 Struts、WebWork、Tapestry 类似。其特点在于对 HTML 和代码进行了有效的分离(有利于程序员和美工的合作),基于规则的配置(减少了 XML 等配置文件的使用),学习曲线较低(开发方式与C/S相似),更加易于调试(错误类型比较少,而且容易定位)。

6.   Dojo Tookit

Dojo 是一个强大的面向对象的 JavaScript 框架。主要由三大模块组成:Core、Dijit、DojoX。Core 提供 AJAX、events、packaging、CSS-based querying、animations、JSON 等相关操作 API;Dijit 是一个可更换皮肤、基于模板的 WEB UI 控件库;DojoX 包括一些创新/新颖的代码和控件:DateGrid、charts、离线应用、跨浏览器矢量绘图等。

7.   DWR(Direct Web Remoting)

DWR 是一个 Java 库,可以帮助开发者轻松实现服务器端的 Java 和客户端的 JavaScript 相互操作、彼此调用。

8.   Spry Framework

Adobe Spry 是一个面向 Web 设计人员而不是开发人员的 AJAX 框架,它使得设计人员不需要了解复杂的 AJAX 技巧也能在一个 HTML 页面中创建丰富体验成为了可能。

9.   YUI (Yahoo User Interface) Library

YUI(Yahoo User Interface),是由雅虎开发的一个开源的 JavaScript 函数库,它采用了 AJAX、 DHTML 和 DOM 等诸多技术。YUI 包含多种程序工具、函数库以及网页操作界面,能够更快速地开发互动性高且丰富的网站应用程序。

10.   Google Web Toolkit

Google Web Toolkit (GWT) 是一个开源的 Java 开发框架,可以使不会使用第二种浏览器语言的开发人员编写 Google 地图和 Gmail 等 AJAX 应用程序时更加轻松。

11.   ZK Framework

ZK 是一套开源、兼容 XUL/HTML 标准、使用 Java 编写的 AJAX 框架,使用该框架,你无需编写 JavaScript 代码就可以创建一个支持 Web 2.0 的富互联网应用程序(RIA)。其最大的好处是,在设计 AJAX 网络应用程序时,轻松简便的操作就像设计桌面程序一样。ZK 包含了一个以 AJAX 为基础、事件驱动(event-driven)、高互动性的引擎,同时还提供了多样丰富、可重复使用的 XUL 与 HTML 组件,以及以 XML 为基础的使用接口设计语言 ZK User-interfaces Markup Language(ZUML)。

原文:11 Commonly used AJAX Frameworks

[转载]Asp.Net MVC路由调试的好帮手RouteDebugger

mikel阅读(1111)

[转载]Asp.Net MVC路由调试的好帮手RouteDebugger – Lyon.L – 博客园.

RouteDebugger是什么?

ASP.NET MVC程序中,路由(Route)是一个非常核心的概念,可以说是MVC程序的入口,因为每一个Http请求都要经过路由计算,然后匹配到相应的 Controller和Action。通常我们的路由都会注册在Global.asax.cs文件中的RegisterRoutes方法中,路由会从上往 下依次匹配,因此自定义的(优先级高)的路由需要放在默认(通用)路由的前面。但是,如何确保所有的路由都是正确的,或者是没有重复的 呢?RouteDebugger就是这样一个分析工具。

安装

可以使用NuGet很方便的安装RouteDebugger,在项目上面右键-“Manage NuGet Packages”-“Online”输入”RouteDebugger”然后”Install”,或者在”Package Manager Console”中输入:”Install-Package routedebugger”安装即可。由于NuGet有时会莫名其妙被墙,我上传了一个压缩文件:RouteDebugger 2.1.3

nuget-route-debugger

注意:RouteDebugger 2.x版本对应.NET4.0和MVC3,旧版本请参考Phil Haack的ASP.NET Routing Debugger

使用

当你成功安装后,可以看到项目引用了”RouteDebugger”,然后?然后按F5运行程序你就可以看到效果了。是的,你甚至都不需要配置任何 一行代码!这是因为.NET4.0新增的程序集Microsoft.Web.Infrastructure允许动态注册 HttpModule,RouteDebugger将格式化的路由调试信息追加(append)到每一个request里。对于.NET3.5和MVC3 之前的项目,如果要使用RouteDebugger,还需要在Application_Start中注册:

1
2
3
4
5
protected void Application_Start(object sender, EventArgs e)
{
  RegisterRoutes(RouteTable.Routes);
  RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
}

route-debugger-info

从浏览器的输出我们可以看到详细的Route匹配信息,当前请求可以成功匹配哪些路由。

注意:{*catchall}路由是RoutDebugger自动添加的,这样即使没有任何一个路由匹配成功,也会显示相应的路由信息,而不是404(不过我本地测试发现非法url还是返回了404,有人在Phil Haack的博客提出了这个问题但是没有得到回答)。

 

禁用RouteDebugger

当使用了RouteDebugger后,你会发现每一个页面的后面都会附带路由信息。如果你需要禁用这个功能,打开Web.config:

将RouteDebugger:Enabled后面的value=”false”即可关闭RouteDebugger模式。

webconfig

[转载]程序崩溃后如何更好的反馈信息

mikel阅读(928)

[转载]程序崩溃后如何更好的反馈信息 – 2012 – 博客园.

我们使用Windows都知道,整个Windows和微软提供的应用软件的稳定性和崩溃后的体验是逐步提升的。在大牛的作品《软件调试》 中有描述。

在Windows下,软件出错,常见的报告手段:

1、事件日志:这里记录了很多关键的错误信息

2、崩溃报告:可以自动提交错误的信息,或者手动提交,以不断的改进软件和方便排错

3、专用的日志工具:如Netmon,sysinternal等

 

那我们的软件在崩溃后如何处理呢?主要目的:

1、方便我们的排错

2、提供更好的使用体验

最近在设计一个应用系统时对这个问题进行处理。

这方面有比较多的参考:

首先在stackoverflow上可以看到,c++ release  program crashes report

http://stackoverflow.com/questions/112831/how-to-get-a-stack-trace-when-c-program-crashes-using-msvc8-2005

以上有比较多的框架和工具

Windows应用程序处理的一个框架和详细参考资料

Integrating Crash Reporting into Your Application – A Beginners Tutorial

http://www.codeproject.com/Articles/308634/Integrating-Crash-Reporting-into-Your-Application

http://code.google.com/p/crashrpt/

http://www.codeproject.com/Articles/1934/Post-Mortem-Debugging-Your-Application-with-Minidu

Google breakPad

Google breakpad是 一个非常实用的跨平台的崩溃转储和分析模块,他支持Windows,Linux和Mac和Solaris。由于他本身跨平台,所以很大的减少我们在平台移 植时的工作,毕竟崩溃转储,每个平台下都不同,使用起来很难统一,而Google breakpad就帮我们做到了这一点,不管是哪个平台下的崩溃,都能够进行统一的分析。现在很多工程都在使用他:最著名的几个如 Chrome,Firefox,Picasa和Google Earth。另外他的License是BSD的,也就是说,我们即便是在商业软件中使用,也是合法的

http://code.google.com/p/google-breakpad/

http://bigasp.com/archives/450

 

调试Release发布版程序的Crash错误

http://www.cppblog.com/Walker/articles/146153.html

http://blog.sina.com.cn/s/blog_48f93b530100fsln.html

 

对于托管程序也有 Good crash reporting library in C#

http://stackoverflow.com/questions/49224/good-crash-reporting-library-in-c-sharp

顺便看到了这个比较有意思的内容,35个你也许不知道的Google开源项目 http://www.enet.com.cn/article/2009/1228/A20091228592251.shtml

 

由于我的应用 需要处理跨平台问题,因此使用了Google breakPad

顺便把整理的资料放在了skydriver上,

https://skydrive.live.com/#cid=56B433AD3D1871E3&id=56B433AD3D1871E3%21427

https://skydrive.live.com/view.aspx?cid=56B433AD3D1871E3&resid=56B433AD3D1871E3%21433

[转载]Android开发手机上的消息推送

mikel阅读(957)

[转载]手机上的消息推送 – 心笑峰 – 博客园.

最近在找Android手机上的消息推送的解决方案。目前看来有以下几种常用的方式:1.定期查询:按照指定的时间间隔连接服务器查询获取最新的消息。实现起来简单,非实时,查询时间过短则流量耗费多,耗电量大。下面是一个爱立信的测试结果:

 

2.短信方式:需要及时发送消息给客户端时也可以通过这种方式,但大家都懂的,这个很花钱。

 

3.长轮询:基本上与目前很多网站使用的方式一样(WEB阿里旺旺、微博、人人等等)。客户端发起一个很长超时时间的请求,然后服务器端在没有消息的时候阻塞这个请求(一直不给返回值)直到快要超时为止,有消息到来再返回响应。客户端收到响应或超时后立即再发起请求。

这种算是比较好的方式了,消息能够及时地到达客户端。但考虑到移动互联网的特点(网络不稳定、设备内存小)这种方式不能保证重要的消息一定能推送到客户端,另外anroid在手机内存小的情况下可能会杀这个在等待PUSH消息不怎么活动的进程。

 

4.C2DM:GOOGLE提供了消息的PUSH功能,需要和GOOGLE账号绑定,目前看来这种方式在国内是没戏的。

 

5.XMPP:在客户端集成asmack,服务器端使用ejabberd或openfire等开源的XMPP服务器软件也是一种可行的方式。

缺点就是先要有注册、登陆等过程,无线网络环境下连接的效果不怎么样。重要消息的PUSH需要自己实现确认逻辑。

 

6.MQTT:基于代理发布/订阅 模式的消息传输协议,适用于受限环境:

网络代价昂贵、带宽低、不可靠;

在嵌入设备中运行、处理器和内存资源有限。

特点是:

使用发布/订阅模式,解除应用程序耦合;

对负载内容屏蔽的消息传输;

使用TCP/IP;

提供“至多一次”、“至少一次”、“有且仅有一次”三种级别的消息传输;

小型传输、流量开销少;

使用LAST WILL 和TESTAMENT特性通知有关各方客户端异常中断机制。

听起来简直就是为移动互联网设计的

下面是基于MQTT的简单实现方案:

 

服务器:

可以采用IBM的MQTT服务器RSMB;

开源的Mosquitto

 

客户端:

IBM的wmqtt.jar 适用于JAVA客户端。

 

1.下载安装运行Mosquitto服务器。

2.在anroid客户端集成以下代码:

import com.ibm.mqtt.IMqttClient;
import com.ibm.mqtt.MqttClient;
import com.ibm.mqtt.MqttException;
import com.ibm.mqtt.MqttPersistence;
import com.ibm.mqtt.MqttPersistenceException;
import com.ibm.mqtt.MqttSimpleCallback;

public class MQTTConnection implements MqttSimpleCallback{
    IMqttClient mqttClient = null;    
    private static int MQTT_PORT =1883; 
    private static MqttPersistence MQTT_PERSISTENCE=null;
    public static String MQTT_CLIENT_ID ="";
    private static boolean MQTT_CLEAN_START = true;
    private static short MQTT_KEEP_ALIVE = 60 * 15;
    private static int[] MQTT_QUALITIES_OF_SERVICE = { 0 } ;
    private long mStartTime;
        
    public MQTTConnection(String brokerHostName, String initTopic) throws MqttException {
        String mqttConnSpec = "tcp://" + brokerHostName + "@" + MQTT_PORT;
        // Create the client and connect
        mqttClient = MqttClient.createMqttClient(mqttConnSpec, MQTT_PERSISTENCE);
        String clientID = MQTT_CLIENT_ID;
        mqttClient.connect(clientID, MQTT_CLEAN_START, MQTT_KEEP_ALIVE);

        // register this client app has being able to receive messages
        mqttClient.registerSimpleHandler(this);
        
        // Subscribe to an initial topic, which is combination of client ID and device ID.
        subscribeToTopic(initTopic);

        // Save start time
        mStartTime = System.currentTimeMillis();
        // Star the keep-alives
        //startKeepAlives();            
    }
    
    private void subscribeToTopic(String topicName) throws MqttException {
        
        if ((mqttClient == null) || (mqttClient.isConnected() == false)) {
            // quick sanity check - don't try and subscribe if we don't have
            //  a connection
            System.out.println("subscribe to topic fail");
        } else {                                    
            String[] topics = { topicName };
            mqttClient.subscribe(topics, MQTT_QUALITIES_OF_SERVICE);
        }
    }    
    
    public void disconnect() {
        try {            
            mqttClient.disconnect();
        } catch (MqttPersistenceException e) {
            System.out.println("disconnection to server error");
        }
    }

    
    @Override
    public void connectionLost() throws Exception {
        // TODO Auto-generated method stub
        System.out.println("connection to server closed");
    }

    @Override
    public void publishArrived(String topicName, byte[] payload, int qos, boolean retained)
            throws Exception {
        // TODO Auto-generated method stub
        String s = new String(payload);
        System.out.println("push message recived :"+s);
    }
}

3.运行客户端程序,在命令窗口中使用Mosquitto_pub.exe -q [Qos级别] -t [主题] -m [发布的内容] 进行测试。

另:Mosquitto由于使用socket select 模型,能支持的客户端连接数量有限。

如果要支持更高并发量,一方面可以考虑采用“策略服务器+Mosquitto集群”的方式,另一方面可以考虑erlang实现的一些MQTT服务器替换Mosquitto(上次见到的一个类似的发布/订阅系统每秒可以完成向40W订阅用户广播的任务,够牛逼了吧)。

[转载]Android系统如何实现UI的自适应

mikel阅读(916)

[转载]Android系统如何实现UI的自适应 – Melanie Deng – 博客园.

Android应用的人都知道,要一个apk适用多个不同的手机屏幕是很容易的,就是在项目的res文件夹下面有多套相关的资源文件。程序运行的 时候,Android系统会根据当前设备的信息去加载不同文件夹下的资源文件。但是Android系统是怎么做到这一点的呢?上网上搜了一下,很少有这方 便的介绍,只好自己研究下代码了。下面是我研究代码得到的结果(正确性有待确认),在这里分享一下。

 

这里以ICS上在Activity的onCreate()方法里面调用setContentView(int resourceID)为例,介绍一下系统如何根据我们的id(R.layout.xxxx)找到合适的layout文件进行解析加载:

如果你的res下面有三种不同的layout:layout, layout-sw480dp和 layout-sw600dp,这里的sw<N>dp表示这个layout文件夹下面的布局文件只有在设备短边的最小宽带为N时才加载。你的 设备是800×480的分辨率,那么这个apk安装在你的设备上就会加载 layout-sw480dp里面的布局文件。下面是framework的 java层调用链:

Activity.setContentView(int resourceID) -> PhoneWindow.setContentView(int resourceID) -> LayoutInflater.inflate(int resource, ViewGroup root) -> LayoutInflater.inflate(int resource, ViewGroup root, boolean attachToRoot) -> Resources.getLayout(int id) -> Resources.loadXmlResourceParser(int id, String type) -> Resources.getValue(int id, TypedValue outValue, boolean resolveRefs) -> AssetManager.getResourceValue(int ident, int density, TypedValue outValue, boolean resolveRefs) -> AssetManager.loadResourceValue(int ident, short density, TypedValue outValue, boolean resolve)

 

在上面的掉用链中:

1. 最后加载的是哪个xml是由Resources.getValue(int id, TypedValue outValue, boolean resolveRefs)调用完成之后的outValue.string决定的,因为outValue.string的值就是你的资源文件的具体路径, 如:

1) xxx/values/xxx.xml

2) xxx/layout-sw600dp/xxx.xml

2. AssetManager.loadResourceValue()调的是frameworks/base/core/jni /android_util_AssetManager.cpp里面的native方法, 如何获得正确的outValue值,在native方法俩面主要有以下几步:

1) 调用frameworks/base/libs/utils/ResourceTypes.cpp 的ResTable::getResource(),遍历所有资源文件

2) 在ResTable::getResource()里面调用ResTable::getEntry()来确定资源文件来自哪个entry,即 layout,或者layout-sw<N>dp,由此可见,ResTable::getEntry()是我们这个问题的关键

3) 在ResTable::getEntry()里面:

a) 首先获取本设备的configurion信息,屏幕分辨率,屏幕大小,locale,横竖屏等。

b) 根据得到的本设备的configurion信息,过滤掉不适应本设备的entry,比如设备是800×480的,那么超过此分辨率的资源 (例:layout-sw600dp)就要被过滤掉,实现在frameworks/base/include/utils /ResourceTypes.h中ResTable_config的match函数中

c) 对过滤后的resource进行最佳适配,找到最符合的entry文件。因为之前已经将不符合的,即大分辨率的entry已经被过滤掉了,所以这里就找剩 下的最大的就是最佳适配的。实现在frameworks/base/include/utils/ResourceTypes.h中 ResTable_config的isBetterThan()函数中。

3. 我做了一个尝试,就是想让800×480分辨率的设备上的应用都加载 layout-sw600dp里面的资源文件。所以将上面b)步骤的 frameworks/base/include/utils/ResourceTypes.h里面ResTable_config的match函数改动 如下:

/*if (smallestScreenWidthDp != 0
              && smallestScreenWidthDp > settings.smallestScreenWidthDp){
          return false;
}*/
if (smallestScreenWidthDp != 0
              && smallestScreenWidthDp > 600) {
          return false;
}

我将settings.smallestScreenWidthDp强制换成了600,这样的话,所有比600dp小的(包含600)在内的资源文 件在做过滤时就被保留了下来,而c)步骤不做检查,只找最大的,所以layout-sw600dp就成了系统认为的“最合适”的资源问价了。

 

将重新编译frameworks/base/libs/utils/生成的lib库push到/system/libs下面,再重启手机,然后启动上述应用,就可以了看见程序加载的layout-sw600dp的ui了。

[转载]JavaScript倒计时跳转或后退

mikel阅读(1024)

[转载]JavaScript倒计时跳转或后退 – 前端组qianduanzu.com – 博客园.

有的时候用户访问到一个错误或不可访问的页面,我们可以做一些这样的处理:倒计时N秒跳转到另一个页面或后退到前一个页面。

你可直接前往DEMO页看效果!

这种效果可以用JavaScript实现:

HTML

 <p>
     <span id="timer"></span> 秒后自动返回
     <a href="http://www.qianduanzu.com/">直接跳转</a>
 </p>

JavaScript

window.onload = function(){
     var time = 3; //设置时间为几秒
     var timer = document.getElementById('timer'); //获取ID为timer的对象
     timer.innerHTML = time; //初始化显示秒数
     time = time - 1;
     var g = window.setInterval(function(){
         if(time<0){
             window.clearTimeout(g); //清除动画
             window.location.href = 'http://www.qianduanzu.com/'; //跳转到指定地址
             //window.history.back(-1); //后退
         } else {
             showTime();
         }
     },1000);
     //显示函数
     function showTime(){
         timer.innerHTML = time;
         time--;
     }
 };

如果不想做这种动画效果,而直接用HTML实现,如下:

<meta http-equiv="refresh" content="3;url=http://www.qianduanzu.com/">