Skip to content

Kvrocks 最佳实践

Alfejik edited this page Aug 2, 2021 · 2 revisions

1 版本选择

推荐使用 2.0 以上的最新版本,新版本在部署和运维上更方便。

2 数据兼容性

在集群模式下为了数据迁移高效,我们将 key 对应的 slot id 编码到 key 中从而使相同 slot 的 key 存放在一起,有利于特定 slot 的 key 进行迭代。而在单机模式下,为了兼容之前已发布的版本,并未将 slot id 编码到 key 中,这也就导致了集群模式和单机模式的底层数据格式是不兼容的,所以,如果要进行模式切换,需要进行数据迁移。也就是说,集群的开关一旦开启就不能关闭,反之亦然。

3 线程数

线程数量建议和 CPU 总线程数一致,当然如果是单机多实例的部署,每个 Kvrocks 实例的线程数量可以适当减少,但是为了保证 RocksDB 性能,预留线程数最好不要少于 4 个。

4 存储介质

由于 Kvrocks 底层使用的 RocksDB 存储引擎,对存储介质的要求是非常高的,推荐使用支持 NVMe 协议的 SSD,IO 性能较差的存储设备可能导致性能下降,吞吐降低,延迟增加等问题。

5 内存使用

Kvrocks 的内存使用主要来自 RocksDB 的内存使用,主要包括以下几块:

  • Memtables:对应于 Kvrocks 的配置为 rocksdb.write_buffer_size (单个 memtable 大小),rocksdb.max_write_buffer_number (memtable 的个数)
  • Block cache:数据块的缓存大小,对应于 Kvrocks 的配置为 rocksdb.metadata_block_cache_size (metakey 的缓存),rocksdb.subkey_block_cache_size (subkey 的缓存)
  • Block cache Indexes and bloom filters:数据块中索引和布隆过滤器的大小,在 Kvrocks 中,默认该部分内存占用计入到 Block cache 的大小
  • Blocks pinned by iterators:迭代器持有的 Block 内存大小,这部分内存的使用于用户的命令相关,一般占用较少

关于 RocksDB 内存的消耗详情,请查看: https://github.com/facebook/rocksdb/wiki/Memory-usage-in-RocksDB

跟 Redis 一样,如果客户端发送了大量请求而没有及时读取回复,或者 PUBSUB 的订阅者没有读取消息,也会导致客户端占用大量内存。

6 复杂数据类型的大小

对于 list、hash、set、zset 等复杂数据类型,尽管不会存在大 Key 删除导致的堵塞问题,但是我们仍然不推荐单个 Key 中元素个数过多,因为有些命令复杂度命令很高,可能导致性能急剧下降,延迟明显增加。对于复杂数据类型的大小,最好可以控制在 1000 以内。

7 复杂命令

对于 lrange, hgetall, smembers, zrange*, zremrange*, zrevrange*,keys 等命令,复杂度为 O (N),也就意味着需要进行多次 IO 操作,如果一次操作的元素格式比较多,这些命令的耗时会很大,磁盘消耗也会增加,同时与 Redis 一样也会消耗大量的内存。我们不推荐线上使用该部分指令,或者尽量减少一次操作元素的个数,用户也可以使用对应的 scan 命令来完成其操作。此外,我们提供了与 Redis 一样的 rename-command 功能,用户可以屏蔽对应命令。

8 主从全量复制

Kvrocks 的全量复制是通过传输 RocksDB 的 CheckPoint 来完成了,默认情况下,主库在收到从库全量同步的请求时会产生 CheckPoint,然后告诉从库文件列表,从库采用多线程的方式将所有的文件拉取到本地保存,待数据拉取完成后再用主库的 CheckPoint 恢复数据。从库在全量同步时自身可能占用较多的磁盘空间,Kvrocks 提供了一个配置下 slave-empty-db-before-fullsync,如果启用则会在拉取数据前清空本地数据,以减少磁盘空间占用,类似于 Redis 6.0 的 repl-diskless-load on-empty-db 配置。

主从全量复制期间,由于从库是多线程拉取主库的文件,会对网卡和磁盘造成较大的负载,可能影响正常请求的处理,所以应该根据实际情况对全量同步进行限速,配置下为 max-replication-mb,例如不要超过网卡速度的一半,千兆网卡限速 50MB。

9 主从增量复制

Kvrocks 的增量复制是通过传输 RocksDB 的 WAL 来实现的,主库根据从库请求的 sequence id 从 WAL 查找并迭代 WAL 进行复制。为了能够应对从库断开重连后仍进行增量同步,以及避免主库高写入量导致主从断链,Kvrocks 需要保留一定数量的 WAL,WAL 越多越能保证主从可以进行增量复制,但也会额外占用更多的磁盘空间。通过配置项 rocksdb.wal_ttl_seconds 和 rocksdb.wal_size_limit_mb 控制保留的 WAL 的大小,如果流量较高可以增加这两个值,若较低可以适当的减少该值。如果 Kvrocks 以单主模式运行,也就是说没有从库,可以将其均设置为 0,来减少磁盘空间的占用。

10 清空数据

Kvrocks 提供了 FLUSHDB 和 FLUSHALL 来清空数据,但执行命令后,Kvrocks 仍可能占用大量磁盘空间,需要手动发送 compact 命令并过一段时间后,磁盘空间才会释放。

11 Key 的数量

通过 INFO 和 DBSIZE 命令获得到 key 的数据一直不变,是因为无法直接获得全部 key 的数量,需要通过后台线程迭代 RocksDB 才能获得到。用户可以通过 DBSIZE SCAN 命令来更新 key 的数量,但要注意的是该操作对磁盘会产生较大的负载,应在服务空闲的时候进行操作。

12 数据备份

从 2.0 版本开始,Kvrocks 的备份调用 RocksDB 的 CheckPoint 方法完成的,尽管 CheckPoint 采用硬链接文件的方式不会额外占用过多的磁盘空间,但如果时间过长和数据写入,老的 SST 会删除,而硬链接的 SST 将真正占用磁盘空间,所以待备份工具将文件拷贝完成后,应及时删除备份。可通过 flushbackup 命令来删除备份或者减小配置项 max-backup-keep-hours,避免备份占用大量的磁盘空间。此外,配置项 purge-backup-on-fullsync 用来控制主库在全量同步期间是否清理备份,主库在全量同步时可能会再次产生 CheckPoint 会导致磁盘占用增加,推荐开启该功能来减少磁盘空间使用。

13 手动 compact

为了降低 Kvrcoks 磁盘空间的占用,我们可能需要需要手动执行 compact,但需要注意的是,在业务压力大、IO 使用率高的时候尽量不要执行 compact,因为在 IO 资源不足时 compact 过程中带来的 IO 消耗可能会与业务请求发生 IO 争抢,使实例整体性能下降。此外,compact 时会额外产生新的文件,所以不应该在磁盘快要满的时候进行 compact。

14 慢日志

Kvrocks 实现了与 Redis 慢日志一样的功能,用户可以使用该功能定位慢请求问题。

15 Perflog

除了通过慢日志命令来确认是否有慢请求产生之外,还提供了 perflog 命令用来定位 RocksDB 访问慢的问题。config set profiling-sample-commands 可以用与设定对特定命令进行收集,config set profiling-sample-ratio 用来设定随机采样的比例,config set profiling-sample-record-threshold-ms 用于设定超过多长时间的命令才写到 perf 日志里面,之后通过 perflog get 命令可以观察到命令在 RocksDB 各个阶段的耗时。

16 从库读服务

Kvrocks 的从库支持只读模式,可以通过添加从库的方式提示一个主从复制组的读性能。与 Redis 一样,Kvrocks 提供了配置项 slave-serve-stale-data 来控制从库在与主库全量同步时是否提供服务,如果用户对于读到旧数据问题不敏感,可以开启该配置项,保证从库在全量同步时不要将读流量全部发送到主库上。但如果业务不能容忍,则不能开启该配置项,甚至代理层不应该将读请求路由到从库上,因为 Kvrocks 的复制模式与 Redis 一样,采用异步复制,只能保证最终一致性。

17 客户端

客户端数目过多可能导致进程的 FD 耗尽和占用过多内存,Kvrocks 提供了 maxclients 的配置项来限制客户端的数量。Kvrocks 也提供了 CLIENT LIST 用于检测各个客户的指标,以及 CLIENT KILL ip:port 的命令杀掉特地客户端,比如一些长时间执行复杂命令并占用很多内存的客户端。

18 数据迁移工具

  • 从 Redis 迁移到 Kvrocks,可以使用 vipshop 的 redis-migrate-tool
  • 从 Kvrocks 迁移到 Redis,可以使用本项目的 kvrocks2redis 工具,同样该功能也可以实现 Kvrocks 间的数据迁移,比如从单机模式到集群模式的数据迁移
Clone this wiki locally