RRD 数据库简介及操作
最近公司在部署了一套小米开源的监控平台 — open-falcon,有机会认识了 RRD(更多被称作 RRDTool)。好久没 blog 了,今天跟大家分享一下这个数据库。

RRD 简介

RRDtool refers to Round Robin Database tool.

Round robin is a technique that works with a fixed amount of data, and
a pointer to the current element.

Think of a circle with some dots plotted on the edge. These dots are
the places where data can be stored.

Draw an arrow from the center of the circle to one of the dots; this
is the pointer.

When the current data is read or written, the pointer moves to the
next element.

As we are on a circle there is neither a beginning nor an end, you can
go on and on and on.

After a while, all the available places will be used and the process
automatically reuses old locations.

This way, the dataset will not grow in size and therefore requires no
maintenance.

RRDtool works with Round Robin Databases (RRDs). It stores and
retrieves data from them.

以上摘自RRDTool 官网的表述。

RRD 数据库是一个环形的数据库,你可以把它想象成表,中心处有一个指针,随着时间的变化,指针也在变,当指针指到 12 点处,也就是这个记录要被擦除覆盖的时候,所以它是大小固定的。

总结起来,RRD 的关键词就是:

环形、大小固定、无需运维、绘图

时序数据库
看完 RRD 的简介,尤其是它的关键词,是不觉得“我靠这么nb”。数据库按照功能和种类可以分成很多,各自术业有专攻,针对专门的领域进行数据存储。比如 mysql 的优势就是存储结构型数据,nosql 就是针对非结构型数据的。

在运维领域里,需要不断的对服务器、交换机等支持性设备的性能和运行进行监控,这类监控的数据的特点就是跟着时间走 —— 它们的指标只会随着时间的变化而变化,不会涉及回头修改。这就区别于常见的数据库,会不断的修改历史数据,对数据进行 U(pdate) 操作。

所以就应运而生了一系列数据库,名曰“时序数据库”。常见的时序数据库有:influxdb、opentsdb、rrd 等。

其实如果你对 mysql 的引擎有足够了解的话,除了常见的 innodb 和 myisam ,还有一个引擎叫 archive ,它的作用和 rrd 差不多,支持插入和查询操作,比较专。

RRD 的安装
我的环境是 centos6.5 。
安装的 rrdtool 版本是 1.4.7 ,源码安装。

wget http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.4.7.tar.gz
tar zxvf rrdtool-1.4.7.tar.gz
cd rrdtool-1.4.7
./configure --prefix=/usr/local
make
make install

至此 rrdtool 的安装已经完成。读者要是缺啥依赖,请自行 google || baidu 解决。

RRD 的操作
下面介绍一下 RRD 数据库的操作,包括创建、增、查、绘:

创建
命令:

rrdtool create filename \
               [--start|-b start time] \
               [--step|-s step] \
               [DS:ds-name:DST:heartbeat:min:max] \
               [RRA:CF:xff:steps:rows] \

               [--template|-t template-file] \
               [--source|-r source-file] \
               [--no-overwrite|-O] \
               [--daemon|-d address] 

例子:

rrdtool create test.rrd             \
            --start 1469341292       \
            DS:speed:COUNTER:600:U:U  \
            RRA:AVERAGE:0.5:1:24       \
            RRA:AVERAGE:0.5:6:10

讲解:

命令中,有用的基本上只有前五行,逐行解释。
rrdtool create filename

这个一看就很明白,就是通过 rrdtool 命令进行数据库创建,名为 filename 。
例子里创建了一个名为 test.rrd 的数据库,其中 .rrd 为 rrd 数据库的后缀名。

[--start|-b start time]

这句的意思是要指明该数据库的起始时间戳,例如例子中的 1469341292 是一个写此文的 unix timestamp 。
你当然要指明起始时间了,因为这是时序数据库,肯定得有一个开始。

[--step|-s step]

这个 step 的意思是“你多久向数据库上报一次数据”,以秒为单位。例如例子中的,噢,例子里没有。。。。好吧,默认是 300s 一上报,也就是 5 分钟一上报,你可以按自己的需求上报。

[DS:ds-name:DST:heartbeat:min:max]

这个复杂些,我们拆开看。
DS, DataSource,数据源,后面 ds-name 是需要你来指明的,比如,我给我们的数据源起个标示名为:speed。意思是,我这个数据库里面记录的都是关于 speed 的数据。
DST, DataSourceType,数据源类型,DST 是你需要指明的。有几个选择,其中有 4 个是常用的,来说一下:

COUNTER,
GAUGE
DERIVE
ABSOLUTE
举例说明他们之间的不同:

我在 10:30 和 10:31 分别插入了两个数据:
10:30 600
10:31 1200

那么如果你的 DST 是

COUNTER,(1200-600)/step = 600/300 = 2,这个结果 2 就是最后要往数据库里插入的数据,并且是 10:31 时刻的数值。
GAUGE,10:30 的值是600,10:31 的值是1200 。都是原值存储,这个是最常用的。
DERIVE,他的原理跟 COUNTER 一样,不同的是 COUNTER 只能递增,但是 DERIVE 可增可减。比如,对于 COUNTER ,10:30 是 600 , 10:31 是 1200 , 那么 10:32 的值必须大于 1200 ;但是 DERIVE 在 10:32 的值可以是小于 1200 的。
ABSOLUTE,这个是直接做均值,600/step = 1 , 1200/step = 2, 所以 10:30 的值是 1 , 10:31 的值是 2 。
heartbeat , 心跳。心跳的意思,我自己经过实践和文档,总结出来心跳是跟 step 配合使用的。还是举例:

