如何减少SQLite内存消耗?

Ped*_*ves 15 c sqlite performance memory-consumption

我正在寻找在我的应用程序中减少SQLite3的内存消耗的方法.

在每次执行时,它都会创建具有以下模式的表:

(main TEXT NOT NULL PRIMARY KEY UNIQUE,count INTEGER DEFAULT 0)
Run Code Online (Sandbox Code Playgroud)

之后,数据库每秒充满50k操作.只写.

当一个项目已经存在时,它会使用更新查询更新"count"(我认为这称为UPSERT).这些是我的疑问:

INSERT OR IGNORE INTO table (main) VALUES (@SEQ);
UPDATE tables SET count=count+1 WHERE main = @SEQ;
Run Code Online (Sandbox Code Playgroud)

这样,每个事务有500万次操作,我可以非常快速地写入数据库.

我真的不关心这个问题的磁盘空间,但我有一个非常约束的RAM空间.因此,我不能浪费太多记忆.

使用sqlite3_user_memory()通知其执行期间内存消耗增长到近3GB.如果我通过sqlite3_soft_heap_limit64()将其限制为2GB,则当达到2GB时,数据库操作性能几乎降至零.

我不得不将缓存大小提高到1M(默认页面大小)以达到理想的性能.

我该怎么做才能减少内存消耗?

Pei*_*Zhu 11

似乎高内存消耗可能是由于太多操作集中在一个大事务中.尝试像每1M操作一样提交较小的事务可能会有所帮助.每个事务5M操作将消耗许多内存.

但是,我们会平衡操作速度和内存使用情况.

由于较小的交易没有帮助,PRAGMA shrink_memory可能是一种选择.

使用sqlite3_status()with SQLITE_STATUS_MEMORY_USED来跟踪动态内存分配,并找到该点.

  • 你的评论让我意识到我使用的是旧版本的sqlite3.我有3.7.9,它不支持PRAGMA shrink_memory或sqlite3_db_release_memory().感谢=)使用PRAGMA shrink_memory + sqlite3_db_memory_release(),我看到调用此函数时内存消耗下降得非常快.我认为这只是释放所有缓存和堆内存,因为插入性能也会下降,直到缓存再次被填充并且内存消耗更接近之前的状态.它并不完美,但这可能是一个有用的解决方案=) (2认同)

Ali*_*xel 7

我会:

  • 准备声明(如果你还没有这样做)
  • 降低每笔交易的 INSERT 数量(10 秒 = 500,000 听起来合适)
  • 使用PRAGMA locking_mode = EXCLUSIVE;,如果你能

另外,(我不确定您是否知道)PRAGMA cache_size以页为单位,而不是以 MB 为单位。确保在 asPRAGMA cache_size * PRAGMA page_size或 SQLite >= 3.7.10 中定义目标内存,您也可以这样做PRAGMA cache_size = -kibibytes;。将其设置为 1 M(illion) 将导致 1 或 2 GB。

我很好奇cache_size 在 INSERT 中有什么帮助...

如果PRAGMA temp_store = FILE;有所不同,您也可以尝试进行基准测试。

当然,只要您的数据库没有被写入:

  • PRAGMA shrink_memory;
  • VACUUM;

根据您对数据库的操作,这些也可能有所帮助:

  • PRAGMA auto_vacuum = 1|2;
  • PRAGMA secure_delete = ON;

我使用以下编译指示运行了一些测试:

busy_timeout=0;
cache_size=8192;
encoding="UTF-8";
foreign_keys=ON;
journal_mode=WAL;
legacy_file_format=OFF;
synchronous=NORMAL;
temp_store=MEMORY;
Run Code Online (Sandbox Code Playgroud)

测试#1:

INSERT OR IGNORE INTO test (time) VALUES (?);
UPDATE test SET count = count + 1 WHERE time = ?;
Run Code Online (Sandbox Code Playgroud)

每秒达到约 109k 更新的峰值。

测试#2:

REPLACE INTO test (time, count) VALUES
(?, coalesce((SELECT count FROM test WHERE time = ? LIMIT 1) + 1, 1));
Run Code Online (Sandbox Code Playgroud)

达到每秒约 12 万次更新的峰值。


我也尝试过PRAGMA temp_store = FILE;,更新速度下降了约 1-2k 每秒。


对于事务中的 7M 更新,journal_mode=WAL它比其他所有更新都慢。


我用 35,839,987 条记录填充了一个数据库,现在我的设置每批 65521 次更新需要近 4 秒 - 但是,它甚至没有达到 16 MB 的内存消耗。


好的,这是另一个:

INTEGER PRIMARY KEY 列上的索引(不要这样做)

当您使用 INTEGER PRIMARY KEY 创建列时,SQLite 使用此列作为(索引)表结构的键。这是此列上的隐藏索引(因为它未显示在 SQLite_Master 表中)。不需要在列上添加另一个索引,也永远不会使用。此外,它会减慢 INSERT、DELETE 和 UPDATE 操作的速度。

您似乎将 PK 定义为 NOT NULL + UNIQUE。PK 是唯一的。