1) 基础使用
安装Tokyo Tyrant后,可以通过在终端执行命令'ttserver'来立即启动服务器。默认,服务器在1978端口监听,为on-memory hash database(适合存储缓存数据)提供访问服务。
1
|
[terminal-1]$ ttserver
|
在另外一个终端中执行下面的命令以测试存储操作,'tcrmgr put'调用函数'tcrdbput':
1
2
3
|
[terminal-2]$ tcrmgr put localhost one first
[terminal-2]$ tcrmgr put localhost two second
[terminal-2]$ tcrmgr put localhost three third
|
执行下面的命令来一次获取多条记录。'tcrmgr mget' 调用函数 'tcrdbget3':
1
|
[terminal-2]$ tcrmgr mget localhost one two three
|
在服务器的终端中按Ctrl-C来终止服务器。
通过指定后缀为'.tch'的文件名,我们运行服务器来处理 hash database:
1
|
[terminal-1]$ ttserver casket.tch
|
保存一些记录
1
2
3
|
[terminal-2]$ tcrmgr put localhost one first
[terminal-2]$ tcrmgr put localhost two second
[terminal-2]$ tcrmgr put localhost three third
|
用Ctrl-C终止服务器,然后重新启动服务器:
1
|
[terminal-1]$ ttserver casket.tch
|
检查保存的记录的一致性。
1
|
[terminal-2]$ tcrmgr mget localhost one two three
|
为了后续的教程,用Ctrl-C终止服务器并删除数据库。
1
|
[terminal-1]$
rm
casket.tch
|
2) 后台进程
指定选项"-dmn"以后台进程的方式运行服务器。此外,可以指定"-pid"选项来指定文件以记录进程ID。注意后台进程的当前工作目录被修改到root目录。因此,文件路径参数需要用绝对路径来表示。
1
|
[terminal-1]$ ttserver -dmn -pid
/tmp/ttserver
.pid
/tmp/casket
.tch
|
为了终止服务器,查看'_pid'指定的进程Id文件并发送SIGTERM 信号给进程。
1
|
[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) 备份和恢复
让我们再次运行服务器以继续这个教程。
1
|
[terminal-1]$ ttserver casket.tch
|
保存一些记录。
1
2
3
|
[terminal-2]$ tcrmgr put localhost one first
[terminal-2]$ tcrmgr put localhost two second
[terminal-2]$ tcrmgr put localhost three third
|
为了备份数据库文件,使用命令'tcrmgr copy'并指定目标路径。注意备份文件将在服务器上的本地文件系统中创建(不是在客户端这边).
1
|
[terminal-2]$ tcrmgr copy localhost backup.tch
|
按Ctrl-C 终止服务器并删除数据库
1
|
[terminal-1]$
rm
casket.tch
|
从备份文件中恢复数据库并重启服务器。
1
2
|
[terminal-1]$
cp
backup.tch casket.tch
[terminal-1]$ ttserver casket.tch
|
检查存储的记录的一致性。
1
|
[terminal-2]$ tcrmgr mget localhost one two three
|
为了后续的教程,用Ctrl-C终止服务器并删除数据库。
1
|
[terminal-1]$
rm
casket.tch backup.tch
|
4) 更新日志
让我们开启更新日志来运行服务器。选项'-ulog'指定包含更新日志文件的目录。
1
2
|
[terminal-1]$
mkdir
ulog
[terminal-1]$ ttserver -ulog ulog casket.tch
|
存储一些记录.
1
2
3
|
[terminal-2]$ tcrmgr put localhost one first
[terminal-2]$ tcrmgr put localhost two second
[terminal-2]$ tcrmgr put localhost three third
|
按Ctrl-C 终止服务器并删除数据库
1
|
[terminal-1]$
rm
casket.tch
|
备份更新日志目录并重新启动服务器。
1
2
3
|
[terminal-1]$
mv
ulog ulog-back
[terminal-1]$
mkdir
ulog
[terminal-1]$ ttserver -ulog ulog casket.tch
|
在客户端使用'tcrmgr restore'命令从备份的更新日志中恢复数据库
1
|
[terminal-2]$ tcrmgr restore localhost ulog-back
|
检查存储的记录的一致性。
1
|
[terminal-2]$ tcrmgr mget localhost one two three
|
为了后续的教程,用Ctrl-C终止服务器并删除数据库。
1
|
[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服务器。
1
2
|
[terminal-1]$
mkdir
ulog-1
[terminal-1]$ ttserver -port 1978 -ulog ulog-1 -sid 1 casket-1.tch
|
下一步,在另一个终端运行slave服务器。
1
2
|
[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中存储一些记录。
1
2
3
|
[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中检查存储记录的一致性。
1
2
|
[terminal-2]$ tcrmgr mget -port 1978 localhost one two three
[terminal-2]$ tcrmgr mget -port 1979 localhost one two three
|
让我们模拟master崩溃的情况。Ctrl-C终止master并删除数据库文件。
1
|
[terminal-1]$
rm
casket-1.tch
|
Ctrl-C终止slave并重启作为master。
1
|
[terminal-2]$ ttserver -port 1979 -ulog ulog-2 -sid 2 casket-2.tch
|
添加新的slave(使用端口1980)。
1
2
|
[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中检查存储记录的一致性。
1
2
|
[terminal-2]$ tcrmgr mget -port 1979 localhost one two three
[terminal-2]$ tcrmgr mget -port 1980 localhost one two three
|
Ctrl-C终止两个服务器并删除数据库和相关文件。
1
2
3
|
[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)。
1
2
3
4
5
|
#! /bin/sh
srcpath=
"$1"
destpath=
"$1.$2"
rm
-f
"$destpath"
cp
-f
"$srcpath"
"$destpath"
|
下一步,让我们开启更新日志来启动master。
1
2
|
[terminal-1]$
mkdir
ulog-1
[terminal-1]$ ttserver -port 1978 -ulog ulog-1 -sid 1 casket-1.tch
|
往master中存入一些记录。
1
|
[terminal-2]$ tcrtest write -port 1978 localhost 10000
|
检查存储的记录的一致性。
1
|
[terminal-2]$ tcrmgr list -port 1978 -pv localhost
|
备份数据库
1
|
[terminal-2]$ tcrmgr copy -port 1978 localhost
'@./ttbackup.sh'
|
确认备份文件被保存为"casket-1.tch.xxxxx"("xxxxx"为备份文件的时间戳)。然后,使用备份文件运行slave。
1
2
3
4
5
|
[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。
1
2
3
|
[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的差异。
1
2
|
[terminal-3]$ tcrmgr inform -port 1978 localhost
[terminal-3]$ tcrmgr inform -port 1979 localhost
|
给slave指定master,这样将启动复制并解决这个差异。
1
|
[terminal-3]$ tcrmgr setmst -port 1979 -mport 1978 localhost localhost
|
确认slave知道master并且解决了差异。
1
|
[terminal-3]$ tcrmgr inform -port 1979 -st localhost
|
Ctrl-C终止两个服务器并删除数据库和相关文件。
1
2
|
[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的费伯那其数字(注:数列中每个数字是前两个数字的和)
1
2
3
4
5
6
|
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
|
让我们启动服务器,让它读取这个脚本文件
1
|
[terminal-1]$ ttserver -ext
test
.lua
|
在客户端命令中调用这个函数。
1
2
3
4
5
6
|
[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"可以从数据库中返回下一个费伯那其数字。状态信息被保存在数据库中。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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
|
然后,重启服务器并测试新的算法。
1
2
3
4
5
6
|
[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服务器,下面的脚本是一个典型例子。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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服务器,下面的脚本是一个典型例子。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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"列的数值超过当前日期时将使记录过期。
1
2
3
4
5
6
7
8
9
10
|
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 函数。
1
|
[terminal-1]$ ttserver -ext ttexpire.lua -extpc expire 1.0
"casket.tct#idx=x:dec#dfunit=8"
|
在另外一个终端中存储测试记录。
1
2
3
4
|
[terminal-2]$ now=`
date
+%s`
for
((i=1;i<=60;i++));
do
tcrmgr put -sep
'|'
localhost
"$i"
"x|$((now+i))"
done
|
确认数据正在被过期机制删除:
1
|
[terminal-2]$ tcrmgr list -pv -sep
'|'
localhost
|