如何使用系列数据减少大型 SQLite 数据库的大小

yau*_*nka 10 sqlite

我正在考虑将 SQLite 数据库用于处理大量数据系列的 C# 应用程序。数据目前位于多个 CSV 文件中,每个文件的大小最大为 20GB,格式如下:

2019.07.31 00:00:03.855,1.11568,1.11571,3,0

在迁移到 SQLite 时,我希望大大减小大小,但由于某种原因,我得到了一些不同的结果。

CSV 格式的示例字符串占用 44 个字节(43 个字符 + 新行)。据我了解的 SQLite 类型定义在 sqlite.org 中数据库中应该大致相同:23 字节日期时间文本 + 8*2 实数 + 1*2 整数 = 41 字节

dateTime 可以存储为 8 字节整数 (c# DateTime.Ticks),这应该将大小减少到每行约 26 个字节,几乎比 CSV 格式少两倍。

对于一个实验,我尝试导入 142,157 行数据。CSV 文件大小约为 6kk 字节,但生成的 DB 文件约为 5kk,几乎相同。压缩(在 DB Browser for SQLite 中可用)不会改变任何东西。

表架构是:

CREATE TABLE "Data" ( "dateTime" INTEGER, "value1" REAL, "value2" REAL, "value3" INTEGER, "value4" INTEGER )

示例行如下所示:

637001280038550000 1.11568 1.11571 3 0

是 SQLite 内部结构造成了如此大的开销吗?我怎样才能减小尺寸?

作为旁注,在 dateTime 字段上设置 Primary Key 会将示例数据文件的大小减少 0.3kk 字节,但添加唯一约束会增加 3kk 字节,使其总共约 8kk,这比 CSV 大得多。

更新

output from sqlite3_analyzer

w/t PK and contraints
Size of the file in bytes......................... 5242880
Bytes of user payload stored...................... 4349676     83.0%
Bytes of payload.................................. 4349852     83.0%
Bytes of metadata................................. 858115      16.4%

with PK
Size of the file in bytes......................... 4976640
Bytes of user payload stored...................... 3212420     64.5%
Bytes of payload.................................. 3212622     64.6%
Bytes of metadata................................. 1733838     34.8%

with PK and Unique constraint
Size of the file in bytes......................... 8134656
Bytes of user payload stored...................... 3212420     39.5%
Bytes of payload.................................. 3212622     64.6%
Bytes of metadata................................. 1733838     34.8%
*** Page counts for all tables and indices separately
TABLE............................................. 1214        61.1%
SQLITE_AUTOINDEX_TABLE_1.......................... 771         38.8%
Run Code Online (Sandbox Code Playgroud)

所以结果是内部结构占了 16%,PK 占了额外的 19%,但仍然减少了总大小,而独特的约束占了近 40%!

这个问题仍然有效。是否可以缩小数据库的大小,或者只能从中获得?

tan*_*ius 9

有一些方法。这里有两个似乎适合您的情况:

没有 ROWID 表

ROWID 是 SQLite 表默认具有的内部主键,每条记录占用 64 位。由于您正在讨论 datetime 列的 UNIQUE 约束,因此该列似乎是主键的一个很好的候选者(它总是附加一个唯一约束)。所以你可以创建一个没有 ROWID 的 SQLite 表,如下所示:

CREATE TABLE "Data" (
    "dateTime" INTEGER PRIMARY KEY,
    "value1" REAL, 
    "value2" REAL, 
    "value3" INTEGER, 
    "value4" INTEGER 
) WITHOUT ROWID;
Run Code Online (Sandbox Code Playgroud)

存储增量值

实际上,我从未在 SQLite 中尝试过这种方法,但是:通过仅存储与前一行的差异,可以更紧凑地存储某些数据。在这种情况下,如果日期时间值可以按顺序存储到数据库中,那么日期时间值非常适合。在这种情况下,您将使用三列:

  1. 按顺序编号的整数主键。
  2. 一个日期时间整数列,只为每(比如)1000 条记录填充。
  3. datetime_delta 列,用填充的日期时间列存储与最后一行的差异。

SQLite 将整数值单独存储为 2-9 个字节(包括一个存储模式字节),具体取决于它们的值。假设顺序主键平均为 4 个字节,增量值平均为 2.5 个字节,与当前的 9 字节整数相比,将节省 2.5 个字节。如果您的表中有多个可从增量存储中获益的列,则节省会更大,因为您只需要一次顺序主键。例如,如果value1……value4是随着时间的推移趋于增长和缩小的测量值,它们也是很好的候选者。

在主键值加载一行后n,您还必须检索主键值n DIV 1000(意味着整数除法)的行以获得增量值的基础。