【数据库】时序数据库选型指南:在大数据与工业4.0时代,为何 Apache IoTDB 成为智慧之选?

文章目录


一、引言

在大数据时代,时序数据的重要性日益凸显。从工业生产中的设备监测数据,到物联网中海量设备产生的实时信息,时序数据无处不在。如何选择一款合适的时序数据库成为了企业和开发者面临的重要问题。本文将从大数据角度以及与国外产品对比等方面,为大家提供时序数据库选型的参考指南,并着重介绍国产时序数据库IoTDB的独特优势。

官网下载

官网介绍

二、时序数据库选型的关键因素

1. 数据压缩能力

时序数据往往具有海量的特点,数据压缩能力直接影响到存储成本。高效的压缩算法可以在不损失数据精度的情况下,大幅减少存储空间的需求。例如,一些优秀的时序数据库能够采用专有的压缩算法,实现高达90%以上的存储节省,这对于长期存储大量时序数据的企业来说,意味着巨大的成本降低。

2. 分布式架构

随着数据量的不断增长,分布式架构成为了时序数据库的必备特性。分布式架构可以实现数据的水平扩展,在无需复杂数据迁移的情况下,通过增加节点实现秒级扩容,降低运维压力。同时,分布式架构还能提高系统的容错性和可用性,确保数据的稳定存储和快速访问。

3. 工业场景适配性

工业领域是时序数据的重要应用场景,时序数据库需要能够深入工业场景,适配各种复杂的工业环境和采集协议。例如,支持数百种采集协议,能够实现乱序写入、一键备份等功能,可以更好地满足工业生产中的实时监测、故障预警等需求。

4. 性能表现

包括数据写入速度、查询响应时间以及点位处理能力等。单节点每秒千万级的数据写入能力、毫秒级的查询响应以及支持单设备万级点位和多设备亿级点位的能力,都是衡量时序数据库性能的重要指标。

三、与国外时序数据库产品的对比

1. 功能特点对比

国外一些知名的时序数据库可能在某些通用功能上具有一定优势,但在工业场景的深度适配方面往往存在不足。例如,部分国外产品对工业协议的支持不够全面,无法满足国内工业企业多样化的数据采集需求。而IoTDB作为国产时序数据库,深入理解国内工业场景,能够更好地适配各种工业设备和协议。

2. 本地化服务对比

在本地化服务方面,国产时序数据库具有明显优势。IoTDB提供及时的技术支持和服务响应,能够快速解决用户在使用过程中遇到的问题。而国外产品可能由于时区、语言等因素,在服务响应速度和质量上会打折扣。

3. 成本对比

国外时序数据库产品往往价格较高,不仅包括软件授权费用,还可能涉及到后续的维护和升级费用。而国产IoTDB在保证高性能和功能的前提下,具有更高的性价比,能够为企业降低数据管理的成本。

四、IoTDB:国产时序数据库的佼佼者

1. 高压缩底层文件格式

IoTDB采用自研底层文件格式TsFile,并配备专有压缩算法,能够实现高达90%以上的存储节省。这一特性使得企业在存储海量时序数据时,能够显著降低存储成本,提高存储效率。


2. 优秀的分布式架构

IoTDB是完全开源的分布式时序数据库,在无需数据迁移的情况下,可以实现秒级扩容,大大降低了运维压力。其分布式架构保证了系统的高可用性和容错性,能够确保数据的稳定存储和快速访问。

3. 深度适配工业场景

IoTDB深入工业场景,适配数百种采集协议,支持乱序写入、一键备份等功能。在钢铁冶炼、能源电力、交通运输、航空航天、物联网等多个领域都有广泛的应用。例如,在钢铁冶炼中,可以对冶炼设备和生产线进行实时数据采集、存储和分析;在能源电力领域,能够有效提升能源利用效率,降低运营成本,确保能源生产的安全性和可持续性。

4. 强大的性能表现

IoTDB具有单节点每秒千万级数据写入、10倍无损压缩、TB数据毫秒级查询响应以及支持单设备万级点位和多设备亿级点位的能力。这些性能指标使得IoTDB能够满足各种复杂场景下的时序数据处理需求。

5. 丰富的生态和工具支持

IoTDB提供了一系列配套工具,如可视化控制台,用户可以通过图形化工具管理数据库连接、元数据、数据等重要功能。此外,还有Apache IoTDB专家服务和Timer时序大模型等,为用户提供全方位的技术支持和解决方案。

