MDD
度量驅(qū)動(dòng)開(kāi)發(fā) Metrics-Driven Development 即在軟件開(kāi)發(fā)過(guò)程中, 設(shè)計(jì)和實(shí)現(xiàn)方案的選擇及優(yōu)化建立在度量的基礎(chǔ)之上.
一切拿數(shù)據(jù)說(shuō)話(huà), 杜絕臆想和猜測(cè), 這是在大數(shù)據(jù)和 DevOPS 浪潮之下順應(yīng)而生的現(xiàn)代軟件開(kāi)發(fā)方法
- 監(jiān)控: 采集產(chǎn)品的性能, 用量以及業(yè)務(wù)相關(guān)的統(tǒng)計(jì)數(shù)據(jù)
- 存儲(chǔ): 把收集到的數(shù)據(jù)基于時(shí)間線(xiàn)或其他基準(zhǔn)分門(mén)別類(lèi)進(jìn)行存儲(chǔ)
- 分析: 對(duì)于數(shù)據(jù)進(jìn)行各種維度的分析計(jì)算, 生成圖表, 并對(duì)關(guān)鍵指標(biāo)設(shè)置閥值, 在條件滿(mǎn)足時(shí)觸發(fā)報(bào)警
牽涉到的開(kāi)源工具不勝枚舉, 常用的就有
- LogStash
- Kibana
- ElasticSearch
- Collectd
- Metrics
- Graphite
- InfluxDB
- Grafana
- Splunk
- Circonus
- Seyren
- New Relic
- Cassandra
我們先從時(shí)間序列數(shù)據(jù)庫(kù) InfluxDB 說(shuō)起
時(shí)間序列數(shù)據(jù)
光陰似箭, 日月如梭, 很多事情都可以重頭再來(lái), 唯有時(shí)間一去不回頭.
在軟件應(yīng)用領(lǐng)域, 一寸光陰一寸金, 基于時(shí)間的數(shù)據(jù)收集, 度量和監(jiān)控每時(shí)每刻都在進(jìn)行.
比如股指的變化, 房?jī)r(jià)的漲跌, 天氣的冷暖, 應(yīng)用訪(fǎng)問(wèn)的頻率和次數(shù)等等
這些數(shù)據(jù)都有一個(gè)共通的地方, 那就是全部都是以時(shí)間為基軸
我們稱(chēng)這些數(shù)據(jù)為 Time-series Data 時(shí)間序列數(shù)據(jù), 由一系列來(lái)自相同數(shù)據(jù)源, 按一定時(shí)間間隔采樣的連續(xù)數(shù)據(jù)組成.
這些數(shù)據(jù)的重要性不言自明, 在 DevOps 的大潮下, 人們逐漸達(dá)成共識(shí), 一定要基于數(shù)據(jù)說(shuō)話(huà)和做決定
時(shí)間序列數(shù)據(jù)的存儲(chǔ)可以是數(shù)據(jù)庫(kù), 主鍵是時(shí)間點(diǎn), 字段是具體的數(shù)據(jù)值.
但有由于時(shí)間序列數(shù)據(jù)有它自身的特點(diǎn), 就如 Baron Schwartz 所總結(jié)的
- 90%以上的數(shù)據(jù)庫(kù)負(fù)載在于大量和高頻率的寫(xiě)操作
- 寫(xiě)操作一般都是在以有的數(shù)據(jù)度量之后按時(shí)間追加
- 這些寫(xiě)操作都是典型地按時(shí)間序列產(chǎn)生的, 比如每秒或每分
- 數(shù)據(jù)庫(kù)的主要瓶頸在于輸入輸出的限制
- 對(duì)于已存在的單個(gè)數(shù)據(jù)點(diǎn)的糾正和修改極少
- 刪除數(shù)據(jù)幾乎都是一個(gè)比較大的時(shí)間段(天, 月或年), 極少會(huì)只針對(duì)單條記錄
- 查詢(xún)數(shù)據(jù)庫(kù)一般都是基于某個(gè)序列的連續(xù)數(shù)據(jù), 并根據(jù)時(shí)間或時(shí)間的函數(shù)排序
- 執(zhí)行查詢(xún)常用并發(fā)讀取一個(gè)或多個(gè)序列
對(duì)于時(shí)間序列數(shù)據(jù)當(dāng)然可以使用傳統(tǒng)的數(shù)據(jù)庫(kù)來(lái)存儲(chǔ), 比如Oracle, MySQL, PostgreSQL, 或者一些KV store, 比如 Cassandra, Riak 等
但是對(duì)于上述時(shí)間序列數(shù)據(jù)的特點(diǎn)有針對(duì)性的優(yōu)化的一些時(shí)間序列數(shù)據(jù)庫(kù)逐漸流行起來(lái), 比如
時(shí)間序列數(shù)據(jù)庫(kù) InfluxDB
這里就重點(diǎn)講講現(xiàn)在比較流行的 InfluxDB , 它是由InfluxData開(kāi)發(fā)的一個(gè)開(kāi)源的時(shí)間序列數(shù)據(jù)庫(kù)。 它是由 Go 語(yǔ)言寫(xiě)的, 為了快速并且高可靠性的時(shí)間序列數(shù)據(jù)存取做了一些優(yōu)化仍秤, 應(yīng)用范圍包括操作監(jiān)視, 應(yīng)用度量临燃, 物聯(lián)傳感器數(shù)據(jù)以及實(shí)時(shí)分析等方面。
概述
首先 InfluxDB是一個(gè)基于時(shí)間序列數(shù)據(jù)組織的壁拉,例如 cpu占用率, 溫度等度量數(shù)據(jù)谬俄, 在時(shí)間序列上有一個(gè)或多個(gè)數(shù)據(jù)在連續(xù)的時(shí)間間隔上的采樣
理論上講, 你可以認(rèn)為 measurement 就象傳統(tǒng)數(shù)據(jù)庫(kù)中的表 table, 它的主鍵永遠(yuǎn)是 time, tags 和 fields 是表中的字段弃理, tags 是有索引的字段溃论,而 fields 沒(méi)有.
橫向比較一下 InfluxDBI, Oracle 和 Cassandra
InfluxDB | Oracle | Cassandra |
---|---|---|
Measurement | Table | Column Family |
Timestamp as PK | Customized PK | Parition Key |
Tag | Indexed field | Clustering column |
Field | not Indexed field | column |
區(qū)別在于, InfluxDB 中可以有數(shù)百萬(wàn)的 measurements, 無(wú)需事先定義 schema, 不會(huì)有 null 類(lèi)型的數(shù)據(jù)痘昌, 數(shù)據(jù)點(diǎn) Point 寫(xiě)到 InfluxDB 中是采用 Line 協(xié)議钥勋, 即如下格式
<measurement>[,<tag-key>=<tag-value>...] <field-key>=<field-value>[,<field2-key>=<field2-value>...] [unix-nano-timestamp]
下面就是一個(gè)例子
cpu,host=serverA,region=us_west value=0.64payment,device=mobile,product=Notepad,method=credit billed=33,licenses=3i 1434067467100293230stock,symbol=AAPL bid=127.46,ask=127.48temperature,machine=unit42,type=assembly external=25,internal=37 1434067467000000000
** 注:** 更多有關(guān) line 協(xié)議的信息可參見(jiàn) 行協(xié)議書(shū)寫(xiě)語(yǔ)法.
存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)大體如下
{
"database" : "mydb",
"retentionPolicy" : "bar",
"points" :
[
{"name" : "disk",
"tags" : {"server" : "bwi23", "unit" : "1"},
"timestamp" : "2015-03-16T01:02:26.234Z",
"fields" : {"total" : 100, "used" : 40, "free" : 60}}
]
}
安裝
MAC OS
brew install influxdb
CentOS
先添加一個(gè) yum 配置文件
cat <<EOF | sudo tee /etc/yum.repos.d/influxdb.repo
[influxdb]
name = InfluxDB Repository - RHEL \$releasever
baseurl = https://repos.influxdata.com/rhel/\$releasever/\$basearch/stable
enabled = 1
gpgcheck = 1
gpgkey = https://repos.influxdata.com/influxdb.key
EOF
測(cè)試
打開(kāi) http://localhost:8083/,這是它的 Web 控制臺(tái)
(注: /etc/influxdb/influxdb.conf 中的# https-enabled = false 要改成 true)
還有兩種方式訪(fǎng)問(wèn)influxdb, 命令行方式和API方式
- http://localhost:8086/
- influx 命令行工具
用法
創(chuàng)建數(shù)據(jù)庫(kù)
curl -i -XPOST http://localhost:8086/query --data-urlencode "q=CREATE DATABASE mydb"
或者
influx -execute 'create database mydb'
查詢(xún)數(shù)據(jù)結(jié)構(gòu)
- 查看 databases
SHOW DATABASES
- 查看 retention policies
SHOW RETENTION POLICIES
- 查看 series
SHOW SERIES
- 查看 measurements
SHOW MEASUREMENTS
- 查看 tag keys
SHOW TAG KEYS
- 查看 tag values
SHOW TAG VALUES
- 查看 field keys
SHOW FIELD KEYS
插入數(shù)據(jù)
curl -XPOST 'http://localhost:8086/write?db=mydb' \\\\\\\\\\\\\\\\-d 'cpu,host=server01,region=uswest load=42 1434055562000000000'curl -XPOST 'http://localhost:8086/write?db=mydb' \\\\\\\\\\\\\\\\-d 'cpu,host=server02,region=uswest load=78 1434055562000000000'curl -XPOST 'http://localhost:8086/write?db=mydb' \\\\\\\\\\\\\\\\-d 'cpu,host=server03,region=useast load=15.4 1434055562000000000'
或
influx -execute 'INSERT into mydb.autogen cpu,host=serverA,region=us_west value=0.64'
查詢(xún)數(shù)據(jù)
influx -execute 'show databases'
influx -execute 'show measurements' -database mydb
influx -execute 'SELECT host, region, value FROM cpu' -database mydb
或
curl -G 'http://localhost:8086/query?db=mydb' --data-urlencode "q=SHOW MEASUREMENTS"
curl -G http://localhost:8086/query?pretty=true --data-urlencode "db=mydb" --data-urlencode "q=SELECT * FROM cpu WHERE host='server01' AND time < now() - 1d"
分析數(shù)據(jù)
curl -G http://localhost:8086/query?pretty=true --data-urlencode "db=mydb" --data-urlencode "q=SELECT mean(load) FROM cpu WHERE region='uswest'"
InfluxDB Client library
Java Client library
Maven 依賴(lài)
<dependency>
<groupId>org.influxdb</groupId>
<artifactId>influxdb-java</artifactId>
<version>2.4</version>
</dependency>
示例
import org.influxdb.InfluxDB;
import org.influxdb.InfluxDBFactory;
import org.influxdb.dto.BatchPoints;
import org.influxdb.dto.Point;
import org.influxdb.dto.Pong;
import org.influxdb.dto.Query;
import org.influxdb.dto.QueryResult;
import java.util.concurrent.TimeUnit;
public class InfluxDbSample {
public static void main(String[] args) {
InfluxDB influxDB = InfluxDBFactory.connect("http://127.0.0.1:8086", "root", "");
Pong pong = influxDB.ping();
System.out.println("pong: " + pong);
String dbName = "aTimeSeries";
influxDB.createDatabase(dbName);
// Flush every 2000 Points, at least every 100ms
//influxDB.enableBatch(2000, 100, TimeUnit.MILLISECONDS);
System.out.println("--- created database and will insert data ---");
BatchPoints batchPoints = BatchPoints
.database(dbName)
.tag("async", "true")
.retentionPolicy("default")
.consistency(InfluxDB.ConsistencyLevel.ALL)
.build();
Point point1 = Point.measurement("cpu")
.time(System.currentTimeMillis(), TimeUnit.MILLISECONDS)
.addField("idle", 90L)
.addField("user", 9L)
.addField("system", 1L)
.build();
Point point2 = Point.measurement("disk")
.time(System.currentTimeMillis(), TimeUnit.MILLISECONDS)
.addField("used", 80L)
.addField("free", 1L)
.build();
batchPoints.point(point1);
batchPoints.point(point2);
influxDB.write(batchPoints);
Query query = new Query("SELECT idle FROM cpu", dbName);
QueryResult rs = influxDB.query(query);
System.out.println("result: " + rs.toString());
influxDB.deleteDatabase(dbName);
}
}
--- output ---
pong: Pong{version=0.13.0, responseTime=245}
--- created database and will insert data ---
result: QueryResult [results=[Result [series=[Series [name=cpu, tags=null, columns=[time, idle], values=[[2016-11-03T14:51:42.486Z, 90.0], [2016-11-03T14:52:33.328Z, 90.0], [2016-11-03T14:54:03.474Z, 90.0], [2016-11-03T14:55:55.839Z, 90.0], [2016-11-03T15:00:15.402Z, 90.0], [2016-11-03T15:02:50.596Z, 90.0]]]], error=null]], error=null]