运维指南

部署配置说明

默认配置说明

zankv数据节点配置参数说明:

{
 "server_conf": {
  "cluster_id": "test-qa-alpha-1",   ### 集群id, 用于区分不同的集群
  "etcd_cluster_addresses": "http://127.0.0.1:2379,http://127.0.0.2:2379", ### etcd 集群地址列表
  "broadcast_interface": "eth0",  ### 绑定的网卡, 使用网卡可以避免每台机子配置绑定ip
  "broadcast_addr": "",   ### 绑定ip, 推荐使用网卡配置, 此项可以留空
  "redis_api_port": 12381,   ### redis协议监听地址端口
  "http_api_port": 12380,     ### http api 端口
  "grpc_api_port": 12382,     ### grpc内部集群通信端口
  "profile_port": 0,          ### debug数据端口, 默认是6666
  "data_dir": "/data/zankv",   ### 数据目录
  "data_rsync_module": "zankv",   ### rsync 模块, 名字必须保持和rsync配置吻合, rsync模块配置的数据目录路径必须和本配置的数据目录一致
  "local_raft_addr": "http://0.0.0.0:12379",  ### 内部raft 传输层监听地址
  "tags": null,    ### tag属性, 用于标识机器属性, rack-aware会使用此配置
  "syncer_write_only": false,    ### 此配置用于跨机房多集群部署, 默认不需要
  "syncer_normal_init": false,   ### 此配置用于跨机房数据同步初始化, 默认不需要
  "learner_role": "",            ### 配置raft learner角色, 用于跨机房同步, 默认不需要
  "remote_sync_cluster": "",     ### 跨机房集群的备机房地址, 默认不需要
  "state_machine_type": "",      ### 状态机类型, 用于未来区分不同的状态机, 暂时不需要配置,目前仅支持rocksdb
  "rsync_limit": 0,   ### 限制rsync传输的速度, 一般不需要配置, 会使用默认限制
  "election_tick": 30,   ### raft leader失效间隔, 建议使用默认值
  "tick_ms": 200,   ### raft 心跳包间隔, 建议使用默认值
  "use_redis_v2": true,  ### 是否在raft entry里面启用新的redis序列化, 默认不开启, 0.8.4以上版本支持, 不兼容低版本, 开启后可以提升写入性能
  "log_dir": "/data/logs/zankv", ### 设置glog目录, 0.8.4以上版本支持
  "use_rocks_wal": false,  ### 是否使用rocksdb存储raft wal日志, 默认不启用, 启用后可以优化内存占用, 性能会有一定影响
  "shared_rocks_wal": true, ### 是否在不同raft分组使用共享的rocksdb来存储wal, 默认不启用
  "wal_rocksdb_opts": {
      "use_shared_cache":true,
      "use_shared_rate_limiter":true,
      "rate_bytes_per_sec":80000000,
      "max_write_buffer_number":10,
      "optimize_filters_for_hits":true  ### 用于存储wal时, 可以配置此项提升性能, 因为wal查询都会命中
  },
  "rocksdb_opts": {   ### rocksdb参数参见调优
   "verify_read_checksum": false,
   "use_shared_cache": true,
   "use_shared_rate_limiter": true
  },
  "max_scan_job": 0   ### 允许的最大scan任务并行数量, 一般使用内置的默认值
 }
}

namespace创建参数建议3副本, 分区数可以预估集群最大规模, 一般初始化设置为集群最大机器数*4, 预留部分扩容能力. 一般来说集群建议最小规模是4台机器, 16分区. 理论上可以通过加机器扩容到 50TB容量左右. 如果需要继续扩容, 可以再创建更多分区的新namespace来完成.

注意如果需要在较少的机器上创建较多分区数的namespace, 需要适当调小 rocksdb的 write_buffer_size参数以及namespace的snap_count参数, 以减少每个分区的内存占用

rocksdb调优

由于底层使用rocksdb存储, 为了更好的适应各种硬件配置, 需要了解部分rocksdb的配置, 并根据具体环境做配置优化. 具体可配置的参数含义如下:

{
   "verify_read_checksum": false,  ### 读是否校验crc, 禁用可以减少CPU的使用, 大部分情况下可以禁用
   "block_size": 0,   ### block大小, 一般使用默认值即可, 建议8KB~64KB之间
   "block_cache": 0,  ### 建议使用默认值, 并且启用 use_shared_cache=true, 内部会自动根据操作系统可用内存调整.
   "cache_index_and_filter_blocks": true,  ### 是否将索引和filter放入block cache, 建议小内存机器设置true, 避免数据增长占用过多内存, 大内存>64GB内存可以使用false, 会将block索引都缓存在内存, 加速访问
   // 以下几个参数设置时, 注意保证 level0_file_num_compaction_trigger * write_buffer_size * min_write_buffer_number_tomerge = max_bytes_for_level_base减少写放大效应, 建议使用默认值, 如果机器内存较小, 可以适当等比例缩小
   "write_buffer_size": 0, 
   "min_write_buffer_number_to_merge": 0,
   "level0_file_num_compaction_trigger": 0,
   "max_bytes_for_level_base": 0,
   "max_write_buffer_number": 4,  #### 建议 2~6之间, 根据操作系统不同内存大小调整
   "target_file_size_base": 0,   ### 建议使用默认值
   "max_background_flushes": 0,  ### 建议使用默认值, rocksdb刷盘线程
   "max_background_compactions": 0,   ### 建议使用默认值, rocksdb compact线程
   "min_level_to_compress": 0,   ### 建议使用默认值
   "max_mainifest_file_size": 0,   ### 建议使用默认值
   "rate_bytes_per_sec": 20000000,   ### rocksdb后台IO操作限速, 建议设置避免IO毛刺, 建议限速 20MB ~ 50MB 之间
   "use_shared_cache": true,  ### 建议true, 所有rocksdb实例共享block cache
   "engine_type": "",  ### 支持rocksdb和pebble两种, 默认使用rocksdb
   "use_shared_rate_limiter": true   ### 建议true, 所有实例共享限速指标
}

创建namespace

往placedriver的leader节点发送如下API可以动态创建新的namespace

POST /cluster/namespace/create?namespace=test_p16&partition_num=16&replicator=3&data_version=value_header_v1&expiration_policy=wait_compact

data_version: 存储的数据版本, 不同版本序列化格式会有区别, namespace初始化后不能动态修改, 默认使用老版本, value_header_v1是目前唯一的新版本用于支持精确过期功能
expiration_policy: 配置过期策略, 默认使用非精确过期, 新版本支持wait_compact精确过期策略, 此策略下过期的数据不会返回给客户端, 过期数据的真实清理会等待compact时再判断是否需要清理.

关于ttl的说明:

默认使用非精确ttl, 非精确ttl使用的是报错过期key列表并且定期扫描的策略, 因此只能支持设置一次过期时间, 并且过期精度取决于扫描周期(默认5分钟).

如果需要类似redis的精确ttl(秒级)支持, 可以使用新的wait_compact过期策略, 这种过期策略会将过期时间和key的元数据放到一起, 每次读写的时候会检查是否已经过期, 从而实现更加精确的过期判断能力. 由于只是检查过期的元数据, 并不会真实删除, 因此需要等待底层compact的时候才能物理删除, 理论上空间回收会滞后. 注意, 新的过期策略必须使用新的数据版本value_header_v1

慢写动态限流说明

v0.8新版本开始, 增加了慢写入动态限流和预排队功能, 用于减少某些慢写入命令对其他命令的rt影响. 内部会周期性汇总写入命令在底层DB的rt耗时, 超过一定阈值后, 会被判断为不同程度的慢, 针对不同程度的慢写入, 会使用不同的预排队队列进行排队, 队列长度也会不同, 从而控制这些慢写入命令同时进入raft请求的个数, 来避免这些慢写入在raft apply队列排队写入时占用过多时间, 从而影响队列里面其他的写入rt. 如果超过一定的阈值, 还会触发直接拒绝限流.

动态限流可以通过配置关闭 POST /conf/set?type=int&key=slow_limiter_switch&value=0

监控项说明

除了默认的stats接口, 还有如下几个新增的监控数据, 可以查看内部状态.

Prometheus

通过数据节点的HTTP端口的/metric, 可以查看更多的监控数据, 主要有一下几个大类:

topn

通过HTTP端口的/stats?table_detail=true, 可以查看更详细的key级别统计的topn数据, 包括:

统计结果按照一定的次数和最近时间综合进行淘汰

SLOW_LOGS

部分重要影响集群性能的操作会以慢查日志的方式进行输出, 用于监控和排查一些业务不合理的数据使用导致的性能隐患. 目前有以下几种情况会输出慢日志:

慢日志可以通过API POST /slowlog/set?loglevel=0 单独设置输出级别, 不同级别的输出阈值不同. (-1表示关闭)

操作和接口说明

rsync进程:用于异常恢复时传输zankv的备份数据

传输速度可以动态配置限速:

POST /rsynclimit?limit=80000

集群的一些统计数据, 可以分别从placedriver和zankv的接口获取,统计数据接口如下:

placedriver API

/cluster/stats
获取集群总体状态, 标识集群是否稳定, 此请求需要发送到leader节点

/datanodes
获取存活的数据节点(zankv)信息, 存活节点数量有变更需要warning级别报警

POST /cluster/node/remove?remove_node=xxx
下线不用的节点, xxx信息使用获取节点数信息返回的node_id串替换, 下线节点会触发数据迁移, 等待迁移完成后, 观察log输出再停掉下线的节点.

GET /namespaces
获取集群的namespace列表

GET /listpd
获取集群pd节点列表

GET /query/:namespace?epoch=xxx&disable_cache=false
查询namespace的数据分布情况

GET /querytable/stats/:table?leader_only=true
查询集群表数据详情

POST /learner/stop
停止learner节点, 写入状态值false, 仅用于learner角色的pd, learner一般作数据异步同步用

POST /learner/start
启动learner节点, 开启同步状态, 写入状态值true

GET /learner/state
查询当前的learner状态, 注意如果状态值是false才会返回false, 其他都是true(包括非法状态)

GET /cluster/stats
查询集群稳定状态, 需要发给当前的leader节点, 当集群raft都正常是返回true, 否则返回false. 值为false时一定有不稳定的情况, 但是值为true时也可能有其他问题.

POST /cluster/balance?enable=false
切换开启或禁用平衡功能, 需要发给当前leader节点

POST /cluster/node/remove?remove_node=xxx
下线指定的数据节点, 指定的nid可以通过查询datanodes. 用于永久下线某个数据节点

DELETE /cluster/partition/remove_node?node=xxx&namespace=xxx&partition=xx
将namespace的某个分区从指定节点删除, 用于修复部分分区的数据, 一般用于异常情况处理, 比如这个数据节点上的这个分区数据已经损坏.

POST /cluster/upgrade/begin
开启集群升级状态, 此操作屏蔽集群数据漂移, 用于长时间操作集群单节点升级

POST /cluster/upgrade/done
结束集群升级状态, 重启开始数据平衡能力.

DELETE /cluster/namespace/delete?namespace=xxx&partition=**
删除指定的namespace, partition指定时删除指定分区, 建议一般情况下使用**来删除所有分区

POST /cluster/namespace/meta/update?namespace=xxx&replicator=xx&optimizefsync=true&snapcount=xxx
更新namespace的元数据, 比如副本数, 是否开启fsync优化, 快照间隔等

POST /stable/nodenum?number=xx
为了保护集群大规模故障时, 触发数据平衡, 会自动维护当前集群的最大节点数(自动增加, 不会减少), 如果需要缩容时, 需要手动减少稳定节点数

zankv API

POST /kv/optimize/:namespace/:table
为了避免太多删除数据影响性能, 可以定期执行此API清理优化性能, rocksdb v6及以上新版本已经有部分优化, 因此可以不用执行. 建议指定namespace和table缩小范围, 避免优化时影响读写访问请求

POST /kv/optimize_expire/:namespace
优化过期时间元数据, 大量过期清理后, 可能会影响扫描过期数据的效率, 可以指定compact这一部分数据

POST /kv/backup/:namespace
触发指定namespace的快照备份

POST --header "Content-Type: application/json" --data '{"start_from":"","end_to":"","delete_all":true, "dryrun":false}' /kv/delrange/:namespace/:table
删除指定表的区间数据, 区间由start_from和end_to指定(注意这里需要将内容做base64编码传入, 比如20200301传入MjAyMDAzMDE=), 如果要删除整个表, 需要指定delete_all=true

POST /slowlog/set?loglevel=xx
调整慢查日志的级别, 越大打印越多的慢查日志.

POST /staleread?allow=true
开启stale读, 开启后, 非leader状态也能提供读请求, 可能读到老数据

POST /topn/enable/:namespace
开启topn写入统计, 会统计最近一段时间的写入量最大的几个表统计

POST /topn/disable/:namespace
关闭topn写入统计

POST /topn/clear/:namespace
清理当前统计的topn写入数据

/stats
获取统计数据,其中db_write_stats, cluster_write_stats中两个长度为16的数据对应的数据, 标识对应区间统计的计数器. 其中db_write_stats代表存储层的统计数据, cluster_write_stats表示服务端协议层的统计数据(从收到网络请求开始, 到回复网络请求结束), 具体的统计区间含义可以参考代码WriteStats结构的定义.

如果需要获取集群表统计信息, 需要加上参数table_details=true

/raft/stats
获取raft集群状态, 用于判断异常信息

动态配置支持int和string两种类型, 对应的更改和获取接口如下:

POST /conf/set?type=int&key=xxx&value=xxx
POST /conf/set?type=str&key=xxx&value=xxx

GET /conf/get?type=int&key=xxx
GET /conf/get?type=str&key=xxx

目前可以动态配置的参数如下:
int类型:
check_snap_timeout - 检查快照超时时间, 单位秒
check_raft_timeout - 检查raft是否同步超时时间, 单位秒
max_remote_recover - 跨机房同步时, 最大同时同步的分区数
string类型:
ignore_startup_nobackup - 启动时是否忽略快照不存在的错误.
ignore_remote_file_sync - 是否忽略跨机房同步的快照传输