比如,我的 step 是 60s 上报一次数据
如果我的 heartbeat 是 60s ,那么 10:30 600, 10:31 没上报, 10:32 1200,这时候,图像就会在 10:31 处没有记录,也就是图像断开了;
如果我的 heartbeat 是 120s,那么 10:31 也会有数据,图象是不会中断的。但是,是以什么样的值来填充,是有条件的,后面说明。
其实说白了,这个 heartbeat 就是一个容错时间,如果在这个时间间隔内没有上传数据,那么我就不能给你绘图。

min,能接受的最小值,比如上报的数据不能小于 500 ,那你就设置这里。
max,同理。
一般这两个值都是U,表示无限。

[RRA:CF:xff:steps:rows]

这个意思是创建数据表,刚才创建的是数据库,现在创建数据表。
RRA:Round Robin Archive
CF: Consolidation Function,合成函数、合成方法。
rrd 提供的 CF 有下面这么几种:

Average()
Max()
Min()
Last()
这个函数是做什么的?先继续看后面的参数。

xff,这是一个小于 1 的比例值,表示正常值与异常值的比值,相当于数据的合格率,如果低于这个比例,那么该数据就算废弃。一般设置成 0.5。

steps,这个参数的意思是,多少个值通过 CF 函数合成一个值,并存入数据表。

rows,表示你这个环形的数据库有多少个格子,也就是说你这个数据表能存多少数据。

好了,合起来讲解一下这个 RRA 的命令参数。

还是举例子吧,不太好解释,举例:

time PDP
10:30 1.4
10:31 2.8
10:32 3.7
10:33 4.2
10:34 5.1
10:35 6.6

RRA:CF :xff:steps:rows
RRA:AVERAGE:0.5:1 :24 — ①
RRA:AVERAGE:0.5:6 :10 — ②

如果按①来说,意思是,我这个数据表能存 24 个值,steps 是 1 ,意思是我每上报一个值就算一个存储值。这样的话, 10:30 到 10:35 上报的什么值,就存什么值:

time 上报 存储
10:30 1.4 1.4
10:31 2.8 2.8
10:32 3.7 3.7
10:33 4.2 4.2
10:34 5.1 5.1
10:35 6.6 6.6

如果按②来说,意思是,我这个数据表能存10个值,steps 是 6,意思是我每上报 6 个值,求 Average() ,把结果存到数据表里:

time 上报 存储
10:30 1.4
10:31 2.8
10:32 3.7
10:33 4.2
10:34 5.1
10:35 6.6 (1.4+2.8+3.7+4.2+5.1+6.6)/6 = 3.96

为什么会有多个 RRA ,采样,采不同时间段的样,然后当你画图的时候,给你提供最适合你要求的时间段的数据给你。


命令:

rrdtool {update | updatev} filename [--template|-t ds-name[:ds-name]...] [--skip-past-updates|-s] [--daemon|-d address] [--] N:value[:value]... timestamp:value[:value]... at-timestamp@value[:value]...

例子:

rrdtool update test.rrd \
           1469341292:12420 \
           1469341352:12422 \
           1469341412:12423

rrdtool updatev test.rrd \
           1469341292:12420 \
           1469341352:12422 \
           1469341412:12423

update 和 updatev 的区别就是多了一个 v ,罗嗦、详情。
没错就是这么简单。


命令:

rrdtool fetch filename CF [--resolution|-r resolution] [--start|-s start] [--end|-e end] [--align-start|-a] [--daemon|-d address]

例子:

rrdtool fetch test.rrd AVERAGE --start 1469341292

查看数据库结构的命令:

rrdtool info filename [--daemon|-d address [--noflush|-F]]

例子:
rrdtool info test.rrd
绘图
rrd 的主要功能分为两块,一个就是存储数据,一个就是根据数据绘图。

命令:

rrdtool graph|graphv filename [option ...] [data definition ...] [data calculation ...] [variable definition ...] [graph element ...] [print element ...]

例子:

rrdtool graph test.png  \
         --start 1469341292 --end 1469341412 \
         DEF:myspeed=test.rrd:speed:AVERAGE \
         LINE2:myspeed#FF0000

这里就解释一下 DEF :

DEF:myspeed=test.rrd:speed:AVERAGE

定义了一个 myspeed 的图,其内容来源来自于 test.rrd , DS 名为 speed , CF 是 AVERAGE。

python 操作
通过 pip 安装 rrdtool 模块,该模块可以通过 python 进行 RRD 数据库的创建、绘图等操作。

pip install rrdtool

所有的操作代码:

import rrdtool

rrdtool.create('test.rrd', '--start', '1469341292',
'--step','60','RRA:AVERAGE:0.5:1:3', 'DS:test:GAUGE:120:U:U')

rrdtool.update('test.rrd','1469341292:300')
rrdtool.update('test.rrd','1469341352:600')
rrdtool.update('test.rrd','1469341412:900')
rrdtool.update('test.rrd','1469341472:1200')

rrdtool.graph('test.png','--start','1469341292', '--end','1469341472','DEF:myspeed=test.rrd:test:AVERAGE','LINE2:myspeed#FF0000' )

更过的绘图方法,可以参看这里

总结

RRD 蛮好用。不用怎么操心,属于一劳永逸的数据库。理解起来比较费劲的是创建时候的 DS 那部分和 RRA 的参数那块。多动手操作,边实践边看本文效果更好。