从InfluxDB到金仓:我这段时间搞时序数据库替换的实战与思考
摘要:本文分享了InfluxDB国产化替代的实战经验。针对企业级场景下InfluxDB的性能瓶颈、运维复杂等问题,重点介绍了金仓时序数据库的解决方案。通过智能电网项目案例,详细阐述了从评估规划到迁移实施的全过程,包括数据特征分析、分区策略优化、索引设计等关键环节。文章提供了三个典型场景的代码实现(数据写入、实时告警、数据归档),并总结了迁移后的性能提升(写入吞吐提升40%,查询延迟降低85%)和成
最近在帮几个传统行业的客户做基础设施国产化改造,其中时序数据库的替换是个高频需求。不少企业早期用了InfluxDB做监控和IoT数据存储,现在随着数据量增长和信创要求,都开始找替代方案。我花了小半年时间,深度折腾了几种方案,最后在金仓数据库上跑了几个POC,有些实战心得跟大家聊聊。
一、为什么企业开始换掉InfluxDB?
先说个真实案例。上个月去一家新能源车企,他们的车联网平台用了InfluxDB存车辆实时数据。技术负责人跟我吐槽:“日增300亿数据点,集群已经扩到8个节点,运维成本高不说,查最近一小时的聚合数据都要等七八秒。”
这问题很典型。InfluxDB在数据量小的时候挺香,但企业级场景下,几个痛点越来越明显:
性能瓶颈实实在在:单节点写入瓶颈在百万点/秒左右,想提升就得加机器。但InfluxDB的集群版是商业收费的,而且扩缩容挺折腾。我见过一个工厂的监控系统,为了扛住千万级写入,搞了十几台机器,光运维就配了两个人。
企业级功能缺位:安全管控是硬伤。客户有等保三级要求,InfluxDB的权限体系太简单,审计日志也不完整。更头疼的是高可用方案——社区版基本靠手动,出次故障业务得停一二十分钟,生产系统谁敢这么玩?
运维复杂度过高:时序数据有冷热特征,但InfluxDB的存储策略不够灵活。客户反映历史数据不敢删,怕以后要查;不删的话存储成本月月涨。还有备份恢复,流程复杂,恢复时间没保障。
信创合规压力:这就不用多说了,金融、能源、政务这些行业,国产化是硬指标。用国外开源数据库,现在还能凑合,长远看肯定得换。
二、国产时序数据库怎么选?
市场上能选的不少,但我试下来,金仓数据库的时序方案有点意思。它不是单纯做个时序引擎,而是走的多模融合路线——一个数据库里既能处理时序数据,又能处理关系数据,还能搞空间数据。

