对时序数据的处理有两种方式,如图所示,右边是 SQL,左边是自定义查询语言,也称为 NoSQL,处于中间地带的称为 SQL-LIKE 语言。
本文通过对比 SQL 阵营的 TimescaleDB 与 NoSQL 阵营的 InfluxDB,试图给出一些对比。
TimescaleDB 完全接受了 SQL 语法,因此几乎没有什么学习门槛,更通过可视化操作优化了使用方式。
InfluxDB 创造了一种新的查询语言,这里是 Flux 文法.(了解更多文法相关知识,可以移步 精读《手写 SQL 编译器 - 文法介绍》)
InfluxDB 之所以创造 Flux 语法,而不使用 SQL,主要有两个原因:
- 更强的查询功能:SQL 无法轻松完成时序查询。
- 时间序列的查询需要基于流的函数模型,而不是 SQL 的代数模型。
所谓流模型,就类似 JS 函数式编程中类似概念:
source.pipe(
map(x => x + x),
mergeMap(...),
filter(...)
)
InfluxDB 拿下面例子举例:
Flux:
from(db:"telegraf")
|> range(start:-1h)
|> filter(fn: (r) => r._measurement == "foo")
|> exponentialMovingAverage(size:-10s)
SQL:
select id,
temp,
avg(temp) over (partition by group_nr order by time_read) as rolling_avg
from (
select id,
temp,
time_read,
interval_group,
id - row_number() over (partition by interval_group order by time_read) as group_nr
from (
select id,
time_read,
'epoch'::timestamp + '900 seconds'::interval * (extract(epoch from time_read)::int4 / 900) as interval_group,
temp
from readings
) t1
) t2
order by time_read;
虽然看上去 SQL 写法比 Flux 长了不少,但其实 Flux 代码的核心在于实现了自定义函数 exponentialMovingAverage
,而 PostgreSQL 也有 创建函数 的能力。
通过 SQL 定义一个自定义函数:
CREATE OR REPLACE FUNCTION exponential_moving_average_sfunc
(state numeric, next_value numeric, alpha numeric)
RETURNS numeric LANGUAGE SQL AS
$$
SELECT
CASE
WHEN state IS NULL THEN next_value
ELSE alpha * next_value + (1-alpha) * state
END
$$;
CREATE AGGREGATE exponential_moving_average(numeric, numeric)
(sfunc = exponential_moving_average_sfunc, stype = numeric);
之后可以像 Flux 函数一样的调用:
SELECT time,
exponential_moving_average(value, 0.5) OVER (ORDER BY time)
FROM telegraph
WHERE measurement = 'foo' and time > now() - '1 hour';
可见从函数定义上也和 Flux 打成平手,作者认为既然功能相同,而基于 SQL 的语言学习成本更低,所以不需要创造一个新的语言。
作者认为,虽然有观点认为,Flux 的语法糖比 SQL 更简洁,但代码的可维护性并不是行数越少越好,而是是否容易被人类理解。
对于创造一个函数标准可能破坏 SQL 的可移植性,作者认为那也比完全创造一个新语法要强。
诚然,从功能角度来看,当然函数模型强于代数模型,因为代数模型只是在描述事物,而不能精准控制执行的每一步。
但我们要弄清楚 SQL 的场景,是通过描述一个无顺序的查询问题,让数据库给出结果。而在查询过程中,数据库可以对 SQL 语句作出一些优化。
反观函数模型,是在用业务代码描述查询请求,这种代码是无法被自动优化的,虽然为用户提供了更底层的控制,但其代价是无法被数据库执行引擎所优化。
如果你更看中查询语言,而不是具体执行逻辑,SQLl 依然是最好的选择。
之所以制作这一期精读,是为了探索 SQL 与其他查询语言的关系,去理解为什么 SQL 沿用至今。
SQL 与其他函数类查询语言不在一个层面上,如果用语法糖、可操纵性抨击 SQL,只能得出看似正确,实则荒谬的结论。
SQL 是一个查询语言,与普通编程语言相比,它还在上层,最终会转化为关系代数执行,但关系代数会遵循一些等价的转换规律,比如交换律、结合律、过滤条件拆分等等,通过预估每一步的时间开销,将 SQL 执行顺序重新组合,可以提高执行效率。
如果有多个 SQL 同时执行,还可以整合成一个或多个新的 SQL,合并重复的查询请求。
在数据驱动商业的今天,SQL 依然是数据查询最通用的解决方案。
如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。