Tuning RocksDB - Statistics

RocksDB 是一个性能非常强悍的 Key-Value 存储引擎,很多项目包括我们的 TiKV 都使用它来存储数据。但 RocksDB 也因其复杂的配置著称,要让 RocksDB 在不同的机器上面都能有很好的性能,并不是一件容易的事情。很多时候,千辛万苦在一台机器上面的配的配置,在另一台机器上面性能就是不行。

RocksDB 官方专门写了一篇文章,来讲如何 tuning RocksDB,这里,我会结合我们在 TiKV 里面对于 RocksDB 的调优经验,详细的对其解释一下,当然也不会完全翻译。一方面是跟大家共同学习,另一个方面则是让自己更加熟悉 RocksDB。

要了解如何优化 RocksDB,首先需要知道 RocksDB 的核心是 LSM ,不过这个现在外面已经有太多的文章了,就不做过多说明了。不过如果有可能,还是可能写写 RocksDB 具体是如何处理 LSM 的。

Amplification factors

调优 RocksDB 通常就是在三个 amplification 之间做取舍的。

  • Write amplification - 也就是我们最经常碰到的写放大,假设我们每秒写入 10MB 的数据,但观察到硬盘的写入是 30MB,那么写放大就是 3。所以对于写入压力比较大的应用来说,我们要做的就是尽量减小写放大。写放大能通过 RocksDB 自己的统计看到。
  • Read amplification - 读放大,对于一个 query 需要读取硬盘的次数。譬如一个 query 我们读取了 5 pages,那么读放大就是 5。读放大没法通过 RocksDB 自己的统计很好的查看,通常会通过 iostat 等命令进行评估。
  • Space amplification - 空间放大,这个比较好理解,假设我需要存储 10MB 的数据,但实际硬盘占用了 30MB,那么空间放大就是 3。

RocksDB Statistics

在实际调优 RocksDB 的时候,我们用的最多的就是 RocksDB 自己的统计信息,RocksDB 自己有一个 stats_dump_period_sec 参数,可以定期的将统计刷到 RocksDB 自己的 LOG 文件里面。当然,我们也可以直接通过 db->GetProperty("rocksdb.stats") 以及 options.statistics.ToString() 在程序里面直接得到相关的统计信息。

对于 TiKV 来说,现阶段我们使用了 4 个 Column Family,所以统计如下:

** Compaction Stats [default] **
Level    Files   Size(MB} Score Read(GB}  Rn(GB} Rnp1(GB} Write(GB} Wnew(GB} Moved(GB} W-Amp Rd(MB/s} Wr(MB/s} Comp(sec} Comp(cnt} Avg(sec} KeyIn KeyDrop
----------------------------------------------------------------------------------------------------------------------------------------------------------
  L0      0/0       0.00   0.0      0.0     0.0      0.0      98.5     98.5       0.0   0.0      0.0    205.4       491      1089    0.451       0      0
  L1      5/0     128.68   1.1    127.7    98.7     29.0     120.0     91.0       0.0   1.2    179.1    168.3       730       269    2.715    875M   105M
  L2     98/0    1381.58   1.2    176.2    91.0     85.2     120.8     35.6       0.0   1.3     18.8     12.9      9624      2904    3.314   1588M  6900K
  L3    472/0   12650.65   1.0     97.3     8.5     88.7      96.2      7.5      26.9  11.3     12.7     12.6      7841       571   13.732   1109M    17M
  L4   3228/0   98210.34   0.8    145.3     9.0    136.4     133.8     -2.6      25.6  14.9     12.9     11.9     11502       687   16.742   1775M   392M
 Sum   3803/0   112371.25   0.0    546.5   207.2    339.3     569.4    230.1      52.5   5.8     18.5     19.3     30189      5520    5.469   5350M   523M
 Int      0/0       0.00   0.0      3.7     2.1      1.6       3.7      2.1       0.4   4.8     24.3     24.3       157        50    3.134     34M  4871K
Uptime(secs): 70217.9 total, 70217.9 interval
Flush(GB): cumulative 98.538, interval 0.775
AddFile(GB): cumulative 0.000, interval 0.000
AddFile(Total Files): cumulative 0, interval 0
AddFile(L0 Files): cumulative 0, interval 0
AddFile(Keys): cumulative 0, interval 0
Cumulative compaction: 569.35 GB write, 8.30 MB/s write, 546.53 GB read, 7.97 MB/s read, 30189.2 seconds
Interval compaction: 3.73 GB write, 0.05 MB/s write, 3.72 GB read, 0.05 MB/s read, 156.7 seconds
Stalls(count): 0 level0_slowdown, 0 level0_slowdown_with_compaction, 0 level0_numfiles, 0 level0_numfiles_with_compaction, 0 stop for pending_compaction_bytes, 0 slowdown for pending_compaction_bytes, 0 memtable_compaction, 0 memtable_slowdown, interval 0 total count