五、应用代码示例

Java

css 复制代码
package org.apache.iotdb;
 
import org.apache.iotdb.isession.SessionDataSet;
import org.apache.iotdb.rpc.IoTDBConnectionException;
import org.apache.iotdb.rpc.StatementExecutionException;
import org.apache.iotdb.session.Session;
import org.apache.iotdb.tsfile.write.record.Tablet;
import org.apache.iotdb.tsfile.write.schema.MeasurementSchema;
 
import java.util.ArrayList;
import java.util.List;
 
public class SessionExample {
 
  private static Session session;
 
  public static void main(String[] args)
          throws IoTDBConnectionException, StatementExecutionException {
    session =
            new Session.Builder()
                    .host("172.0.0.1")
                    .port(6667)
                    .username("root")
                    .password("root")
                    .build();
    session.open(false);
    List<MeasurementSchema> schemaList = new ArrayList<>();
    schemaList.add(new MeasurementSchema("s1", TSDataType.FLOAT));
    schemaList.add(new MeasurementSchema("s2", TSDataType.FLOAT));
    schemaList.add(new MeasurementSchema("s3", TSDataType.FLOAT));
    Tablet tablet = new Tablet("root.db.d1", schemaList, 10);
 
    tablet.addTimestamp(0, 1);
    tablet.addValue("s1", 0, 1.23f);
    tablet.addValue("s2", 0, 1.23f);
    tablet.addValue("s3", 0, 1.23f);
    tablet.rowSize++;
    session.insertTablet(tablet);
    tablet.reset();
    try (SessionDataSet dataSet = session.executeQueryStatement("select ** from root.db")) {
      while (dataSet.hasNext()) {
        System.out.println(dataSet.next());
      }
    }
    session.close();
  }
}

Python

css 复制代码
from iotdb.Session import Session
from iotdb.utils.IoTDBConstants import TSDataType
from iotdb.utils.Tablet import Tablet
 
ip = "127.0.0.1"
port = "6667"
username = "root"
password = "root"
session = Session(ip, port, username, password)
session.open(False)
 
measurements = ["s_01", "s_02", "s_03", "s_04", "s_05", "s_06"]
data_types = [
    TSDataType.BOOLEAN,
    TSDataType.INT32,
    TSDataType.INT64,
    TSDataType.FLOAT,
    TSDataType.DOUBLE,
    TSDataType.TEXT,
]
values = [
    [False, 10, 11, 1.1, 10011.1, "test01"],
    [True, 100, 11111, 1.25, 101.0, "test02"],
    [False, 100, 1, 188.1, 688.25, "test03"],
    [True, 0, 0, 0, 6.25, "test04"],
]
timestamps = [1, 2, 3, 4]
tablet = Tablet(
    "root.db.d_03", measurements, data_types, values, timestamps
)
session.insert_tablet(tablet)
 
with session.execute_statement(
    "select ** from root.db"
) as session_data_set:
    while session_data_set.has_next():
        print(session_data_set.next())
 
session.close()

C++

css 复制代码
#include "Session.h"
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
 
int main(int argc, char **argv) {
  Session *session = new Session("127.0.0.1", 6667, "root", "root");
  session->open();
 
  std::vector<std::pair<std::string, TSDataType::TSDataType>> schemas;
  schemas.push_back({"s0", TSDataType::INT64});
  schemas.push_back({"s1", TSDataType::INT64});
  schemas.push_back({"s2", TSDataType::INT64});
 
  int64_t val = 0;
  Tablet tablet("root.db.d1", schemas, /*maxRowNum=*/ 10);
  tablet.rowSize++;
  tablet.timestamps[0] = 0;
  val=100; tablet.addValue(/*schemaId=*/ 0, /*rowIndex=*/ 0, /*valAddr=*/ &val);
  val=200; tablet.addValue(/*schemaId=*/ 1, /*rowIndex=*/ 0, /*valAddr=*/ &val);
  val=300; tablet.addValue(/*schemaId=*/ 2, /*rowIndex=*/ 0, /*valAddr=*/ &val);
 
  session->insertTablet(tablet);
  tablet.reset();
 
  std::unique_ptr<SessionDataSet> res = session->executeQueryStatement("select ** from root.db");
  while (res->hasNext()) {
    std::cout << res->next()->toString() << std::endl;
  }
 
  res.reset();
  session->close();
  delete session;
  return 0;
}

