Tokyo Tyrant(TTServer)系列-介绍和安装

Tokyo Cabinet 是日本人 Mikio Hirabayashi(平林幹雄)のページ  开发的一款DBM数据库(注:大名鼎鼎的DBM数据库qdbm就是他开发的),该数据库读写非常快。insert:0.4sec/1000000 recordes(2500000qps),写入100万数据只需要0.4秒。search:0.33sec/1000000 recordes (3000000 qps),读取100万数据只需要0.33秒。下图为各种key-value数据库读写数据的性能测试,可以看出Tokyo Cabinet的速度是非常快的。
Tokyo Tyrant(TTServer)系列-介绍和安装_安装 ttserver



Tokyo Tyrant 提 供dbm数据库Tokyo Cabinet的网络接口。它使用简单的基于TCP/IP的简单二进制协议进行通信。同时它拥有Memcached兼容协议并且可以用HTTP/1.1协 议进行数据交换。所以实现了跨平台,跨语言使用Tokyo Tyrant。采用热备份,更新日志记录,复制(replication)来实现高可用性和高可靠性。到目前为止,Tokyo Tyrant只能运行在inux, FreeBSD, Mac OS X, Solaris。

     下面我们介绍如何安装Tokyo Tyrant。

      首先我们安装Tokyo Cabinet.

     #wget http://tokyocabinet.sourceforge.net/tokyocabinet-1.4.9.tar.gz
     #tar -xzvf tokyocabinet-1.4.9.tar.gz
     # cd tokyocabinet-1.4.9
     # ./configure
     # make
     # make install

     OK,我们装好了Tokyo Cabinet,再继续安装Tokyo Tyrant。

     # wget http://tokyocabinet.sourceforge.net/tyrantpkg/tokyotyrant-1.1.16.tar.gz
     # tar -xzvf tokyotyrant-1.1.16.tar.gz
     #cd tokyotyrant-1.1.16
     # ./configure
     # make
     #make install
  
     OK,我们目前就完成了Tokyo Tyrant的安装。安装非常的简单。安装好的Tokyo Tyrant的启动程序文件叫ttserver,所以我们也简称ttserver.下面我们简单启动一个数据库,测试我们是否安装成功。

     # ./ttservctl start
    
启动完成。这是自带的一个启动脚本,你可以拷贝到到任意目录,修改里面的参数。
   
# telnet 127.0.0.1 1978
   
然后我们telnet到1978端口,因为刚才的脚本默认启动的数据库使用了1978端口。
   
如果顺利,你可以看到可以连通,键入stats然后回车,可以看到一些类似如下的状态信息

 
stats
STAT pid 24755
STAT uptime 37
STAT time 1236396011
STAT version 1.1.16
STAT rusage_user 0.000000
STAT rusage_system 0.008998
STAT curr_items 0
STAT bytes 4198720
END

   请注意:在32位操作系统下,Tokyo Cabinet的单个数据库文件不能超过2G,而在64位操作系统下则没有这一限制。所以推荐使用64位操作系统和CPU。

Tokyo Tyrant(TTServer)系列-启动参数和配置

启动参数介绍

ttserver命令可以启动一个数据库实例。因为数据库已经实现了Tokyo Cabinet的抽象API,所以可以在启动的时候指定数据库的配置类型。
支持的数据库类型有:
1、内存hash数据库
2、内存tree数据库
3、hash数据库
4、B+ tree数据库
命令通过下面的格式来使用,‘dbname’制定数据库名,如果省略,则被视作内存hash数据库。

ttserver [-host name] [-port num] [-thnum num] [-tout num] [-dmn] [-pid path] [-log path] [-ld|-le] [-ulog path] [-ulim num] [-uas] [-sidnum] [-mhost name] [-mport num] [-rts path] [-ext path] [-extpc name period] [-mask expr] [dbname]

下面来说这些参数的功能:

•    -host name :指明服务器的hostname或者ip地址。默认服务器的所有地址都会被绑定。比如:指定127.0.0.1这样的ip,就只是本地可以访问了。
•    -port num : 指定服务启动的端口. 默认1978.如果要启动多个数据库实例,端口需要不一样。
•    -thnum num : 指定服务工作的线程数。默认8.
•    -tout num : 指定每个会话的超时时间。默认永不超时。
•    -dmn : 以守护进程方式运行。
•    -pid path : 输出进程IP到指定的文件。
•    -log path : 输出日志信息到指定文件。
•    -ld : 日志中记录debug信息。
•    -le :日志中只记录错误信息。
•    -ulog path : 指定存放更新日志(update log)的目录.可以用来备份恢复数据库,主从库之间的同步。
•    -ulim num : 指定每个更新日志文件的大小限制.
•    -uas :使用异步IO记录更新日志。(使用此项可以减少写入日志的IO开销,但是在服务器意外关机,进程被kill时可能会丢失数据。根据经验,一般可以不使用)。
•    -sid num : 指定服务的ID号。主从复制的时候通过不同的ID号来识别。
•    -mhost name : 指定主从复制模式下的主服务器的IP或域名。
•    -mport num : 指定主从模式下主服务器的端口号.
•    -rts path : 指定用于主从复制的时间戳存放文件.
•    -ext path : 指定扩展脚本语言文件。
•    -extpc name period : 指定被周期调用的函数名和间隔时间.
•    -mask expr : 指定被禁止的命令名(比如可以禁止使用清空vanish).
•    -unmask expr : 指定被允许的命令名.
数据库类型
下面我们再来看下数据库类型的详细配置。
1、    数据库名的命名方式被Tokyo Cabinet的抽象API指定。
2、    如果数据库名为"*",表示内存hash数据库。
3、    如果数据库名为"+"表示内存tree数据库。
4、     如果数据库名为".tch",则数据库为hash数据库。
5、    如果数据库名的后缀为".tcb",数据库将为B+ tree数据库。
6、    如果数据库名的后缀为".tcf"。则数据库将为fixed-length数据库。
7、     如果数据库名的后缀为".tct",则数据将为一个table数据库(有表的概念)。
数据库的调整参数通过数据库名的延伸来指定,通过"#"分开,每个参数通过一个参数名和值来指定,用"="隔开。

内存hash数据库支持"bnum", "capnum", 和 "capsiz"
内存tree数据库支持"capnum" 和 "capsiz"
capnum指定记录的最大容量,capsiz指定最大的内存使用量(在内存数据库中),记录通过存储的顺序移除。
hash数据库支持"mode", "bnum", "apow", "fpow", "opts", "rcnum", 和 "xmsiz".
`rcnum'指定最大的缓存记录数。如果它不大于零,那么缓存记录不可用。默认不可用。
xmsiz   指定外部内存的大小。如果不大于0,内存不可用。默认是67108864,即64M。
`bnum' 指定bucket存储桶的数量。如果指定的数目不大于0,将会使用默认的数值131071.推荐数量应该在所有需要存储的记录总数的0.4-4倍
`apow' 跟一个key关联的记录数,2的N次方表示.  如果不指定,默认2^4=16.
`fpow' specifies the maximum number of elements of the free block pool by power of 2.  默认2^10=1024.
`opts' 指定选项,位或:`HDBTLARGE' 指定数据库的大小通过使用64位数组桶能够超过2G。
         `HDBTDEFLATE'  指定每个记录被Deflate encoding压缩。
         `HDBTBZIP' 指定每个记录被BZIP2 encoding压缩
         `HDBTTCBS'指定每个记录被 TCBS encoding压缩.
B+ tree数据库支持"mode", "lmemb", "nmemb", "bnum", "apow", "fpow", "opts", "lcnum", "ncnum", 和 "xmsiz".
Fixed-length 数据库 支持 "mode", "width", and "limsiz".
Table 数据库支持 "mode", "bnum", "apow", "fpow", "opts", "rcnum", "lcnum", "ncnum", "xmsiz", 和 "idx"
"idx"指定表的索引。
"mode"可以包含 "w" 写, "r" 读, "c" 创建, "t" 截断,"e" 无锁,和"f" 非阻塞锁。默认的的mod为"wc"。
优化性能

如果使用hash数据库我们可以指定#bnum=xxx来提高性能。xxx大于或等我我们的记录总数。

      如果使用B+ tree数据库我们可以通过指定"#lcnum=xxx#bnum=yyy" 来提高性能.第一个参数指定被缓存的最大叶子节点数,受内存容量限制,第二个参数指定桶的数量,它应该大于总记录数的1/128.

如果有大量的客户端连接,确保我们的文件描述符够用。系统默认是1024,我们可以用使用“ulimit”来重新设定

比如下面的单机实例启动脚本(一个正在线上运行的脚本):

#!/bin/sh
ulimit -SHn 51200
ttserver -host 192.168.0.136  -port 11212 -thnum 8 -dmn -pid /data/ttserver/ttserver.pid -log /data/ttserver/ttserver.log -le -ulog /data/ttserver/ -ulim 128m -sid 1 -rts /data/ttserver/ttserver.rts /data/ttserver/database.tch#bnum=10000000#xmsiz=434217728#rcnum=20000

使用hash数据库,最大会缓存20000个记录,最大使用内存434217728bytes(414M),bucket存储桶的数量10000000。

目前的库大小:

-rw-r--r--  1 root root  28G Mar  8 12:19 bbsdatabase.tch

因为使用了64位操作系统,所以文件大小不受2G的限制。

我们再看下读取数据的速度:

当前获取memcache Threads_cdb_threads_tid3565732_displayorder_0 使用时间 0.00054812431335449

以上是程序打印出来的通过memcache协议读取key为memcache Threads_cdb_threads_tid3565732_displayorder_0的数据所花的时间0.00054812431335449(s),可以看到速度还是非常快的。
启动实例

个人推荐通过修改ttservctl来实现启动。下面我们举几个简单的启动例子。

单机启动例子,下面是ttservctl文件的部分:

#! /bin/sh
#----------------------------------------------------------------
# Startup script for the server of Tokyo Tyrant
#----------------------------------------------------------------
# configuration variables
prog="ttservctl"
cmd="ttserver"
basedir="/var/ttserver" #数据库存放的路径,比如改为"/data/mydata"
port="1978" #启动的端口
pidfile="$basedir/pid"
logfile="$basedir/log"
ulogdir="$basedir/ulog"
ulimsiz="256m"
sid=1
dbname="$basedir/casket.tch#bnum=1000000" #上面讲的数据库类型配置
maxcon="65536"
retval=0

双机互为主辅模式,比如两台机器的Ip分别为192.168.1.176和192.168.1.1.177,以下为ttservctl文件的一部分。

176的配置:

#! /bin/sh
#----------------------------------------------------------------
# Startup script for the server of Tokyo Tyrant
#----------------------------------------------------------------
# configuration variables
prog="ttservctl"
cmd="ttserver"
basedir="/data/data/data1"
port="11211"
pidfile="$basedir/pid"
logfile="$basedir/log"
ulogdir="$basedir/"
mhost="192.168.1.177" #主ip即另外机器的ip
ulimsiz="256m"
sid=6#注意要每台机器不一样
dbname="$basedir/casket.tch#bnum=100000000#xmsiz=104857600#rcnum=1000000"
rts="$basedir/ttserver.rts" #在ttservctl基础上增加
maxcon="65536"
retval=0
# locale clear
LANG=C
LC_ALL=C
export LANG LC_ALL
# start the server
start(){
  printf 'Starting the server of Tokyo Tyrant\n'
  ulimit -n "$maxcon"
  mkdir -p "$basedir"
  if [ -f "$pidfile" ] ; then
    pid=`cat "$pidfile"`
    printf 'Existing process: %d\n' "$pid"
    retval=1
  else
    $cmd \
      -port "$port" \
      -dmn \
      -pid "$pidfile" \
      -log "$logfile" \
      -ulog "$ulogdir" \
      -ulim "$ulimsiz" \
      -sid "$sid" \
      -mhost "$mhost" \#在ttservctl基础上增加
      -mport "$port" \#在ttservctl基础上增加
      -rts "$rts" \#在ttservctl基础上增加
      "$dbname"
    if [ "$?" -eq 0 ] ; then
      printf 'Done\n'
    else
      printf 'The server could not started\n'
      retval=1
    fi
  fi
}




177的配置:

#! /bin/sh
#----------------------------------------------------------------
# Startup script for the server of Tokyo Tyrant
#----------------------------------------------------------------
# configuration variables
prog="ttservctl"
cmd="ttserver"
basedir="/data/data/data1"
port="11211"
pidfile="$basedir/pid"
logfile="$basedir/log"
ulogdir="$basedir/"
mhost="192.168.1.176" #主ip即另外机器的ip
ulimsiz="256m"
sid=7#注意要每台机器不一样
dbname="$basedir/casket.tch#bnum=100000000#xmsiz=104857600#rcnum=1000000"
rts="$basedir/ttserver.rts" #在ttservctl基础上增加
maxcon="65536"
retval=0
# locale clear
LANG=C
LC_ALL=C
export LANG LC_ALL
# start the server
start(){
  printf 'Starting the server of Tokyo Tyrant\n'
  ulimit -n "$maxcon"
  mkdir -p "$basedir"
  if [ -f "$pidfile" ] ; then
    pid=`cat "$pidfile"`
    printf 'Existing process: %d\n' "$pid"
    retval=1
  else
    $cmd \
      -port "$port" \
      -dmn \
      -pid "$pidfile" \
      -log "$logfile" \
      -ulog "$ulogdir" \
      -ulim "$ulimsiz" \
      -sid "$sid" \
      -mhost "$mhost" \#在ttservctl基础上增加
      -mport "$port" \#在ttservctl基础上增加
      -rts "$rts" \#在ttservctl基础上增加
      "$dbname"
    if [ "$?" -eq 0 ] ; then
      printf 'Done\n'
    else
      printf 'The server could not started\n'
      retval=1
    fi
  fi
}

Tokyo Tyrant(TTServer)系列-memcache协议
通过memcache协议使用ttserver

通过telnet 127.0.0.1 1978 telnet连接到到我们第一节的启动实例。

以下我们通过add增加key为key1和value为value1的数据。

通过get key1获取数据,如果你看不明白,可以搜索下memcache协议的东西看下。

add key1 1 0 6
value1
STORED
get key1
VALUE key1 0 6
value1
END
通过php使用


$mem=new Memcache();
$mem->connect("192.168.15.178",1978);
$mem->add("key2","value2");
print_r( $mem->get("key2"));
echo "<hr>";
$mem->add("key3",array("value3"=>"this is value3"));
print_r($mem->get("key3"));
?>

运行后输出:

 
value2
________________________________________
a:1:{s:6:"value3";s:14:"this is value3";}

需要注意的问题
序列化问题
如果你熟悉memcache协议,或者你曾经用php的memcache来使用ttserver,你可能马上就发现了上面的问题。
比如我们key3是一个数组,但是我们取回来的是一个序列化的字符串,没有自动反序列化,在memcached服务器上是会自动反序列化的。
    通过上面的telnet示例我们可以看到,我们add key1的时候设置flag参数为1,但是我们get回来的时候,返回的flag参数是0,实际上,ttserver是没有存储flag参数的,统一的都 使用0,这就造成了php使用时不会自动反序列化,当然,如果你使用压缩参数,一样会有这样的问题。
    怎么样解决这个问题,如果要修改ttserver的代码实为不方便。我们完全可以在php,或者我们的客户端来控制。
比如value我们统一的都序列化后存储,取出来的时候我们再反序列化。
自增问题

//使用ttserver自增
$mem=new Memcache();
$mem->connect("192.168.15.178",1978);
var_dump($mem->increment("incr"));
//结果为int(1)
?>
 
//使用memcache自增
$mem=new Memcache();
$mem->connect("192.168.15.178",11211);
var_dump($mem->increment("incr"));
//结果为bool(false)
?>

我们看到同样的代码用在memcache返回了失败(false).我们可以在php手册上看到这样一句话“Memcache::increment() does not create an item if it didn't exist.”但是同样的,用在ttserver上就是成功的。这一点要特别注意。













Tokyo Tyrant(TTServer)系列-高可用性和高可靠性+nginx

1.基于memcache协议的高可用性

1.1构建一个互为主辅的ttserver.
 
图中构建了在ip为177和178两台机器的互为主辅结构的ttserver.
1.2在php中实现高可用
非常幸运,php的memcache客户端直接就可以实现故障转移的功能。其中的addServer函数或者方法非常好。
bool Memcache::addServer ( string $host [, int $port [, bool $persistent [, int $weight [, int $timeout [, int $retry_interval [, bool $status [, callback$failure_callback [, int $timeoutms ]]]]]]]] )

$memcache = new Memcache;
$memcache->addServer('192.168.0.177', 11211);
$memcache->addServer('192.168.0.178', 11211);

$memcache->get("key");
?>
addServer 会把其中加入的ip放到一个池中,然后使用对键hash的方式(默认是crc32(key) % current_server_num)进行存取选择服务器,当其中一台服务器宕机不可用时,current_server_num就会减一,进行重新hash连接新的服务器,这样就实现了故障的自动转移。具体其中的原理可以查看php手册中关于memcache部分。
1.3其他语言
http://code.google.com/p/memcached/wiki/Clients这是一个memcached的client API的各种语言实现的列表,几乎所有的api都有提供php类似的addServer实现故障转移功能。
2.利用nginx实现http协议的高可用性
2.1为什么要把nginx扯进来?
ttserver提供了http接口
ttserver没有提供安全保证
nginx在处理http请求和反向代理的性能非常好
nginx的反向代理具有负载均衡和健康检查功能

2.2如何结合nginx实现高可用性
2.2.1配置
在nginx配置文件中增加:
upstream backend {
server 192.168.0.177:11211 weight=5 max_fails=3 fail_timeout=30s;
server 192.168.2.178:11211 weight=1 max_fails=3 fail_timeout=30s;
}
 
利用upstream指向后端的两台机器。
location ~* /count(.*) {
if ($request_method = PUT ) {
return 403;
}
if ($request_method = DELETE ) {
return 403;
}
if ($request_method = POST ) {
return 403;
}
proxy_method GET;
proxy_pass http://backend;
}
当路径包含/count的时候,则代理到ttserver后端进行请求数据。请注意,这里屏蔽了PUT,DELETE,POST方法,只是使用了GET,主要目的是为了安全性,因为DELETE,POST,PUT是可以修改数据的。
2.2.2使用
使用以上的配置文件,启动nginx就可以访问ttserver中的内容了。比如访问http://host/count_key1将会显示ttserver中key为”count_key1”的内容。当我们不同时停掉177和178时,前端通过nginx http://host/count_key1都是同样可以可用的。





Tokyo Tyrant(TTServer)系列-备份恢复和增加从库
前面我们说了TTServer 的使用,以及如何在开始的时候配置主从。在使用中它非常高效和可靠,那么如果我们的服务运行一段时间后,需要增加一个从库(ttserver)来分担压力 或者用于负载均衡和HA,因为是线上系统,那么就不应该停机,那么我们应该怎么对数据进行备份和拷贝,然后启动一个slave从库呢? 
1.如何备份和恢复
登录到ttserver服务器,执行如下命令
      # tcrmgr copy -port 11211 localhost  /data/backup.tch
       localhost :ttserver主机名
       /data/backup.tch  备份文件保存的路径,请注意,这里指ttserver所在机器的路径。
同样,也可以备份远程机器,
      # tcrmgr copy -port 11211 remotehost  /data/backup.tch
备份文件将保存在remotehost的/data/backup.tch
接下来恢复数据就比较简单,直接替换掉原有ttserver的数据文件,启动就可以了。
这种方式适用于定期的数据备份,在出现灾难故障时可以恢复到某一个时间的数据。不适合用来增加从库时需要的数据库文件。
2.如何不停机增加从库
从库需要进行同步,那么就需要有个文件的时间版本,防止数据冲突。
首先我们建立一个一个如下的脚本文件,存为"backup.sh",并赋予执行权限(chmod 755 backup.sh)。比如存在:/data/backup/路径下。



#! /bin/sh
srcpath="$1"
destpath="$1.$2"
rm -f "$destpath"
cp -f "$srcpath" "$destpath"

接下执行如下命令,执行的时间会根据你的磁盘性能和数据大小而定。

tcrmgr copy -port 11211 masterhost '@/data/backup/backup.sh' 
materthost:主库主机名,请注意:backup.sh存放的路径是在materthost机器上。
'@/data/backup/backup.sh'  :如果tcrmgr copy命令中,参数以@开始,那么后面的字符串作为命令行执行。
 
执行完成后,我们进入ttserver的数据目录,执行ls -lh会发现类似下面的文件名,
-rw-r--r--  1 root root 9.2G Aug 25 14:31 casket.tch.1251181588942889

请确认产生casket.tch.xxxxx类似的文件名,xxxxx代表时间戳。
接下来在从库salve上以我们备份的文件来运行ttserver,把时间错写入到一个时间戳文件ttserver.rts。
 
$ ls
$ cp casket.tch.xxxxx casket.tch
$ echo xxxxx >ttserver.rts
$ mkdir ulog
$ ttserver -mhost masterhost -mport mport -port 11211 -ulog ulog -sid 2 -rts ttserver.rts  casket.tch
运行后,ttserver将会以此时间戳进行同步。
masterhost :主库主机名
mport :  主库端口号
到此我们就完成了一个salve从库ttserver的增加。
3.小结
 推荐备份也使用2中用到的方式,上面带上了时间戳,可以精确的知道数据的版本时间。
另外请注意:在使用此方法进行备份的时候,ttserver会在备份过程中一直保持同步阻塞和不修改数据。所以可以保证备份出来的版本跟时间戳的一致性。
Tokyo Tyrant(TTServer)系列-数据压缩
由于忙于工作,好久继续写关于ttserver的东西了。最近由于服务器准备的原因,只能在32位上做ttserver的测试,很快数据就达到 2G,ttserver无法正常工作,于是想到了ttserver的压缩功能,发现压缩比例挺大。所以进行了一些测试并在项目中做了一些应用。本文将展示 在64下面进行压缩的压缩比以及进行压缩的好处。
      压缩测试
数据:2720268条原始新闻数据,每条数据使用json格式进行存储。
A机器ttserver配置:casket.tch#bnum=100000000#xmsiz=524288000#rcnum=200000#opts=ld
B机器ttserver配置:casket.tch#bnum=100000000#xmsiz=524288000#rcnum=200000
分别把原始新闻数据存储A机器和B机器。其中A和B的ulog日志文件大小都为19G。接下来来看ttserver的数据库文件大小,
A机器(有压缩)6.1G,B机器 (无压缩)17G。压缩后大概只占原来文件的1/3。
配置中的#opts=ld的d表示使用压缩。关于opts可以参考官方的原话:
"opts" can contains "l" of large option, "d" of Deflate option, "b" of BZIP2 option, and "t" of TCBS option.
经过初步测试,d是速度比较快的一种了。
压缩的优点
存储空间,压缩首先给人带来的优点肯定就是节约存储空间,这点在服务器空间不足的情况下使用非常有用,当然,你在32位2G文件大小的限制下,使用压缩的话就可以存储更多的数据。
IO性能:经过压缩,数据本身变得小了,对磁盘的IO也会相应减小,如果你的磁盘IO对你的应用形成了瓶颈,那么压缩也是可以为你来带益处的。对于IO的性能提高在硬盘结构和工作方式上也能得到解释。
压缩的缺点
使用压缩肯定就会耗费CPU资源了,所以CPU和空间只是一个权衡,根据经验,一般来说使用压缩都是没有问题的。比如nginx的gzip压缩静态文件(js,css)。
ttserver的压缩跟memcached的压缩的比较
提交到了比较,肯定就有不一样的地方。ttserver使用了内部压缩,就是服务器端(ttserver)负责压缩。而memcached的压缩则是客户端进行的,memcached只是负责存储压缩后的数据。客户端client根据flag参数来确定是否解压。ttserver是服务器负责压缩,memcache是客户端负责压缩。
ttserver输出的是解压后的数据,memcached输出的就是输入的压缩数据(数据使用压缩的情况)。这样就形成一个区别,memcached使用压缩后,由于输出的是压缩数据,会降低网络传输,而ttserver则不会有任何改变。
关于memcached的使用请网上搜索相应的介绍。







请注意Tokyo Tyrant (ttserver)在大数据量下的不稳定

ttserver不稳定案例
1. CMS a系统的文章采用了ttserver存储。在数据达到30多G的时候,经常出现写入失败,还出现了几次意外崩溃,无法重启成功,只得从slave恢复数据。
2.CMS b系统的图片使用ttserver存储,在数据导到65G的时候出现无法写入的情况,重启后问题依旧,只得从slave重新恢复数据重启。
3.BBS系统采用ttserver做缓存,设计上采用如果没有更新,那么缓存永久存在的策略。在数据达到65GB,数据条数在700多万条的时 候,出现大量写入失败的情况,系统负载开始上升,日志出现大量do_mc_set: operation failed。当然这里作为缓存比较好处理,删掉,重新建个新的就OK。(注:启动参数 data.tch#bnum=10000000#xmsiz=434217728#rcnum=20000)
测试中ttserver写入表现不稳定
1.#bum=10000000
   插入940多万数据(一条7.2k)时出现崩溃(测了两次都一样,看起来好像内部出现了死锁,连上后,无法进行任何响应,没有任何操作时,也占了大量CPU)。越到后面插入的qps(每秒响应数)越小。而且波动比较大。
   #bum=100000000
   可以顺利插入上千万的数据,但是越到后面qps越小。越到后面插入的qps(每秒响应数)越小。而且波动比较大。
2.跟其他的nosql(mongodb,bdb je,cassandra)对比测试,在写入上,ttserver是表现最不稳定的,其他的nosql都非常好。
总结和注意
1.经过对Tokyo Cabinet 的测试,同样跟ttserver一样,所以应该是Tokyo Cabinet 导致的不稳定。在选择btree方式存储时,随着数据量的增加一样不稳定。在优化参数上也做了各种尝试。
2.ttserver在性能的表现上非常不俗,特别是内存占用和cpu占用都很低,能同时响应上万的并发。但是你应该注意,他一般在数据导到20G以上就会出现不稳定情况。
  所以用ttserver来存储单条比较小的数据非常好,存储大文本或者大的二进制文件,由于空间占用上升很快,很快就会变得不稳定。
3.注意备份,使用master-slave是个非常好的主意,在ttserver崩溃导致文件损坏或者其他什么原因无法启动时,你可以从slave 拷贝数据文件来进行恢复。
4.如果你一定要使用ttserver,建议对数据进行分片(sharding)存储,或者你自己使用一个网络接口,底层使用Tokyo Cabinet 来进行分片存储。
5.以上只是个人在使用中遇到的情况,本人正在尝试在存储大文本的ttserver进行替换掉。
Tokyo Tyrant (ttserver)的master-slave复制协议分析
                                        The  replication protocol  of  Tokyo Tyrant (ttserver)
                              [文章作者:孙立 链接:http://www.cnblogs.com/sunli/ 更新时间:2010-06-26]
目的
     ttserver已经自带了主从复制功能,而且运行非常稳定,高效,使我们在使用ttserver实现高可靠性的不二选择。为什么我还要分析它的复制协议呢?
     1.需要实时从ttserver同步数据到一个异构库。比如mysql,其他的nosql数据库。
     2.数据的无缝迁移,如果需要从ttserver数据库无缝切换到其他数据库,可以使用这种复制协议
     3.高性能的异步处理队列,你可以设置主库为mem类型,然后使用同步协议获取数据,这实际上就是一个队列,而且还是一个持久化的队列服务。
     4.其他存储写一个服务,可以用ttserver做slave。

协议结构分析

     咱们从数据流向的流程开始:
1.        客户端(slave)连接到服务器端(master)
2.        slave连接成功后向master发送
+--------------------------------------+
|0xC8  |0xa0     | slave rts  | slave sid  |
|1byte |1 byte   | 8 bytes    | 4 bytes   |
+--------------------------------------+
3.    master收到后,想客户端发送:
+---------+
|mid         |
|4 bytes   |
+---------+
4.    slave收到后可以得到服务器端的master sid.
5.    这里就开始复制了,master继续想客户端循环不断的发送复制的数据的object.
+------+
|0xca   | 表示没有任何操作
|1 byte|
+------+
发送一个操作项:
+--------------------------------------------------+
|0xc9   |rts         | rsid       |rsize      |data            |
|1 byte |8 bytes   |4 bytes  |4 bytes   |rsize bytes  |
+--------------------------------------------------+

其中data的结构:
+-----------------------------------+
|magic   |cmd       |[data]               |
|1 bytes |1 bytes  | key-value data  |
+-----------------------------------+
cmd表示各种操作类型,比如put putkeep 等
6.    slave循环接受数据进行数据ok了。

协议实现

     ttserver的复制协议非常简单,因为官方并没有把复制协议写到文档中,其他操作都有文档的。就需要去分析它的C代码,结构分析出来实现就非常简单了。 我会开放一些实现,到时会在www.cnblogs.com/sunli/公布


Tokyo(ttserver)使用手册摘要
1    概述
Tokyo是Tokyo Cabinet和Tokyo Tyrant的简称,合在一起用也称ttserver。
Tokyo Cabinet 是日本人 平林幹雄 开发的一款 NoSQL 数据库,数据库由一系列key-value对的记录构成,类似bdb。
Tokyo Tyrant 是由同一作者开发的 Tokyo Cabinet 数据库网络接口。它拥有Memcached兼容协议,也可以通过HTTP协议进行数据交换。Tokyo Tyrant 加上 Tokyo Cabinet,构成了一款支持高并发的分布式持久存储系统Tokyo。
对Tokyo内部机制感兴趣的请参考陈海涛 chenht@ucweb.com:《Tokyo代码剖析》;
针对使用tt时出现太多误用,参数误设或未设,提供使用手册的摘要。在此开一个讨论的头。
2  Tokyo的安装
测试了在32位机器和64位机器都运行正常,64位机器可以支持>2G的单个db文件;
32位机器加上编译选项” --enable-off64”后也能支持>2G的单个db文件,但是不能突破32位Linux 3G用户内存空间的限制,所以内存最大只能用3G的,除非安装64位操作系统。
可用普通用户安装和运行。
Tokyo Cabinet依赖下面的库,一般Linux会自带,编译出错的话则需先安装:
       zlib : 用来压缩. 1.2.3版本或更新的.
       bzip2 : 用来压缩. 1.0.5版本或更新的.
下面的例子都是基于$HOME/local目录,使用时可更改。
 
     下载最新的包(注意别下载到旧包了),2010年8月推荐用的最新版本是:

http://1978th.net/tokyotyrant/Latest Source Package (version 1.1.41)
 
http://1978th.net/tokyocabinet/Latest Source Package (version 1.4.46)
 
         注:新版本主要进行bugfix,请尽量下载最新版本,changelog在源代码包里面。
         下载后传到Linux的目录解压:
         tar zxvf  ***.tar.gz
 
2.1  Tokyocabinet
cd tokyocabinet-***
 
32位操作系统:
./configure --prefix=$HOME/local/tokyocabinet/ --enable-off64

64位操作系统:

./configure --prefix=$HOME/local/tokyocabinet/
make
make install
 
2.1.1  32位操作系统支持大于2G的db文件

32位操作系统要支持大于2G的db文件请加上” --enable-off64”,否则会导致严重问题,db文件超过2G时表现为:
Tcrmgr inform显示记录数为0,实际数据并没有被清空;

连接暴多但都操作失败,ttserver.log内提示“ERROR   ttacceptsock failed”和“ERROR   do_mc_set: operation failed”等;
Ttserver检测到文件异常会悄悄退出。
 
2.2  Tokyotyrant

环境变量设置,cd $HOME 后vi .bash_profile,修改PATH和LD_LIBRARY_PATH加上Tokyo的相关安装路径:

PATH=$HOME/local/tokyocabinet/bin:$HOME/local/tokyotyrant/bin:$PATH
 
LD_LIBRARY_PATH=$HOME/local/tokyocabinet/lib:$HOME/local/tokyotyrant/lib:$LD_LIBRARY_PATH
export PATH
export LD_LIBRARY_PATH
 
然后安装:

./configure --prefix=$HOME/local/tokyotyrant \
        --with-tc=$HOME/local/tokyocabinet
       make
       make install
      
安装后在$HOME/local目录下生成了安装目录:

cd $HOME/local/tokyotyran
mkdir  log data               
新建两个目录用来存放ulog和db文件等。
在两台主机都安装完后,于是可以执行下面的启动步骤了。
 
2.2.1  支持lua脚本

末尾加上–enable-lua表示支持lua脚本:

./configure --prefix=$HOME/local/tokyotyrant \
        --with-tc=$HOME/local/tokyocabinet –enable-lua
 
2.3  版本查看

查看tc版本:
tcamgr  --version
查看tt版本:
ttserver  --version
3  Tokyo的启动
生产环境可用本例的目录结构,可将#后面的参数再调整。
3.1  Hash关键性能参数

Hash是B+tree的基础,数据put时直接写入文件或文件映射,并非写缓存,是比较安全的数据存储方式。数据量小时,全在缓存或内存映射内操作,读写很快。
而且写数据是单条写入,所以数据量超过内存映射xmsiz时后要直接写入db文件,性能下降明显,io升高明显。
"bnum", 用来保存计算完hash值后的每个key在db文件的位置,bnum太小时hash值冲突比例就大。冲突的key依次链接起来,查找时增加了搜寻次数;
该值持久化到db文件,全映射在内存,默认很小是131071,最大可以设几亿以上。,要减少hash冲突次数,则要设置bnum >= key个数,一般一年后key要达到5000万个,bnum则设置为5千万:50 000 000。
"opts", 指定压缩方式(hash的短数据压缩比不高)和bucket array对每条数据用4字节还是8字节保存文件偏移量,要支持db文件>2G请加“#opts=l”。要支持db文件>2G并且用效果较好的gzip用到的压缩算法请加“#opts=ld”。
hash方式单条数据很短(< 100字节)时压缩效果差,可以不用这个参数,否则请带上这个参数,但会占用更多cpu。
"rcnum", hash map缓存的记录数,get时放到这个缓存,set时从缓存清掉该key,默认0不缓存,设置则最小256,B+tree不用这个参数,缓存热点数据的,在get访问确定没明显热点时不要设置这个参数;在有明显热点时才需设置这个参数,在保证xmsiz足够大时这个参数推荐设小些,例如100万,因为会把rnum条key和value不加压缩地放内存,除非机器内存很充裕。
"xmsiz",指定文件映射尺寸,默认且最小为:256(放head等关键信息)+ bucket array的尺寸。注意这个最重要的参数必须>=db文件尺寸才能正常速度地响应,否则很慢,例如db文件最大会达到10G,则xmsiz设置为10G,单位字节。
上述参数中只有这个xmsiz参数在启动后还可通过重启调整。
3.2    B+tree关键性能参数
B+tree是基于hash机制实现的,复用上述的db文件格式和文件映射,不同的只是最上层缓存机制。
写入db数据时,是按叶节点或非页节点进行了打包分页,整页写入;压缩也是整页压缩,压缩比更高。写也进行了缓存。

但必须在备机上定期执行tcrmgr sync,否则异常重启丢掉数据。
 
"bnum", 同hash方式的,但bnum的数量大于或等于存储总记录数的1/128即可,因为b+tree是整页保存到hash db内。
"opts", 同hash方式的该选项,要支持db文件>2G请加“#opts=l”。要支持db文件>2G并且用效果较好的gzip压缩算法请加“#opts=ld”。
b+tree方式压缩是整页(128条记录)压缩,压缩效果很好,数据量大时用b+tree时带上压缩参数降低内存使用量效果明显,但会稍占用更多cpu。
"xmsiz",同hash方式的。
"lcnum", b+tree缓存叶节点数,每个叶节点上可放128条记录作为一页,减轻读写压力,缓存热点数据的,默认1024,设的话最小64,太大则stop和sync时耗时很多。在xmsiz足够大时这个参数别设置。
"ncnum", b+tree缓存非叶节点数,每个树节点上可放256条索引作为一页,索引指向叶节点,减轻读写压力, 缓存热点数据,默认512,设的话最小64,太大则stop和sync时耗时很多。通常为叶节点数的一半。
在xmsiz足够大时以上两个参数可不设置。

#define BDBDEFLCNUM    1024              // default number of leaf cache
#define BDBDEFNCNUM    512               // default number of node cache
 
3.3 Hash主-备启动命令
./ttserver -host 192.168.4.150 -port 11230 -thnum 8  -dmn -pid $HOME/local/tokyotyrant/bin/ttserver.pid -log $HOME/local/tokyotyrant/log/ttserver.log -le -ulog $HOME/local/tokyotyrant/data/ -ulim 128m -sid 91 $HOME/local/tokyotyrant/data/database.tch#bnum=10000000#xmsiz=1024000000 #rcnum=1000000#opts=ld
 
./ttserver -host 192.168.4.151 -port 11230 -thnum 8 -dmn -pid $HOME/local/tokyotyrant/bin/ttserver.pid -log $HOME/local/tokyotyrant/log/ttserver.log -le -ulog $HOME/local/tokyotyrant/data/ -ulim 128m -sid 92 -mhost 192.168.4.150 -mport 11230 -rts $HOME/local/tokyotyrant/data/ttserver.rts $HOME/local/tokyotyrant/data/database.tch#bnum=10000000#xmsiz=1024000000 #rcnum=1000000#opts=ld
 
3.4 B+tree主-主启动命令
./ttserver -host 192.168.4.150 -port 11230 -thnum 8  -dmn -pid $HOME/local/tokyotyrant/bin/ttserver.pid -log $HOME/local/tokyotyrant/log/ttserver.log -le -ulog $HOME/local/tokyotyrant/data/ -ulim 128m -sid 91 -mhost 192.168.4.151 -mport 11230 -rts $HOME/local/tokyotyrant/data/ttserver.rts $HOME/local/tokyotyrant/data/database.tcb#bnum=10000000#xmsiz=2000000000 #lcnum=1024#ncnum=512#opts=ld
 
./ttserver -host 192.168.4.151 -port 11230 -thnum 8 -dmn -pid $HOME/local/tokyotyrant/bin/ttserver.pid -log $HOME/local/tokyotyrant/log/ttserver.log -le -ulog $HOME/local/tokyotyrant/data/ -ulim 128m -sid 92 -mhost 192.168.4.150 -mport 11230 -rts $HOME/local/tokyotyrant/data/ttserver.rts $HOME/local/tokyotyrant/data/database.tcb#bnum=10000000#xmsiz=2000000000 #lcnum=1024#ncnum=512#opts=ld