ceph-kvstore-tool 使用说明

参考链接:
https://github.com/ceph/ceph/blob/master/doc/man/8/ceph-kvstore-tool.rst
http://www.idcat.cn/ceph-kvstore-tool%E5%B7%A5%E5%85%B7%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D.html
https://blog.csdn.net/Z_Stand/article/details/98967671

1. 简介

ceph-kvstore-tool 是一个 kvstore 操作工具。它允许用户离线操作 leveldb/rocksdb 的数据(就像 OSD 的 omap)。

[root@node-1 ceph-objectstore-tool-test]# ceph-kvstore-tool -h
Usage: ceph-kvstore-tool <leveldb|rocksdb|bluestore-kv> <store path> command [args...]Commands:list [prefix]list-crc [prefix]dump [prefix]exists <prefix> [key]get <prefix> <key> [out <file>]crc <prefix> <key>get-size [<prefix> <key>]set <prefix> <key> [ver <N>|in <file>]rm <prefix> <key>rm-prefix <prefix>store-copy <path> [num-keys-per-tx] [leveldb|rocksdb|...] store-crc <path>compactcompact-prefix <prefix>compact-range <prefix> <start> <end>destructive-repair  (use only as last resort! may corrupt healthy data)stats

使用前,关闭 mon 服务 或者 osd 服务

systemctl stop <ceph-mon@node-1>|<ceph-mon@0>

使用完,记得重启服务

systemctl restart <ceph-mon@node-1>|<ceph-mon@0>

2. 示例

查询数据库表项(prefix,前缀)

# cat /var/lib/ceph/mon/ceph-node-1/kv_backend 查询 mon 使用何种 db
ceph-kvstore-tool <rocksdb|leveldb> /var/lib/ceph/mon/ceph-node-1/store.db/ listceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0 list# 查看 mon 数据库中都有哪些表项
ceph-kvstore-tool rocksdb /var/lib/ceph/mon/ceph-node-1/store.db/ list|awk '{print $1}'|uniq# bluestore 表项前缀
# const string PREFIX_SUPER = "S";       // field -> value
# const string PREFIX_STAT = "T";        // field -> value(int64 array)
# const string PREFIX_COLL = "C";        // collection name -> cnode_t
# const string PREFIX_OBJ = "O";         // object name -> onode_t
# const string PREFIX_OMAP = "M";        // u64 + keyname -> value
# const string PREFIX_PGMETA_OMAP = "P"; // u64 + keyname -> value(for meta coll)
# const string PREFIX_PERPOOL_OMAP = "m"; // s64 + u64 + keyname -> value
# const string PREFIX_PERPG_OMAP = "p";   // u64(pool) + u32(hash) + u64(id) + keyname -> value
# const string PREFIX_DEFERRED = "L";    // id -> deferred_transaction_t
# const string PREFIX_ALLOC = "B";       // u64 offset -> u64 length (freelist)
# const string PREFIX_ALLOC_BITMAP = "b";// (see BitmapFreelistManager)
# const string PREFIX_SHARED_BLOB = "X"; // u64 offset -> shared_blob_t
# const string PREFIX_ZONED_FM_META = "Z";  // (see ZonedFreelistManager)
# const string PREFIX_ZONED_FM_INFO = "z";  // (see ZonedFreelistManager)
# const string PREFIX_ZONED_CL_INFO = "G";  // (per-zone cleaner metadata)
ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0/ list | awk '{print $1}'| uniq

打印数据库中(kv键值对)的crc校验码

ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0/ list-crc | grep osdmap | head -10# 查询 kv 数量
ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0/ list-crc | wc -l# 查询 crc 校验码数量
ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0/ list-crc | awk '{print $3}'| uniq | wc -l

打印 Key-Value 键值对

ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0 dump [prefix]

查询表项是否存在(可指定 key)

ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0/ exists <pfefix> [key]

查询指定 value(可选是否指定输出文件)

ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0/ get <prefix> <key> [out <file>]# 通过 ceph-dencoder 工具反序列化解析
ceph-kvstore-tool rocksdb /var/lib/ceph/mon/ceph-node1/store.db/ get osdmap full_999 out ./osdmap.full
ceph-dencoder import osdmap.full type OSDMap decode dump_json

查询对象大小,若不指定前缀+key,则返回整个 db 大小

ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0 /var/lib/ceph/osd/ceph-0/ get-size [<prefix> <key>]

更改 key 或 value

# 这条命令含义是:把指定【前缀+key】的 key 替换为 N,或者把该指定【前缀+key】的内容替换为 file
ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0 set <prefix> <key> [ver <N>lin <file>]

