Sky's blog

我和我追逐的梦

常用链接

统计

其他链接

友情链接

最新评论

Tokyo Tyrant基本规范(5)-- 教程

Tokyo Tyrant教程

1. 基础使用

    安装Tokyo Tyrant后,可以通过在终端执行命令'ttserver'来立即启动服务器。默认,服务器在1978端口监听,为on-memory hash database(适合存储缓存数据)提供访问服务。

[terminal-1]$ ttserver

    在另外一个终端中执行下面的命令以测试存储操作,'tcrmgr put'调用函数'tcrdbput':

[terminal-2]$ tcrmgr put localhost one first
[terminal-2]$ tcrmgr put localhost two second
[terminal-2]$ tcrmgr put localhost three third

    执行下面的命令来一次获取多条记录。'tcrmgr mget' 调用函数 'tcrdbget3':

[terminal-2]$ tcrmgr mget localhost one two three

    在服务器的终端中按Ctrl-C来终止服务器。

    通过指定后缀为'.tch'的文件名,我们运行服务器来处理 hash database:

[terminal-1]$ ttserver casket.tch

    保存一些记录

[terminal-2]$ tcrmgr put localhost one first
[terminal-2]$ tcrmgr put localhost two second
[terminal-2]$ tcrmgr put localhost three third

    用Ctrl-C终止服务器,然后重新启动服务器:

[terminal-1]$ ttserver casket.tch

    检查保存的记录的一致性。

[terminal-2]$ tcrmgr mget localhost one two three

    为了后续的教程,用Ctrl-C终止服务器并删除数据库。

[terminal-1]$ rm casket.tch

2) 后台进程

    指定选项"-dmn"以后台进程的方式运行服务器。此外,可以指定"-pid"选项来指定文件以记录进程ID。注意后台进程的当前工作目录被修改到root目录。因此,文件路径参数需要用绝对路径来表示。

[terminal-1]$ ttserver -dmn -pid /tmp/ttserver.pid /tmp/casket.tch

    为了终止服务器,查看'_pid'指定的进程Id文件并发送SIGTERM 信号给进程。

[terminal-1]$ kill -TERM `cat /tmp/ttserver.pid`

    为了通过操作系统的RC脚本运行服务器,请使用'ttservctl'。对于Linux发型版本,添加下面的行到/etc/rc.local.

/etc/rc.local

    默认,数据库文件和相关文件被放置在'/var/ttserver'下。因为'ttservctl'是一个很小的shell脚本,您可以随意的复制并编辑它。同样,也可以安装修改后的脚本到'/etc/init.d'并设置符号链接/etc/rc3.d/S98ttserver' and `/etc/rc5.d/S98ttserver'.


3) 备份和恢复

    让我们再次运行服务器以继续这个教程。

[terminal-1]$ ttserver casket.tch

    保存一些记录。

[terminal-2]$ tcrmgr put localhost one first
[terminal-2]$ tcrmgr put localhost two second
[terminal-2]$ tcrmgr put localhost three third

    为了备份数据库文件,使用命令'tcrmgr copy'并指定目标路径。注意备份文件将在服务器上的本地文件系统中创建(不是在客户端这边).

[terminal-2]$ tcrmgr copy localhost backup.tch

    按Ctrl-C 终止服务器并删除数据库

[terminal-1]$ rm casket.tch

    从备份文件中恢复数据库并重启服务器。

[terminal-1]$ cp backup.tch casket.tch
[terminal-1]$ ttserver casket.tch

    检查存储的记录的一致性。

[terminal-2]$ tcrmgr mget localhost one two three

    为了后续的教程,用Ctrl-C终止服务器并删除数据库。

[terminal-1]$ rm casket.tch backup.tch


4) 更新日志

    让我们开启更新日志来运行服务器。选项'-ulog'指定包含更新日志文件的目录。

[terminal-1]$ mkdir ulog
[terminal-1]$ ttserver -ulog ulog casket.tch

    存储一些记录.

[terminal-2]$ tcrmgr put localhost one first
[terminal-2]$ tcrmgr put localhost two second
[terminal-2]$ tcrmgr put localhost three third

    按Ctrl-C 终止服务器并删除数据库

[terminal-1]$ rm casket.tch

    
    备份更新日志目录并重新启动服务器。

