Tokyo Tyrant基本规范,翻译自Tokyo Tyrant官网,原文地址http://fallabs.com/tokyotyrant/spex.html。
主要内容为Tokyo Tyrant的介绍,安装,服务器程序,客户端程序,远程数据库API,Lua 扩展,协议和一个针对初学者的教程。
一.介绍
Tokyo Tyrant是名为Tokyo Cabinet的DBM的网络接口的套装。既然这个DBM拥有高性能,你可能烦恼于多个程序共享同一个数据库,或者远程程序访问数据库。因此,Tokyo Tyrant提供并发和远程连接到Tokyo Cabinet。它由管理数据库的服务器程序和用于客户端程序的访问类库组成。
因为线程池模型实现和现代Linux/*BSD核心的epoll/kqueue机制,该服务器提供高并发支持。服务器端和它的客户端通过基于TCP/IP的简单二进制协议相互交流。协议兼容memcached,另外HTTTP也同样支持,因此几乎所有主流平台和编程语言都可以使用Tokyo Tyrant。因为类似诸如热备份,更新日志和复制的机制,高可用性和高完整性(integrity)也同样提供。服务器还可以内嵌Lua,一个轻量级的脚本语言,以便能够定义任意的数据库操作。
因为服务器使用Tokyo Cabinet的抽象API,所有的6个API:on-memory hash database API,on-memory tree database API, hash API,B+ tree database API,fixed-length database API和table database API, 都可以在客户端使用通用接口访问。此外,table extension用于使用table database的特定功能。
到目前为止,服务器仅仅工作于Linux, FreeBSD, Mac OS X, Solaris。(注:主流平台中就差windows,据说正在开发中,但是不知何时发布)
二.安装
预先安装好Tokyo Cabinet的最新版本并获取到Tokyo Tyrant的安装包。
当Tokyo Tyrant的压缩包被解开时,更改当前工作目录到生成的目录下并执行安装。
运行配置脚本。为了启用Lua扩展,添加'--enable-lua'选项。
./configure
构建程序.
make
安装程序。这个操作必须由root用户执行。
make install
当一系列的工作结束时,下面的文件将被安装
/usr/local/include/ttutil.h
/usr/local/include/tculog.h
/usr/local/include/tcrdb.h
/usr/local/lib/libtokyotyrant.a
/usr/local/lib/libtokyotyrant.so.x.y.z
/usr/local/lib/libtokyotyrant.so.x
/usr/local/lib/libtokyotyrant.so
/usr/local/lib/ttskelmock.so
/usr/local/lib/ttskeldir.so
/usr/local/lib/ttskelproxy.so
/usr/local/lib/ttskelnull.so
/usr/local/lib/pkgconfig/tokyotyrant.pc
/usr/local/bin/ttserver
/usr/local/bin/ttultest
/usr/local/bin/ttulmgr
/usr/local/bin/tcrtest
/usr/local/bin/tcrmttest
/usr/local/bin/tcrmgr
/usr/local/sbin/ttservctl
/usr/local/share/tokyotyrant/...
/usr/local/man/man1/...
/usr/local/man/man3/...
运行下面的命令来测试服务器。可在终端按Ctrl-C结束。
ttserver
为了测试连接到上述运行服务器的客户端,在另外的终端运行下面的命令:
make check
三. 服务器程序
1) ttserver
命令'ttserver'运行服务器端管理一个数据库实例。因为数据库被视为Tokyo Cabinet的抽象API,你可以在服务器启动时选择方案。支持的方案有on-memory hash database, on-memory tree database, hash database, 和 B+ tree database. 这个命令的使用格式如下。'dbname'指定数据库名称。缺省使用on-memory hash database。
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]
选项说明如下:
-host name : 指定服务器的主机名或地址。默认,每个网络地址都将被绑定
-port num : 指定端口号。默认为1978
-thnum num : 指定工作线程。默认为8
-tout num : 指定每个会话(session)的超时时间,单位秒。默认不超时。
-dmn : 作为守护进程工作
-pid path : 输出进程id到文件
-kl : 如果发现进程id文件存在,杀死已存在的进程
-log path : 输出日志信息到文件
-ld : 将debug信息写入日志文件
-le : 仅仅写入错误信息到日志文件
-ulog path : 指定更新日志目录
-ulim num : 指定每个更新日志文件的大小限制
-uas : 为更新日志使用异步I/O
-sid num : 指定服务器ID
-mhost name : 指定复制主服务器的主机名
-mport num : 指定复制主服务器的端口号
-rts path : 指定复制时间戳文件
-rcc : 检查复制的一致性
-skel name : 指定骨架(skeleton,基本?)数据库类库的名称
-mul num : 指定多数据库机制的分区数量
-ext path : 指定脚本语言扩展文件
-extpc name period : 指定周期命令的函数名和调用周期
-mask expr : 指定禁用命令的名称
-unmask expr : 指定容许使用的命令的名称
发送SIGINT或SIGTERM到进程可正常关闭服务器,。也可以在控制终端中按Ctrl-C。发送SIGHUP可重启服务器。如果端口号不大于0,将使用UNIX domain socket同时socket文件的路径将由主机参数指定。这个命令成功时返回0,失败返回其他。
数据库命名协定由Tokyo Cabinet的抽象API指定。名称为
名称 数据库
"*" on-memory hash database
'+' on-memory tree database
".tch"后缀 hash database
".tcb"后缀 B+ tree database
".tcf" fixed-length database
".tct" table database
别的名称则这个方法失败。
名字后面可以追加调整参数,用"#"分隔。每个参数由名称和值组成,用"="分隔。
数据库 支持参数
On-memory hash database "bnum", "capnum", "capsiz"
On-memory tree database "capnum", "capsiz"
Hash database "mode", "bnum", "apow", "fpow", "opts", "rcnum", "xmsiz", "dfunit"
B+ tree database "mode", "lmemb", "nmemb", "bnum", "apow", "fpow", "opts", "lcnum", "ncnum", "xmsiz", "dfunit"
Fixed-length database "mode", "width", "limsiz"
Table database "mode", "bnum", "apow", "fpow", "opts", "rcnum", "lcnum", "ncnum", "xmsiz", "dfunit", "idx"
参数名 作用
"bnum" 指定bucket number
"capnum" 指定记录的容量数
"capsiz" 指定使用内存的容量大小. 超过容量的记录将基于存储顺序被删除.
"mode" 可包含"w"/写, "r"/读, "c"/创建, "t"/缩短(truncating), "e"/不加锁, "f"/不阻塞锁. 默认模式为"wc".
"opts" 可包含"l" / large选项, "d" / Deflate选项,"b" / BZIP2 选项, "t" / TCBS 选项
"idx" 指定索引的列名和类型,用":"分隔。例如,"casket.tch#bnum=1000000#opts=ld" 表示数据库文件名是"casket.tch", bucket number 是 1000000, 选项是large和Deflate.
命令掩码表达式(command mask expression)是由","分隔的命令列表。例如,"out,vanish,copy"表示"out", "vanish"和"copy"的集合。memcached 兼容协议命令和HTTP 兼容协议命令也同样被禁止或容许,和每个初始命令的掩码相关。此外,还有meta 表达式:
表达式 表示的命令
"all" 所有命令
"allorg" 所有原始二进制协议的命令
"allmc" 所有memcached兼容协议的命令
"allhttp" 所有HTTP兼容协议的命令"allread" 缩写,包括'get', 'mget', 'vsiz', 'iterinit', 'iternext', 'fwmkeys', 'rnum', 'size', 'stat'.
"allwrite" 缩写,包括'put', 'putkeep', 'putcat', 'putshl', 'putnr', 'out', 'addint', 'adddouble', 'vanish', 'misc'.
"allmanage" 缩写,包括'sync', 'optimize', 'copy', 'restore', 'setmst'.
"repl" 作为master复制
"slave" 作为slave复制
2) ttservctl
'ttservctl'命令是服务器的启动脚本。可以被操作系统的启动程序的RC脚本调用。这个命令的使用格式如下:
1. ttservctl start
启动服务器
2. ttservctl stop
停止服务器
3. ttservctl restart
重启服务器
4. ttservctl hup
为日志轮换发送HUP信号到服务器
数据库放置于"/var/ttserver/casket.tch"。日志和相关文件放置于"/var/ttserver"。这个命令成功时返回0,失败时返回其他。
3) ttulmgr
'ttulmgr'命令是导入导出更新日志的工具。对于通过使用文本工具如grep和sed来过滤日志文件非常有用。这个命令的使用格式如下。'upath'指定更新日志目录。
1. ttulmgr export [-ts num] [-sid num] upath
导出更新日志为TSV文本数据到标准输出
2. ttulmgr import upath
从标准输入中导入TSV文本数据到更新日志
选项如下
-ts num : 指定开始的时间戳
-sid num : 指定自己的服务器ID
这个命令成功时返回0,失败时返回其他。
四. 客户端程序
1) tcrtest
'tcrtest'命令是用于敏捷测试和性能测试的工具。这个命令的使用格式如下。'host'指定服务器的主机名, 'rnum'指定重复测试的数目。
1. tcrtest write [-port num] [-cnum num] [-tout num] [-nr] [-rnd] host rnum
使用8字节作为key存储记录。key依次更改为'00000001', '00000002'...
2. tcrtest read [-port num] [-cnum num] [-tout num] [-mul num] [-rnd] host
获取数据库中的所有记录
3. tcrtest remove [-port num] [-cnum num] [-tout num] [-rnd] host
删除数据库中的所有记录
4. tcrtest rcat [-port num] [-cnum num] [-tout num] [-shl num] [-dai|-dad] [-ext name] [-xlr|-xlg] host rnum
使用连锁模式(concatenate mode)用中途重复的key来存储记录
5. tcrtest misc [-port num] [-cnum num] [-tout num] host rnum
执行不同操作的混合测试。
6. tcrtest wicked [-port num] [-cnum num] [-tout num] host rnum
随机选取list和map来执行更新操作
7. tcrtest table [-port num] [-cnum num] [-tout num] [-exp num] host rnum
执行table extension的混合测试
选项如下:
选项 意义
-port num 指定端口号
-cnum num 指定连接数
-tout num 指定每个会话的超时时间,单位秒
-nr 使用函数'tcrdbputnr'代替'tcrdbput'
-rnd 随机选取key
-mul num 为mget命令指定记录数
-shl num 使用'tcrdbputshl'并指定宽度
-dai 使用'tcrdbaddint'代替'tcrdbputcat'
-dad 使用'tcrdbadddouble'代替'tcrdbputcat'
-ext name 调用脚本语言扩展函数
-xlr 执行记录锁定
-xlg 执行全局锁定
-exp num 指定过期测试的生存时间
如果端口号不大于0,将使用UNIX domain socket同时socket文件的路径将由主机参数指定。这个命令成功时返回0,失败返回其他。
2) tcrmttest
'tcrmttest'是多线程环境下的敏捷测试工具。这个命令的使用格式如下。'host'指定服务器的主机名, 'rnum'指定重复测试的数目。
1. tcrmttest write [-port num] [-tnum num] [-nr] [-rnd] [-ext name] host rnum
使用8字节作为key存储记录。key依次更改为'00000001', '00000002'...
2. tcrmttest read [-port num] [-tnum num] [-mul num] host
获取数据库中的所有记录
3. tcrmttest remove [-port num] [-tnum num] host
删除数据库中的所有记录
选项如下:
选项 意义
-port num 指定端口号
-tnum num 指定运行的线程数
-nr 使用函数'tcrdbputnr'代替'tcrdbput'
-rnd 随机选取key
-ext name 调用脚本语言扩展函数
-mul num 为mget命令指定记录数
如果端口号不大于0,将使用UNIX domain socket同时socket文件的路径将由主机参数指定。这个命令成功时返回0,失败返回其他。
3) tcrmgr
'tcrmgr'是用于远程数据库API和应用程序的测试和调试工具。'host'指定服务器主机名,'key'指定记录的key。'value'指定记录的值。'params'指定调试参数。'dpath'指定目标文件。'func'指定函数名。'arg'指定函数的参数。'file'指定输入文件。'upath'指定更新日志目录。'mhost'指定复制时master的主机名。'url'指定目标URL。
1. tcrmgr inform [-port num] [-st] host
打印各种信息到标准输出
2. tcrmgr put [-port num] [-sx] [-sep chr] [-dk|-dc|-dai|-dad] host key value
存储记录
3. tcrmgr out [-port num] [-sx] [-sep chr] host key
删除记录
4. tcrmgr get [-port num] [-sx] [-sep chr] [-px] [-pz] host key
打印记录的值
5. tcrmgr mget [-port num] [-sx] [-sep chr] [-px] host [key...]
打印多条记录的key和value
6. tcrmgr list [-port num] [-sep chr] [-m num] [-pv] [-px] [-fm str] host
打印所有记录的key,每行一条
7. tcrmgr ext [-port num] [-xlr|-xlg] [-sx] [-sep chr] [-px] host func [key [value]]
调用脚本语言扩展函数
8. tcrmgr sync [-port num] host
使用数据库文件同步更新内容
9. tcrmgr optimize [-port num] host [params]
优化数据库文件
10. tcrmgr vanish [-port num] host
删除所有记录
11. tcrmgr copy [-port num] host dpath
复制数据库文件
12. tcrmgr misc [-port num] [-mnu] [-sx] [-sep chr] [-px] host func [arg...]
为多种操作调用通用函数
13. tcrmgr importtsv [-port num] [-nr] [-sc] host [file]
从TSV文件的每行中读取并存储记录
14. tcrmgr restore [-port num] [-ts num] [-rcc] host upath
用更新日志恢复数据库
15. tcrmgr setmst [-port num] [-mport num] [-ts num] [-rcc] host [mhost]
设置复制的master。
16. tcrmgr repl [-port num] [-ts num] [-sid num] [-ph] host
复制更新日志
17. tcrmgr http [-ah name value] [-ih] url
使用HTTP获取URL的资源
18. tcrmgr version
打印Tokyo Tyrant的版本信息
五. 远程数据库API
远程数据库是一组用于使用Tokyo Cabinet抽象数据库的接口,由Tokyo Tyrant服务器作为中介。查看'tcrdb.h'获取全部说明。
注:该API是以.h文件提供,适用于C语言。对java开发似乎意义不大,因此考虑跳过此章节。
六. Lua 扩展
数据库服务器可以在启动时读取通过'-ext'选项指定的Lua脚本文件。客户端可以通过远程数据库API的'tcrdbext'函数来调用定义在脚本文件中的函数。
1) 用户自定义函数
可以在脚本文件中定义一些任意的函数。每个函数接收2个字符串参数(key和value)。返回值将被发送回客户端。如果函数返回'nil',服务器发送错误码到客户端。
'tcrdbext'提供两种类型的加锁操作。一种是全局锁定,意味着同一时刻仅有一个线程能操作这个函数。另外一种是记录锁定,意味着同一时刻仅有一个线程能操作这个指定key的记录。
注意Lua解释器的实例被每个原生线程分别处理。因为Lua的全局变量无益于在远程线程或者会话中共享数据,需要使用数据库或者stash functions来共享数据。
Built-in Functions
2) 内建函数
下列用于数据库操作的内建函数可以在用户自定义函数中使用。'key'和'value'参数的类型可以是string或者number。如果给出number,将被转换为十进制字符串。
注: 详细的内建函数列表就不一一翻译了,请参考原文。
*****************************
内建函数,名称以'_'开头,不能被客户端直接调用。当服务器启动时,如果有定义函数'_begin'则该函数被隐式的调用。当服务器停止时,如果有定义函数'_end'则该函数被隐式的调用。
以下为内建的全局变量:
全局变量 信息
'_version' 服务器的版本信息
'_pid' 进程ID
'_sid' 服务器ID
'_thnum' 原生线程的数目
'_thid' 每个原生线程的ID数字
3) 实例代码
下面的代码是增加记录的值并存储为十进制数字的字符串的例子。这个函数可以在调用时使用记录锁定来保证原子性。
function incr(key, value)
value = tonumber(value)
if not value then
return nil
end
local old = tonumber(_get(key))
if old then
value = value + old
end
if not _put(key, value) then
return nil
end
return value
end
七. 协议
服务器和客户端的协议是基于TCP/IP。默认,服务端口被绑定在本机的每个地址上,端口号位1978。每个服务器和客户端的会话由request和response组成。服务器在同一端口上使用三种协议。
1) 原始二进制协议
在原始二进制协议中,请求被分类为下列命令。请求和应答的结构由命令决定。请求和应答的整型字节码顺序是big endian。
注: 详细的二进制协议命令就不一一翻译了,请参考原文。
*****************************
客户端可以在任意时间结束和关闭socket来结束会话。如果不关闭,则连接可以被下一次会话重用。如果发生违反协议或者某些知名错误,服务器会立即终止会话并关闭连接。
2) Memcached 兼容协议
作为memcached (ASCII)兼容协议,服务器实现下列命令: "set", "add", "replace", "get", "delete", "incr", "decr", "stats", "flush_all", "version" 和 "quit". 更新命令的"norely"选项同样支持。但是, "flags", "exptime" 和 "cas unique" 参数将被忽略。
3) HTTP 兼容协议
作为HTTP (1.1) 兼容协议, 服务器实现下列命令:
命令 类似于
"GET" 'tcrdbget'
"HEAD" 'tcrdbvsiz'
"PUT" 'tcrdbput'
"POST" 'tcrdbext'
"DELETE" 'tcrdbout'
"OPTIONS" 'tcrdbstat'
每个请求的URI将被作为URL编码过的key对待。而实体内容被作为value。不过,除"Connection"和"Content-Length"外所有的header被忽略。
"PUT"有header "X-TT-PDMODE",值可以是1(同'tcrdbputkeep'), 2(同'tcrdbputcat'), 或者其他(同'tcrdbput')。
"POST"可以有header "X-TT-XNAME" 或 header "X-TT-MNAME"中的一个."X-TT-XNAME"等同于'tcrdbext',用于指定函数名。header "X-TT-XOPTS" 用于 bitwise-or 选项,1(记录锁)和2(全局锁). 每个请求的URI将被作为URL编码过的key对待。而实体内容被作为value。结果表示在应答的实体正文中。header"X-TT-MOPTS"用于 bitwise-or 选项 , 1(忽略更新日志)。 请求参数用"application/x-www-form-urlencoded"格式表示在实体正文中。名字被忽略而值被作为参数列表。结果在应答的实体正文中用"application/x-www-form-urlencoded"格式表示。
八 教程
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