压缩数据库(缓解 rocksdb 写放大)

# rocksdb 数据分为多层,并且每层都可能有重复数据,启用 compact 可以把数据层层压缩到最底层,并且删除重复数据,起到压缩空间的作用
# 压缩整个数据库
ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0 compact# 压缩指定前缀的数据
ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0 compact-prefix <prefix># 压缩指定前缀的 key=[start~end] 范围的数据
ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0 compact-range <prefix> <start> <end>

删除

ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0 rm <prefix> <key># 删除整个前缀下的数据
ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0 rm-prefix <prefix>

备份

# path:备份到指定路径,num-keys-per-tx:每次事务中进行备份的 kv 数量
ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0 store-copy <path> [num-keys-per-tx] [leveldb|rocksdb|...] # 备份所有 kv 的 crc
ceph-kvstore-tool bluestore-kv /var/lib/ceph/osd/ceph-0 store-crc <path>

3. 源码分析

main

int main(int argc, const char *argv[])
{// 参数解析vector<const char*> args;argv_to_vec(argc, argv, args);// global 初始化auto cct = global_init(&defaults, args,CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY,CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);common_init_finish(g_ceph_context);// 根据 store-type 初始化 storetoolStoreTool st(type, path, to_repair, need_stats);// 根据命令行 cmd 参数,执行对应的函数if (cmd == "destructive-repair") {... }else if(cmd == "rm")...
}

StoreTool

StoreTool::StoreTool(const string& type,const string& path,bool to_repair,bool need_stats): store_path(path)
{// 根据 type,创建 kv 句柄,其中 bluestore-kv,需要调用 bluestore->open_db_environmentif (type == "bluestore-kv") {if (load_bluestore(path, to_repair) != 0)exit(1);} else {// 创建 kv 句柄  auto db_ptr = KeyValueDB::create(g_ceph_context, type, path);}
}int StoreTool::load_bluestore(const string& path, bool to_repair)
{// 创建 bluestoreauto bluestore = new BlueStore(g_ceph_context, path);KeyValueDB *db_ptr;// 通过 bluestore 获取 kvdbint r = bluestore->open_db_environment(&db_ptr, to_repair);db = decltype(db){db_ptr, Deleter(bluestore)};return 0;
}

destructive-repair

数据因为宕机发生遗失时,可以尝试通过 repair 修复,注意:没有写入日志文件的数据无法恢复。

int StoreTool::destructive_repair()
{return db->repair(std::cout);
}
/*目前 ceph 后端的 kvdb 一般使用 rocksdb,其修复过程分为以下4步骤:查找文件把日志转化为表导出metadata写描述符
*/

list/list-crc

void StoreTool::list(const string& prefix, const bool do_crc,const bool do_value_dump)
{traverse(prefix, do_crc, do_value_dump,& std::cout);
}uint32_t StoreTool::traverse(const string& prefix,const bool do_crc,const bool do_value_dump,ostream *out)
{// 获取数据库迭代器KeyValueDB::WholeSpaceIterator iter = db->get_wholespace_iterator();// 如果设置了 prefix,则把迭代器调到第一次出现 prefix 的位置 if (prefix.empty())iter->seek_to_first();elseiter->seek_to_first(prefix);uint32_t crc = -1;// 循环查询,直到迭代器为 invalidwhile (iter->valid()) {pair<string,string> rk = iter->raw_key();// 设置跳出循环条件if (!prefix.empty() && (rk.first != prefix))break;if (out)*out << url_escape(rk.first) << "\t" << url_escape(rk.second);if (do_crc) {bufferlist bl;bl.append(rk.first);bl.append(rk.second);bl.append(iter->value());// 计算 crc 值crc = bl.crc32c(crc);if (out) {*out << "\t" << bl.crc32c(0);}}if (out)*out << std::endl;iter->next();}return crc;
}

exist

bool StoreTool::exists(const string& prefix, const string& key)
{// 未指定 key,调用 StoreTool::exists(const string& prefix)if (key.empty()) {return exists(prefix);}bool exists = false;// 在指定 key,则调用 get(),下文介绍该方法get(prefix, key, exists);return exists;
}bool StoreTool::exists(const string& prefix)
{KeyValueDB::WholeSpaceIterator iter = db->get_wholespace_iterator();// 通过迭代器去查询第一次出现 prefix 位置,查不到则为 invaliditer->seek_to_first(prefix);return (iter->valid() && (iter->raw_key().first == prefix));
}

get