Go

css 复制代码
package main
 
import (
        "fmt"
        "log"
 
        "github.com/apache/iotdb-client-go/client"
)
 
func main() {
 
        config := &client.Config{
                Host:     "127.0.0.1",
                Port:     "6667",
                UserName: "root",
                Password: "root",
        }
        session := client.NewSession(config)
        if err := session.Open(false, 0); err != nil {
                log.Fatal(err)
        }
        defer session.Close() // close session at end of main()
        
        rowCount := 3
        tablet, err := client.NewTablet("root.db.d1", []*client.MeasurementSchema{
                {
                        Measurement: "restart_count",
                        DataType:    client.INT32,
                        Encoding:    client.RLE,
                        Compressor:  client.SNAPPY,
                }, {
                        Measurement: "price",
                        DataType:    client.DOUBLE,
                        Encoding:    client.GORILLA,
                        Compressor:  client.SNAPPY,
                }, {
                        Measurement: "description",
                        DataType:    client.TEXT,
                        Encoding:    client.PLAIN,
                        Compressor:  client.SNAPPY,
                },
        }, rowCount)
 
        if err != nil {
                fmt.Errorf("Tablet create error:", err)
                return
        }
 
        timestampList := []int64{0, 1, 2}
        valuesInt32List := []int32{5, -99999, 123456}
        valuesDoubleList := []float64{-0.001, 10e5, 54321.0}
        valuesTextList := []string{"test1", "test2", "test3"}
        for row := 0; row < rowCount; row++ {
                tablet.SetTimestamp(timestampList[row], row)
                tablet.SetValueAt(valuesInt32List[row], 0, row)
                tablet.SetValueAt(valuesDoubleList[row], 1, row)
                tablet.SetValueAt(valuesTextList[row], 2, row)
        }
        session.InsertTablet(tablet, false)
 
        var timeoutInMs int64
        timeoutInMs = 1000
        sql := "select ** from root.db"
        dataset, err := session.ExecuteQueryStatement(sql, &timeoutInMs)
        defer dataset.Close()
 
        if err == nil {
                for next, err := dataset.Next(); err == nil && next; next, err = dataset.Next() {
                        record, _ := dataset.GetRowRecord()
                        fields := record.GetFields()
                        for _, field := range fields {
                                fmt.Print(field.GetValue(), "\t")
                        }
                        fmt.Println()
                }
        } else {
                log.Println(err)
        }
}

总结

在进行时序数据库选型时,需要综合考虑数据压缩能力、分布式架构、工业场景适配性和性能表现等多个因素。与国外产品相比,国产IoTDB在功能特点、本地化服务和成本等方面都具有独特优势。其高压缩底层文件格式、优秀的分布式架构、深度适配工业场景、强大的性能表现以及丰富的生态和工具支持,使得IoTDB成为了时序数据库选型中的优质选择。无论是工业企业还是物联网企业,都可以通过选择IoTDB,实现高效的时序数据管理,为企业的数字化转型提供有力支持。随着大数据技术的不断发展,相信IoTDB将在更多领域发挥重要作用,为用户创造更大的价值。

相关推荐
程序员三明治3 小时前
详解Redis锁误删、原子性难题及Redisson加锁底层原理、WatchDog续约机制
java·数据库·redis·分布式锁·redisson·watchdog·看门狗
chenzhou__3 小时前
MYSQL学习笔记(个人)(第十五天)
linux·数据库·笔记·学习·mysql
熊猫钓鱼>_>3 小时前
AI驱动的专业报告撰写:从信息整合到洞察生成的全新范式
大数据·人工智能·百度
一只自律的鸡4 小时前
【MySQL】第二章 基本的SELECT语句
数据库·mysql
liliangcsdn5 小时前
如何使用python创建和维护sqlite3数据库
数据库·sqlite
TDengine (老段)11 小时前
TDengine 数学函数 DEGRESS 用户手册
大数据·数据库·sql·物联网·时序数据库·iot·tdengine
TDengine (老段)11 小时前
TDengine 数学函数 GREATEST 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
安当加密11 小时前
云原生时代的数据库字段加密:在微服务与 Kubernetes 中实现合规与敏捷的统一
数据库·微服务·云原生
爱喝白开水a12 小时前
LangChain 基础系列之 Prompt 工程详解:从设计原理到实战模板_langchain prompt
开发语言·数据库·人工智能·python·langchain·prompt·知识图谱