Tokyo Tyrant教程
1. 基礎使用
安裝Tokyo Tyrant后,可以通過在終端執(zhí)行命令'ttserver'來立即啟動服務器。默認,服務器在1978端口監(jiān)聽,為on-memory hash database(適合存儲緩存數(shù)據(jù))提供訪問服務。
[terminal-1]$ ttserver
在另外一個終端中執(zhí)行下面的命令以測試存儲操作,'tcrmgr put'調(diào)用函數(shù)'tcrdbput':
[terminal-2]$ tcrmgr put localhost one first
[terminal-2]$ tcrmgr put localhost two second
[terminal-2]$ tcrmgr put localhost three third
執(zhí)行下面的命令來一次獲取多條記錄。'tcrmgr mget' 調(diào)用函數(shù) '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
為了后續(xù)的教程,用Ctrl-C終止服務器并刪除數(shù)據(jù)庫。
[terminal-1]$ rm casket.tch
2) 后臺進程
指定選項"-dmn"以后臺進程的方式運行服務器。此外,可以指定"-pid"選項來指定文件以記錄進程ID。注意后臺進程的當前工作目錄被修改到root目錄。因此,文件路徑參數(shù)需要用絕對路徑來表示。
[terminal-1]$ ttserver -dmn -pid /tmp/ttserver.pid /tmp/casket.tch
為了終止服務器,查看'_pid'指定的進程Id文件并發(fā)送SIGTERM 信號給進程。
[terminal-1]$ kill -TERM `cat /tmp/ttserver.pid`
為了通過操作系統(tǒng)的RC腳本運行服務器,請使用'ttservctl'。對于Linux發(fā)型版本,添加下面的行到/etc/rc.local.
/etc/rc.local
默認,數(shù)據(jù)庫文件和相關文件被放置在'/var/ttserver'下。因為'ttservctl'是一個很小的shell腳本,您可以隨意的復制并編輯它。同樣,也可以安裝修改后的腳本到'/etc/init.d'并設置符號鏈接/etc/rc3.d/S98ttserver' and `/etc/rc5.d/S98ttserver'.
3) 備份和恢復
讓我們再次運行服務器以繼續(xù)這個教程。
[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
為了備份數(shù)據(jù)庫文件,使用命令'tcrmgr copy'并指定目標路徑。注意備份文件將在服務器上的本地文件系統(tǒng)中創(chuàng)建(不是在客戶端這邊).
[terminal-2]$ tcrmgr copy localhost backup.tch
按Ctrl-C 終止服務器并刪除數(shù)據(jù)庫
[terminal-1]$ rm casket.tch
從備份文件中恢復數(shù)據(jù)庫并重啟服務器。
[terminal-1]$ cp backup.tch casket.tch
[terminal-1]$ ttserver casket.tch
檢查存儲的記錄的一致性。
[terminal-2]$ tcrmgr mget localhost one two three
為了后續(xù)的教程,用Ctrl-C終止服務器并刪除數(shù)據(jù)庫。
[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 終止服務器并刪除數(shù)據(jù)庫
[terminal-1]$ rm casket.tch
備份更新日志目錄并重新啟動服務器。
[terminal-1]$ mv ulog ulog-back
[terminal-1]$ mkdir ulog
[terminal-1]$ ttserver -ulog ulog casket.tch
在客戶端使用'tcrmgr restore'命令從備份的更新日志中恢復數(shù)據(jù)庫
[terminal-2]$ tcrmgr restore localhost ulog-back
檢查存儲的記錄的一致性。
[terminal-2]$ tcrmgr mget localhost one two three
為了后續(xù)的教程,用Ctrl-C終止服務器并刪除數(shù)據(jù)庫。
[terminal-1]$ rm -rf casket.tch ulog ulog-back
5) 復制
復制是同步兩臺或更多數(shù)據(jù)庫服務器的機制,實現(xiàn)高可用性和高完整性。復制源服務器被稱為"master"而所有目標服務器都被稱為"slave"。復制需要以下前提條件:
1. master必須記錄更新日志
2. master必須指定唯一的服務器ID
3. 每個slave必須記錄更新日志,因為在master當機時它將成為master
4. 每個slave必須指定唯一的服務器ID,因為在master當機時它將成為master
5. 每個slave必須指定它的master服務器的地址和端口號
6. 每個slave必須指定復制時間戳文件
這個章節(jié)將描述何如建立一個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并刪除數(shù)據(jù)庫文件。
[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終止兩個服務器并刪除數(shù)據(jù)庫和相關文件。
[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"復制可以提供更高的可用性。要實現(xiàn)它,運行兩個服務器各自復制對方。注意同時更新兩個master可能導致數(shù)據(jù)庫不一致。默認,服務器不報錯即使檢測到不一致。'-rcc'選項將使得服務器檢查一致性并在檢測到不一致時停止服務。
6) 按需設置復制
可以為正在運行的數(shù)據(jù)庫服務設置復制而不必停工。首先,為備份操作準備下面的腳本并保存為"ttbackup.sh",設置好可執(zhí)行權限(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
備份數(shù)據(jù)庫
[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知道m(xù)aster并且解決了差異。
[terminal-3]$ tcrmgr inform -port 1979 -st localhost
Ctrl-C終止兩個服務器并刪除數(shù)據(jù)庫和相關文件。
[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) 調(diào)整
如果使用hash database,設置調(diào)增參數(shù)"#bnum=xxx"來改進性能。它指定bucket 數(shù)量,應該要比存儲的記錄數(shù)多。
如果使用B+ tree database,設置調(diào)整參數(shù)"#lcnum=xxx#bnum=yyy"來改進性能。前一個參數(shù)指定緩存的葉節(jié)點的最大數(shù)量,應該和系統(tǒng)容許的內(nèi)存容量一樣大。后一個參數(shù)指定bucket 的數(shù)量,應該比要存儲的記錄數(shù)的1/128大。
如果有非常大量的客戶端訪問服務器,確認已清除每個進程的文件描述符數(shù)量的限制。在大多數(shù)系統(tǒng)中默認設置為1024。如果沒有,使用'ulimit'來清理它。
為了處理服務波峰時間的突發(fā)請求,可以用復制來聯(lián)合使用on-memory hash/tree database和file hash/tree database。master服務器處理on-memory database,它可以從容處理波峰時間的突發(fā)請求。但是on-memory database不保證數(shù)據(jù)的持久化,用于復制的slave通過將記錄存儲到文件數(shù)據(jù)庫中來彌補這個缺陷。
8) Lua擴展
如果你需要比已有更加復雜的數(shù)據(jù)庫操作,請使用Lua擴展。舉例,準備下列腳本并保存為"test.lua"。這里有一個名為"fibonacci"的函數(shù),返回key的費伯那其數(shù)字(注:數(shù)列中每個數(shù)字是前兩個數(shù)字的和)
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
在客戶端命令中調(diào)用這個函數(shù)。
[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可以通過其他算法來產(chǎn)生。添加下列腳本到"test.lua"。這里有函數(shù)"fibnext"可以從數(shù)據(jù)庫中返回下一個費伯那其數(shù)字。狀態(tài)信息被保存在數(shù)據(jù)庫中。
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'.
如你所見,被調(diào)用的函數(shù)接收兩個字符串參數(shù),key和value。返回值被發(fā)送回客戶端。你可以始終諸如"_put", "_out", "_get"等內(nèi)建函數(shù)來進行數(shù)據(jù)庫操作。這里有一個實例文件'ext/senatus.lua'。
9) 使用memcached 客戶端
這個章節(jié)描述如何使用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 客戶端
這個章節(jié)介紹如何使用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信息這樣的數(shù)據(jù),但是想避免因服務器當機而造成的數(shù)據(jù)丟失,Tokyo Tyrant是一個方案,也就是說,持久化而支持過期的緩存。它需要下面的前提條件:
1. 服務器必須開啟table database
2. 客戶端保存每條記錄時要使用過期數(shù)據(jù)列
3. 數(shù)據(jù)庫在過期數(shù)據(jù)列上要有索引
4. 數(shù)據(jù)庫要開啟自動重新組合
5. 服務器必須周期性的調(diào)用通過Lua擴展提供的用戶自定義函數(shù)
首先,為過期準備下面的腳本并保存為"ttexpire.lua"。當"X"列的數(shù)值超過當前日期時將使記錄過期。
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"列是有索引的,并計劃每秒鐘調(diào)用一次expiration 函數(shù)。
[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
確認數(shù)據(jù)正在被過期機制刪除:
[terminal-2]$ tcrmgr list -pv -sep '|' localhost