bufferlist StoreTool::get(const string& prefix,const string& key,bool& exists)
{map<string,bufferlist> result;std::set<std::string> keys;keys.insert(key);// 获取指定 prefix、key 的 value,结果保存在 resultdb->get(prefix, keys, &result);// result 中的键值对数量大于0,则表示存在该 key if (result.count(key) > 0) {exists = true;return result[key];} else {exists = false;return bufferlist();}
}// rocksdb get 查询使用方法如下
int RocksDBStore::get(const string &prefix,const std::set <string> &keys,std::map <string, bufferlist> *out) {rocksdb::PinnableSlice value;if (cf_handles.count(prefix) > 0) {for (auto &key : keys) {// 若指定 prefix,则需要创建 cf(columfamily)列族句柄auto cf_handle = get_cf_handle(prefix, key);// 调用 Get 方法查询auto status = db->Get(rocksdb::ReadOptions(),cf_handle,rocksdb::Slice(key),&value);if (status.ok()) {(*out)[key].append(value.data(), value.size());} else if (status.IsIOError()) {ceph_abort_msg(status.getState());}value.Reset();}} else {for (auto &key : keys) {string k = combine_strings(prefix, key);// 未指定 prefix,使用默认句柄:rocksdb::ColumnFamilyHandle *default_cf = nullptr;auto status = db->Get(rocksdb::ReadOptions(),default_cf,rocksdb::Slice(k),&value);if (status.ok()) {(*out)[key].append(value.data(), value.size());} else if (status.IsIOError()) {ceph_abort_msg(status.getState());}value.Reset();}}return 0;
}

get-size

// 返回 bl 大小
bufferlist bl = st.get(prefix, key, exists);
std::cout << "(" << url_escape(prefix) << "," << url_escape(key)<< ") size " << byte_u_t(bl.length()) << std::endl;

set

// 解析命令行参数    if (subcmd == "ver") {version_t v = (version_t) strict_strtoll(argv[7], 10, &errstr);encode(v, val);} else if (subcmd == "in") {int ret = val.read_file(argv[7], &errstr);}
// 调用 StoreTool::set 方法bool ret = st.set(prefix, key, val);bool StoreTool::set(const string &prefix, const string &key, bufferlist &val)
{// rocksdb 支持事务提交,步骤如下:/*  rocksdb::WriteBatch bat;*  rocksdb::WriteOptions woptions;*  woptions.sync = !disableWAL;*  auto cf = db->get_cf_handle(prefix, k);*  put_bat(bat, cf, k, to_set_bl);*  int result = submit_common(woptions, t);*/KeyValueDB::Transaction tx = db->get_transaction();tx->set(prefix, key, val);int ret = db->submit_transaction_sync(tx);return (ret == 0);
}

store-copy

int StoreTool::copy_store_to(const string &type, const string &other_path,const int num_keys_per_tx,const string &other_type)
{// 在参数路径位置新建或者打开 kvdb// open or create a leveldb store at @p other_pathboost::scoped_ptr <KeyValueDB> other;KeyValueDB *other_ptr = KeyValueDB::create(g_ceph_context,other_type,other_path);if (int err = other_ptr->create_and_open(std::cerr); err < 0) {return err;}other.reset(other_ptr);// 获取旧 db 的迭代器KeyValueDB::WholeSpaceIterator it = db->get_wholespace_iterator();// 调整迭代器位置it->seek_to_first();// 循环提交事务,直到旧 db 的迭代器遍历完数据库do {int num_keys = 0;// 创建新 db 事务KeyValueDB::Transaction tx = other->get_transaction();// 通过旧迭代器查询,向新 db 的单次事务中写入 num-keys-per-tx 数量的写操作while (it->valid() && num_keys < num_keys_per_tx) {auto[prefix, key] = it->raw_key();bufferlist v = it->value();tx->set(prefix, key, v);num_keys++;total_size += v.length();it->next();}// 提交事务if (num_keys > 0)other->submit_transaction_sync(tx);} while (it->valid());print_summary(total_keys, total_size, total_txs, store_path, other_path,duration());return 0;
}

compact

void RocksDBStore::compact()
{logger->inc(l_rocksdb_compact);rocksdb::CompactRangeOptions options;// 压缩db->CompactRange(options, default_cf, nullptr, nullptr);for (auto cf : cf_handles) {for (auto shard_cf : cf.second.handles) {db->CompactRange(options,shard_cf,nullptr, nullptr);}}
}

ceph-kvstore-tool相关推荐

  1. 一文囊括Ceph所有利器(工具)

    原文链接: 知乎专栏: 一文囊括Ceph所有利器(工具) - 知乎 前言 ceph的工具很多,包括集群管理与运维,还有性能分析等等. 所以本文期望应收尽收所有的工具,也当做自己的一个梳理与总结,当自己 ...

  2. ceph存储引擎bluestore解析

    原文链接:http://www.sysnote.org/2016/08/19/ceph-bluestore/ ceph后端支持多种存储引擎,以插件式的方式来进行管理使用,目前支持filestore,k ...

  3. ceph1--ceph基础/搭建ceph高可用集群

    一.ceph基础 0.存储分类 单机存储 SCSI/IDE/SATA//SAS/USB/PCI-E/SSD/M.2NVME协议(提升性能) https://item.jd.com/4962067795 ...

  4. K8S章节2 — k8s集群中通过rook方式部署ceph

    1.k8s部署 参考:kubernetes简介及单master集群搭建. 部署完成后如下: hostname IPADDR k8s-master 192.168.1.11 k8s-node01 192 ...

  5. Ceph分布式存储系列(二):ceph-deploy方式部署三节点ceph集群

    承接上文:Ceph分布式存储系列(一):Ceph工作原理及架构浅析梳理 之前都是使用Deepsea方式部署的ceph,长时间不用ceph-deploy了,这次来回顾,顺便总结下! 前言: ceph-d ...

  6. 【ceph】cmake管理Ceph编译+Ceph工程目录+cmake 实战学习

    前言 Ceph cmake 工程 cmake生成的目录 cmake工程添加新模块(CMakeLists.txt) 添加动态库依赖 cmake导入外部链接库 *.cmake文件 cmake生成编译DEB ...

  7. ceph-mimic版本的安装使用1

    ceph部署实践(mimic版本) 关于luminous版本的请参照这篇文章 一.准备环境 4台adminos7.4 环境,存储节点上两块磁盘(sda操作系统,sdb数据盘) client admin ...

  8. pb利用datawindow查询符合条件的数据并且过滤掉其他数据_eBay将CAL上PB级的日志存储迁移到Ceph的实践...

    供稿 | Unified Monitoring Platform 翻译&编辑 | 顾欣怡 本文3663字,预计阅读时间11分钟 本文转载自"eBay技术荟",更多干货请关注 ...

  9. ceph性能测试工具总结

    https://blog.csdn.net/Motred_/article/details/52268225 性能测试工具 1.1    磁盘性能测试 dd 1.2 网络测试 1.2.1 iperf工 ...

  10. 分布式存储系统之Ceph(理论详解)

    目录 Ceph 简介 ceph的构成 ceph 集群基础: Monitor(ceph-mon):ceph 监视器 Managers(ceph-mgr)的功能: Ceph OSDs(对象存储守护程序 c ...

最新文章

  1. 阿里云证书 | 套路太深,还是我打开姿势不对?
  2. 在单块磁盘上安装2000和XP操作系统
  3. 第三次小组实践作业小组每日进度汇报:2017-12-8
  4. 监控利器之 Prometheus
  5. MySQL Transaction--快照读和当前读
  6. java处理日期的工具类DateUtil
  7. java jlable添加gif,Java动画GIF而不使用JLabel
  8. 倒计时3天:3D音带你起飞
  9. 数据结构课程设计------c实现散列表(二次探测再哈希)电话簿(文件存储)
  10. mysql function DATE_FORMA T(date, format)
  11. 高斯计工作原理和高斯计使用方法图解
  12. java实训项目百度脑图
  13. flea-db使用之JPA封装介绍
  14. 法国内政部选择IDEMIA和Sopra Steria为其开发新标准边境管制系统
  15. 树莓派(ubuntu系统)连接后使用HDMI-VGA,VGA无信号
  16. ppt编辑器android,ppt编辑器
  17. shell的并行执行
  18. jQuery事件-鼠标事件
  19. 如何使用Selenium自动化测试工具获取动态图片验证码?
  20. 动起来!好玩的CSS抖动样式 – CSS Shake

热门文章

  1. LaTeX 第一页不显示页码
  2. 【肌电信号】脉搏信号分析(去噪+特征提取)matlab源码含GUI
  3. 最大扇入数怎么判断_如何判断,你的运动是否有效?
  4. 优秀案例 | 长江鲲鹏中地数码:打造智慧城市“数字底座”
  5. 入驻华为严选商城性能测试案列
  6. 调光LED RGB色准校准方案
  7. 董明珠的权力危机:半年两位元老出局,格力进入动荡时刻
  8. netty-读半包处理--ByteToMessageDecoder
  9. pandas 终极版1:创建和查看DataFrame数据 mysql读取数据
  10. 智能镜子制作_更智能的镜子及其制作方法