引言
本篇要评测的NoSQL产品是MongoDB,它是现在开源社区里越来越受到关注的一个NoSQL产品,按照官方的说法,是一个可扩展的、高性能的、开源的、面向文档的数据库。Craigslist、foursquare、国内的淘宝网等知名互联网公司都有在他们的生产环境部署了MongoDB。
介绍
MongoDB是用C++开发的面向文档的数据库,因为是面向文档的,也就是反传统的数据库范式来设计的,把相关的对象都记录到一个文档里,每个文档内是schema-free的,通俗的理解,也就是列名可以自由定义,比较灵活,特别是面对业务逻辑多变的应用场景十分给力。数据以BSON(二进制JSON)的格式存储。因为每个文档是一份完整的记录,所以MongoDB和传统的关系型数据库相比,可能带来一定的数据冗余和存储开销。
很明显,MongoDB这种面向文档的数据库和传统的关系型数据库的设计思路是差别很大的,因为每个文档都包含了所有信息,和其他文档是没有关联的,这样传统的Join操作就完全没必要了,也正是因为去除了这种“关系”,使得MongoDB的水平拆分更加容易,这也是面对海量数据的一个很好的处理思路。另外,MongoDB的索引机制和MySQL等数据库是一样的,可以利用传统的关系型数据库的经验来使用MongoDB的索引。
不像其他很多NoSQL产品由个别工程师根据应用场景开发出来,MongoDB是有一个专门的公司10gen来维护。有一点要注意的是,MongoDB自己是不管理内存的,无法指定内存大小,完全交给操作系统来管理,因此有时候是不可控的,在生产环境使用必须在OS层面监控内存使用情况。
安装和使用
mongodb目前最新的版本是1.8.2-rc3,其源码安装用了很多第三方的东西,比如JS引擎(目前官方推荐的是mozilla的Spider Monkey,以后可能改成google的V8)、正则表达式引擎(pcre)、安装构建工具scons(用python来安装)、boost C++库等等。下面是安装过程:
1,下载需要的源文件和相关软件包:
[root@localhost mongodb]# wget http://downloads.mongodb.org/src/mongodb-src-r1.8.2-rc3.tar.gz
[root@localhost mongodb]# wget http://sourceforge.net/projects/scons/files/scons/2.1.0.alpha.20101125/scons-2.1.0.alpha.20101125.tar.gz/download
[root@localhost mongodb]# wget http://ftp.mozilla.org/pub/mozilla.org/js/js-1.7.0.tar.gz
[root@localhost mongodb]# wget http://sourceforge.net/projects/pcre/files/pcre/7.4/pcre-7.4.tar.gz/download
2,安装scons:
[root@localhost mongodb]# tar zxvf scons-2.1.0.alpha.20101125.tar.gz
[root@localhost mongodb]# cd scons-2.1.0.alpha.20101125
[root@localhost scons-2.1.0.alpha.20101125]# python setup.py install
3,安装pcre:
[root@localhost mongodb]# tar zxvf pcre-7.4.tar.gz
[root@localhost mongodb]# cd pcre-7.4
[root@localhost pcre-7.4]# ./configure
[root@localhost pcre-7.4]# make
[root@localhost pcre-7.4]# make install
4,安装Spider Monkey:
[root@localhost mongodb]# tar zxvf js-1.7.0.tar.gz
[root@localhost mongodb]# cd js/src
[root@localhost src]# export CFLAGS="-DJS_C_STRINGS_ARE_UTF8"
[root@localhost src]# make -f Makefile.ref
[root@localhost src]# JS_DIST=/usr make -f Makefile.ref export
5,yum方式安装boost:
[root@localhost src]# yum -y install boost boost-devel
6,安装mongodb:
[root@localhost mongodb]# tar zxvf mongodb-src-r1.8.2-rc3.tar.gz
[root@localhost mongodb]# cd mongodb-src-r1.8.2-rc3
[root@localhost mongodb-src-r1.8.2-rc3]# scons all
[root@localhost mongodb-src-r1.8.2-rc3]# scons --prefix=/usr/local/mongodb --full install
这样就安装完毕了,可以简单的启动mongod进程来验证一下:
[root@localhost bin]# ./mongod --dbpath /tmp
Wed Jun 8 11:57:38 [initandlisten] MongoDB starting : pid=29700 port=27017 dbpath=/tmp 64-bit
Wed Jun 8 11:57:38 [initandlisten] db version v1.8.2-rc3, pdfile version 4.5
Wed Jun 8 11:57:38 [initandlisten] git version: nogitversion
Wed Jun 8 11:57:38 [initandlisten] build sys info: Linux localhost.localdomain 2.6.18-128.el5 #1 SMP Wed Jan 21 10:41:14 EST 2009 x86_64 BOOST_LIB_VERSION=1_33_1
Wed Jun 8 11:57:38 [initandlisten] waiting for connections on port 27017
Wed Jun 8 11:57:38 [websvr] web admin interface listening on port 28017
可见mongod默认在27017端口监听,而28017端口是web管理的端口,可通过http方式来访问。为了规范,我们用以下命令启动一个mongod进程:
[root@localhost data]# /usr/local/mongodb/bin/mongod --fork --dbpath /home/mongo/data/ --logpath /home/mongo/mongo.log --logappend --directoryperdb --journal --rest
这样一个mongod进程就启动了,它监听27017端口来提供服务,可以在应用程序中进行建立数据库等操作,它不像传统的Oracle等关系数据库那样,建库是个很慎重的工作。要了解更详细的使用MongoDB的信息,读者可以参看官方文档,这里就不提及了。
要了解更详细的信息,读者可以参看官方文档。
测试说明
一、测试环境
MongoDB部署在一台PC 服务器上,配置如下:
CPU为Xeon 2.80GHz *4
内存为4G
硬盘为一块400G SATA盘
操作系统为64位CentOS 5.3版本
二、测试方法
这里仍然采用PHP客户端进行测试,MongoDB官方为PHP开发了一个扩展包可以操作MongoDB,网址为http://pecl.php.net/package/mongo,可以编译到PHP运行环境中来使用。
为了不对测试服务器产生额外的影响,测试客户端部署在另外一台独立的服务器上,运行的PHP的版本是5.3.5,web server是Nginx 0.8.54,通过fastcgi的方式调用PHP服务。使用apache ab工具实现多个请求和并发操作。
测试过程中就使用上文提到的已经启动的数据库实例,首先是进行写操作,通过500个请求,每个请求写入10000条记录,并发度为2来共写入500万条数据,MongoDB的数据格式是BSON方式,不同于其他的NoSQL是key value的方式,因此这里写入的数据格式为:{id,data}的形式,ID为数字1到5000000,data大小为100个字节。然后是读操作,也是用500个请求,每个请求随机根据ID值读出10000条记录,并发度为10共读出500万条记录,评测的重点是写入和读出数据的时间,以及在此过程中服务器的资源使用情况。
需要说明的是,MongoDB默认会为每个记录建立一个名字为_id的索引,在没有索引的情况下MongoDB读数据都要进行全表扫描,效率还是很低的,因此在写完500万条记录后,我在id字段上建立了一个索引来加快读的过程。
测试结果及总结
一、写操作
成功写入416万条记录,写入失败84万条记录,共耗时525秒,平均每秒写入数据7924笔。数据文件大小近4G。写入过程中,服务器内存、CPU和磁盘等资源使用情况如下图所示:
可见,内存使用呈阶梯状上升,最后占用了近3.5个G,主要用来缓存数据,对比数据文件的大小可以设想,在操作系统内存可用的情况下,内存的分配和数据文件的大小是大致相当的。CPU和磁盘IO都表现出周期性的上下波动,估计和操作系统mmap缓存数据和刷新到磁盘的实现机制有关。从图表可见本次测试的瓶颈同样出现在磁盘IO上,磁盘的使用率最后达到100%,导致无法成功写入数据。
二、读操作
成功读出500万条记录,共耗时432秒,平均每秒读出数据11574笔。
读数据过程中没有发生磁盘IO。CPU较繁忙,Idle值稳定在27左右,等待CPU资源的进程一直在5到10个之间。内存表现平稳没有波动。
通过以上测试结果可以看出,MongoDB占用的磁盘空间很大,这是因为其占用的磁盘空间是预分配的,每次以上一个数据文件的两倍大小来预分配空间,并以0来填充,以避免在繁忙时期分配磁盘空间导致性能下降,因此我们看到的数据文件大小并不是实际占用的空间大小。另外其内存是由操作系统管理的,自身并不管理内存使用。从功能角度看MongoDB提供的查询等操作接口是最为丰富的,在面对海量数据的拆分等可扩展方面也有很好的设计思路,是一个很好的将数据模型从面向关系转向面向文档的NoSQL产品,相信将来会看到更多的线上产品选用MongoDB。
现在的很多网站都有消息通知系统,比如新浪微博页面右上角的小黄签,比如Facebook页面左上角的Notifications。但是消息通知系统的说法是个笼统的概念,我理解的其本质功能是网站把某些对用户有价值的信息及时告知用户。比如常见的SNS关系中谁关注了你,谁评价了你发布的内容,谁邀请你加入某个小组等。
这类消息可以大体上分为两类,一种是告知性质的,就是用户知道有这么回事就行了,最多是具体看一下内容,比如其他用户对你发布的内容做了评论。另外一种是需要用户处理的,用户必须做出某种动作来回应,比如好友请求,你是接受、拒绝还是忽略。
纵观现在一些网站的消息通知产品设计,可以分为两种实现方式,一种是把各个功能模块的消息分类,然后每类有多少数字告知用户,用户需要到具体的功能模块页面查看同类的内容,典型的是新浪微博的设计。如下图所示:
其按功能分类通知每类新消息的数目,然后可以点击链接到某个功能模块查看同类消息。对应的,在功能设计上就有专门的评论汇总地方,有@我汇总的地方。这样的好处在同类消息很多的时候,比如收到几十条新评论的时候,用户不必频繁的在消息通知页面和具体评论页面来回切换,因为所有的评论在一个页面都能查看了。不好的地方就是不够直观,需要再次点击才能查看用户是对你的哪些内容发布了评论。另外,新浪微博中并没有用户必须处理的操作,用户之间是以关注为表现形式的弱关系,不需要确认就能关注一个人。
相对的,Facebook是所谓的强关系,就是用户加一个人为好友,必须得到对方的确认,为了处理好告知性质和操作性质两方面的消息通知,Facebook把好友请求部分独立出来了,可以理解为是一种比较复杂的消息通知。其界面如下:
用户必须在这个界面进行确认才能真正成为朋友,但是在消息通知里告知用户并能马上确认,对用户操作来说是很方便的。Facebook传统的消息通知和新浪微博不同,它可以在消息里显示具体的内容,而不是单纯的数量提示:
这样做的好处就是,不必设计一个单独的功能汇总某一类的消息,不好的地方就是在消息很多的时候,用户需要频繁的在消息通知界面和具体的内容界面切换来查看未读的内容,比较麻烦。国内模仿quora的知乎也是这样设计消息通知功能的:
那么有没有更好的方式来展现消息通知及其具体内容呢,Google Plus做成了更好的尝试,首先在消息通知的小窗口就能查看某一条具体消息的内容:
以下是在小窗口查看具体消息内容的情况,在这个页面可以进行消息详细内容的前后切换:
然后在完整的消息列表页,是直接显示了消息的详细内容:
正如你看到的,前两条消息就要占用一屏以上的空间,这在消息很多的情况下,是很不方便的。那么有没有更好的展现方式呢,我认为Twitter的界面风格就是最佳的方式:
在左边展示完整的消息列表,右边展示某个消息的具体内容及操作动作,用户可以很清晰的知道自己当前查看或处理的消息,并且不需要界面切换,perfect!
消息通知的合并也很重要,可以避免大量同样的消息对用户造成干扰,新浪微博的通知数目的方式本身就是一种合并,Facebook和G+也都对合并做的很好。还要注意的是,Facebook对于一段时间以前的历史消息就不予显示了,这无论从消息通知的功能本质来说,还是系统的性能方面考虑,都是可以理解的。
Mongodb为了提高处理大数据量的性能,提供了分片集群的功能,sharding可以理解为传统数据库表分区的扩展,其实现架构图如下:
可见,这个架构由三部分进程组成:两组mongod进程(shard和config server)了一组mongos进程。
shard可以理解为每一个数据分片,也就是一个大数据量中的一部分分片数据。为了提高高可用性,一般在生产系统中每个shard分片都是一个replica set,也就是提供了主从复制的功能,同时一般有第三个节点作为仲裁。
config server顾名思义是配置服务,主要保存各个shard的元数据信息,为了实现高可用,config server一般也要配置多个,每个config server的数据完全一致,mongodb是通过两阶段提交来保证最终一致性的,并不是常用的日志同步或共享存储的方式。有点让人费解的是,在启用config server进程的时候并不需要指定其要管理的shard进程,而是通过下面要说的mongos进程将二者联系起来。
客户端和mongos进程进行交互,mongos提供了routing功能,它使得客户端以为是和一个单一简单的系统在打交道。在启动mongos的时候要指定config server,然后就可以把shard添加进来。
以下演示了建立一个三个shard的集群,同时每个shard由主、从、仲裁三部分组成一个replica set。
建立mongodb用户和组,安装mongdb程序,如果用源代码安装,可以参考我的另一篇文章:
[root@localhost software]# groupadd -g 20001 mongodb [root@localhost software]# useradd -u 20001 -g mongodb mongodb [root@localhost software]# passwd mongodb[root@localhost software]# su - mongodb[mongodb@localhost ~]$ tar xf mongodb-linux-x86_64-1.8.2.tgz[mongodb@localhost ~]$ tree mongodb-linux-x86_64-1.8.2 mongodb-linux-x86_64-1.8.2 |-- GNU-AGPL-3.0 |-- README |-- THIRD-PARTY-NOTICES `-- bin |-- bsondump |-- mongo |-- mongod |-- mongodump |-- mongoexport |-- mongofiles |-- mongoimport |-- mongorestore |-- mongos |-- mongosniff `-- mongostat 1 directory, 14 files
建立对应的数据文件目录,在三台服务器启动shard进程,当然在一台服务器上测试指定不同的端口也可以:
[mongodb@localhost ~]$ cd mongodb-linux-x86_64-1.8.2/bin/ [mongodb@localhost bin]$ ./mongod --shardsvr --replSet shard1 --port 27017--dbpath /home/mongodb/data/shard11 --oplogSize 100--logpath /home/mongodb/data/shard11.log --logappend --fork[mongodb@localhost bin]$ ./mongod --shardsvr --replSet shard1 --port 27018--dbpath /home/mongodb/data/shard12 --oplogSize 100--logpath /home/mongodb/data/shard12.log --logappend --fork[mongodb@localhost bin]$ ./mongod --shardsvr --replSet shard1 --port 27019--dbpath /home/mongodb/data/shard13 --oplogSize 100--logpath /home/mongodb/data/shard13.log --logappend --fork
连接到任意shard节点,把三个服务器上的shard组成一个replica set,这里指定第三个shard为仲裁节点:
[mongodb@localhost bin]$ ./mongo 192.168.0.29:27019
MongoDB shell version: 1.8.2
connecting to: 192.168.0.29:27019/test
>
> config = {_id:'shard1',members: [
... {_id: 0, host: '192.168.0.27:27017'},
... {_id: 1, host: '192.168.0.28:27018'},
... {_id: 2, host: '192.168.0.29:27019',arbiterOnly:true}]
... }
{
"_id" : "shard1",
"members" : [
{
"_id" : 0,
"host" : "192.168.0.27:27017"
},
{
"_id" : 1,
"host" : "192.168.0.28:27018"
},
{
"_id" : 2,
"host" : "192.168.0.29:27019"
}
]
}
>
> rs.initiate(config);
{
"info" : "Config now saved locally. Should come online in about a minute.",
"ok" : 1
}
等待几分钟可查看replica set已经建立完毕:
shard1:ARBITER> rs.status();
{
"set" : "shard1",
"date" : ISODate("2011-10-14T05:30:38Z"),
"myState" : 7,
"members" : [
{
"_id" : 0,
"name" : "192.168.0.27:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 160417,
"optime" : {
"t" : 1313413539000,
"i" : 1
},
"optimeDate" : ISODate("2011-08-15T13:05:39Z"),
"lastHeartbeat" : ISODate("2011-10-14T05:30:37Z")
},
{
"_id" : 1,
"name" : "192.168.0.28:27018",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 160403,
"optime" : {
"t" : 1313413539000,
"i" : 1
},
"optimeDate" : ISODate("2011-08-15T13:05:39Z"),
"lastHeartbeat" : ISODate("2011-10-14T05:30:37Z")
},
{
"_id" : 2,
"name" : "192.168.0.29:27019",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"optime" : {
"t" : 0,
"i" : 0
},
"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
"self" : true
}
],
"ok" : 1
}
shard1:ARBITER>
在三台服务器上启动config server 进程:
[mongodb@localhost bin]$ ./mongod --configsvr --dbpath /home/mongodb/data/config --port 20000--logpath /home/mongodb/data/config.log --logappend --fork
在三台服务器上启动mongos进程,这里要指定对应的config servers:
[mongodb@localhost bin]$ ./mongos --configdb 192.168.0.27:20000,192.168.0.28:20000,192.168.0.29:20000--port 30000 --chunkSize 5 --logpath /home/mongodb/data/mongos.log --logappend --fork
连接到任意mongos进程,添加shard节点,要注意的是只需添加数据节点(主、从)就可以,仲裁节点不能添加:
[mongodb@localhost bin]$ ./mongo 192.168.0.29:30000/admin> db.runCommand({addshard:"shard1/192.168.0.27:27017,192.168.0.28:27018,192.168.0.29:27019",name:"s1",maxsize:20480}); { "ok" : 0, "errmsg" : "host 192.168.0.29:27019 does not belong to replica set as a non-passive membershard1" }> db.runCommand({addshard:"shard1/192.168.0.27:27017,192.168.0.28:27018",name:"s3",maxsize:20480}); { "shardAdded" : "s3", "ok" : 1 } > > db.runCommand({listshards:1}) { "shards" : [ { "_id" : "s3", "host" : "shard1/192.168.0.27:27017,192.168.0.28:27018" } ], "ok" : 1} >> db.stats(); { "raw" : { "192.168.0.27:20000,192.168.0.28:20000,192.168.0.29:20000" : { "db" : "admin", "collections" : 0, "objects" : 0, "avgObjSize" : 0, "dataSize" : 0, "storageSize" : 0, "numExtents" : 0, "indexes" : 0, "indexSize" : 0, "fileSize" : 0, "ok" : 1 } }, "objects" : 0, "avgObjSize" : NaN, "dataSize" : 0, "storageSize" : 0, "numExtents" : 0, "indexes" : 0, "indexSize" : 0, "fileSize" : 0, "ok" : 1 }
这样一个mongodb sharding cluster就搭建完成了。
引言
本篇要评测的NoSQL产品是Tokyo Cabinet和Tokyo Tyrant,Tokyo Cabinet是一个性能优秀的数据存储引擎,而Tokyo Tyrant则提供了访问Tokyo Cabinet数据的网络接口。这是一个很成熟的产品,在国内外也有众多的成功案例。
介绍
Tokyo Cabinet(简称TC)和Tokyo Tyrant(简称TT),顾名思义,是源自日本的开源项目。由FAL Labs维护,主要的开发人员是Mikio Hirabayashi。最早应用在日本最大的SNS网站mixi.jp上成功后而声名鹊起。
TC是一个用C写的数据存储引擎,以key-value的方式存储数据,支持Hash、B+ tree、Hash Table等多种数据结构。同时提供了C、 Perl、 Ruby、Java和Lua等多种语言的API支持,但是如果通过网络来访问,就需要用TT。TT同样是用C写的,支持从网络端高并发、多线程的访问TC。另外TC/TT支持master/slave架构,可以通过配置实现高可用性,这也是很不错的一个特性。
TC因为支持灵活的数据结构而倍受欢迎,特别是Hash Table类型,很像传统的关系型数据库,只是每条存储记录的列是自由定义的,可以通过列作为条件来查询,十分方便。这是很多key-value结构的NoSQL产品所不具备的特性。当然,Hash Table类型和Hash、B+ tree相比存取效率会低一些,任何事物都是有两面性的,在带来高度灵活度的同时,必然要牺牲部分的效率。
TC/TT是一个久经考验的很稳定的产品,在千万及以下数据量级别表现出色。但是开发者由于种种原因,已经很长时间没有更新版本了,而是推出了对应升级产品,叫做Kyoto Cabinet和Kyoto Tycoon,这也给TC/TT的前景带来了不明朗的因素,很明显作者是鼓励人们使用升级的产品,但是由于新产品没有更多的成功案例,在业界的影响力反而不如TC/TT,因此在现阶段,TC/TT仍然是一个不错的NoSQL选择。
安装和使用
一、下载相关软件的最新版本(TC、TT和TC的lua扩展):
[root@localhost tctt]# wget http://fallabs.com/tokyocabinet/tokyocabinet-1.4.47.tar.gz
[root@localhost tctt]# wget http://fallabs.com/tokyotyrant/tokyotyrant-1.1.41.tar.gz
[root@localhost tctt]# wget http://fallabs.com/tokyocabinet/luapkg/tokyocabinet-lua-1.9.tar.gz
注意这里的lua扩展是可选的,如果不需要使用lua接口,可以不必安装。
二、安装lua脚本,注意这里不能通过yum的方式安装,否则后面装tt时会提示找不到lua.h文件:
wget http://www.lua.org/ftp/lua-5.1.4.tar.gz
make linux
make install
安装过程中一般会报错如下:
luaconf.h:275:31: error: readline/readline.h: No such file or directory
luaconf.h:276:30: error: readline/history.h: No such file or directory
通过yum安装readline-devel即可:
yum install readline-devel
三、安装TC、TT和TC的lua扩展,注意的是如果要使用LUA接口,那么编译TT的时候要加--enable-lua参数:
[root@localhost tctt]# tar zxvf tokyocabinet-1.4.47.tar.gz
[root@localhost tctt]# cd tokyocabinet-1.4.47
[root@localhost tokyocabinet-1.4.47]# ./configure
[root@localhost tokyocabinet-1.4.47]# make
[root@localhost tokyocabinet-1.4.47]# make install
[root@localhost tctt]# tar zxvf tokyocabinet-lua-1.9.tar.gz
[root@localhost tokyocabinet-lua-1.9]# make
[root@localhost tokyocabinet-lua-1.9]# make install
[root@localhost tctt]# tar zxvf tokyotyrant-1.1.41.tar.gz
[root@localhost tctt]# cd tokyotyrant-1.1.41
[root@localhost tokyotyrant-1.1.41]# ./configure --enable-lua
[root@localhost tokyotyrant-1.1.41]# make
[root@localhost tokyotyrant-1.1.41]# make install
至此就安装完成了,整个过程很简单,当然安装过程可能需要一些其他的程序包,可以根据提示进行安装即可。
TT提供了很多命令行工具来管理数据库,比较常用的两个是ttserver和tcrmgr。
Ttserver的用法如下:
ttserver [-host name] [-port num] [-thnum num] [-tout num] [-dmn] [-pid path] [-kl] [-log path] [-ld|-le] [-ulog path] [-ulim num] [-uas] [-sid num] [-mhost name] [-mport num] [-rts path] [-rcc] [-skel name] [-mul num] [-ext path] [-extpc name period] [-mask expr] [-unmask expr] [dbname]
各个参数的说明可查看官方文档,这里就不一一列举了。我们可以建立一个hash table类型和一个B+ tree类型的数据库:
ttserver -host 192.168.0.35 -port 11301 -thnum 8 -dmn -pid /home/tc/test_data/test_data_b.pid -log /home/tc/test_data/test_data_b.log -le -ulog /home/tc/test_data/ -ulim 128m -sid 1 -rts /home/tc/test_data/test_data_b.rts /home/tc/test_data/test_data.tcb
ttserver -host 192.168.0.35 -port 11302 -thnum 8 -dmn -pid /home/tc/test_data/test_data_t.pid -log /home/tc/test_data/test_data_t.log -le -ulog /home/tc/test_data/ -ulim 128m -sid 1 -rts /home/tc/test_data/test_data_t.rts /home/tc/test_data/test_data.tct
注意最后一个参数的扩展名tcb和tct决定了数据库的类型分别是B+ tree和table。
通过tcrmgr命令行工具可以管理远程数据库,比如我们要查看key为1的记录可以这样操作:
[root@localhost tc]# tcrmgr get -port 11301 192.168.0.35 1
要了解更详细的信息,读者可以参看官方文档。
测试说明
一、测试环境
TC/TT部署在一台PC 服务器上,配置如下:
CPU为Xeon 2.80GHz *4
内存为4G
硬盘为一块400G SATA盘
操作系统为64位CentOS 5.3版本
二、测试方法
这里仍然采用第三方实现的PHP客户端进行测试,网址为http://pecl.php.net/package/tokyo_tyrant,这是一个标准的PHP扩展程序,可以编译到PHP运行环境中。要说明的是,这个PHP客户端实现的是TT接口,肯定比使TT自带的tcrtest效率要低一些,但是我们的测试要尽量模拟实际的生产环境,所以这里使用了第三方的PHP客户端。
为了不对测试服务器产生额外的影响,测试客户端部署在另外一台独立的服务器上,运行的PHP的版本是5.3.5,web server是Nginx 0.8.54,通过fastcgi的方式调用PHP服务。使用apache ab工具实现多个请求和并发操作。
为了更全面的反应TC/TT的性能,我对B+ tree和Hash Table两种数据库类型分别进行了测试,就使用上文提到的ttserver示例语句来建立测试数据库,每个类型的测试又分为两个步骤,首先是写操作,通过500个请求,每个请求写入10000条记录,并发度为2来共写入500万条数据,数据的key为数字1到5000000,value大小为100个字节。然后是读操作,也是用500个请求,每个请求随机根据key值读出10000条记录,并发度为10共读出500万条记录,评测的重点是写入和读出数据的时间,以及在此过程中服务器的资源使用情况。
测试结果及总结
一、B+tree类型写操作
成功写入500万条记录,共耗时739秒,平均每秒写入数据6766笔。数据文件大小137M。写入过程中,服务器内存、CPU和磁盘等资源使用情况如下图所示:
可见,CPU使用率平稳,Idle值稳定在73到83之间,wait值稳定在7到12之间。内存分配上的变化较大,主要用来缓存数据,cache部分上升了1.1G,但是没有交换区到内存的换入换出。磁盘IO表现有周期性的上下波动,估计和TC的实现机制有关,数据先写入内存缓冲区,然后定期的刷新到磁盘。
二、B+tree类型读操作
成功读出500万条记录,共耗时1171秒,平均每秒读出数据4270笔。
读数据过程中没有发生磁盘IO。CPU较繁忙,Idle值稳定在38左右,等待CPU资源的进程一直在1到6个之间。内存表现平稳没有波动。
三、Hash Table类型写操作
成功写入445万条记录,写入失败55万条记录,共耗时1560秒,平均每秒写入数据2853笔。数据文件大小538M。写入过程中,服务器内存、CPU和磁盘等资源使用情况如下图所示:
CPU使用率较平稳,Idle值稳定在60到80之间,wait值最高在50左右,稳定在20到40之间。内存cache部分上升了0.7G用于缓存数据。磁盘IO表现有呈阶梯状的上升,最后达到100%,导致无法成功写入数据。
四、Hash Table类型读操作
成功读出500万条记录,共耗时175秒,平均每秒读出数据28571笔。
读数据过程中没有发生磁盘IO。CPU较繁忙,Idle值稳定在37到41之间,等待CPU资源的进程一直在1到9个之间。内存表现平稳没有波动。
通过以上测试结果可以说明,TC/TT写入的数据的时候,先缓冲到内存中,然后通过一定的机制写入磁盘,这也是写的效率较高的原因,但是由此带来了周期性的磁盘繁忙,也可能有丢失数据的风险。写入的数据完全缓存到了文件系统中,所以cache部分占用的内存大量增加,这也是读取数据的时候没有发生磁盘IO的原因。B+ tree类型写入性能十分优异,令人惊讶的是读出的速度反而慢于写入的速度。Hash Table类型写入性能一般,但读取性能良好。总体上来说TC/TT在非海量数据的情况下表现不错,服务器资源占用稳定,读写效率较高。
备注:本文发表在IT168网站上,版权所有,转载请注明出处。
技术组织
最近评论
历史归档
广告位


