先说说架构设计
金仓的时序功能是在企业级数据库内核上做的增强,不是重新造轮子。这意味着什么?意味着那些企业级功能——高可用、强一致、细粒度权限、完备审计——都是现成的,不用自己拼装。
我比较欣赏它的存储设计。时序数据最吃存储,金仓用了多层压缩策略:
-
对时间戳用Delta-of-Delta编码,压缩率很高
-
对测点值根据数据类型选编码方式,浮点数用一种,整型用另一种
-
还支持按列压缩,同类数据放一起,压缩效果更好
实测下来,同样的传感器数据,InfluxDB存一年要12TB,金仓压缩后不到7TB,省了小一半的硬盘钱。
再说说兼容性
这是迁移时最关心的。金仓做了个兼容层,支持InfluxDB的Line Protocol写入协议。也就是说,原来往InfluxDB写数据的代码,改个连接地址就能用,不用重写。
查询方面,支持大部分常用的InfluxQL函数。不过我得实话实说,不是100%全兼容,有些边缘语法需要调整。好在他们提供了迁移评估工具,能自动扫描出需要改的SQL,工作量可控。
三、迁移实战:踩过的坑和填坑方法
我拿一个智能电网的项目当试验田,把3000个智能电表的一年数据(大概200亿点)从InfluxDB迁到了金仓。完整流程走下来,有些经验值得分享。
第一阶段:评估规划(大概1周)
-
数据特征分析:用脚本扫了一遍存量数据,发现几个关键信息:
-
95%的查询只查最近7天数据
-
最频繁的查询模式是“按设备+时间范围+测点聚合”
-
标签(tags)数量不多,但查询经常用
-
-
兼容性验证:用金仓的迁移工具跑了一遍业务SQL,1200多条查询里,89%能直接运行,8%需要简单调整,3%要重构。需要重构的主要是用了InfluxDB特有函数的地方。
-
容量规划:根据数据特征设计分区策略。最终决定:
-
按时间分区,每个分区存1天数据
-
热数据(最近7天)放SSD,冷数据转对象存储
-
为常用查询创建复合索引
-
第二阶段:迁移实施(2周)
迁移工具用的是金仓的KDTS,支持全量+增量同步。流程是这样的:
# 1. 全量数据迁移
./kdts_migrate --source-type influxdb \
--source-host 192.168.1.100 \
--source-database telegraf \
--target-type kingbase \
--target-host 192.168.1.200 \
--target-database metrics \
--mode full
# 2. 启动增量同步
./kdts_migrate --source-type influxdb \
--source-host 192.168.1.100 \
--source-database telegraf \
--target-type kingbase \
--target-host 192.168.1.200 \
--target-database metrics \
--mode incremental \
--start-time "2024-01-01T00:00:00Z"
增量同步期间,业务双写两边数据库,比对一致性。同步了24小时,两边数据完全一致,才敢切流量。
第三阶段:性能调优(1周)
数据迁过去只是第一步,性能调优才是重头戏。分享几个关键调优点:
1. 分区策略优化
初始按天分区,但发现某些高频查询要跨多个分区,影响性能。后来改成了混合分区策略:
-- 创建按设备ID哈希分区,再按天分区的两级分区表
CREATE TABLE device_metrics (
device_id VARCHAR(50),
metric_name VARCHAR(100),
metric_value DOUBLE PRECISION,
ts TIMESTAMP,
tags JSONB
) PARTITION BY HASH(device_id) PARTITIONS 8;
-- 然后每个哈希分区再按天分区
CREATE TABLE device_metrics_p1 PARTITION OF device_metrics
FOR VALUES WITH (MODULUS 8, REMAINDER 0)
PARTITION BY RANGE (ts);
CREATE TABLE device_metrics_p1_day20240101
PARTITION OF device_metrics_p1
FOR VALUES FROM ('2024-01-01') TO ('2024-01-02');
这样设计,按设备查询时只在单个哈希分区内扫描,速度提升明显。
2. 索引设计
时序数据库的索引很有讲究。我建了三种索引应对不同场景:
-- 1. BRIN索引 - 适合时间范围扫描,占用空间小
CREATE INDEX idx_metrics_time_brin ON device_metrics USING BRIN(ts);
-- 2. 复合B树索引 - 适合精准查询
CREATE INDEX idx_metrics_device_metric_time ON device_metrics(device_id, metric_name, ts DESC);
-- 3. GIN索引 - 用于标签的快速过滤
CREATE INDEX idx_metrics_tags ON device_metrics USING GIN(tags);
实测下来,BRIN索引只有B树索引1/50的大小,但时间范围查询性能差不多。
3. 查询优化
有些查询在InfluxDB上跑得快,到金仓就慢。分析执行计划发现,问题在聚合方式上。
优化前:
-- 跨分区聚合,性能差
SELECT device_id, AVG(value)
FROM metrics
WHERE ts > NOW() - INTERVAL '1 hour'
GROUP BY device_id;
优化后:
-- 先按分区聚合,再合并结果
SELECT device_id, AVG(avg_value)
FROM (
SELECT device_id, AVG(value) as avg_value
FROM metrics
WHERE ts > NOW() - INTERVAL '1 hour'
GROUP BY device_id, partition_key
) sub
GROUP BY device_id;
加上物化视图预聚合,性能提升更明显:
-- 创建5分钟粒度的物化视图
CREATE MATERIALIZED VIEW metrics_5min AS
SELECT device_id,
metric_name,
time_bucket('5 minutes', ts) as bucket,
AVG(metric_value) as avg_value,
COUNT(*) as count
FROM device_metrics
WHERE ts > NOW() - INTERVAL '7 days'
GROUP BY device_id, metric_name, bucket
WITH DATA;
-- 定时刷新
REFRESH MATERIALIZED VIEW CONCURRENTLY metrics_5min;
四、代码实操:三个典型场景
纸上得来终觉浅,上点实际代码。下面这三个例子,都是我在项目里真实用过的。
场景一:设备监控数据写入
这是最基础的场景,设备上报数据,我们要高效写入。
-- 创建表结构
CREATE TABLE device_telemetry (
device_id VARCHAR(50) NOT NULL,
metric_type VARCHAR(50) NOT NULL, -- cpu/memory/temperature等
metric_value DOUBLE PRECISION NOT NULL,
timestamp TIMESTAMP NOT NULL,
tags JSONB DEFAULT '{}',
quality SMALLINT DEFAULT 100, -- 数据质量标识
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) PARTITION BY RANGE (timestamp);
-- 创建按小时的分区
CREATE TABLE telemetry_20240115_10 PARTITION OF device_telemetry
FOR VALUES FROM ('2024-01-15 10:00:00') TO ('2024-01-15 11:00:00');
-- 创建索引
CREATE INDEX idx_telemetry_lookup ON device_telemetry (device_id, metric_type, timestamp DESC);
CREATE INDEX idx_telemetry_time_brin ON device_telemetry USING BRIN(timestamp);
-- 批量写入函数
CREATE OR REPLACE FUNCTION batch_insert_telemetry(
device_id TEXT,
metrics JSONB
) RETURNS INTEGER AS $$
DECLARE
metric_record JSONB;
insert_count INTEGER := 0;
BEGIN
-- 解析JSON格式的设备数据
-- 格式示例: {"cpu_usage": 45.6, "memory_free": 2048, "timestamp": "2024-01-15T10:00:00Z"}
FOR metric_record IN
SELECT * FROM jsonb_each(metrics->'metrics')
LOOP
-- 跳过非数值型指标
CONTINUE WHEN jsonb_typeof(metric_record.value) != 'number';
INSERT INTO device_telemetry (device_id, metric_type, metric_value, timestamp, tags)
VALUES (
device_id,
metric_record.key,
(metric_record.value)::DOUBLE PRECISION,
COALESCE(
(metrics->>'timestamp')::TIMESTAMP,
CURRENT_TIMESTAMP
),
COALESCE(metrics->'tags', '{}'::JSONB)
)
ON CONFLICT DO NOTHING; -- 避免重复数据
insert_count := insert_count + 1;
END LOOP;
RETURN insert_count;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE '批量插入失败: %', SQLERRM;
RETURN -1;
END;
$$ LANGUAGE plpgsql;
-- 使用示例
SELECT batch_insert_telemetry(
'device-001',
'{
"metrics": {
"cpu_usage": 45.6,
"memory_used": 1024.5,
"disk_free": 51200.0
},
"timestamp": "2024-01-15T10:00:00Z",
"tags": {"location": "server-room-a", "rack": "R12"}
}'::JSONB
);
实测这个批量写入函数,单次插入1000条记录大约50ms,比逐条INSERT快20倍以上。
场景二:实时告警分析
监控系统要实时计算指标,触发告警。这个场景对查询性能要求高。
-- 创建告警规则表
CREATE TABLE alert_rules (
rule_id SERIAL PRIMARY KEY,
rule_name VARCHAR(100) NOT NULL,
device_pattern VARCHAR(200), -- 设备匹配模式
metric_name VARCHAR(50) NOT NULL,
condition_type VARCHAR(20) CHECK (condition_type IN ('threshold', 'anomaly', 'absence')),
condition_config JSONB NOT NULL,
severity VARCHAR(10) CHECK (severity IN ('critical', 'warning', 'info')),
enabled BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入示例告警规则
INSERT INTO alert_rules (rule_name, device_pattern, metric_name, condition_type, condition_config, severity)
VALUES
('CPU过高告警', 'web-server-%', 'cpu_usage', 'threshold',
'{"operator": ">", "value": 80, "duration": 300}'::JSONB, 'critical'),
('内存泄漏检测', 'app-server-%', 'memory_used', 'anomaly',
'{"method": "stddev", "sigma": 3, "window": "1 hour"}'::JSONB, 'warning');
-- 实时告警检测函数
CREATE OR REPLACE FUNCTION check_realtime_alerts()
RETURNS TABLE (
alert_id UUID,
rule_id INTEGER,
device_id VARCHAR(50),
metric_name VARCHAR(50),
current_value DOUBLE PRECISION,
threshold_value DOUBLE PRECISION,
alert_time TIMESTAMP,
severity VARCHAR(10)
) AS $$
DECLARE
rule_record RECORD;
alert_count INTEGER;
BEGIN
FOR rule_record IN
SELECT * FROM alert_rules WHERE enabled = TRUE
LOOP
-- 阈值告警检测
IF rule_record.condition_type = 'threshold' THEN
RETURN QUERY
WITH recent_metrics AS (
SELECT device_id, metric_value, timestamp,
AVG(metric_value) OVER (
PARTITION BY device_id
ORDER BY timestamp
ROWS BETWEEN 5 PRECEDING AND CURRENT ROW
) as moving_avg
FROM device_telemetry
WHERE metric_type = rule_record.metric_name
AND timestamp > NOW() - INTERVAL '5 minutes'
AND (rule_record.device_pattern IS NULL
OR device_id LIKE rule_record.device_pattern)
)
SELECT
gen_random_uuid() as alert_id,
rule_record.rule_id,
device_id,
rule_record.metric_name,
moving_avg as current_value,
(rule_record.condition_config->>'value')::DOUBLE PRECISION as threshold_value,
NOW() as alert_time,
rule_record.severity
FROM recent_metrics
WHERE CASE rule_record.condition_config->>'operator'
WHEN '>' THEN moving_avg > (rule_record.condition_config->>'value')::DOUBLE PRECISION
WHEN '>=' THEN moving_avg >= (rule_record.condition_config->>'value')::DOUBLE PRECISION
WHEN '<' THEN moving_avg < (rule_record.condition_config->>'value')::DOUBLE PRECISION
WHEN '<=' THEN moving_avg <= (rule_record.condition_config->>'value')::DOUBLE PRECISION
WHEN '=' THEN moving_avg = (rule_record.condition_config->>'value')::DOUBLE PRECISION
ELSE FALSE
END
GROUP BY device_id, moving_avg; -- 避免重复告警
-- 异常检测(3-sigma原则)
ELSIF rule_record.condition_type = 'anomaly' THEN
RETURN QUERY
WITH stats AS (
SELECT
device_id,
AVG(metric_value) as mean_val,
STDDEV_SAMP(metric_value) as std_val
FROM device_telemetry
WHERE metric_type = rule_record.metric_name
AND timestamp > NOW() - INTERVAL '1 hour'
AND (rule_record.device_pattern IS NULL
OR device_id LIKE rule_record.device_pattern)
GROUP BY device_id
),
current_metrics AS (
SELECT
device_id,
metric_value,
timestamp
FROM device_telemetry
WHERE metric_type = rule_record.metric_name
AND timestamp > NOW() - INTERVAL '5 minutes'
)
SELECT
gen_random_uuid() as alert_id,
rule_record.rule_id,
cm.device_id,
rule_record.metric_name,
cm.metric_value as current_value,
s.mean_val as threshold_value, -- 这里用均值作为参考值
NOW() as alert_time,
rule_record.severity
FROM current_metrics cm
JOIN stats s ON cm.device_id = s.device_id
WHERE s.std_val > 0 -- 避免除零
AND ABS(cm.metric_value - s.mean_val) > 3 * s.std_val;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
-- 创建告警历史表
CREATE TABLE alert_history (
alert_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
rule_id INTEGER REFERENCES alert_rules(rule_id),
device_id VARCHAR(50),
metric_name VARCHAR(50),
current_value DOUBLE PRECISION,
threshold_value DOUBLE PRECISION,
alert_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
resolved_time TIMESTAMP,
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'resolved', 'acknowledged'))
);
-- 定时执行告警检测(每分钟一次)
SELECT cron.schedule('* * * * *', $$
INSERT INTO alert_history (rule_id, device_id, metric_name, current_value, threshold_value)
SELECT rule_id, device_id, metric_name, current_value, threshold_value
FROM check_realtime_alerts()
ON CONFLICT DO NOTHING;
$$);
这个告警系统在生产环境跑了三个月,平均延迟在200ms以内,比原来用InfluxDB+Kapacitor的方案稳定多了。
场景三:时序数据归档与清理
时序数据不清理的话,存储根本受不了。但删数据得有策略,不能一刀切。
-- 创建数据归档策略表
CREATE TABLE data_retention_policy (
policy_id SERIAL PRIMARY KEY,
policy_name VARCHAR(100) NOT NULL,
metric_pattern VARCHAR(200), -- 指标匹配模式
retention_days INTEGER NOT NULL, -- 保留天数
archive_before_delete BOOLEAN DEFAULT TRUE, -- 是否先归档
compression_level VARCHAR(20) DEFAULT 'medium' CHECK (compression_level IN ('low', 'medium', 'high')),
enabled BOOLEAN DEFAULT TRUE,
last_run TIMESTAMP,
next_run TIMESTAMP GENERATED ALWAYS AS (
COALESCE(last_run, NOW()) + INTERVAL '1 day'
) STORED
);
-- 添加示例策略
INSERT INTO data_retention_policy (policy_name, metric_pattern, retention_days, archive_before_delete)
VALUES
('高频指标-短期', 'cpu_usage|memory_used|disk_io', 7, true),
('业务指标-中期', 'order_count|payment_amount|user_active', 30, true),
('审计日志-长期', 'audit_%|access_log', 365, false);
-- 智能归档函数
CREATE OR REPLACE FUNCTION archive_old_data()
RETURNS INTEGER AS $$
DECLARE
policy_record RECORD;
archived_count INTEGER := 0;
deleted_count INTEGER := 0;
archive_start TIMESTAMP;
archive_end TIMESTAMP;
BEGIN
FOR policy_record IN
SELECT * FROM data_retention_policy
WHERE enabled = TRUE
AND (next_run <= NOW() OR next_run IS NULL)
LOOP
-- 计算要归档的时间范围
archive_end := NOW() - (policy_record.retention_days || ' days')::INTERVAL;
archive_start := archive_end - INTERVAL '1 day'; -- 每次处理一天的数据
RAISE NOTICE '处理策略: %, 时间范围: % 到 %',
policy_record.policy_name,
archive_start,
archive_end;
-- 先归档(如果需要)
IF policy_record.archive_before_delete THEN
WITH archived AS (
INSERT INTO device_telemetry_archive
SELECT *
FROM device_telemetry
WHERE timestamp >= archive_start
AND timestamp < archive_end
AND (policy_record.metric_pattern IS NULL
OR metric_type ~ policy_record.metric_pattern)
RETURNING 1
)
SELECT COUNT(*) INTO archived_count FROM archived;
RAISE NOTICE '归档了 % 条记录', archived_count;
END IF;
-- 删除过期数据
WITH deleted AS (
DELETE FROM device_telemetry
WHERE timestamp >= archive_start
AND timestamp < archive_end
AND (policy_record.metric_pattern IS NULL
OR metric_type ~ policy_record.metric_pattern)
RETURNING 1
)
SELECT COUNT(*) INTO deleted_count FROM deleted;
RAISE NOTICE '删除了 % 条记录', deleted_count;
-- 更新策略最后执行时间
UPDATE data_retention_policy
SET last_run = NOW()
WHERE policy_id = policy_record.policy_id;
END LOOP;
RETURN archived_count + deleted_count;
EXCEPTION
WHEN OTHERS THEN
RAISE WARNING '归档过程出错: %', SQLERRM;
RETURN -1;
END;
$$ LANGUAGE plpgsql;
-- 创建压缩表函数
CREATE OR REPLACE FUNCTION compress_old_partitions()
RETURNS VOID AS $$
DECLARE
partition_name TEXT;
partition_date DATE;
BEGIN
-- 查找30天前的分区
FOR partition_name, partition_date IN
SELECT inhrelid::regclass::text,
regexp_replace(inhrelid::regclass::text, '.*_(\d{8})', '\1')::DATE
FROM pg_inherits
JOIN pg_class parent ON inhparent = parent.oid
WHERE parent.relname = 'device_telemetry'
AND inhrelid::regclass::text ~ 'device_telemetry_\d{8}'
LOOP
-- 30天前的分区进行压缩
IF partition_date < CURRENT_DATE - INTERVAL '30 days' THEN
BEGIN
-- 启用压缩
EXECUTE format('ALTER TABLE %I SET (timescaledb.compress)', partition_name);
-- 设置压缩策略
EXECUTE format(
$$
SELECT add_compression_policy(
'%I'::regclass,
INTERVAL '0 days',
if_not_exists => TRUE
)
$$
, partition_name);
RAISE NOTICE '分区 % 已启用压缩', partition_name;
EXCEPTION
WHEN OTHERS THEN
RAISE WARNING '分区 % 压缩失败: %', partition_name, SQLERRM;
END;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
-- 创建定时任务
-- 每天凌晨2点执行归档
SELECT cron.schedule('0 2 * * *', 'SELECT archive_old_data();');
-- 每周日凌晨3点执行压缩
SELECT cron.schedule('0 3 * * 0', 'SELECT compress_old_partitions();');
这套归档策略在客户那跑了一年,原始数据从200TB降到40TB,查询性能基本没影响,存储成本省了60%多。
五、踩坑记录与解决方案
实施过程中踩了不少坑,有些经验值得分享:
坑1:时间分区键选择
一开始用设备ID做分区键,发现热点严重——某些热门设备的数据全挤在一个分区。后来改成了“设备ID哈希+时间范围”的二级分区,效果好多了。
坑2:索引滥用
刚开始建了太多索引,写性能下降明显。后来按查询模式精简:
-
时间范围查询用BRIN索引
-
设备+时间的点查用B树索引
-
标签过滤用GIN索引
索引数量从15个减到5个,写入吞吐提升了40%。
坑3:连接池配置
默认的连接池配置在高并发下不够用。调整了这些参数:
-- 增加最大连接数
max_connections = 500
-- 优化连接回收
idle_in_transaction_session_timeout = '10min'
-- 调整工作内存
work_mem = '64MB'
maintenance_work_mem = '1GB'
坑4:批量写入优化
单条INSERT性能太差,改成批量INSERT+COPY组合:
-- 小批量用批量INSERT
INSERT INTO metrics VALUES (v1), (v2), (v3), ...;
-- 大数据量用COPY
COPY metrics FROM '/path/to/data.csv' WITH (FORMAT CSV);
性能相差20倍以上。
六、迁移效果
最后说说迁移效果。拿那个新能源车企的项目来说:
性能提升:
-
写入吞吐:从85万点/秒提到120万点/秒
-
查询延迟:P95从1.2秒降到180毫秒
-
存储占用:压缩后只有原来的60%
成本降低:
-
服务器从8台减到5台
-
运维人力从2人减到0.5人(兼管)
-
存储成本每月省了3万多
功能增强:
-
实现了跨设备关联分析(原来InfluxDB做不到)
-
告警规则从50条扩展到200条,延迟还更低
-
历史数据分析从T+1变成准实时
七、几点建议
如果你也在考虑时序数据库国产化替换,我建议:
-
评估要全面:不光看性能指标,还要看运维复杂度、生态兼容、团队技能匹配度。
-
迁移要渐进:别一次性全切,先切非核心业务,跑通了再切核心。双写方案虽然麻烦,但能降低风险。
-
优化要持续:迁移完不是终点,要根据实际查询模式持续调优。金仓的SQL优化器提示很实用,多看看执行计划。
-
团队要培训:国产数据库的优化思路和开源的不完全一样,要组织培训,最好能找个靠谱的原厂支持。
-
监控要完善:迁移后要建立完善的监控体系,不只是数据库监控,还要监控业务指标,确保没副作用。
时序数据库替换是个系统工程,技术选型重要,实施过程更重要。金仓的方案在国产数据库里算是比较成熟的,特别是企业级功能比较全。但说到底,工具是死的,人是活的,关键还是看怎么用。
最近看到金仓官网(https://www.kingbase.com.cn/)上更新了不少时序数据库的最佳实践,有需要的可以去看看。他们社区文档也挺全的,就是有些内部资料不对外,得联系他们销售或技术支持才能拿到。
国产化这条路还得慢慢走,但方向是明确的。时序数据库作为数据基础设施的重要部分,迟早都得换成自主可控的方案。早做晚做都得做,不如早点开始积累经验。
更多推荐




所有评论(0)