[Rust]Influxdb初体验

1.Bucket和Measurement

InfluxDB 数据模型将时间序列数据组织成Bucket和Measurement。

2.初体验

横着每一条数据对应一个Point,所有按照顺序排列的Point构成Series

如果使用了influxdb就要引入以下依赖:

Cargo.toml

Rust 复制代码
influxdb2 = "0.5.2"
influxdb2-structmap = "0.2"
dotenvy = "0.15"
num-traits = "0.2"
chrono = "0.4.41"
tokio = { version = "1", features = ["full"]}
futures = "0.3.31"

客户端初始化:

Rust 复制代码
async fn init_client() -> Client {
    let host = dotenvy::var("INFLUXDB_HOST").expect("INFLUXDB_HOST not set in environment variables");
    let org = dotenvy::var("INFLUXDB_ORG").expect("INFLUXDB_ORG not set in environment variables");
    let token = dotenvy::var("INFLUXDB_TOKEN").expect("INFLUXDB_TOKEN not set in environment variables");

    Client::new(host, org, token)
}

同时需要创建.env文件,放在Cargo.toml同级目录下,对应的格式为

ini 复制代码
INFLUXDB_HOST = http://localhost:8086
INFLUXDB_ORG = manager
INFLUXDB_TOKEN = your-token

3.写入数据

行协议元素

行协议的每一行都包含以下元素

  • measurement:标识要将数据存储在其中的measurement的字符串。
  • tag set(标签集) :逗号分隔的键值对列表,每个键值对代表一个标签。标签键和值是不带引号的字符串。
  • field set(字段集) :逗号分隔的键值对列表,每个键值对代表一个字段。字段键是不带引号的字符串。
  • timestamp(时间戳) :与数据关联的 Unix时间戳。

对应的行协议

Rust 复制代码
weather,city=London,country=UK temperature=12.0 1641038400000000000
weather,city=London,country=UK temperature=12.1 1643716800000000000

Rust实现

Rust 复制代码
async fn write_stock_data(client: Client) -> Result<(), Box<dyn std::error::Error>> {
    let apple = DataPoint::builder("stock_price")
        .tag("ticker", "AAPL")
        .field("open", 182.51)
        .field("high", 184.13)
        .field("low", 182.13)
        .field("close", 183.86)
        .field("volume", 78591230.0)
        .build()?;

    let tesla = DataPoint::builder("stock_price")
        .tag("ticker", "TSLA")
        .field("open", 242.68)
        .field("high", 247.77)
        .field("low", 239.60)
        .field("close", 246.39)
        .field("volume", 103929123.0)
        .build()?;

    client.write("stock-prices", stream::iter(vec![apple, tesla])).await?;

    println!("成功写入股票数据");
    Ok(())
}

4.查询数据

使用Flux查询

  • from():从 InfluxDB 存储桶查询数据。
  • range():根据时间范围过滤数据。
  • filter() :根据列值过滤数据。每一行由 r 表示,每一列由 r 的属性表示。
php 复制代码
from(bucket: "testt")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
  |> filter(fn: (r) => r["_measurement"] == "weather")
  |> filter(fn: (r) => r["_field"] == "temperature")
  |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
  |> yield(name: "mean")

InfluxQL查询

  • SELECT:指定要查询的字段和标签。
  • FROM:指定要查询的测量。使用测量名称或完全限定的测量名称,其中包括数据库和保留策略。例如:db.rp.measurement
  • WHERE:根据字段、标签和时间过滤数据。
sql 复制代码
SELECT MEAN("temperature") AS "mean_temperature"
FROM "weather"
WHERE time >= '2023-01-01T00:00:00Z' AND time <= '2023-01-02T00:00:00Z'

Rust实现

在Rust实现查询功能时,我们需要创建一个StockPrice结构体,因为后面在获得查询结果时,我们需要显示声明res的类型为Vec<StockPrice>,同时为其实现Default的trait,即默认构造方法。

Rust 复制代码
#[derive(Debug, FromDataPoint)]
pub struct StockPrice {
    ticker: String,
    value: f64,
    time: DateTime<FixedOffset>,
}

impl Default for StockPrice {
    fn default() -> Self {
        Self {
            ticker: "".to_string(),
            value: 0_f64,
            time: chrono::MIN_DATETIME.with_timezone(&FixedOffset::east(7 * 3600)),
        }
    }
}

具体查询代码

Rust 复制代码
async fn get_stock_data(client: Client) -> Result<(), Box<dyn std::error::Error>> {
    let ticker = "AAPL";
    let query_str = format!(
        r#"
        from(bucket: "stock-prices")
            |> range(start: -1w)
            |> filter(fn: (r) => r.ticker == "{}")
            |> last()
        "#,
        ticker
    );

    let query = Query::new(query_str);
    let res: Vec<StockPrice> = client.query(Some(query)).await?;

    if let Some(data) = res.first() {
        println!("最新股票数据:");
        println!("Ticker: {}", data.ticker);
        println!("价格: ${:.2}", data.value);
        println!("时间: {}", data.time.format("%Y-%m-%d %H:%M:%S"));
    } else {
        println!("未找到 {} 的数据", ticker);
    }

    Ok(())
}

5.处理数据

处理数据一般是通过各种函数来实现,看下面这个案例。

Rust 复制代码
from(bucket: "stock-prices")
  |> range(start: today())
  |> filter(fn: (r) => r._field == "price")
  |> group(columns: ["ticker"]) // 按股票分组
  |> aggregateWindow( // 每10分钟计算高-低波动范围
    every: 10m,
    fn: (values) => ({
      high: max(values: values.value),
      low: min(values: values.value),
      spread: max(values: values.value) - min(values: values.value)
    })
  )
  |> pivot( // 转换为波动率热力图数据
    rowKey: ["_start"],
    columnKey: ["ticker"],
    valueColumn: "spread"
  )

各个函数的功能如下:

相关推荐
JaguarJack几秒前
PHP 现代特性速查 写出更简洁安全的代码(中篇)
后端·php
Victor3561 小时前
Redis(104)Redis的最大数据量是多少?
后端
Victor3561 小时前
Redis(105)Redis的数据类型支持哪些操作?
后端
鬼火儿8 小时前
SpringBoot】Spring Boot 项目的打包配置
java·后端
cr7xin8 小时前
缓存三大问题及解决方案
redis·后端·缓存
间彧9 小时前
Kubernetes的Pod与Docker Compose中的服务在概念上有何异同?
后端
间彧9 小时前
从开发到生产,如何将Docker Compose项目平滑迁移到Kubernetes?
后端
间彧10 小时前
如何结合CI/CD流水线自动选择正确的Docker Compose配置?
后端
间彧10 小时前
在多环境(开发、测试、生产)下,如何管理不同的Docker Compose配置?
后端
间彧10 小时前
如何为Docker Compose中的服务配置健康检查,确保服务真正可用?
后端