好大的风

2010年07月9日

对比一下APC、Memcache和MySQL的速度。

类归于: 你要去哪里 — 好大的风 @ 9:19 下午

1,使用apc_fetch获取数据,每次大约3微秒。

2,使用Memcache::get通过localhost获取本服务器的数据,每次大概38微秒。

3,使用Memcache::get通过本机IP地址获取本服务器的数据,每次大概36微秒。

4,使用Memcache::get通过IP地址获取同网段(千兆以太网)其他机器的数据,每次大概110微秒。

5,使用PDO::query通过IP地址获取同网段(千兆以太网)其他机器的数据,每次大概110微秒。

2010年07月1日

2009年2月6日发表于新浪开发者博客:嵌入式设备

类归于: 你要去哪里 — 好大的风 @ 11:24 上午

现在的嵌入式设备,性能真是越来越好了,我家里的那个ADSL无线路由器,里面的CPU的主频竟然高达260Mhz,内存也有32M,还带USB口,可以连接移动硬盘、摄像头、打印机等。比我05年从公司拍卖得到的那台IBM PC机差不了多少,这个PC机的CPU是赛扬433,内存64M。配置这么高的嵌入式设备,软件也不能落后,那天有空telnet进去看了看,运行的是linux系统,内核版本还是2.6.x的,顿时觉得这个路由器是个宝贝,我可以在上面做些有意思的事情。
研究了一会,发现这个路由器没有关于硬件的文档,也没有找到可以刷新系统的方法,所以就打消了自己另装一套linux的想法,转念又想,装系统也太没技术含量了,哪里有时间折腾,还是在上面写点程序比较有实际意义。
这个系统裁剪的还不算太厉害,有uClibc库,有pthread库,貌似支持epoll,网络支持那肯定不用说了,本身就是路由器,还有iptables可以用,所以还算够用。CPU是ARM10的,这也算是RISC的CPU了吧,哈哈,我终于也在非X86机器上写过程序了。
于是想弄一个能运行在这个路由器上的BT下载程序,路由器连接上USB存储设备,嗯,省电!估计总功耗不过10W吧,我一直用来BT的那台IBM PC机,我估计功耗至少得50W。
于是找来BitTorrent的协议文档,研究了一下,发现协议还算简单,又翻看了很多BT客户端的实现代码,发现都写得很复杂,最讨厌看这么复杂的代码了,还是自己写吧,就当练练手了。
具体的程序实现还在规划中,预计今年能够完成吧,哈哈,这种路由器是中国网通和电信定制的产品,用户安装ADSL的时候就赠送这个,所以据传闻这个路由器的用户量至少有1000万,恩,等我把这个程序完成了,卖给他们每人一份,每份就算10块钱吧,不算太黑吧……
想起来我还有一个嵌入式设备,是一个卫星电视接收设备:DM500,里面的CPU是PowerPC的,主频大概也是250Mhz,内存32M,这个设备比较开放,已经有很多网友在上面开发各种各样的好玩的程序,只可惜这个DM500没有USB接口可以连接存储设备,所以很多想法就被限制了。

2009年3月1日发表于新浪开发者博客:今天在我的一台桌面电脑上测试了一下跑在U盘上的数据库的性能

类归于: 你要去哪里 — 好大的风 @ 11:17 上午

今天在我的一台桌面电脑上测试了一下跑在U盘上的数据库的性能,机器配置大概是:
CPU:超低电压版U2500,2X1.2Ghz,2M二级缓存。
内存:1G DDR2 单通道。
存储:
1,日立500G SATA硬盘。
2,金士顿2G U盘。
软件环境:
Windows XP SP3,xampplite 1.7.0 MySQL 5.1.30

测试程序:
delimiter ;;
create procedure ikv(c int unsigned)
begin
while (c > 0) do
insert into kv(v) values (floor(rand()*1000000000000));
set c=c-1;
end while;
end;;
delimiter ;

drop procedure if exists skv;
delimiter ;;
create procedure skv(c int unsigned)
begin
declare vv bigint unsigned;
declare kk int unsigned;
while (c > 0) do
set kk=floor(rand()*6000000);
select v into vv from kv where k=kk;
set c=c-1;
end while;
end;;
delimiter ;

测试结果:

《测试结果截图丢失,印象中,U盘的表现胜过普通磁盘10倍以上》

测试结论:
U盘的数据库读取的性能表现是硬盘的4倍。
U盘或者类似的存储结构,具有十分优秀的随机访问时间,对于单笔小数据量的随机读取,十分适合。
很有发展前景。

2009年3月2日发表于新浪开发者博客:系统开发部内部交流-MemcacheDB的扩展应用

类归于: 你要去哪里 — 好大的风 @ 11:15 上午

本文通过整理系统开发部一次内部交流的资料,总结了一下SPACE产品开发初期形成的一些MemcacheDB的衍生应用。业务需求是千变万化的,我们需要灵活的运用这些基础设施,简单高效的达到我们的目标。

《原有PPT截图丢失》

以下是针对 MemcacheDB-Queue的技术实现的一些文字简介:
memcachedb-queue 设计与实现

0,基于memcachedb的代码,memcachedb为单进程单线程程序,所以全局变量不须任何锁操作。

1,增加两个全局变量:head和tail,类型为unsigned long。
用于表示头节点和尾节点所对应的key值。

2,精简memcache协议,只支持add和get两个命令,add表示入队列,get表示出队列。
此时add和get的key参数和expire参数已经无实际意义,用途仅为协议兼容。

3,增加一个init_queue函数,用于初始化head和tail的值,默认从db中取得,如果没有,则都为0。

4,增加一个save_queue函数,并通过atexit注册,在程序退出时负责将head和tail的值保存到db中。

5,add的实现伪代码:
key=tail;
save_to_db(key,value);
tail++;

6,get的实现伪代码:
key=head;
get_from_db(key,&value);
del_db(key);
head++;

具体实现参见 memcachedb-queue.c
目前经过几天的测试,基本实现目标。
计划用于纸条箱的发纸条队列,还有其他的一些模型类似“生产者/消费者”的程序。

本次交流已经年代久远,其中很多观点和技术以现在的眼光来看,已经显得有些落伍,我目前正在筹划新的一版队列系统的设计与实现,队列是很有用的基础组件之一,值得深入的研究。完成之后,会在博客上与大家分享,敬请期待!

2009年3月2日发表于新浪开发者博客:【初级文章】如何在程序中动态加载shared object,以及不引入额外变量实现swap函数

类归于: 你要去哪里 — 好大的风 @ 11:12 上午

记得我在几年前看到过一个面试的题目,如何不引入额外的变量,实现两个数交换的函数,当时觉得不可思议,按照传统的做法:
swap的代码应该是:
void swap1(int *a,int *b)
{
int c;
c=*a;
*a=*b;
*b=c;
}

怎么不使用c这个变量来实现swap呢?现在看来好像是一个脑筋急转弯的问题,类似怎么只挪动一根火柴让等式成立之类的游戏。
看下面这个swap函数:
void swap2(int *a,int *b)
{
*a=*a-*b;
*b=*a+*b; // a-b+b=a
*a=*b-*a; // a-(a-b)=a-a+b=b
}
恩,应该是没有引入外部变量。
更深入一步,我们来看看这两个函数编译成汇编语言的的结果(gcc -O2 -S):

swap1:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %edx
movl 12(%ebp), %ecx
pushl %ebx
movl (%edx), %ebx
movl (%ecx), %eax
movl %eax, (%edx)
movl %ebx, (%ecx)
popl %ebx
popl %ebp
ret
swap2:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %ecx
movl 12(%ebp), %edx
movl (%ecx), %eax
subl (%edx), %eax
movl %eax, (%ecx)
addl (%edx), %eax
movl %eax, (%edx)
subl (%ecx), %eax
movl %eax, (%ecx)
popl %ebp
ret

对比一下,swap2在汇编语言里面确实节省了一个寄存器ebx的使用。

然后看看怎么动态加载shared object(就是俗称的 嗖文件):

#include
#include
#include
#include
int main()
{
char *so="./swap.so";
void *hdl=dlopen(so,RTLD_LAZY);
if(!hdl) exit(1);
void (*f)(int *,int *);
f=dlsym(hdl,"swap");
if(!f) exit(2);
int a=2,b=3;
printf("a=%d,b=%d\n",a,b);
(*f)(&a,&b);
printf("a=%d,b=%d\n",a,b);
}

这样做的一个主要用途就是方便对程序进行扩展,比如MySQL的udf扩展,比如PHP的扩展,比如csf的扩展等等。

以上几个小技巧都是我感觉比较有意思的,也是曾经让我很迷惑的,再次分享给大家,有错误的地方,还请大家指正。

2009年3月10日发表于新浪开发者博客:我也用上“云计算”啦

类归于: 你要去哪里 — 好大的风 @ 11:11 上午

我也用上这么阳春白雪的东西了,哈哈。
Amazon做的确实不错,使用很方便,价格制定的也很灵活,按时间和流量计费,我试验了一下,还是很精确的。

《《本来有图,但是原来的博客图片丢失了,是一个AWS的WEB界面截图》》

不过有一个小问题让我听迷惑的,每个实例是不是只能运行一次?如果shutdown或者选择终止这个实例,是不是就不能再启动了?反正我是没找到启动的按钮。《现在知道了,只能一次性运行,要想保留,得花钱买持久化的,呵呵,现在看来,所谓云计算,就是卖虚拟机的?》

2009年3月16日发表于新浪开发者博客:来玩玩Lua吧!

类归于: 你要去哪里 — 好大的风 @ 11:08 上午

早就想研究一下这个新潮的东西了,无奈Lua的资料实在是很少,目前Lua最新版是5.1,Lua网站上那本书是第一版,主要是针对5.0的,好多API和5.1都不一样,我还是老老实实买了一本纸版的第二版,研究了一下,弄出来第一个比较有意义的Lua应用:
cat sum.lua

function sum(a,b)
return a-b
end

cat main.c
#include
#include
#include
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

int main(int argc,char **argv)
{
int a=100,b=200,c=0;
void *s;
lua_State *L=luaL_newstate();
luaL_openlibs(L);
luaL_loadfile(L,”sum.lua”);
lua_pcall(L,0,0,0);
lua_getglobal(L,”sum”);
lua_pushnumber(L,a);
lua_pushnumber(L,b);
if(lua_pcall(L,2,1,0) != 0) {
printf(“%s\n”,lua_tostring(L,-1));
exit(2);
}
if(!lua_isnumber(L,-1)) exit(3);
c=lua_tonumber(L,-1);
lua_pop(L,1);
printf(“a+b=%d\n”,c);
}

以上就是用C语言程序来调用Lua脚本实现的函数的做法,编译方法:
gcc -o main main.c /path/to/liblua.a -I /path/to/lua/include/ -lm -ldl
最后的-lm 和 -ldl不能少。
好书还是要买的,一本书才几十块钱,但好书让你学到知识和技能,这些知识和技能所能够创造的价值,可不是几十块钱这个数量级的。

2009年3月24日发表于新浪开发者博客:又是一年春风来

类归于: 你要去哪里 — 好大的风 @ 11:04 上午

提要
一年之计在于春。
本文对我最近两年所参与设计的系统进行概述,总结期间的经验,希望有助于日后的工作,也期望对本文的读者有所帮助。
轰轰烈烈的纸条箱
2007年的春天,我参与设计开发纸条箱系统,产品定位是新浪站内的便捷通讯系统。当时产品设计对系统容量有很大的期望,技术设计人员也对系统有很高的要求,要求能够做到分布式异地部署,导致初期花费大量时间对系统底层进行设计。期间,在负载均衡方面,通过改装LIGHTTPD,设计出七层交换系统,并冠名以F7来抗衡当时著名的并且获得过新浪年度创新奖的F6系统(现在已经出现最新一代的F8系统);在存储方面,总结以前邮箱的经验,借鉴了TINYMIC的重要思想,设计出单纯使用文件系统进行消息存储和索引的存储策略;在系统间通信方面,计划对DNS系统进行改装,来作为存储和前端的通信系统;对设备的要求也从初期的预计八台演变到最终上线时的二十五台。上线期间紧张而又兴奋,但通过不断的观察系统负载情况,渐渐的很失望,服务器利用率很低。此时产品人员热情依旧高涨,当时有想法要做移动版的纸条箱,捆绑嵌入到手机等移动终端设备,誓言要超过中国移动短信的发送数量。随着日子一天天过去,大家对纸条箱的热情逐渐降温,纸条箱的访问量也随之下降,反倒是当初纸条箱的一个附加产品:黑白名单系统日渐活跃,后逐渐演变为目前的好友系统。
SPACE上线初期,对纸条箱系统进行了改造,存储方式由纯粹的文件系统转向数据库方式,对数据进行了迁移,最终两台数据库即承担了整个纸条箱业务的服务。
总结:
产品设计人员对产品的容量有不切实际的夸大,技术设计人员对高新技术有与生俱来的喜好,二者相辅相成,使得产品开发阶段过分看重技术实现,忽视业务逻辑,最终产品上线后,因为不能达到预期目标而造成资源的浪费。
顽强生存的好友系统
好友系统的前身只是纸条箱系统的一个附加功能:让用户可以设置黑白名单,来控制垃圾纸条的接收,随着互动社区的发展,好友系统的轮廓日渐清晰,重要性也与日俱增,曾经出去对效率与负载的考虑,试图增加Cache机制,但后来发现,即使不用任何外部的Cache机制,好友系统依然效率很好,如果增加Cache机制,反倒经常因为缓存不同步造成用户投诉甚至故障。后经过总结,主要是由于好友系统由简单的数据库存储,简单的数据结构、简单的应用逻辑构成,使得系统即使承担每秒钟3000次以上的查询,依然能够保持快速的响应。
总结:
无心插柳柳成荫。
不要重新发明轮子。