动态调整部分rocksdb参数使用如下API:

POST /db/options/set?key=xxx&value=xxx

说明: 目前仅有rocksdb底层存储支持动态调整, 可调整的key如下: - "rate_limiter_bytes_per_sec": rocksdb的ratelimiter限流字节数调整 - "max_background_compactions": rocksdb后台compaction任务上限调整 - "max_background_jobs": rocksdb后台jobs任务上限调整

备份恢复

备份文件格式

备份工具备份的文件名格式: datatype:yyyy-mm-dd:namespace:table.db, 每个不同类型的数据备份文件有一个相同的备份文件头, 数据内容和具体的数据格式有关.

公共文件头格式

在所有的文件头会写入如下几个信息:

字段 字节数 说明
MAGIC 5 魔数,目前为"ZANKV"
VERSION 4 版本,目前为"0001"
NameSpace Len 4 namespace的长度
NameSpace 变长,由namespace len指定 namespace名称
TableName Len 4 table name的长度
TableName 变长 table name
Type 1 类型。0:kv;1:hash;2:set;3:zset;4:list

kv文件格式

kv文件中除去公共文件格式之外,就是key-value的内容。格式如下:

字段 字节数 说明
Key Len 4 key的长度
Key 变长 key的内容
Value Len 4 value的长度
Value 变长 value的内容

hash文件格式

除去公共文件格式之外,就是key-field-value的内容。格式如下:

字段 字节数 说明
Key Len 4 key的长度
Key 变长 key的内容
Field number 4 所有field个数
field Len 4 field长度
field 变长 field内容
Value Len 4 value的长度
Value 变长 value的内容

set文件格式

list文件格式

zset文件格式

备份工具backup

backup -lookup lookuplist -ns namespace -table table_name [-data_dir backup -type all|kv[,hash,set,zset,list] -qps 100]

参数说明:
-lookup            zankv lookup服务器,可以是用","分割的一组服务器
-ns                 要备份的namespace
-table              要备份的table
-data_dir         备份目录,默认当前目录下打data目录
-type              要备份的数据结构类型,支持kv,hash,set,zset,list,all表示备份所有类型,输入all了,就不能输入其他的类型了,默认为all
-qps               速度控制,默认1000 qps

恢复工具restore

restore -lookup lookuplist -data restore  [-ns namespace -table table_name -qps 1000]
参数说明:
-lookup            zankv lookup服务器,可以是用","分割的一组服务器
-data               需要恢复的备份文件
-ns                  要备份的namespace,如果不输入,则恢复的时候,按照备份的ns进行恢复
-table              要备份的table,如果不输入,则恢复的时候,按照备份的table进行恢复
-qps               速度控制,默认1000 qps

跨机房运维

同城3机房的情况, 使用默认的跨机房大集群模式部署即可, 使用raft自动同步和做故障切换.

异地机房部署, 为了减少同步延迟, 一般部署成跨机房异步同步模式, 运维流程如下: 假设部署了A机房和B机房集群, 开始使用A机房做主

初始化

zankv 0.9.0版本之前

zankv 0.9.0版本以及更新版本之后

部署好之后, 架构如下: zankv-cluster-sync

机房正常切换

zankv 0.9.0版本之前

zankv 0.9.0版本以及更新版本之后

切换完成

机房异常切换

故障时切换完成, 后继等待A机房故障恢复.

故障处理

单分区数据故障

假如某些异常导致单个分区下的一个副本数据异常, 可以尝试将该副本手动剔除来尝试恢复一致. 操作如下:

DELETE /cluster/partition/remove_node?namespace=xxx&partition=xxx&node=xxxx

往placedriver的leader节点发送以上命令, 其中node需要输入节点在该namespace的唯一节点id, 可以通过/query/xxx 查询namespace元数据方式获取对应的node_id字段.

观察集群是否自动将该分组下的异常节点剔除, 剔除后, 集群会自动重新分配新的副本, 确保集群有足够的副本数.

raft某个副本分组无法和etcd保持一致

假如raft副本集合和etcd不一致, 正常情况下, 会通过调度使得最终副本集合和etcd上配置一致, 假如某些原因导致raft分组无法增加或者调整副本集, 需要手动强制重置副本, 并修复etcd配置, 首先尝试使用单分区数据故障处理方式, 将异常副本剔除, 如果可以的话, 一个个剔除后, 理论上可以恢复. 如果不行, 说明leader节点已经无法形成, raft分组无法选举, 那么针对某个分组使用如下API强制重新组成新的单副本集群(注意选择数据完整的节点发送此API).

POST /cluster/raft/forcenew/namespace-pid

然后, 修改etcd元数据, 使得namespace对应分区的副本集元数据只包含这个新的节点id, 然后等待副本自动增加.