[terminal-1]$ mv ulog ulog-back
[terminal-1]$ mkdir ulog
[terminal-1]$ ttserver -ulog ulog casket.tch

    在客户端使用'tcrmgr restore'命令从备份的更新日志中恢复数据库
[terminal-2]$ tcrmgr restore localhost ulog-back

    检查存储的记录的一致性。

[terminal-2]$ tcrmgr mget localhost one two three

    
    为了后续的教程,用Ctrl-C终止服务器并删除数据库。

[terminal-1]$ rm -rf casket.tch ulog ulog-back

5) 复制

    复制是同步两台或更多数据库服务器的机制,实现高可用性和高完整性。复制源服务器被称为"master"而所有目标服务器都被称为"slave"。复制需要以下前提条件:

1. master必须记录更新日志
2. master必须指定唯一的服务器ID
3. 每个slave必须记录更新日志,因为在master当机时它将成为master
4. 每个slave必须指定唯一的服务器ID,因为在master当机时它将成为master
5. 每个slave必须指定它的master服务器的地址和端口号
6. 每个slave必须指定复制时间戳文件

    这个章节将描述何如建立一个master(使用端口1978)和一个slave(使用端口1979)的复制。首先,让我们运行master服务器。

[terminal-1]$ mkdir ulog-1
[terminal-1]$ ttserver -port 1978 -ulog ulog-1 -sid 1 casket-1.tch

    下一步,在另一个终端运行slave服务器。

[terminal-2]$ mkdir ulog-2
[terminal-2]$ ttserver -port 1979 -ulog ulog-2 -sid 2 -mhost localhost -mport 1978 -rts 2.rts casket-2.tch

    在master中存储一些记录。

[terminal-3]$ tcrmgr put -port 1978 localhost one first
[terminal-3]$ tcrmgr put -port 1978 localhost two second
[terminal-3]$ tcrmgr put -port 1978 localhost three third

    在master和slave中检查存储记录的一致性。

[terminal-2]$ tcrmgr mget -port 1978 localhost one two three
[terminal-2]$ tcrmgr mget -port 1979 localhost one two three

    让我们模拟master崩溃的情况。Ctrl-C终止master并删除数据库文件。

[terminal-1]$ rm casket-1.tch

    Ctrl-C终止slave并重启作为master。

[terminal-2]$ ttserver -port 1979 -ulog ulog-2 -sid 2 casket-2.tch

    添加新的slave(使用端口1980)。

[terminal-1]$ mkdir ulog-3
[terminal-1]$ ttserver -port 1980 -ulog ulog-3 -sid 3 -mhost localhost -mport 1979 -rts 3.rts casket-3.tch

    在新的master和新的slave中检查存储记录的一致性。

[terminal-2]$ tcrmgr mget -port 1979 localhost one two three
[terminal-2]$ tcrmgr mget -port 1980 localhost one two three

    Ctrl-C终止两个服务器并删除数据库和相关文件。

[terminal-1]$ rm -rf casket-1.tch ulog-1 1.rts
[terminal-2]$ rm -rf casket-2.tch ulog-2 2.rts
[terminal-1]$ rm -rf casket-3.tch ulog-3 3.rts

    Tokyo Tyrant支持"双master"复制可以提供更高的可用性。要实现它,运行两个服务器各自复制对方。注意同时更新两个master可能导致数据库不一致。默认,服务器不报错即使检测到不一致。'-rcc'选项将使得服务器检查一致性并在检测到不一致时停止服务。

6) 按需设置复制

    可以为正在运行的数据库服务设置复制而不必停工。首先,为备份操作准备下面的脚本并保存为"ttbackup.sh",设置好可执行权限(0755)。

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

下一步,让我们开启更新日志来启动master。

[terminal-1]$ mkdir ulog-1
[terminal-1]$ ttserver -port 1978 -ulog ulog-1 -sid 1 casket-1.tch

    往master中存入一些记录。

[terminal-2]$ tcrtest write -port 1978 localhost 10000

    检查存储的记录的一致性。

[terminal-2]$ tcrmgr list -port 1978 -pv localhost

    备份数据库

[terminal-2]$ tcrmgr copy -port 1978 localhost '@./ttbackup.sh'

    确认备份文件被保存为"casket-1.tch.xxxxx"("xxxxx"为备份文件的时间戳)。然后,使用备份文件运行slave。