未见天日的Twitter
纸条箱后期、SPACE前期我参与设计新浪Twitter系统,当初产品设计人员对系统容量没有明确的目标,反倒是技术人员有更多的想法,认为这个系统的访问量会非常高(当时用“恐怖”来形容),消息发送量可能会超过UC的IM系统,所以技术人员站在了很高的高度,投入很多的时间和精力设计了“下一代分布式统一存储系统”,设计目标要不仅能够分布式部署,还能够灵活选用多种存储介质,性能当然也不能落后,要能够很好的解决互联网应用对于分布式存储的需求。当时这个系统还有一个辅助的高性能的队列系统,已经动工并有阶段性成果,但由于整体系统设计及其复杂,以当时的人力物力,尚不能实现。后来产品方逐渐热情不再,导致这个系统都像泡沫一样在大家心中破灭。
总结:对技术的狂热,蒙蔽了我们的眼睛,使我们看不清真正的敌人。
年度大戏
SPACE这场年度大戏终于上演了,我作为系统开发人员,对系统做了部分设设计,存储部分计划采用MYSQL多主库分布式部署方案,这样的方案,技术比较成熟,开发成本很低,实施难度并不是很大,系统运维部同事也大力配合,分别在北京、广州、天津三地机房部署了大量机器,多主库部署的结构也基本搭建完成。当时为了应对想像中SPACE巨大的用户量和数据量,不惜成本采购了大量的磁盘阵列系统,并部署到各个机房,由于部署盘阵的经验不足,当时费了很大周折。之后SPACE第一版的程序陆续开发完成,但在第一次系统测试的过程中,北京到天津机房之间的专线网络出现了严重的故障,导致测试很难进行,北京到广州的网络也有很大的延迟,公司网络访问广州机房很慢,使测试人员以及领导对系统的印象很不好。后来经过深刻反思,决定弃繁从简,放弃异地分布部署,全部收缩到北京进行部署(这一决定值得SPACE至今仍然只部署在北京一地)。此后一直到系统上线,基本顺利。上线后,系统负载基本平稳。预料中可能出现问题的地方是SPACE的核心部分:FEED系统。FEED部分由于数据量巨大,一个月内积累的数据达到数亿条,频繁出现数据读取延迟的现象。为此系统运维同事作出很大的努力,当时硬件资源比较匮乏,找一台大内存的机器都很困难,如果当初有两台16G内存的机器,FEED系统读取数据延迟的现象会大为缓解。后也曾设计很多解决FEED数据量的技术方案,但大多由于实施复杂、或者运维人员见解不一直而未能施行。
总结:
Make It Simple。
复杂的问题总是有简单的解决方案,《UNIX编程艺术》这部著作中写道:
优化一个系统的最好的方式就是什么都不做——等待着新一代更快的机器出现。
有时候我们辛苦几个月实施一套复杂的优化方案,效果反而不如增加几条内存明显。
平淡的留言
接下来设计开发了平台留言系统,此时我对那些先进的系统架构没有了太多兴趣,当时领导对平台留言系统的期望亦不是很高,加之开发时间紧迫,所以平台留言系统平移了SPACE留言系统的存储方式、存储操作程序接口,只是简单的包装了一层应用逻辑。现在看来,留言系统在在系统架构上是大有文章可做的,平台留言采取页面读取数据接口的方式,很适合对数据接口进行异地分布式部署。我错过了一个千载难逢的实践分布式部署的机会。目前数据库多主库异地部署,在SINA貌似还没有先例,这应该是一个比较有意义的实践。
总结:
技术人员需要激情。
又一个订阅系统
最近的正在做邮箱信息订阅系统,因为产品人员不能拿出一个有说服力的系统容量数据而搁置。我觉得产品人员拿不出这个数据是很正常的,如果我们来做产品设计,也不一定能够拿出有理有据的数据,在很大程度上会是“拍脑门”定出来的。我认为应该以系统开发人员的经验来评估这个容量数据,迅速开发简单的系统,上线后在针对运行情况做进一步评估,来决定系统容量是否需要扩充。很有可能这个系统表现平淡,在运行一段时间后,就没有人关注了,然后黯然下线。所以我们没有必要在这个容量因素上纠缠太久,实践是检验真理的唯一标准。
总结:
产品人员畏缩了,技术人员保守了,就算是理性么?
没有结尾
系统设计还在一个接一个的进行中,其中的纠结怎能用一篇短文道出!

2009年4月1日发表于新浪开发者博客:关于HTTP 协议中的 KeepAlive

类归于: 你要去哪里 — 好大的风 @ 11:01 上午

这篇文章已经写完将近一年了,最近从历史邮件里面翻出来,和大家分享一下。
其中使用PHP实现持久的HTTP连接,让我费了很多心思。
曾经想过使用C语言编写一个PHP的扩展来实现,后来发现pfsockopen这个函数,让我豁然开朗,避免重新发明一个轮子,呵呵。

一,KeepAlive的概念:

参见 http://en.wikipedia.org/wiki/HTTP_persistent_connection

二,KeepAlive的客户端实现:

使用了PHP支持的 pfsockopen 来实现,参见:http://cn.php.net/pfsockopen

KeepAlive必要的Header有:

Connection: Keep-Alive
Content-Length: xxx

三,性能对比测试:

几种对比实现方式:

1,使用fsockopen来实现,读取body内容后,关闭连接,参见测试程序中的ohttp_get实现。
2,使用pfsockopen来实现,读取body内容后,不关闭连接,参见测试程序中的phttp_get实现。
3,php实现的file_get_contents
4,第三方测试工具ab

前三种测试在测试程序中都包含了。

测试用例 一:

前三种php实现的客户端单进程单线程请求lighttpd服务器一个16字节的静态文件。顺序请求10000次。
客户端与服务器部署在不同服务器,通过内网请求。

测试结果:

第一次:

[root@localhost ~]# /opt/bin/php tp.php
phttp_get: 5.3641529083252
ohttp_get: 8.1628580093384
file_get_contents: 12.217950105667

第二次:

[root@localhost ~]# /opt/bin/php tp.php
phttp_get: 5.033059835434
ohttp_get: 9.589075088501
file_get_contents: 12.775387048721

第三次:

[root@localhost ~]# /opt/bin/php tp.php
phttp_get: 5.0181269645691
ohttp_get: 8.2286441326141
file_get_contents: 11.089616060257

测试用例 二:

使用第三方工具ab来进行测试,-k参数开打开keepalive支持,不做并发测试,顺序请求10000次。
客户端与服务器部署在不同服务器,通过内网请求。

以下测试结果部分省略:

未打开keepalive:

[root@localhost ~]# ab -n 10000 -c 1 “http://10.69.2.206:8080/sms/ns2/save_msg.txt”

Finished 10000 requests

Concurrency Level: 1
Time taken for tests: 10.410467 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 2480000 bytes
HTML transferred: 160000 bytes
Requests per second: 960.57 [#/sec] (mean)
Time per request: 1.041 [ms] (mean)
Time per request: 1.041 [ms] (mean, across all concurrent requests)
Transfer rate: 232.55 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 30.0 0 3002
Processing: 0 0 0.4 0 9
Waiting: 0 0 0.3 0 9
Total: 0 0 30.0 0 3003

打开keepalive:

[root@localhost ~]# ab -k -n 10000 -c 1 “http://10.69.2.206:8080/sms/ns2/save_msg.txt”

Finished 10000 requests

Concurrency Level: 1
Time taken for tests: 4.148619 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Keep-Alive requests: 9412
Total transferred: 2527060 bytes
HTML transferred: 160000 bytes
Requests per second: 2410.44 [#/sec] (mean)
Time per request: 0.415 [ms] (mean)
Time per request: 0.415 [ms] (mean, across all concurrent requests)
Transfer rate: 594.66 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 5
Processing: 0 0 2.1 0 203
Waiting: 0 0 2.1 0 203
Total: 0 0 2.1 0 203

四,在实际中的应用

以上实现的phttp_get和mysql memcache的 中的“保持连接”概念类似,这种技术一般来说,只适用于fastcgi模式的web服务器。
对于本机之间的http通信,在测试过程中发现phttp_get的优势有限,基本合乎逻辑。
对于本身处理时间比较长的服务,phttp_get的优势也不明显。
综上,phttp_get适用于fastcgi模式的web应用调用远程http服务,且此http服务器响应时间比较短的情况。

五,服务端需要注意的事项

1,http服务器必须支持HTTP/1.1协议
2,php应用必须返回Content-Length:的header,具体实现参见:

http://cn.php.net/manual/en/function.ob-get-length.php

需要在代码中加入:

ob_start();
$size=ob_get_length();
header(”Content-Length: $size”);
ob_end_flush();

最后附上测试代码:

<?php

//$url=http://10.69.2.206:8080/sms/ns2/save_msg.txt

function ohttp_get($host,$port,$query,&$body)
{
$fp=pfsockopen($host,$port,$errno,$errstr,1);
if(!$fp)
{
var_dump($errno,$errstr);
return -1;
}
$out = “GET ${query} HTTP/1.1\r\n”;
$out.= “Host: ${host}\r\n”;
$out.= “Connection: close\r\n”;
$out.= “\r\n”;
fwrite($fp,$out);
$line=trim(fgets($fp));
$header.=$line;
list($proto,$rcode,$result)=explode(” “,$line);
$len=-1;
while( ($line=trim(fgets($fp))) != “” )
{
$header.=$line;
if(strstr($line,”Content-Length:”))
{
list($cl,$len)=explode(” “,$line);
}
if(strstr($line,”Connection: close”))
{
$close=true;
}
}
if($len < 0)
{
echo “ohttp_get must cope with Content-Length header!\n”;
return -1;
}
$body=fread($fp,$len);
if($close)
fclose($fp);
return $rcode;
}
function phttp_get($host,$port,$query,&$body)
{
$fp=pfsockopen($host,$port,$errno,$errstr,1);
if(!$fp)
{
var_dump($errno,$errstr);
return -1;
}
$out = “GET ${query} HTTP/1.1\r\n”;
$out.= “Host: ${host}\r\n”;
$out.= “Connection: Keep-Alive\r\n”;
$out.= “\r\n”;
fwrite($fp,$out);
$line=trim(fgets($fp));
$header.=$line;
list($proto,$rcode,$result)=explode(” “,$line);
$len=-1;
while( ($line=trim(fgets($fp))) != “” )
{
$header.=$line;
if(strstr($line,”Content-Length:”))
{
list($cl,$len)=explode(” “,$line);
}
if(strstr($line,”Connection: close”))
{
$close=true;
}
}
if($len < 0)
{
echo “phttp_get must cope with Content-Length header!\n”;
return -1;
}
$body=fread($fp,$len);
if($close)
fclose($fp);
return $rcode;
}

$time1=microtime(true);
for($i=0;$i<10000;$i++)
{
$host=”10.69.2.206″;
$port=8080;
$query=”/sms/ns2/save_msg.txt”;
$body=””;
$r=ohttp_get($host,$port,$query,$body);
if($r != 200)
{
echo “return code : $r\n”;
}
}
$time2=microtime(true);
for($i=0;$i<10000;$i++)
{
$url=”http://10.69.2.206:8080/sms/ns2/save_msg.txt”;
$host=”10.69.2.206″;
$port=8080;
$query=”/sms/ns2/save_msg.txt”;
$body=””;
$r=phttp_get($host,$port,$query,$body);
if($r != 200)
{
echo “return code : $r\n”;
}
}
$time3=microtime(true);
for($i=0;$i array( ‘timeout’ => 1 )
)
);
$body=file_get_contents($url, 0, $ctx);
$r=200;
if($r != 200)
{
echo “return code : $r\n”;
}
}
$time4=microtime(true);

