转载: http://www.360doc.com/content/11/0401/17/28217_106451082.shtml
如果你知道 Tokyo Cabinet ,那么就应该知道 Kyoto Cabinet,因为他们都是同一个作者(平林幹雄)开发出来的 Key-Value 数据库。
- 改进的空间效率:更小的数据库文件
- 改进的时间效率:更快的处理速度
- 改进的并行性:多线程环境下的高性能
- 改进的可用性:简单的API
- 改进的健壮性:即使在灾难情况下数据库文件也不会损坏
- 支持64位架构:巨大的内存空间和数据库文件可用
- 改进的空间效率:更小的数据库文件
- 改进的并行性:多线程环境下的高性能
- 改进的可移植性:对底层的抽象来支持 非POSIX系统
- 改进的可用性:简单的API,面向对象的设计
- 改进的健壮性:即使在灾难情况下数据库文件也不会损坏
- tune_buckets:设置hash数据库的 bucket 数量
- tune_options:设置可选特性(optional features)
- tune_buckets:设置hash数据库的 bucket 数量
- tune_compressor:设置数据压缩方法
- cap_count:设置记录数的容量
- cap_size:设置内存使用的容量
1 |
db.tune_buckets(10LL * 1000 * 1000); db.cap_count(10LL * 1000 * 1000); db.cap_size(8LL << 30); db.open(...); |
- tune_page:设置每个页大小
- tune_page_cache:设置页缓存(page cache)容量大小
- tune_comparator:设置记录比较器
1 |
db.tune_options(GrassDB::TCCOMPESS); db.tune_buckets(500LL * 1000); db.tune_page(32768); db.tune_page_cache(1LL << 20); db.open(...); |
- tune_alignment:设置记录的对齐幂数
- tune_fbp:设置空闲块池的容量幂数
- tune_options:设置可选特性
- tune_buckets:设置哈希表的bucket数量
- tune_map:设置内部内存映射区域的大小
- tune_defrag:设置自动碎片整理的单位步数
- tune_compressor:设置数据压缩器
1 |
db.tune_alignment(0); db.tune_options(HashDB::TSMALL | HashDB::TLINEAR); db.tune_buckets(10LL * 1000); db.tune_defrag(8); db.open(...); |
1 |
db.tune_options(HashDB::TLINEAR); db.tune_buckets(20LL * 1000 * 1000 * 1000); db.tune_map(300LL << 30); db.open(...); |
- tune_page:设置每个页大小
- tune_page_cache:设置页缓存(page cache)容量大小
- tune_comparator:设置记录比较器
1 |
db.tune_options(TreeDB::TLINEAR | TreeDB::TCCOMPESS); db.tune_buckets(1LL * 1000); db.tune_defrag(8); db.tune_page(32768); db.open(...); |
1 |
db.tune_options(TreeDB::TLINEAR); db.tune_buckets(1LL * 1000 * 1000 * 1000); db.tune_map(300LL << 30); db.tune_page_cache(8LL << 30); db.open(...); |
- tune_options:设置可选特性
- 时间效率:CacheDB > StashDB > ProtoHashDB > ProtoTreeDB > GrassDB
- 空间效率:GrassDB > StashDB > CacheDB > ProtoHashDB > ProtoTreeDB
- 时间效率:HashDB > TreeDB > DirDB > ForestDB
- 空间效率:TreeDB > HashDB > ForestDB > DirDB
1 |
db.begin_transaction(); db.set("japan", "tokyo"); db.set("korea", "seoul"); db.end_transaction(); |
1 |
db.open("casket.kch", HashDB::OWRITER | HashDB::OCREATE | HashDB::OAUTOTRAN); db.set("japan", "tokyo"); db.set("china", "beijing"); |
1 |
db.copy("backup.kch"); |
1 |
class BackupImpl : public FileProcessor { bool process(const std::string& path, int64_t size, int64_t count) { char cmd[1024]; sprintf(cmd, "snapshot.sh %s", path.c_str()); return system(cmd) == 0; } } proc; db.synchronize(&proc); |
1 |
db.dump_snapshot("backup.kcss"); db.load_snapshot("backup.kcss"); |
1 |
ArcfourCompressor comp; comp.set_key("foobarbaz", 9); TreeDB db; db.tune_options(kc::TreeDB::TCOMPRESS); db.tune_compressor(&comp); db.open(...); comp.begin_cycle((uint64_t)getpid() << 32 + (uint64_t)time()); |
1 |
PolyDB db; db.open("casket.kct#zcomp=arc#zkey=foobarbaz", ...); |
std::map<std::string, std::string>' (ProtoTreeDB) 使用大约 1.2GB内存。同样的情况下,stash 数据库使用 465MB 内存;cache hash数据库使用 618MB 内存;cache tree数据库使用 318MB 内存。在这种情况下,cache tree数据库提供了最佳的空间效率。然而,关于时间效率, stash 数据库和 cache 数据库 优于 cache tree数据库,由于哈希表和B+ tree的不同。注意B+ tree是非常适合顺序访问,但不适合随机访问的。要改进B+ tree的时间效率,设置 页大小为1024或更小。
GNU General Public License
Kyoto Cabinet is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.
Kyoto Cabinet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/
‘.
FOSS License Exception
The FOSS License Exception is also provided in order to accommodate products under other free and open source licenses. See the body text for details.
Commercial License
If you use Kyoto Cabinet within a proprietary software, a commercial license is required.
The commercial license allows you to utilize Kyoto Cabinet by including it in your applications for purpose of developing and producing your applications and to utilize Kyoto Cabinet in order to transfer, sale, rent, lease, distribute or sublicense your applications to any third parties. See the license guide for details.
Author
Kyoto Cabinet was written and is maintained by FAL Labs. You can contact the author by e-mail to `info@fallabs.com
‘.
在Tokyotyrant中有支持根据前缀来查询的方式,命令行如下:
tcrmgr list [-port num] [-sep chr] [-m num] [-pv] [-px] [-fm str] host
参数中的-fm 就是要指定的key的前缀,试想:不同的存储方式中,这种操作是怎么实现的呢?
1. 对于hash存储,遍历所有的key,然后和提供的前缀来比较,显然效率是很低的,所以如果有这种需求,就不要使用hash存储
2. 对于tree的存储方式,比hash方式查找要比较的少一些
3. B+tree, 这个就比较快一下,这个是比较适合的存储方式
缘起:
tokyotyrant自有的master<->master, master->slave 模式已经不能满足业务的需要,现在期望可以随意复制,选择性复制。
办法:
1. 我们可以直接分析ulog来实现
2. 我们可以参考tokyotyrant的复制协议,通过socket来复制,或者使用C来实现,或者使用自己熟悉的语言实现
3. tokyotyrant提供了一个tcrmgr的工具,其中的repl子命令可以帮助我们通过socket复制数据,我们就可以使用自己熟悉的语言,将输出的信息解析后处理,同步到自己想同步到的地方去
PHP代码:
这里仅仅实现了两个命令的解析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php define("PUT", "10"); define("OUT", "20"); while(!feof(STDIN)) { $str = trim(fgets(STDIN)); if (!$str) continue; $arr = explode("\t", $str); parse_record($arr[3]); } exit; function parse_record($str) { $str = str_replace(" ", "", $str); if (strlen($str) < 4) return false; $magic = substr($str, 0, 2); $cmd = substr($str, 2, 2); if ($magic != "C8") return false; switch($cmd) { case OUT: $ksiz = hexdec(substr($str, 4, 8)) * 2; $key = pack("H*", substr($str, 12, $ksiz)); echo "out\t", $key ,"\n"; break; case PUT: $ksiz = hexdec(substr($str, 4, 8)) * 2; $key = pack("H*", substr($str, 20, $ksiz)); $vsiz = hexdec(substr($str, $ksiz + 18, 8)) * 2; $val = pack("H*", substr($str, $ksiz + 20, $vsiz)); echo "put\t", $key , "\t", $val, "\n"; break; default: } return true; } |
使用方法:
tcrmgr repl -port 4242 -ts 1345999492000000 -sid 1234 -ph 10.71.6.28|php repl.php
记录格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
struct record{ int magic; // 0xc8 int cmd; // put: 0x10; delete 0x20; ... ... } struct put_record{ int magic; // 0x80 int cmd; // 0x10 int32 ksiz; char *key; int32 vsiz; char *val; } struct delete_record{ int magic; // 0x80 int cmd; // 0x20 int32 ksiz; char *key; } |
使用Tokyotrant有相当一段时间了,确实有几次莫名的退出,今天再次出现,查了一下系统日志,报错如下:
$grep ttserver /var/log/messages
Jun 18 22:00:42 localhost kernel: [2463228.773583] ttserver[32282] general protection rip:2b6557922dcb rsp:4a0b2c08 error:0
又出现一次;
Jul 18 07:00:21 localhost kernel: [11461817.641400] ttserver[612]: segfault at 000000001ffc2000 rip 0000003f5407c08f rsp 000000004732ec08 error 4
下面的8001端口进程使用的是默认的malloc内存分配器, 8002端口是使用了tcmalloc内存分配器,线上数据运行了1个月的时间,发现tcmalloc使用的内存更多一些。
1. 同样在一组磁盘上的两个TT端口,其读写操作是根据均匀的hash方式来分配的,但是,其中一个3001端口同步数据就很慢,而3002端口同步数据就很快,重启3001端口依然很慢; 把3002端口停掉之后,3001端口依然很慢。
2. 通过tcpdump抓包发现:
3001端口的master 2001端口,数据流表现为:
2001 ——–很多数据给—–》 3001
3001 ——–因为吃不消,所以act 一个win(0) 给 —-》 2001
2001 ——–不断询问 ———-》3001
2001 ——–不断询问 ———-》3001
2001 ——–不断询问 ———-》3001
3001 ——–响应,说在给点儿吧 ——-》 2001
2001 ——–很多数据给 ———…..
使用netstat也可以发现,对于3001的同步连接中的接受队列总是会阻塞很多数据,看来是执行的太慢
3. 观察 3001端口的执行情况,下面是使用strace跟踪的这个繁忙的ttserver进程的系统调用情况:
为什么futex耗费的时间如此惊人?
1. TokyoTrant(后面简称TT)在备份的时候不能提供写服务也不能提供读服务2. 备份的速度取决于磁盘的好坏
下面有一个TT备份的参考:
date; tcrmgr copy -port 3004 10.xx.x.xx @/data1/tokyotyrant/bin/ttbackup.sh; date
Thu Dec 22 23:22:59 CST 2011
Thu Dec 22 23:25:30 CST 2011
这里备份了12G的数据,用了2.5分钟;
磁盘性能:
5块 146G的磁盘做了逻辑卷的, 备份到同一个目录下, 如果按照300MB/s 的写速度, 300MB/s 的读速度,则每秒钟复制150MB的数据,则大约100s可以复制15G的数据;
而这里12G的数据复制了2.5分钟,看来我的计算是有些偏差的
另外一台机器的备份情况,备份数据量: 9.4G
关于复制时的一点注意事项:
当我们将复制好的从库启动之后,可能会发现延迟越来越大,可能是因为,数据都在磁盘上,虽然配置了很大的内存,现在还没能用上,磁盘的随机读写操作可能比较严重,过一段时间之后,随着磁盘数据越来越多地载入内存,这时候的读写操作都会很快了,延迟自然就越来越小了。
不同的情景适合不同的参数,我们的场景是读写比例2:1, 写操作高峰时间为 6000/s ;资源为 2台8核16G内存机器。每台机器 6 * 149G的磁盘做了raid10。
每台机器2个端口,每个端口数据文件为5G; 存储方式为 .tch ; 没有任何调谐参数时, 每个端口处理写操作量为:
200~400/s ; 添加调谐参数 #xmsiz=5242880000 后, 每个端口处理写操作量为:
1000~2000/s ; 速度整整提高了原来的4倍。
关于负载的比较(只取了部分时间点来比较):
调整参数前:
sar -f /var/log/sa/sa05
调整参数后:
sar -f /var/log/sa/sa06
关于磁盘的读写数据的变化,因为没有sar没有收集,这里就不能给出了
关于页面的换入换出的比较:
调整参数前:
sar -B -f /var/log/sa/sa05
调整参数后:
sar -B -f /var/log/sa/sa06
可以看到,页面的换入换出明显减少了大约一半, 或许你发现了fault变多了,这也是使用了mmap的缘故,具体需要了解一下mmap了。
关于mmap:
因为tc是从文件的开始固定地map文件的一段区域的,所以,如果map的那段区域如果慢慢变冷的话,这样的map也就变的没有意义了;或者文件太大,能map到内存的也不过1/10或者更少,则map的意义也是很小的,故要根据实际情况来选择之。
如果tc能聪明地选择map热数据的话,自然是件好事,但是目前确实还没有这么做,而且其升级版kc也没有这么做,至少到1.2.70版本是没有这么做的。
最近由于使用TokyoTyrant的全内存存储方式导致内存吃紧,于是突然想到TokyoTyrant支持压缩存储,抱着试一试的态度,看看内存存储是不是也能压缩;测试了一下发现似乎没有压缩,看了一下代码,如下图:
不解释了,意思就是说内存型的不支持压缩,其实也可以理解