[terminal-2]$ ls
[terminal-2]$ cp casket-1.tch.xxxxx casket-2.tch
[terminal-2]$ echo xxxxx > 2.rts
[terminal-2]$ mkdir ulog-2
[terminal-2]$ ttserver -port 1979 -ulog ulog-2 -sid 2 -rts 2.rts casket-2.tch

    注意上面的操作并不是指定master为slave。作为教程,让我们模拟当你正在设置复制时,有一些记录被用户存储进master。

[terminal-3]$ tcrmgr put -port 1978 localhost one first
[terminal-3]$ tcrmgr put -port 1978 localhost two second
[terminal-3]$ tcrmgr put -port 1978 localhost three third

    检查master和slave的差异。

[terminal-3]$ tcrmgr inform -port 1978 localhost
[terminal-3]$ tcrmgr inform -port 1979 localhost

    给slave指定master,这样将启动复制并解决这个差异。

[terminal-3]$ tcrmgr setmst -port 1979 -mport 1978 localhost localhost

    确认slave知道master并且解决了差异。

[terminal-3]$ tcrmgr inform -port 1979 -st localhost

    Ctrl-C终止两个服务器并删除数据库和相关文件。

[terminal-1]$ rm -rf casket-1.tch casket-1.tch.* ulog-1 1.rts ttbackup.sh
[terminal-2]$ rm -rf casket-2.tch ulog-2 2.rts

7) 调整

    如果使用hash database,设置调增参数"#bnum=xxx"来改进性能。它指定bucket 数量,应该要比存储的记录数多。

    如果使用B+ tree database,设置调整参数"#lcnum=xxx#bnum=yyy"来改进性能。前一个参数指定缓存的叶节点的最大数量,应该和系统容许的内存容量一样大。后一个参数指定bucket 的数量,应该比要存储的记录数的1/128大。

    如果有非常大量的客户端访问服务器,确认已清除每个进程的文件描述符数量的限制。在大多数系统中默认设置为1024。如果没有,使用'ulimit'来清理它。

    为了处理服务波峰时间的突发请求,可以用复制来联合使用on-memory hash/tree database和file hash/tree database。master服务器处理on-memory database,它可以从容处理波峰时间的突发请求。但是on-memory database不保证数据的持久化,用于复制的slave通过将记录存储到文件数据库中来弥补这个缺陷。


8) Lua扩展

    如果你需要比已有更加复杂的数据库操作,请使用Lua扩展。举例,准备下列脚本并保存为"test.lua"。这里有一个名为"fibonacci"的函数,返回key的费伯那其数字(注:数列中每个数字是前两个数字的和)

function fibonacci(key, value)
   local num = tonumber(key)
   local large = math.pow((1 + math.sqrt(5)) / 2, num)
   local small = math.pow((1 - math.sqrt(5)) / 2, num)
   return (large - small) / math.sqrt(5)
end

    让我们启动服务器,让它读取这个脚本文件

[terminal-1]$ ttserver -ext test.lua

    在客户端命令中调用这个函数。

[terminal-2]$ tcrmgr ext localhost fibonacci 1
[terminal-2]$ tcrmgr ext localhost fibonacci 2
[terminal-2]$ tcrmgr ext localhost fibonacci 3
[terminal-2]$ tcrmgr ext localhost fibonacci 4
[terminal-2]$ tcrmgr ext localhost fibonacci 5
[terminal-2]$ tcrmgr ext localhost fibonacci 6

    Fibonacci可以通过其他算法来产生。添加下列脚本到"test.lua"。这里有函数"fibnext"可以从数据库中返回下一个费伯那其数字。状态信息被保存在数据库中。


function fibnext(key, value)
   local cur = tonumber(_get("fibcur"))
   if not cur then
      _put("fibold", 0)
      _put("fibcur", 1)
      return 1
   end
   local old = tonumber(_get("fibold"))
   _put("fibold", cur)
   cur = old + cur
   _put("fibcur", cur)
   return cur
end

    然后,重启服务器并测试新的算法。

[terminal-2]$ tcrmgr ext localhost fibnext
[terminal-2]$ tcrmgr ext localhost fibnext
[terminal-2]$ tcrmgr ext localhost fibnext
[terminal-2]$ tcrmgr ext localhost fibnext
[terminal-2]$ tcrmgr ext localhost fibnext
[terminal-2]$ tcrmgr ext localhost fibnext

