Hello World
Spiga

MongoDB与Tokyo Tyrant性能比较(1):基础CRU操作

2010-02-24 23:45 by 老赵, 14285 visits

以前的项目大都把数据存放在关系型数据库中,关系型数据库的优势在于使用普及,资料丰富,且有大量辅助类库来简化开发。当然它们的问题比较明显的,一是在数据量上升的情况下伸缩性比较差,且进行结构调整的代价比较高。因此现在有个所谓NoSQL的“运动”也逐渐普遍起来了,它便是借助一些非关系型存储方式来开发项目(个人认为其实将它解释为Not Only SQL更为合适)。因此在新项目里,我也想尝试一下使用之前一直只是“听说”的存储方式。

在和同事的交流过程中,我了解到他们的项目正在尝试使用Tokyo Tyrant(后称TT)进行存储,并且据说效果不错,因此我一开始也打算尝试使用TT进行主要存储,为此也花了一定时间为其编写.NET平台下的驱动程序。不过在驱动程序的开发过程中,我逐渐意识到TT的功能有着严重的限制,似乎并不适合作为接下来项目的主要存储方式。因此,我又将眼光转向了之前关注过的MongoDB上。MongoDB也是NoSQL的代表之一,是一个面向文档的,架构灵活(Schema-less)的存储方式,在仔细阅读相关资料之后,我发现它的功能与TT相比可谓天上地下,非常适合许搭建各类项目(关于这点以后有机会再谈)。

不过,既然选择NoSQL的原因是性能,那么他们的性能表现究竟如何呢?TT的性能表现在业界非常出名,而MongoDB的使用便相对较少了(当然,官网列举了不少案例,最近著名的开源网站SourceForge也打算使用MongoDB重新设计他们的网站)。为此,我决定亲手比较一下两者的性能表现。

测试环境及数据

由于缺乏合适的环境,因此我不得不在我的MBP上进行这次性能比较,参数如下:

  • OS:Mac OS X v10.6.2(Snow Leopard)
  • RAM:4GB / 1067MHz / DDR 3
  • CPU:2.53GHz Intel Core 2 Duo
  • 64-bit Kernel and Extensions:Yes
  • TT在64 bit环境下编译,MongoDB使用64 bit版本。

在测试执行时,我尽量关闭多余的应用程序,避免其它因素造成影响。同样,由于条件限制,我只得把客户端和服务器跑在同一台机器上。测试代码使用Ruby编写,这是由于两者都有官方提供的驱动程序。此外,由于我对于两者的优化都不太熟悉,因此它们都使用了默认的配置。

关于测试数据,我将存入110万条“新闻”数据,包含以下字段:

  • ID:标识,32位整数,带索引
  • Title:标题,字符串
  • CategoryID:分类ID,32位整数,带索引
  • CreateTime:日期,保存为32位整数,带索引
  • UserID:用户ID,32位整数,带索引
  • Tags:标签集合,字符串数组,带索引
  • Source:来源,字符串
  • SourceUrl:来源URL,字符串
  • Status:状态,32位整数

为了相对接近真实的环境的数据分布特征(便于以后进行查询操作比较),我设计了这样的测试数据(具体可阅读代码):

  • 2万个分类,分别拥有10个至100条新闻,总共110万条记录。
  • 1万个用户,根据分类id取模得到其归属。
  • 创建时间从2010年1月1日起递增,每条记录增加1秒。
  • 每条记录拥有2到6个Tag,除了其中一个之外,都从一个1.7万个Tag库中获取。
  • 每条记录的字符串字段都不长(20-30字符)

由于TT只支持字符串的值(但可以建立数字索引,会将字符串当作数字来识别),因此事实上所有的值都会转化为字符串进行保存。此外,由于存在“根据Tag查找新闻”的业务,因此对于Tags字段也建立了索引,其中MongoDB直接支持对字符串数组的索引(索引其中每个元素),而在存入TT时则转化为空格分隔的字符串,并为其建立倒排索引(Token Inverted Index)以便进行全文查找。

在存储方式上,所有数据将放入MongoDB的同一个集合内,而TT则选用Table Database存储结构。在使用TT时,另一种做法是完全使用键值对来保存一条记录的各个字段,不过这样做会大大限制TT的功能,或是会为系统编写带来额外的复杂度,便不考虑Hash / B+ Tree / Fixed-length等存储方式了。