** Compaction Stats [raft] **
Level    Files   Size(MB} Score Read(GB}  Rn(GB} Rnp1(GB} Write(GB} Wnew(GB} Moved(GB} W-Amp Rd(MB/s} Wr(MB/s} Comp(sec} Comp(cnt} Avg(sec} KeyIn KeyDrop
----------------------------------------------------------------------------------------------------------------------------------------------------------
  L0      1/0     102.02   0.2      0.0     0.0      0.0     866.7    866.7       0.0   0.0      0.0    440.3      2016      9404    0.214       0      0
  L1      4/0      96.85  19.9    974.5   866.6    107.9     331.6    223.8       0.0   0.4    703.6    239.4      1418      1923    0.738    741M   197M
  L2      7/3      24.49   0.5    339.3   223.7    115.5     130.4     14.9       0.0   0.6     30.0     11.5     11563      7513    1.539    694M    37M
  L3    185/77   5793.93   0.3   1180.5    13.8   1166.7    1171.3      4.6       1.0  84.7     12.0     11.9    100814      1760   57.280    602M   266M
 Sum    197/80   6017.28   0.0   2494.3  1104.2   1390.1    2500.1   1109.9       1.0   2.9     22.1     22.1    115810     20600    5.622   2038M   500M
 Int      0/0       0.00   0.0     32.2     9.4     22.8      32.0      9.2       0.0   4.3     16.7     16.5      1979       181   10.931     26M  7633K
Uptime(secs): 70217.9 total, 70217.9 interval
Flush(GB): cumulative 866.720, interval 7.361
AddFile(GB): cumulative 0.000, interval 0.000
AddFile(Total Files): cumulative 0, interval 0
AddFile(L0 Files): cumulative 0, interval 0
AddFile(Keys): cumulative 0, interval 0
Cumulative compaction: 2500.08 GB write, 36.46 MB/s write, 2494.33 GB read, 36.38 MB/s read, 115810.5 seconds
Interval compaction: 31.96 GB write, 0.47 MB/s write, 32.17 GB read, 0.47 MB/s read, 1978.5 seconds
Stalls(count): 0 level0_slowdown, 0 level0_slowdown_with_compaction, 0 level0_numfiles, 0 level0_numfiles_with_compaction, 0 stop for pending_compaction_bytes, 0 slowdown for pending_compaction_bytes, 0 memtable_compaction, 0 memtable_slowdown, interval 0 total count

** DB Stats **
Uptime(secs): 70217.9 total, 600.1 interval
Cumulative writes: 305M writes, 13G keys, 303M commit groups, 1.0 writes per commit group, ingest: 2090.72 GB, 30.49 MB/s
Cumulative WAL: 305M writes, 0 syncs, 305710569.00 writes per sync, written: 2090.72 GB, 30.49 MB/s
Cumulative stall: 00:03:12.944 H:M:S, 0.3 percent
Interval writes: 3971K writes, 124M keys, 3943K commit groups, 1.0 writes per commit group, ingest: 18679.94 MB, 31.13 MB/s
Interval WAL: 3971K writes, 0 syncs, 3971919.00 writes per sync, written: 18.24 MB, 31.13 MB/s
Interval stall: 00:00:0.000 H:M:S, 0.0 percent

上面因为统计量比较大,我们只输出了 default 和 raft 两个 CF compaction 的统计,以及整个 DB 的统计。

Compaction stats

Compaction stats 就是在 level N 和 N + 1 层做 compaction 的统计,在 N + 1 层输出结果。

  • Level:也就是 LSM 的 level,Sum 表示的是所有 level 的总和,而 Int 则表示从上一次 stats 到现在的数据。
  • Files:有两个值 a/b,a 用来表示当前 level 有多少文件,而 b 则用来表示当前用多少个线程正在对这层 level 做 compaction。
  • Size(MB}:当前 level 总共多大,用 MB 表示。
  • Score:除开 level 0,其他几层的 score 都是通过 (current level size) / (max level size) 来计算,通常 0 和 1 是正常的,但如果大于 1 了,表明这层 level 就需要做 compaction 了。对于 level 0,我们通过 (current number of files) / (file number compaction trigger) 来计算。
  • Read(GB}:对 level N 和 N + 1 做 compaction 的时候总的读取数据量
  • Rn(GB}:对 level N 和 N + 1 做 compaction 的时候从 level N 读取的数据量
  • Rnp1(GB}:对 level N 和 N + 1 做 compaction 的时候从 level N + 1读取的数据量
  • Write(GB}:对 level N 和 N + 1 做 compaction 的时候总的写入数据量
  • Wnew(GB}:新写入 level N + 1 的数据量,使用 (total bytes written to N+1) - (bytes read from N+1 during compaction with level N)
  • Moved(GB}:直接移动到 level N + 1 的数据量。这里仅仅只会更新 manifest,并没有其他 IO 操作,表明之前在 level X 的文件现在在 level Y 了。
  • W-Amp:从 level N 到 N + 1 的写放大,使用 (total bytes written to level N+1) / (total bytes read from level N) 计算。
  • Rd(MB/s}:在 level N 到 N + 1 做 compaction 的时候读取速度。使用 Read(GB) * 1024 / duration 计算,duration 就是从 N 到 N + 1 compaction 的时间。
  • Wr(MB/s}:在 level N 到 N + 1 做 compaction 的时候写入速度,类似 Rd(MB/s)
  • Comp(sec}:在 level N 到 N + 1 做 compaction 的总的时间。
  • Comp(cnt}:在 level N 到 N + 1 做 compaction 的总的次数。
  • Avg(sec}:在 level N 到 N + 1 做 compaction 平均时间。
  • KeyIn:在 level N 到 N + 1 做 compaction 的时候比较的 record 的数量
  • KeyDrop:在 level N 到 N + 1 做 compaction 的时候直接丢弃的 record 的数量