echo “phttp_get: “.($time3-$time2).”\n”;
echo “ohttp_get: “.($time2-$time1).”\n”;
echo “file_get_contents: “.($time4-$time3).”\n”;

?>

2009年6月2日发表于新浪开发者博客:说说一个数据库的查询方法

类归于: 你要去哪里 — 好大的风 @ 10:57 上午

最近做了博客关注系统的方案设计,主要的数据表有两个:
数据内容表:

CREATE TABLE `story` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(10) unsigned NOT NULL,
`type` tinyint(3) unsigned NOT NULL,
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`data` varbinary(8192) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `i_ut` (`uid`,`type`,`time`)
) ENGINE=InnoDB;

以及关注关系表:

CREATE TABLE `attention` (
`uid` int(10) unsigned NOT NULL,
`aid` int(10) unsigned NOT NULL,
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`uid`,`aid`),
KEY `i_a` (`aid`)
) ENGINE=InnoDB;

我们的要查询出uid为1001的用户A关注的所有人最近发表的内容,结果集大概在10000条,我们只取前20条。
简单直接的查询方法可以是:

select id,data from story,attention
where story.uid=attention.aid and attention.uid=1001 order by time desc,id desc limit 20;

但是这个查询语句效率很低,换用一种貌似比较复杂的查询:

select story.* from
story,
(select id from story,attention
where story.uid=attention.aid and attention.uid=1001 order by time desc,id desc
) relation
where story.id=relation.id limit 20;

这个查询效率就很好了,通过对比发现,至少比上一个查询快几十倍。

我认为这样的差异主要取决于数据库中“查询优化”的实现,可能MySQL在这方面做的有限,需要我们精确的告诉他一个比较优化的查询方案。

早前文章 »

WordPress 所驱动