插入操作性能比较

具体代码在tt-insert.rb及mdb-insert.rb文件中。两段代码均使用一个连接,使用循环每次插入一条:由于如果每次都建立连接,会在TCP/IP连接的打开关闭上消耗大部分时间,由于实际项目中基本都会使用连接池等机制来复用连接,因此这方面便不多做考虑;再者,由于实际插入时几乎不可能批量操作,因此这里并不使用两者提供的批量插入功能。脚本每插入10万条记录便打印出所耗时间,结果如下:

从结果上看,MongoDB大约有10%的领先,不过总体来说两者的差距不大。值得一提的是,TT在数据量越大的情况下,每插入10万条记录的耗时越长(也从同事的使用过程中确认了这一点)。因此,一开始两者插入速度几乎没有差距,但是慢慢地TT便落后于MongoDB了。目前推测这可能是TT的Table Database存储结构特有的问题,不知道随着数据量的增长TT表现如何,因为对于一个大型系统来说,100多万条记录实际上只是一个很小的数目。

有同事推测,TT插入性能低是因为建立了Tags字段的倒排索引,于是我也测试了不在Tags字段上建索引的情况。令人惊讶的是,同样去除Tags字段的索引之后,MongoDB的性能提升超过20%,而TT的性能提升却微乎其微。

值得一提的是,放入相同的记录之后,TT的文件为400多M,但MongoDB则为整整2G。因此推测这是MongoDB进行空间预分配的结果,邮件列表上的这个讨论也证实了这一点。此外,上面每次测试都是从零开始的,经测试如果在插入前为MongoDB预分配文件空间,则性能还会有些许提高。

通过主键获取记录性能比较

具体代码在tt-get-by-key.rb及mdb-get-by-key.rb文件中。两段代码同样均使用一个连接,使用循环进行100万次Get操作,每次都随机获取一个110万以内的数字,并作为ID进行Get操作。同样,每10万次Get操作后打印出所耗时间,结果如下:

在Get操作方面,MongoDB有大约20%的领先。不过从实际情况分析,这方面的差距也不是太大,因为在项目中根据主键获取记录的操作8成以上都是落在缓存上的,因此在这方面对存储的要求并不高。

通过主键更新记录性能比较

具体代码在tt-update-by-key.rb及mdb-update-by-key.rb文件中。两段代码各自使用同一个连接,使用循环进行大量的更新操作。每次随机获取一个110万以内的数字,并作为ID更新其CreateTime、Title、Source、SourceUrl、Status五个字段。结果如下:

首先是MongoDB的三次测试结果,每次更新100万条数据。从数据上看,三次查询的性能越来越高,这个现象在重启服务器之后可以重现(三次测试之间并没有重启服务器)。

请注意,TT的三次测试均只更新了10万条记录(因为100万条记录耗时太长)。和MongoDB类似,一开始查询性能较差,但是会慢慢提高,这个现象在重启服务器后也能重现,于是猜测是缓存的结果。即便如此,TT的更新时的最高速度只有大约没秒1500次,而MongoDB却超过了每秒5000次。这是因为TT在功能上有硬伤:它无法像SQL的UPDATE语句那样更新某条记录的部分字段,因此必须全部取出,修改后再整体写入。而MongoDB支持高效的直接修改——这也成为MongoDB放在首页上的“招牌功能”

因此,TT对于“更新某个分类下所有新闻的状态”这样的操作会很不适应,而它的这个限制导致我们很难实现如“乐观并发控制”这样的手段。此外,在实际应用中客户端与服务器不会使用本地连接,因此在生产环境中TT和MongoDB在这方面的差距可能会更明显一些。

总结

到目前为止,我们测试了TT和MongoDB在CRU三种基本操作下的性能,似乎MongoDB全面胜过Tokyo Tyrant。不过需要强调的是,这个测试还很不全面:它没有使用合适的环境,也没有对两者进行适当的优化。此外,两者在并发情况下,或是同时读写下的表现如何也是值得进一步考察的,我也会设计更多的测试用例。因此,这里的数据仅供参考,如果有合适的环境我也会重新进行测试。

本文所有代码:http://github.com/JeffreyZhao/mdb-tt-benchmark

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

56 条回复

  1. sun8134
    *.*.*.*
    链接

    sun8134 2010-02-24 23:48:00

    少见的竟然能坐到沙发...

  2. Ivony...
    *.*.*.*
    链接

    Ivony... 2010-02-24 23:56:00

    我最近的方向是把关系型数据库变成只存放索引数据的东东。。。努力啃关系模型的底层理论ing。。。。

  3. 老赵
    admin
    链接

    老赵 2010-02-25 00:00:00

    @Ivony...
    其实也就是只存小字段是吧?

  4. Inrie(洪小军)
    *.*.*.*
    链接

    Inrie(洪小军) 2010-02-25 00:00:00

    关注...

    最近也在做这样的选型,目前在TC/TT、MongoDB、Hypertable和Cassandra中选择

  5. Ivony...
    *.*.*.*
    链接

    Ivony... 2010-02-25 00:28:00

    Jeffrey Zhao:
    @Ivony...
    其实也就是只存小字段是吧?



    只存小字段已经是比较流行的趋势了把,image、text啥的现在已经很少用了。。。

    我会更激进一些,就像文件系统,数据和索引是分开的,换言之所有的数据都不在数据库中,数据库中只是存一些需要索引的字段的冗余而已。将来的方向可能是先把数据存到某个地方(如文件系统、分布式文件系统),然后再从数据中抽取关系化索引,再将索引存入数据库。

    当然如果数据本身够小,也可以直接存到数据库里面去。

    和NTFS的思路差不多。

  6. 辰
    *.*.*.*
    链接

    2010-02-25 00:31:00

    核心技术是DHT,豆瓣的架构核心也是DHT。

    而DHT目前最优秀的算法是chord,也是bt,emule的核心算法。

    迟点自己有时间也会做个chord出来,作为分布式缓存。不过查询目前没有好的想法,如果只是key-value就容易,但是要像sql一样。。。就没有想法了。

  7. Ivony...
    *.*.*.*
    链接

    Ivony... 2010-02-25 00:33:00

    其实脑子里想的蓝图还是很大的,我的野心是建立一整套的理论模型,将内存中的Object、缓存中的数据、持久化在硬盘上的数据,统一起来。

    在内存中怎样检索对象,在缓存中怎样检索对象,在磁盘上怎样检索。

    能不能做到,不用关心Load和Save,好像内存永远用不完,对象都在内存里,把这些肮脏的事情都用一个框架搞定。

  8. 老赵
    admin
    链接

    老赵 2010-02-25 00:51:00

    @Ivony...
    我说的小字段不是只类型啦,就是指内容小的需要索引的,比如文章内容就不放在行里。
    这样每行的内容就少了,内存里可以一次性加载的东西也多了,性能可以高一些。

  9. 老赵
    admin
    链接

    老赵 2010-02-25 00:53:00

    @Ivony...
    你都不写你的想法和成果,这个很不好啊……

  10. 老赵
    admin
    链接

    老赵 2010-02-25 00:53:00

    @辰
    那么你可以关注一下MongoDB,我更倾向于不把它看作DHT,DHT还是简单了些了。
    在MongoDB中,除了不能join,基本单表SQL能干的都可以搞了,甚至更丰富。

  11. 老赵
    admin
    链接

    老赵 2010-02-25 00:58:00

    Inrie(洪小军):
    最近也在做这样的选型,目前在TC/TT、MongoDB、Hypertable和Cassandra中选择


    你也要写点东西出来才好

  12. Ivony...
    *.*.*.*
    链接

    Ivony... 2010-02-25 00:59:00

    Jeffrey Zhao:
    @Ivony...
    你都不写你的想法和成果,这个很不好啊……




    我经常太监掉一些计划,这个你知道的。所以吊大家的胃口是很不好的。。。。

  13. 老赵
    admin
    链接

    老赵 2010-02-25 01:02:00

    @Ivony...
    那就再这里多说说然后我来写……

  14. Ivony...
    *.*.*.*
    链接

    Ivony... 2010-02-25 01:07:00

    Jeffrey Zhao:
    @Ivony...
    我说的小字段不是只类型啦,就是指内容小的需要索引的,比如文章内容就不放在行里。
    这样每行的内容就少了,内存里可以一次性加载的东西也多了,性能可以高一些。




    这个就还是比较流行的做法了,不需要索引的数据另行存放。

    我的思想可能最大的不同在于,从任何数据都不要放在数据库中出发,只将必要的数据(需要索引或关联)放在数据库中。同时数据库中才是冗余数据(这是根本的不同吧,即数据库中真的只是索引,数据项自身的完整性,比数据集内每一项索引的数据完整性更重要),根据需求动态规划在数据库中的数据。

  15. 温景良(Jason)
    *.*.*.*
    链接

    温景良(Jason) 2010-02-25 08:17:00

    呵呵,我一直也不知道,不需要sql,这些数据怎么存储,像Google的数据到底是怎么存储,希望老赵多写点啊

  16. 大石头
    *.*.*.*
    链接

    大石头 2010-02-25 08:24:00

    哈哈,多多关注各种存储方式!

    我一直只把数据库当作存储数据的仓库,实际上因为我们环境的多变,往往需要根据不同的情况选择不同的数据库。

    不知道db4o算不算老赵讨论的这个范围

  17. yishh[未注册用户]
    *.*.*.*
    链接

    yishh[未注册用户] 2010-02-25 08:58:00

    非常感谢楼主这个测试,我们之前一直在使用TT和MongoDB,到目前为止都表现良好。不过没有做过这两者直接的对比测试,都是拿来和postgresql做的对比,期盼楼主到这个测试能够完善起来。

  18. 老赵
    admin
    链接

    老赵 2010-02-25 08:59:00

    @大石头
    db4属于这个范畴,我认为

  19. 老赵
    admin
    链接

    老赵 2010-02-25 09:00:00

    @温景良(Jason)
    这个为什么会想不到?不用RDBMS,自己存文件之类的也可以啊。

  20. 温景良(Jason)
    *.*.*.*
    链接

    温景良(Jason) 2010-02-25 09:03:00

    Jeffrey Zhao:
    @温景良(Jason)
    这个为什么会想不到?不用RDBMS,自己存文件之类的也可以啊。


    呵呵,这个我知道,Facebook也有自己的文件系统,但是我想知道这样存储的好处还有原理,如果做企业开发可行吗

  21. 大石头
    *.*.*.*
    链接

    大石头 2010-02-25 09:29:00

    这次过年从家里回来,几经周折,在合肥火车站广场看的db4,觉得还不错。

    建议老赵多多测试几种,一直很敬佩你的严谨的态度^_^

  22. 老赵
    admin
    链接

    老赵 2010-02-25 09:32:00

    @温景良(Jason)
    企业开发是个模糊的概念,你这么说其实等于没说,还是提出具体的需要比较好……

  23. 寒星
    *.*.*.*
    链接

    寒星 2010-02-25 10:07:00

    我记得MongoDB好象没有Daemon service~

  24. 瘦子
    *.*.*.*
    链接

    瘦子 2010-02-25 10:38:00

    很好的文章

    MONGODB实际上是内存DB, 大部分的实时操作都在内存中进行,然后FLUSH到磁盘上的。 因此比给人感觉的要强劲。即便在千万级的数据下也没感觉到性能明显的下降,当然32位的机器内存是不够的。

    想请教2个MONGODB的实践问题,感觉挺重要:

    1、如何获取新插入的数据的ID号?

    2、如何实现MVCC控制(乐观锁)

    盼复

  25. 第一控制.NET
    *.*.*.*
    链接

    第一控制.NET 2010-02-25 10:49:00

    打算试试python+MongoDB

  26. 恒星的恒心
    *.*.*.*
    链接

    恒星的恒心 2010-02-25 12:08:00

  27. 老赵
    admin
    链接

    老赵 2010-02-25 12:47:00

    寒星:我记得MongoDB好象没有Daemon service~


    有mongod还怕没有daemon service呀?

  28. 老赵
    admin
    链接

    老赵 2010-02-25 12:48:00

    瘦子:
    想请教2个MONGODB的实践问题,感觉挺重要:
    1、如何获取新插入的数据的ID号?
    2、如何实现MVCC控制(乐观锁)


    1、ID可以自己生成咯
    2、乐观锁其实就是自己定义一个timestamp字段了,set的时候看看返回true还是false。

  29. 寒星
    *.*.*.*
    链接

    寒星 2010-02-25 12:54:00

    Jeffrey Zhao:

    寒星:我记得MongoDB好象没有Daemon service~


    有mongod还怕没有daemon service呀?


    之间准备选用这个,后来还是选择berkerlydb了。

  30. 韦恩卑鄙 v-zhewg @waynebab…
    *.*.*.*
    链接

    韦恩卑鄙 v-zhewg @waynebaby 2010-02-25 14:54:00

    @Ivony...
    感觉其实就是Hashtable 的思想嘿嘿嘿
    Hashtable只保存Hash和目标地址么

    无论如何都会有各自原定位符号在里面的
    当数据复杂到一定程度 这个定位符号也会无止境增加 oh yeah

  31. 宗哥
    *.*.*.*
    链接

    宗哥 2010-02-26 11:27:00

    连接持等机制来复用连接?
    新名词?OR 连接持=连接池?

  32. 老赵
    admin
    链接

    老赵 2010-02-26 22:05:00

    @宗哥
    改好了

  33. 蛙蛙王子
    *.*.*.*
    链接

    蛙蛙王子 2010-02-28 22:55:00

    刚看了ROBIn的帖子,来博客园就看到老赵也在整这个了。
    以前想研究下MemorCachedDb,就新浪开源的那个东西,底层用BDB做的存储,后来环境都没弄好就开始忙别的了,汗。

  34. xiebbs[未注册用户]
    *.*.*.*
    链接

    xiebbs[未注册用户] 2010-03-02 10:57:00

    @sun8134
    坐沙发,发点话。。

  35. 小翔[未注册用户]
    *.*.*.*
    链接

    小翔[未注册用户] 2010-03-03 10:50:00

    @Ivony...
    子表就可以了.用表当索引.

  36. devin_[未注册用户]
    *.*.*.*
    链接

    devin_[未注册用户] 2010-03-06 19:48:00

    非常好的测试!
    但对TT好像有点不公平,感觉TT性能不应该这样,因为TC(TokyoCabinet)和TT很依赖Hash 桶(bucket)的大小,很可惜的是,这个大小是用户设置的,如果用户没有设置,就是默认大小,搞笑的是它不会随着数据量的增加而自动增大。所以如果没有设置bucket的大小,TT/TC的性能在数据量增加的时候,会因为Hash碰撞,性能急剧下降。

    这个大小默认是
    #define TDBDEFBNUM 131071 // default bucket number

    也就是13万, 而你这里的测试数据有100万,碰撞率已经很高了,这样测试出来的结果TT比MongoDB差是很正常的。

  37. 老赵
    admin
    链接

    老赵 2010-03-06 20:02:00

    @devin_
    有机会提高点bucket数量再试试看

  38. anco[未注册用户]
    *.*.*.*
    链接

    anco[未注册用户] 2010-03-11 16:45:00

    @devin_
    的确如此,对于TT来说默认的bnum太小,这对TT不公平。另外楼主只测试内存命中的情况,当存储的数据远大于内存的时候,对于MongoDB的测试才有意义:不可能数据最多只有内存的大小。很期待能看到更新的结果。

  39. 老赵
    admin
    链接

    老赵 2010-03-11 17:26:00

    @anco
    有道理,不知道有没有办法可以限制MongoDB的内存使用,呵呵

  40. devin_[未注册用户]
    *.*.*.*
    链接

    devin_[未注册用户] 2010-03-14 11:44:00

    Jeffrey Zhao:
    @anco
    有道理,不知道有没有办法可以限制MongoDB的内存使用,呵呵



    即使限制了MongoDB的内存使用,恐怕测试出来的结果也不能反映真实情况。
    以Linux为例,假设现在机器有4G内存,MongoDB限制为只使用1G内存,而测试数据+索引有2G。在测试刚开始的时候,2G数据都在硬盘上,然后MongoDB会把2G数据中的1G逐渐缓冲到MongoDB的进程空间中来,但是剩下的1G数据也不会老老实实的呆在硬盘上,因为操作系统会发现这1G数据也被频繁访问,所以这1G的数据最后也会被几乎完全地缓冲到4G-1G=3G的没有被程序使用的内存中来。这样,等于所有数据都在内存中。

    所以最好的测试是用实际大于硬件内存的数据来测试,如,使用8G数据在1G的内存机器上进行测试,而且为了模拟真实情况,最好数据的访问是完全随机的。

    很期待laozhao的新的测试。

  41. 老赵
    admin
    链接

    老赵 2010-03-14 15:23:00

    @devin_
    case设计容易,还是资源限制难。
    虚拟机也是不合适的。
    你觉得在4G内存里,多大的数据量比较合适?

  42. 九色迷离
    *.*.*.*
    链接

    九色迷离 2010-03-22 18:46:00

    请教一下:
    我在控制台save一条记录时,英文的没问题,一输入中文就弹出mongo.exe已停止工作是怎么回事呢?

  43. 老赵
    admin
    链接

    老赵 2010-03-22 20:33:00

    @九色迷离
    没遇到过,我跑下来很正常……

  44. 链接

    tobeornot 2010-04-23 19:37:28

    我推荐嵌入式数据库BerkeleyDB和sqlite,sqlite。其中sqlite支持‘不复杂的’sql语句,操作方便!BerkeleyDB目前对sql语句支持力度不够,开发繁琐,数据库当掉后我还没办法恢复。我个人觉得NoSql发展到后面可能都会去支持sql语句!Oracle的Berkeleydb就是个例子!

  45. amen
    61.232.0.*
    链接

    amen 2010-06-21 16:22:31

    赵姐夫: “RAM:4GB / 1067MHz / DDR 3 ” 有这样的RAM 吗?

  46. 老赵
    admin
    链接

    老赵 2010-06-21 17:34:12

    @amen

    应该就是从配置单上贴下来的吧。

  47. 助平君
    205.156.160.*
    链接

    助平君 2010-07-14 15:11:04

    http://msdn.microsoft.com/en-us/magazine/ee310029.aspx

    看到32位下只支持大约2GB的数据文件,不知道64位能支持到多大的数据文件?

  48. 助平君
    205.156.160.*
    链接

    助平君 2010-07-14 15:53:34

  49. 老赵
    admin
    链接

    老赵 2010-07-14 17:54:18

    @助平君

    其实就是一个int的可用范围?我猜64位可用数据文件大小是2^63字节,呵呵。

  50. 助平君
    205.156.160.*
    链接

    助平君 2010-07-15 14:13:14

    @老赵

    make sense

    有留意过CouchDB吗?公司有项目使用CouchDB吗?http://tech.qq.com/a/20100715/000323.htm

  51. southsiberia
    110.153.141.*
    链接

    southsiberia 2010-09-25 02:41:44

    老赵能问你一个问题,上面博文中的四个IFrame在你那里能否看见,我这里显示

    连接被重置

    载入页面时到服务器的连接被重置。

    • 此站点暂时不可用或者太忙。请稍后重试。

    • 如果您无法载入任何页面,请检查您计算机的网络连接。

    • 如果您的计算机受到防火墙或代理服务器的保护,请确认 Firefox 被授权访问网页。

    好像被HX了,我用7daili可以看到,你那里不用代理可以看到吗???

  52. 老赵
    admin
    链接

    老赵 2010-09-25 17:22:32

    @southsiberia

    有的地方要代理,有的地方不要。

  53. TechZi
    123.120.15.*
    链接

    TechZi 2010-10-18 21:57:40

    请教老赵 测试结果中的时间的单位什么呀?是毫秒吗?

  54. 老赵
    admin
    链接

    老赵 2010-10-19 10:22:10

    @TechZi

    嘿嘿,十万次Get,就几十毫秒吗?

  55. 初学者
    115.236.66.*
    链接

    初学者 2011-09-27 18:10:07

    老赵你好,文章中提到在TT驱动程序的开发过程中,逐渐意识到TT的功能有着严重的限制,似乎并不适合作为接下来项目的主要存储方式。 请问你意识到的功能的严重限制指的是什么?能详细介绍一下吗?

  56. ATTACKCITYMASTER
    113.108.48.*
    链接

    ATTACKCITYMASTER 2015-07-15 12:06:01

    @瘦子,mongodb的_id是根据4个属性生成的,你可以看源码,大概是机器,时间,还有2个忘了,这个是随时生成的。 想知道也只能去数据库查询

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我