As you see, the called function receives two string parameters of the key and the value. The return value is sent back to the client. You can use such built-in functions for database operations as "_put", "_out", "_get", and so on. There is a sample file `ext/senatus.lua'.

    如你所见,被调用的函数接收两个字符串参数,key和value。返回值被发送回客户端。你可以始终诸如"_put", "_out", "_get"等内建函数来进行数据库操作。这里有一个实例文件'ext/senatus.lua'。


9) 使用memcached 客户端

    这个章节描述如何使用Perl的mamcached客户端类库(Cache::Memcached)来访问Tokyo Tyrant。和平常一样运行Tokyo Tyrant服务器,下面的脚本是一个典型例子。

use Cache::Memcached;

my $memd = Cache::Memcached->new();
$memd->set_servers(['localhost:1978']);

$memd->set('one', 'first');
$memd->set('two', 'second');
$memd->set('three', 'third');

my $val = $memd->get('one');
printf("one: %s\n", $val);

$val = $memd->get_multi('one', 'two', 'three');
printf("one: %s\n", $val->{one});
printf("two: %s\n", $val->{two});
printf("three: %s\n", $val->{three});

$memd->delete('one');


10) 使用HTTP 客户端

    这个章节介绍如何使用Perl的HTTP客户端类库(LWP::UserAgent)来访问Tokyo Tyrant。和平常一样运行Tokyo Tyrant服务器,下面的脚本是一个典型例子。

use LWP::UserAgent;

my $ua = LWP::UserAgent->new(keep_alive => 1);
my $baseurl = 'http://localhost:1978/';

my $req;
$req = HTTP::Request->new(PUT => $baseurl . 'one', [], 'first');
$ua->request($req);
$req = HTTP::Request->new(PUT => $baseurl . 'two', ["X-TT-PDMODE" => 1], 'second');
$ua->request($req);
$req = HTTP::Request->new(PUT => $baseurl . 'three', ["X-TT-PDMODE" => 2], 'third');
$ua->request($req);

$req = HTTP::Request->new(GET => $baseurl . 'one');
my $res = $ua->request($req);
if($res->is_success()){
    printf("%s\n", $res->content());
}

$req = HTTP::Request->new(DELETE => $baseurl . 'one');
$res = $ua->request($req);

$req = HTTP::Request->new(POST => $baseurl . 'foo',
  ["X-TT-XNAME" => "echo", "X-TT-XOPTS" => 1], 'bar');
$res = $ua->request($req);
if($res->is_success()){
    printf("%s\n", $res->content());
}


11) 持久化而支持过期的缓存

    如果你想位你的web应用缓存类似session信息这样的数据,但是想避免因服务器当机而造成的数据丢失,Tokyo Tyrant是一个方案,也就是说,持久化而支持过期的缓存。它需要下面的前提条件:

1. 服务器必须开启table database
2. 客户端保存每条记录时要使用过期数据列
3. 数据库在过期数据列上要有索引
4. 数据库要开启自动重新组合
5. 服务器必须周期性的调用通过Lua扩展提供的用户自定义函数

    首先,为过期准备下面的脚本并保存为"ttexpire.lua"。当"X"列的数值超过当前日期时将使记录过期。

function expire()
   local args = {}
   local cdate = string.format("%d", _time())
   table.insert(args, "addcond\0x\0NUMLE\0" .. cdate)
   table.insert(args, "out")
   local res = _misc("search", args)
   if not res then
      _log("expiration was failed", 2)
   end
end

    启动服务器,table database方式,其中有一个"x"列是有索引的,并计划每秒钟调用一次expiration 函数。

[terminal-1]$ ttserver -ext ttexpire.lua -extpc expire 1.0 "casket.tct#idx=x:dec#dfunit=8"

    在另外一个终端中存储测试记录。

[terminal-2]$ now=`date +%s`
for((i=1;i<=60;i++)); do
   tcrmgr put -sep '|' localhost "$i" "x|$((now+i))"
done

    确认数据正在被过期机制删除:

[terminal-2]$ tcrmgr list -pv -sep '|' localhost


posted on 2010-08-23 08:24 sky ao 阅读(1805) 评论(0)  编辑  收藏 所属分类: nosql


只有注册用户登录后才能发表评论。


网站导航: