1.Victoria Metric数据库介绍

(这一部分主要来自官方文档,但是经过我的筛选,主要是面向使用Victoria Metric,和一些使用它必知必会的概念)

  • VictoriaMetrics 是一种快速、可扩展的监控解决方案和时间序列数据库(tsdb)
  • 它可以用作 Grafana 中 Prometheus 的直接替代品,因为它支持Prometheus 查询 API
  • 它实现了类似 PromQL 的查询语言 - MetricsQL,它在 PromQL 之上提供了改进的功能(就是一种查询语言,类似于mysql和sql的关系),但是语法简单、自然编程语言很像、没有像sql有各种关系和语法需要记忆,所以想要使用Victoria Metric,需要学习promql(学习难度很低)
  • 它为数据摄取数据查询提供高性能和良好的垂直和水平可扩展性

2.VM优势与MySql优势 对比

VictoriaMetrics(VM)和MySQL,作为不同类型的数据库系统,各自具有独特的优势,主要体现在它们的设计目标和使用场景上。下表是具体各方面对比及原理:

vmmysql
数据时间序列数据(如监控数据或指标)通用数据和复杂数据关系
特性

1.        针对时间序列数据的查询,VM 提供了高效的数据聚合和计算能力,特别适合于快速检索和分析这类数据

2.        VM 支持水平扩展,能够通过增加更多节点来处理更多的数据和查询,适合大规模部署。

3.        支持promql的metricql,简单,和自然编程语言很像但更简单,适合快速上手,甚至不需要系统学习

1.        MySQL 提供完整的事务支持,包括原子性、一致性、隔离性和持久性(ACID),适合需要强事务处理的应用。

2.        MySQL 提供完整的事务支持,包括原子性、一致性、隔离性和持久性(ACID),适合需要强事务处理的应用。

3.        sql本身的灵活性、强大性、通用性、mysql社区的丰富性

C特别优化用于快速插入大量的时间序列数据。它可以高效处理高频率的数据点插入,监控和指标收集场景中有大量的数据需要记录

支持多种数据类型和复杂的数据结构的插入,但在处理大量插入操作时,不如专门的tsdb高效。

R优秀的读取性能,特别是对于时间序列数据的聚合和分析。它对 Prometheus 查询语言的原生支持使得查询时间序列数据变得非常高效。提供了复杂的查询能力,包括联接、子查询等。这在需要进行复杂数据关联和分析的应用中非常有用。
U时间序列数据的更新不是这类数据库的主要用途。强大的更新功能,支持通过 SQL 语句进行复杂的、条件化的数据更新。这使得 MySQL 在需要频繁更新数据的应用中更为合适。
D时间序列数据库通常不用于单条数据的删除操作,因为这类数据库的主要用途是长期存储数据用于分析。VM 也是如此,一般不需要也没必要删除。提供强大的删除操作能力,可以精确控制要删除的数据。SQL 语言使得对数据的删除操作既灵活又强大。
技术原理

1.        作为时间序列数据库,VM 主要处理的是时间戳和相关数据值的序列。它使用列式存储,这种结构特别适合于快速读取大量时间序列数据。列式存储优化了数据的压缩率和读取效率,但并不适合频繁的单条数据修改或删除。

2.        它的索引和查询系统特别设计来处理按时间顺序存储的数据。VM 的查询优化主要集中在时间范围查询和数据聚合

1.        作为关系型数据库,MySQL 使用行式存储模型,每行存储一条记录的所有数据。这种结构便于增加、更新和删除单条记录。行式存储提供了更好的通用性和灵活性,但在处理大规模数据聚合查询时,不如列式存储高效。

2.        使用通用的索引机制,如 B-Tree,适用于各种类型的查询,包括基于非时间属性的查询。MySQL 的查询优化更为全面,支持复杂的连接(Join)和子查询。

3.使用docker拉起vm

这是docker-compose.yml文件中vm服务的配置,vm默认的端口号是8428,本人的机器上8428端口有正式项目的vm服务在占用,所以使用8429端口进行测试

使用docker-compose up命令拉起服务

这里对于vm的初学者(mysql的非初学者)需要做出解释,像关系型数据库,有服务之后还需要创建连接,还需要建库,建表,然后不管用原生sql还是什么其它封装了sql的框架组件进行查询;

但是vm拉起服务后就结束了,它是非关系型数据库没有表的概念,用个不完全贴切的比喻,就像一锅“大杂烩”;那么数据怎么插?怎么查?

想要回答这个问题,就必须要明白几个概念(只是quickstart,还有很多概念和语法请自行参阅官方文档):

指标:这是vm中数据的标识,查vm就是查vm中的指标值

标签:指标可以有多个标签,主要用来按照某种逻辑筛选同类型指标

那么,指标名+标签 => 唯一确定了一个时间序列,也就是一个时间序列上的指标值,下面这个例子的指标名就是CPU_OCCUPIED,代表cpu占用率,{machine="2",process_id="1"}就是标签,代表了机器id和进程id,也就是说这个指标记录了id为2的机器上进程id为1的进程在一段时间序列中每分钟的cpu占用率

 # CPU_OCCUPIED{machine="2",process_id="1"}
timestamp        value
12:01             66
12:02             65   
12:03             78

所以数据是存在由指标和标签确定的一个时间序列中的,只有你向vm中插入指标值,这个指标才会存在!

查询则需要用vm提供的ui客户端中()写metricql/promql查询和插入数据:

一旦容器启动,可以通过访问http://localhost:8428/vmui/?#/?g0.range_input=30m&g0.end_input=2023-11-14T09%3A44%3A40&g0.tab=0&g0.relative_time=last_30_minutes来访问VictoriaMetrics的Web界面。可以使用该界面来管理和监控你的指标数据。

或者使用编程语言,构造promql查询和发送http请求插入数据

下面用python编写脚本进行操作,当然,在正式的项目中,是需要高级工程师将各种查询方式封装成工具类来使用的(本blog暂时只提供python脚本方式操作,有工具类需求的朋友可以转发私信,后续可以考虑更新python使用vm工具类操作vm服务的具体实现

4.使用python脚本插入数据

直接上脚本:

import time
import json
import requests
import urllib3

def insert_into_vm2(business, data_source, ad_task, ts, rows, product_type=1):
    # VictoriaMetrics 的地址和端口
    url = 'http://127.0.0.1:8428/api/v1/import'
    # json数据
    data = json.dumps({
        "metric": {
            "__name__": "analysis_data_volume_rows",    # {__name__="ask" or __name__="bid"} 查询ask或bid的数据
            "business": str(business),
            "data_source": str(data_source),
            "ad_task": str(ad_task),
            "product_type": str(product_type)
        },
        "values": [rows],
        "timestamps": [ts]    # 需要int
    })

    # 配置并发请求
    headers = {"Content-Type": "application/json"}
    http_pool = urllib3.PoolManager()
    response = http_pool.request('POST', url, body=data, headers=headers)
    print(response.status, f"data is {response.data}")

看懵了吧?哈哈,上解释!

既然是指标监控,那么就需要application以推送的模式发送到服务器,至于发送的时间和数据格式由应用程序决定,这也就是push模式以及vm支持的几种传输协议:

显然,json是我们最最常用的方式,我们也是用json来进行发送,(官方文档:VictoriaMetrics · VictoriaMetrics

VictoriaMetrics 在/api/v1/import接受 JSON 行格式的数据,并在/api/v1/export导出此格式的数据。

格式遵循JSON 流概念,例如每行包含 JSON 对象,其指标数据采用以下格式():

{
  # metric contans metric name plus labels for a particular time series
  "metric":{
    "__name__": "metric_name",  # this is metric name

    # Other labels for the time series

    "label1": "value1",
    "label2": "value2",
    ...
    "labelN": "valueN"
  },

  # values contains raw sample values for the given time series
  "values": [1, 2.345, -678],

  # timestamps contains raw sample UNIX timestamps in milliseconds for the given time series
  # every timestamp is associated with the value at the corresponding position
  "timestamps": [1549891472010,1549891487724,1549891503438]
}

所以我们插入数据的url和准备的数据才会是那样写的,__name__就是指标名,label就是标签名,value就是指标值,timestamp就是时间戳,只需要把字典json.dumps变成json串就可以了

5.使用python查询数据

vm主要提供了两种方式的查询:即使查询和范围查询。

即时查询

即使查询在给定时间戳执行查询表达式:

GET | POST /api/v1/query?query=...&time=...&step=...

参数:

查询的结果样例:

# json
{
  "status": "success",
  "data": {
    "resultType": "vector",
    "result": [
      {
        "metric": {
          "__name__": "foo_bar"
        },
        "value": [
          1652169780,
          "3"
        ]
      }
    ]
  }
}

所以将结果查询到之后就可以,json.loads成字典来操作了,下面的就是拿到了metrics这层的数据:

    def query_data(self, query=None, time=None, step=None):
        params = {
            "query": query
        }
        if time:
            params["time"] = time

        if step:
            params["step"] = step

        url = self.query_url + urlencode(params)
        try:
            # res = requests.get(url)
            res = self.http_pool.request('GET', url)
            if res.status == 200:
                data = json.loads(res.data).get("data").get("result")
                # logger.info(f"VictoriaMetrics query success, and url is {url}, and data is {data}")
                if data is None:
                    logger.info(f"VictoriaMetrics query null, and url is {url}")
                    return None
                return data
            else:
                logger.error(
                    f"VictoriaMetrics query failed, and url is {url}, response code is {res.status}, "
                    f"and error is {res.text}")
                return None
        except Exception as e:
            logger.error(f"VictoriaMetrics query failed, and url is {url}, exception is {e}")
            return None

但是有一点需要注意,那就是,无论插入数据的json中timestamp和指标值的先后数据,查询出来的数据(即value列表)都是timestamp在前,指标值在后:
返回数据的value列表是        [timestamp,指标值]

value = int(query_data[0].get("value", [])[1])

范围查询

范围查询以给定的步长在给定的时间范围内执行查询表达式:

GET | POST /api/v1/query_range?query=...&start=...&end=...&step=...

参数:

  • queryMetricSQL表达式。
  • start- 评估时间范围的起始时间戳query
  • end- 评估时间范围的结束时间戳query。如果end未设置,则end自动设置为当前时间。
  • step-数据点之间的间隔,必须从范围查询返回。

查询的结果样例:

# json
{
  "status": "success",
  "data": {
    "resultType": "matrix",
    "result": [
      {
        "metric": {
          "__name__": "foo_bar"
        },
        "values": [
          [
            1652169600,
            "1"
          ],
          [
            1652169660,
            "2"
          ],
          [
            1652169720,
            "3"
          ],
          [
            1652169780,
            "3"
          ],
          [
            1652169840,
            "7"
          ],
          [
            1652169900,
            "7"
          ]
        ]
      }
    ]
  }
}

剩下的拿结果的方式后及时查询同理了。

Logo

一起探索未来云端世界的核心,云原生技术专区带您领略创新、高效和可扩展的云计算解决方案,引领您在数字化时代的成功之路。

更多推荐