乐趣区

关于数据库:性能提升近-30DecimalV3-更低成本稳定支持高精度计算场景

数值运算是数据库中非常常见的需要,例如计算数量、分量、价格等,为了适应多样化运算场景,数据库系统通常反对精准的数字类型和近似的数字类型,当咱们须要准确地示意小数并计算小数时,通常会思考应用 Decimal 数据类型。区别于浮点小数,Decimal 作为定点小数类型,能够反对高精度的小数运算,因而实用于各种高精度计算的场景,常见的利用场景有以下几种:

  • 金融行业:在金融交易中常常波及到小数,比方利息、金额的计算,金融场景对数字精确的要求极高,因而准确的小数运算是必要的。
  • 财务软件:财务软件通常须要进行简单的财务计算,Decimal 类型能够提供准确的小数计算,防止计算过程中产生的舍入误差。
  • 科学计算、工程计算等其余场景。

DecimalV3 性能介绍

在 Apache Doris 1.2.1 之前的版本中,咱们已对 Decimal(precision, scale)(precision<=27) 数据类型进行了反对,随着 Apache Doris 用户的持续增长,银行、证券、基金等金融畛域的用户也随之快速增长,对高精度的小数计算场景也提出了更高的要求,旧的 Decimal 数据类型已无奈满足。因而,咱们在 Apache Doris 1.2.1 推出了精度更高、速度更快的 DecimalV3(precision, scale)(precision<=38),实现了真正意义上的高精度定点数,相比于老版本中的 Decimal,DecimalV3 有以下外围劣势:

  1. 可示意范畴更大。DECIMALV3 对 Precision 和 Scale 的取值范畴进行裁减。
  2. 内存占用更低,性能更高。老版本的 Decimal 须要占用 16 Bytes 的内存,而 DecimalV3 对内存可进行自适应调整,如下所示。
+----------------------+-------------------+
|     precision        | 占用空间(内存 / 磁盘)|
+----------------------+-------------------+
| 0 < precision <= 8   |      4 bytes      |
+----------------------+-------------------+
| 8 < precision <= 18  |      8 bytes      |
+----------------------+-------------------+
| 18 < precision <= 38 |     16 bytes      |
+----------------------+-------------------+
  1. 更齐备的精度推演。

精度推演规定

DECIMALV3 有一套很简单的类型推演规定,针对不同的表达式,会利用不同规定进行精度推演,上面来介绍一下推演规定:

  1. 四则运算
  • 加法 / 减法:DECIMALV3(a, b) + DECIMALV3(x, y) -> DECIMALV3(max(a – b, x – y) + max(b, y), max(b, y)),即整数局部和小数局部都别离应用两个操作数中较大的值。
  • 乘法:DECIMALV3(a, b) * DECIMALV3(x, y) -> DECIMALV3(a + x, b + y)
  • 除法:DECIMALV3(a, b) / DECIMALV3(x, y) -> DECIMALV3(a + y, b)
  1. 聚合运算
  • SUM / MULTI_DISTINCT_SUM:SUM(DECIMALV3(a, b)) -> DECIMALV3(38, b)。
  • AVG:AVG(DECIMALV3(a, b)) -> DECIMALV3(38, max(b, 4))(鉴于每个零碎 AVG 的精度不同,且不同用户对精度的需要也不一样,经调研,决定抉择与 SQLServer 雷同的策略,因而抉择“4”既能保障较好的性能,也不会有较大的精度损失。)
  1. 默认规定

除上述提到的函数外,其余表达式都应用默认规定进行精度推演。即对于表达式 expr(DECIMALV3(a, b)),后果类型同样也是 DECIMALV3(a, b)。

后果精度调整

上述几种规定为以后 Doris 的默认行为,而不同场景对 DECIMALV3 的精度要求各不相同,远超出以上几种规定。当用户 有不同的精度需要,能够通过以下形式进行精度调整

  • 当冀望的后果精度大于默认精度时,可通过调整入参精度来调整后果精度。例如用户冀望计算 AVG(col) 失去 DECIMALV3(x, y)作为后果,其中 col 的类型为 DECIMALV3(a, b),则能够改写表达式为AVG(CAST(col as DECIMALV3(x, y)))
  • 当冀望的后果精度小于默认精度时,可通过对输入后果求近似失去想要的精度。例如用户冀望计算 AVG(col) 失去 DECIMALV3(x, y)作为后果,其中 col 的类型为 DECIMALV3(a, b),则能够改写表达式为ROUND(AVG(col), y)

应用演示

这里咱们采纳 Bitcoin 的数据集对 DecimalV3 进行演示。

Bitcoin 的数据集局部示例如下:

  • Unix – 工夫戳
  • Date – 工夫
  • Symbol – 工夫序列数据所指代的交易种类
  • Open – 该时间段的开盘价
  • High – 该时间段的最高价
  • Low – 该时间段的最低价
  • Close – 该时间段的收盘价
  • Volume BTC – BTC 金额
  • Volume USD – USD 金额

以下是在 Doris 中的建表存储数据,其中小数的列别离用 DecimalV3 进行存储:

CREATE TABLE `btc` (`unix` bigint(20) NOT NULL,
  `date` datetime NULL,
  `symbol` varchar(30) NULL,
  `open` decimalv3(8, 2) NULL,
  `high` decimalv3(8, 2) NULL,
  `low` decimalv3(8, 2) NULL,
  `close` decimalv3(7, 2) NULL,
  `Volume_BTC` decimalv3(10, 8) NULL,
  `Volume_USD` decimalv3(38, 30) NULL
) ENGINE=OLAP
DUPLICATE KEY(`unix`)
COMMENT 'OLAP'
DISTRIBUTED BY HASH(`unix`) BUCKETS 4
PROPERTIES ("replication_allocation" = "tag.location.default: 1");

咱们来计算一下 2022 年 1 月 1 日这一天的均匀 Volume_BTC/Volume_USD 以及总的 Volume_BTC/Volume_USD:

mysql> select avg(Volume_BTC),avg(Volume_USD),sum(Volume_BTC),sum(Volume_USD) from btc where to_date(date)='2022-01-01';
+-------------------+--------------------------------------+-------------------+-----------------------------------------+
| avg(`Volume_BTC`) | avg(`Volume_USD`)                    | sum(`Volume_BTC`) | sum(`Volume_USD`)                       |
+-------------------+--------------------------------------+-------------------+-----------------------------------------+
|        0.51494486 | 24236.665942788256243957638888888888 |      741.52060313 | 34900798.957615088991299000000000000000 |
+-------------------+--------------------------------------+-------------------+-----------------------------------------+

通过 SQL 的执行后果能够看到,通过 DecimalV3,在 Volume_USD 这一列的均匀后果和总和上,实现了保留 30 位的小数。而旧的 Decimal 类型在这个例子中只能实现保留不超过 20 位。

性能比照

咱们采纳 TPC-H Benchmark 100G 来比照 DecimalV3 与老版本 Decimal 的执行速度、存储占用、内存占用等性能。

咱们在两个库别离对新版 DecimalV3 和老版本 Decimal 进行建表。建表实现如下:

tpch1 库为 DecimalV3

tpch2 库为老版本 Decimal

执行速度

采纳 TPC-H Benchmark 对 执行速度 进行测试:

SQL Q1

select /*+SET_VAR(exec_mem_limit=8589934592, parallel_fragment_exec_instance_num=16, enable_vectorized_engine=true, batch_size=4096, disable_join_reorder=false, enable_cost_based_join_reorder=false, enable_projection=false) */
    l_returnflag,
    l_linestatus,
    sum(l_quantity) as sum_qty,
    sum(l_extendedprice) as sum_base_price,
    sum(l_extendedprice * (1 - l_discount)) as sum_disc_price,
    sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge,
    avg(l_quantity) as avg_qty,
    avg(l_extendedprice) as avg_price,
    avg(l_discount) as avg_disc,
    count(*) as count_order
from
    lineitem
where
    l_shipdate <= date '1998-12-01' - interval '90' day
group by
    l_returnflag,
    l_linestatus
order by
    l_returnflag,
    l_linestatus;

tpch1 库(DecimalV3)的 SQL 执行后果为 6.38s

tpch2 库(老版本 Decimal)的 SQL 执行后果为 8.13s

SQL Q1 所查问的表是上述展现字段的表 Lineitem,咱们能够看到在 DecimalV3 的状况下,查问 速度较老版本有 27.4% 的晋升。

存储占用

tpch1 库(DecimalV3)的 Lineitem 表的存储占用为 18.475GB

tpch2 库(老版本 Decimal)的 Lineitem 表的存储占用为 20.893GB

能够看到在有四个字段由 Decimal 改为 DecimalV3 的状况下,存储占用有 13.1% 的升高。

内存占用

内存占用测试咱们同样应用 Lineitem 表,采纳本人改写的一条 SQL

select count(*) 
from 
(   select l_quantity,l_extendedprice,l_discount,l_tax 
    from lineitem 
    where l_shipdate < '1995-01-01' 
    group by l_quantity,l_extendedprice,l_discount,l_tax
)tmp;

下图的 Grafana 监控中能够看到执行测试前的 Doris 内存稳固为 12.2GB

别离在两个库执行上述 SQL

在 tpch1 库(DecimalV3)下执行,内存占用峰值为 26.6GB

内存回落失常后,在 tpch2 库(老版本 Decimal)下执行,内存占用峰值为 30.8GB

从上方三张图中能够看到,这条 SQL 在 DecimalV3 的状况下不仅内存占用升高了 15.8%,执行工夫也缩短了 10s。

总结

Apache Doris 1.2.1 版本推出的 DecimalV3 实现了更高的精度,更高的性能,更齐备的精度推演,使得 Doris 更加实用于金融财务、科学计算等有准确计算需要的利用场景,联合 Apache Doris 弱小的剖析计算性能,给相干用户及行业提供了更精确、欠缺的数据服务。

接下来,社区还将实现 JDBC 表面对 DecimalV3 类型的反对,JDBC Catalog 能够通过规范 JDBC 协定,连贯其余数据源,连贯后 Doris 会主动同步数据源下的 Database 和 Table 的元数据,以便快速访问这些内部数据。基于 JDBC 的通用性,联合 Apache Doris 的 高性能剖析能力,实现对各类数据库数据联邦查问的高精度计算。

本文作者:

钟永康,SelectDB 生态研发工程师

李文强,SelectDB 数据库内核研发工程师,Apache Doris Committer

退出移动版