具体相关的计算可以参考 RocksDB internal_stats.cc 里面的 PrintLevelStats 函数。

紧跟着 Compaction 的各个 Level 的统计,是这个 CF 这次 compaction 的一些汇总统计。Cumulative 是到现在为止累加的统计,Interval 则是从上一次统计到现在的变更数据。这里不做过多说明,重点关注的是

Stalls(count): 0 level0_slowdown, 0 level0_slowdown_with_compaction, 0 level0_numfiles, 0 level0_numfiles_with_compaction, 0 stop for pending_compaction_bytes, 0 slowdown for pending_compaction_bytes, 0 memtable_compaction, 0 memtable_slowdown, interval 0 total count

如果出现了 Stall,就表明外面写入的压力太大,RocksDB compaction 不及时,这样就会开始阻塞写入了。

DB Stats

除了不同 CF 的各个 level 的 compaction stats,我们也会得到一个总的汇总的统计,主要关注:

  • Uptime:从启动到现在的时间
  • writes:
    • writes - 总的写入次数
    • keys - 总的写入 key 的数量
    • commit groups - 总的 group 提交的数量
    • writes per commit group - 每个 group 提交包含的写入次数
    • ingest - 直接 ingest 到 DB 的数量
    • MB/s - 写入的速度
  • WAL:
    • writes - 总的写入次数
    • syncs - 强制 sync 的次数
    • writes - 每次 sync 有多少次写入
    • written - 总的写入数据量
    • MB/s - 写入的速度
  • stall:
    • H:M:S - 总的 stall 的时间
    • percent - stall 的时间在总时间的比例

More statistics

除了上面的 statistics,RocksDB 还提供了一个更加细粒度的 statistics,我们需要手动 options.statistics = rocksdb::CreateDBStatistics() 打开,但它会影响性能。根据 RocksDB 官方的文档,打开这个 statistics 会损失 5-10% 的性能,但 TiKV 这边实测其实没到,所以我们默认是打开的。

通过 statistics,我们可以观察更多的统计信息,譬如每层 level 的读数据延迟:

** Level 0 read latency histogram (micros):
Count: 3834542 Average: 19.1113  StdDev: 143.36
Min: 0  Median: 13.6954  Max: 58656
Percentiles: P50: 13.70 P75: 16.70 P99: 42.01 P99.9: 838.63 P99.99: 6676.77

上面就是对于 level 0 的读取延迟 histogram 统计,看一看到读取的次数,瓶颈的时间,标准方差等。

然后还有 cache 的一些统计,譬如:

rocksdb.block.cache.miss COUNT : 349550104
rocksdb.block.cache.hit COUNT : 11172137213

上面表示 cache 总共 miss 了多少次,hit 了多少次。

当然还有 stall 的相关统计:

rocksdb.stall.micros COUNT : 192944077
rocksdb.db.write.stall statistics Percentiles :=> 50 : 0.825706 95 : 4683.057851 99 : 29772.380952

上面表示 stall 总的时间,以及 histogram。

在 TiKV 里面,我们通过 statistics 相关的接口,取出相关的数据,将其发送给 Prometheus,这样我们就能通过 Prometheus 来监控 RockSB 了。

小结

通常上面的 statistics,我们其实还不能确定某一个操作慢到底是怎么导致的。譬如我们 seek 某一个 key,但发现时间非常长,这时候我们就可以通过 Perf Context and IO Stats Context对一个操作进行分析,但实际对于一个运行的服务器程序,这种方式并不方便,所以在 TiKV 中我们并没有使用,但不排除以后也会研究,以及通过 tracing 接口显示的对一些操作打开。

在 TiKV 中,我们定时 10 分钟会将 Statistics 输出,同时也支持将 Statistics 及时的输出到 TiKV 自己的 LOG 里面供我们分析性能瓶颈。很多时候,我们不可能一次在不同的机器上面将 RocksDB 的参数配对,所以我们都是首先根据机器性能评估一个可用配置,然后在实际测试,通过 Statistics 以及系统其他的指标来动态调优的。

你可能感兴趣的:(Tuning RocksDB - Statistics)